Merge lp:~diegosarmentero/ubuntu-system-settings/click-updates into lp:ubuntu-system-settings

Proposed by Diego Sarmentero
Status: Merged
Approved by: Sebastien Bacher
Approved revision: 650
Merged at revision: 667
Proposed branch: lp:~diegosarmentero/ubuntu-system-settings/click-updates
Merge into: lp:ubuntu-system-settings
Diff against target: 1938 lines (+1323/-57)
28 files modified
debian/control (+4/-0)
plugins/system-update/CMakeLists.txt (+18/-3)
plugins/system-update/Configuration.qml (+1/-0)
plugins/system-update/PageComponent.qml (+89/-22)
plugins/system-update/download_tracker.cpp (+137/-0)
plugins/system-update/download_tracker.h (+87/-0)
plugins/system-update/network/network.cpp (+165/-0)
plugins/system-update/network/network.h (+64/-0)
plugins/system-update/plugin.cpp (+2/-0)
plugins/system-update/system_update.cpp (+5/-1)
plugins/system-update/system_update.h (+1/-0)
plugins/system-update/update.cpp (+23/-5)
plugins/system-update/update.h (+15/-0)
plugins/system-update/update_manager.cpp (+179/-18)
plugins/system-update/update_manager.h (+45/-0)
tests/autopilot/ubuntu_system_settings/tests/__init__.py (+2/-4)
tests/autopilot/ubuntu_system_settings/tests/test_system_updates.py (+72/-3)
tests/plugins/system-update/CMakeLists.txt (+18/-1)
tests/plugins/system-update/click.result (+26/-0)
tests/plugins/system-update/fakenetwork.cpp (+51/-0)
tests/plugins/system-update/fakenetwork.h (+49/-0)
tests/plugins/system-update/fakeprocess.cpp (+63/-0)
tests/plugins/system-update/fakeprocess.h (+48/-0)
tests/plugins/system-update/fakessoservice.cpp (+16/-0)
tests/plugins/system-update/fakessoservice.h (+27/-0)
tests/plugins/system-update/fakesystemupdate.cpp (+28/-0)
tests/plugins/system-update/fakesystemupdate.h (+58/-0)
tests/plugins/system-update/tst_updatemanager.cpp (+30/-0)
To merge this branch: bzr merge lp:~diegosarmentero/ubuntu-system-settings/click-updates
Reviewer Review Type Date Requested Status
Sebastien Bacher (community) Approve
PS Jenkins bot continuous-integration Approve
Iain Lane Needs Fixing
Review via email: mp+208567@code.launchpad.net

Commit message

- Add click updates support.

Description of the change

This branch adds click support to System Updates, now system updates is going to show the clicks that needs to be updated along with system updates in the same list.

You can IRL test this using the click updates mock service (Desktop):
https://launchpad.net/clickupdatemockservice
(bzr branch lp:clickupdatemockservice)

Just download that branch, and execute it with: python3 mock_click_server.py system-settings
(This requires that you build and install the code from this branch first)

About the code:
The Network files has been added to make requests to the server for:
- Get the new list of apps and versions for the apps i have locally
- Get the download url for an specific app
- Get the click token for the download url, to be able to sign it and access the server with valid credentials for download.

DownloadTracker:
This QML Plugin uses the ubuntu download manager client lib to trigger the downloads, monitor them and send back to the UI information about the progress, and execute the install command when the download ends.

The rest of the code, mostly in PageComponent.qml and update_manager.cpp/h has been modified to accomodate the changes needed to support both click and system updates.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Sebastien Bacher (seb128) wrote :

Thanks but, as pointed by the CI, that doesn't build, it fails on a missing "#include "fakedownloader.h"" ... where is distributed?

review: Needs Fixing
637. By Diego Sarmentero

fix mention of removed file

Revision history for this message
Sebastien Bacher (seb128) wrote :

Thanks, that builds fine and I was able to test, some comments from a first round of user testing (didn't review the changes yet, that's quite some code to review):

- could you made the downloads a bit slower, so it let time to interact with the UI/pause the download, etc

- the dropping letter is displayed as "500000kb", shouldn't it adapt its units and displays "0.5Gb" instead?

- if you start with some clicks update and a system update, you get a list of 2 clicks and no indication it's checking for the system image status, you should display a spinner while the checking is happening (maybe only displays the list when you get results from both system and click updates)

- clicking the "install 3 updates" triggers the update, clicks are done before the system one ... the button doesn't change though, that's a bit confusing: 2 installed, 1 downloading and it still states "Install 3 updates", could you check with design if we could do better?

- if you do press the "install 3 updates"/"pause all" button a few time you easily get dbus errors displayed over the "installed" for the already upgraded clicks

- once the download are done, if have a system update and click on "not now", you get back to the UI. If you click on the "download 3 updates" button then you get the "installing" screen

I'm going to try to review the code early next week (if others want to help reviewing that would be welcome)

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

> Thanks, that builds fine and I was able to test, some comments from a first
> round of user testing (didn't review the changes yet, that's quite some code
> to review):
>
> - could you made the downloads a bit slower, so it let time to interact with
> the UI/pause the download, etc
>
> - the dropping letter is displayed as "500000kb", shouldn't it adapt its units
> and displays "0.5Gb" instead?
>
> - if you start with some clicks update and a system update, you get a list of
> 2 clicks and no indication it's checking for the system image status, you
> should display a spinner while the checking is happening (maybe only displays
> the list when you get results from both system and click updates)
>
> - clicking the "install 3 updates" triggers the update, clicks are done before
> the system one ... the button doesn't change though, that's a bit confusing: 2
> installed, 1 downloading and it still states "Install 3 updates", could you
> check with design if we could do better?

Done

>
> - if you do press the "install 3 updates"/"pause all" button a few time you
> easily get dbus errors displayed over the "installed" for the already upgraded
> clicks

Couldn't reproduce this.

>
> - once the download are done, if have a system update and click on "not now",
> you get back to the UI. If you click on the "download 3 updates" button then
> you get the "installing" screen
>
>
> I'm going to try to review the code early next week (if others want to help
> reviewing that would be welcome)

Done

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

> Thanks, that builds fine and I was able to test, some comments from a first
> round of user testing (didn't review the changes yet, that's quite some code
> to review):
>
> - could you made the downloads a bit slower, so it let time to interact with
> the UI/pause the download, etc

You can do the download slower because we are using all the real services, but i'll try to add a bigger file in the mock service

>
> - the dropping letter is displayed as "500000kb", shouldn't it adapt its units
> and displays "0.5Gb" instead?
>
> - if you start with some clicks update and a system update, you get a list of
> 2 clicks and no indication it's checking for the system image status, you
> should display a spinner while the checking is happening (maybe only displays
> the list when you get results from both system and click updates)
>
> - clicking the "install 3 updates" triggers the update, clicks are done before
> the system one ... the button doesn't change though, that's a bit confusing: 2
> installed, 1 downloading and it still states "Install 3 updates", could you
> check with design if we could do better?
>
> - if you do press the "install 3 updates"/"pause all" button a few time you
> easily get dbus errors displayed over the "installed" for the already upgraded
> clicks
>
> - once the download are done, if have a system update and click on "not now",
> you get back to the UI. If you click on the "download 3 updates" button then
> you get the "installing" screen
>
>
> I'm going to try to review the code early next week (if others want to help
> reviewing that would be welcome)

638. By Diego Sarmentero

fixing branch as requested

639. By Diego Sarmentero

adding object names

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
640. By Diego Sarmentero

[ Sebastien Bacher ]
* license: use Label widget instead of Text, it looks more integrated
  (LP: #1286262)
* language: don't display an chevron for the language item, the list
  displayed is a sheet widget and not a page stack one. (lp: #1289287)
  (LP: #1289287)
[ Iain Lane ]
* background selector: lock the toolbar open, per the design (LP:
  #1288361)
* Don't crash if the migration script can't set values (LP: #1288833)
[ Michael Terry ]
* Tweak some of the wording of the wizard, and start the language
  combo expanded
[ CI bot ]
* Resync trunk
[ Omer Akram ]
* autopilot tests code: fix pep8 and pyflakes warnings. add tests to
  make sure we don't regress on pep8/pyflakes .
[ Didier Roche ]
* switch to suru icon theme
[ CI bot ]
* Resync trunk
[ Iain Lane ]
* Also store the ringtone, messages and silent mode settings in
  AccountsService. (LP: #1265528)
[ Michael Terry ]
* Add disabled skeleton of a "Welcome to Ubuntu" wizard for first-time
  boot. Will be enabled when it is more complete.
[ CI bot ]
* Resync trunk
[ Andrea Cimitan ]
* Add disabled skeleton of a "Welcome to Ubuntu" wizard for first-time
  boot. Will be enabled when it is more complete.
[ Iain Lane ]
* Add a 'downloading' parameter to updateAvailableFound so that the UI
  can adjust itself. We need to know if a download is already in
  progress when we enter the update screen to display 'pause' or
  'install' as appropriate.
[ Sebastien Bacher ]
* Workarount qt bug that could lead to non working scrolling
[ Iain Lane ]
* Re-enable updates on the main screen
[ Diego Sarmentero ]
* Avoid conflicts between concurrent calls to checkForUpdates ( (LP:
  #1279006)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
641. By Diego Sarmentero

fixed signal

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
642. By Diego Sarmentero

fixing pep8

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
643. By Diego Sarmentero

fixing lint

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
644. By Diego Sarmentero

adding autopilot tests

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
645. By Diego Sarmentero

autopilot tests complete

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

This failures seems to be jenkins related, not an issue with the branch.

646. By Diego Sarmentero

avoid checking for real updates during tests

Revision history for this message
Sebastien Bacher (seb128) wrote :

Thanks for the work, that looks good!

Some small issues:

* the CI issue is real (happen on touch devices as well), Omer suggested that fix, http://paste.ubuntu.com/7120773/, could you apply it?

* the install tests seem to fail if you have outdated click installed on the system, could you ignore those as you did for the system updates?

* I managed to land in a case where the button at the top was hidden but available updates were still in the list, is that supposed to happen?

I think I ended up there by doing
- open the panel
- the list had 3 clicks to update
- click on one to start download
- use the button at the top to pause/restart downloads
- pause some of the individual downloads

the install finished for the unpaused one and the button vanished

* if you enter the panel and have no credential, click on the link to add an UbuntuOne account, then go back, the label still state that there is an auth issue and there is no way to tell it to refresh

* there is quite some code there, I didn't do a detailled review but from an easy reading things seem to be good!

review: Needs Fixing
Revision history for this message
Sebastien Bacher (seb128) wrote :

oh, another small one, since the "click" binary is used by the new code debian/control should probably have a depends or recommends on "click"

647. By Diego Sarmentero

improves in tests

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Sebastien Bacher (seb128) wrote :

Thanks, the CI is still slightly unhappy though, I'm going to test that tomorrow

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Iain Lane (laney) wrote :

Cheers, looks mostly good*, just some small-ish comments.

119 + function open_online_accounts() {
120 + Qt.openUrlExternally("settings:///system/online-accounts");
121 + }

Don't you want to push the page component there? Although this panel does
something weird so doesn't that work?

342 + * Copyright (C) 2013 - Canonical Ltd.

2014 (& other files)

1001 + // PROCESS
1002 + QObject::connect(&(m_process), SIGNAL(finished(int)),
1003 + this, SLOT(processOutput()));

remove &(parens) please

I think you ought to be able to use the new libclick instead of executing the
process directly. cjwatson was working on converting the similar use in the
about panel - perhaps he could share his code.

* I couldn't test it though - the mock doesn't work on the phone for me and on my desktop I just get 'software up to date' and this on console

2014-03-20 12:38:20,293 - CRITICAL - ../../../../lib/SignOn/connection-manager.cpp 106 setupSocketConnection p2p error: QDBusError("org.freedesktop.DBus.Error.FileNotFound", "Failed to connect to socket /run/user/1000/signond/socket: No such file or directory") 1

review: Needs Fixing
Revision history for this message
Alberto Mardegan (mardy) wrote :

On 03/20/2014 02:48 PM, Iain Lane wrote:
> * I couldn't test it though - the mock doesn't work on the phone for me and on my desktop I just get 'software up to date' and this on console
>
> 2014-03-20 12:38:20,293 - CRITICAL - ../../../../lib/SignOn/connection-manager.cpp 106 setupSocketConnection p2p error: QDBusError("org.freedesktop.DBus.Error.FileNotFound", "Failed to connect to socket /run/user/1000/signond/socket: No such file or directory") 1

This is harmless, I need to lower it to a simple DEBUG.

648. By Diego Sarmentero

improves in tests and adding click dependency

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

> oh, another small one, since the "click" binary is used by the new code
> debian/control should probably have a depends or recommends on "click"

Done

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

> Thanks for the work, that looks good!
>
> Some small issues:
>
> * the CI issue is real (happen on touch devices as well), Omer suggested that
> fix, http://paste.ubuntu.com/7120773/, could you apply it?

Done

>
> * the install tests seem to fail if you have outdated click installed on the
> system, could you ignore those as you did for the system updates?

Done

>
> * I managed to land in a case where the button at the top was hidden but
> available updates were still in the list, is that supposed to happen?
>
> I think I ended up there by doing
> - open the panel
> - the list had 3 clicks to update
> - click on one to start download
> - use the button at the top to pause/restart downloads
> - pause some of the individual downloads
>
> the install finished for the unpaused one and the button vanished

Could not reproduce this.

>
> * if you enter the panel and have no credential, click on the link to add an
> UbuntuOne account, then go back, the label still state that there is an auth
> issue and there is no way to tell it to refresh

It seems System Settings is not refreshing the pages on going back, I assume this, because it can not be reproduced on desktop, on desktop you need to go back to online accounts, and then go back to updates and that works.

>
> * there is quite some code there, I didn't do a detailled review but from an
> easy reading things seem to be good!

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Sebastien Bacher (seb128) wrote :

The panel seems mostly fine, we should be able to land it this week but we need to resolve first the remaining failing tests (CI is still red). Diego, could you have a look to that and to the review comments from Laney?

review: Needs Fixing
649. By Diego Sarmentero

apply fixes requested

650. By Diego Sarmentero

fix autopilot tests

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

> The panel seems mostly fine, we should be able to land it this week but we
> need to resolve first the remaining failing tests (CI is still red). Diego,
> could you have a look to that and to the review comments from Laney?

Done

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

> Cheers, looks mostly good*, just some small-ish comments.
>
> 119 + function open_online_accounts() {
> 120 + Qt.openUrlExternally("settings:///system/online-accounts");
> 121 + }
>
> Don't you want to push the page component there? Although this panel does
> something weird so doesn't that work?

Push wasn't working from the system updates page.

>
> 342 + * Copyright (C) 2013 - Canonical Ltd.
>
> 2014 (& other files)

Done

>
> 1001 + // PROCESS
> 1002 + QObject::connect(&(m_process), SIGNAL(finished(int)),
> 1003 + this, SLOT(processOutput()));
>
> remove &(parens) please

Done

>
> I think you ought to be able to use the new libclick instead of executing the
> process directly. cjwatson was working on converting the similar use in the
> about panel - perhaps he could share his code.

I prefer to change this in a future branch, because changing it right now will involve to modify some of the tests and keep delaying this branch with more changes.

>
> * I couldn't test it though - the mock doesn't work on the phone for me and on
> my desktop I just get 'software up to date' and this on console
>
> 2014-03-20 12:38:20,293 - CRITICAL - ../../../../lib/SignOn/connection-
> manager.cpp 106 setupSocketConnection p2p error:
> QDBusError("org.freedesktop.DBus.Error.FileNotFound", "Failed to connect to
> socket /run/user/1000/signond/socket: No such file or directory") 1

Revision history for this message
Sebastien Bacher (seb128) wrote :

Hum, now 6 tests fails on the phone (the ubuntu_system_settings.tests.test_system_updates ones) with

Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/ubuntu_system_settings/tests/test_system_updates.py", line 30, in setUp
super(SystemUpdatesTestCases, self).setUp()
File "/usr/lib/python2.7/dist-packages/ubuntu_system_settings/tests/__init__.py", line 244, in setUp
self.pointer.scroll_to_and_click(button)
AttributeError: 'Pointer' object has no attribute 'scroll_to_and_click'

review: Needs Fixing
Revision history for this message
Sebastien Bacher (seb128) wrote :

sorry, ignore the previous comment, the system upgrade I did overwrote the local install of the new version...

Looks good to me, let's get in a silo and try to land it

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2014-03-17 22:12:26 +0000
3+++ debian/control 2014-03-27 13:16:48 +0000
4@@ -22,6 +22,9 @@
5 qtbase5-dev,
6 qtdeclarative5-dev,
7 libapt-pkg-dev,
8+ libubuntuoneauth-2.0-dev,
9+ libubuntu-download-manager-client-dev,
10+ libubuntu-download-manager-common-dev,
11 xvfb,
12 cmake,
13 pep8,
14@@ -55,6 +58,7 @@
15 whoopsie-preferences (>= 0.9),
16 libsystemsettings1 (= ${binary:Version}),
17 system-image-dbus (>= 0.9),
18+ click,
19 ubuntu-keyboard-data (>= 0.99.trunk.phablet2+13.10.20131001),
20 # for the session-migration script
21 python3,
22
23=== modified file 'plugins/system-update/CMakeLists.txt'
24--- plugins/system-update/CMakeLists.txt 2014-02-06 15:47:12 +0000
25+++ plugins/system-update/CMakeLists.txt 2014-03-27 13:16:48 +0000
26@@ -1,10 +1,25 @@
27 set(QML_SOURCES PageComponent.qml Configuration.qml)
28+SET (CMAKE_AUTOMOC ON)
29
30-add_library(UbuntuUpdatePanel MODULE plugin.h system_update.h update_manager.h update.h plugin.cpp system_update.cpp update_manager.cpp update.cpp
31+add_library(UbuntuUpdatePanel MODULE plugin.h system_update.h update_manager.h update.h
32+network/network.h download_tracker.h plugin.cpp system_update.cpp update_manager.cpp
33+update.cpp network/network.cpp download_tracker.cpp
34 ${QML_SOURCES})
35-qt5_use_modules(UbuntuUpdatePanel Qml Quick DBus)
36+
37+qt5_use_modules(UbuntuUpdatePanel Qml Quick Network DBus)
38 include_directories(/usr/include/apt-pkg/)
39-target_link_libraries(UbuntuUpdatePanel apt-pkg)
40+
41+find_package (PkgConfig REQUIRED)
42+pkg_check_modules(UBUNTU_DOWNLOAD_MANAGER_CLIENT REQUIRED ubuntu-download-manager-client)
43+pkg_check_modules(UBUNTU_DOWNLOAD_MANAGER_COMMON REQUIRED ubuntu-download-manager-common)
44+pkg_check_modules(UBUNTUONEAUTH REQUIRED ubuntuoneauth-2.0)
45+add_definitions(${UBUNTUONEAUTH_CFLAGS} ${UBUNTUONEAUTH_CFLAGS_OTHER})
46+target_link_libraries(UbuntuUpdatePanel
47+ apt-pkg
48+ ${UBUNTUONEAUTH_LDFLAGS}
49+ ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS}
50+ ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS}
51+)
52
53 set(PLUG_DIR ${PLUGIN_PRIVATE_MODULE_DIR}/Ubuntu/SystemSettings/Update)
54 install(TARGETS UbuntuUpdatePanel DESTINATION ${PLUG_DIR})
55
56=== modified file 'plugins/system-update/Configuration.qml'
57--- plugins/system-update/Configuration.qml 2014-01-24 15:17:32 +0000
58+++ plugins/system-update/Configuration.qml 2014-03-27 13:16:48 +0000
59@@ -25,6 +25,7 @@
60
61 ItemPage {
62 id: root
63+ objectName: "configurationPage"
64 property UpdateManager updateManager
65 title: i18n.tr("Auto download")
66
67
68=== modified file 'plugins/system-update/PageComponent.qml'
69--- plugins/system-update/PageComponent.qml 2014-02-26 11:39:47 +0000
70+++ plugins/system-update/PageComponent.qml 2014-03-27 13:16:48 +0000
71@@ -35,6 +35,7 @@
72 title: i18n.tr("Updates")
73
74 property bool installAll: false
75+ property int updatesAvailable: 0
76
77 DeviceInfo {
78 id: deviceInfo
79@@ -78,16 +79,14 @@
80 PropertyChanges { target: notification; visible: false}
81 PropertyChanges { target: installAllButton; visible: false}
82 PropertyChanges { target: checkForUpdatesArea; visible: true}
83- PropertyChanges { target: notification; visible: false}
84- PropertyChanges { target: updatedNotification; visible: false}
85+ PropertyChanges { target: updateNotification; visible: false}
86 },
87 State {
88 name: "NOUPDATES"
89- PropertyChanges { target: updatedNotification; text: i18n.tr("Software is up to date")}
90- PropertyChanges { target: updatedNotification; visible: true}
91+ PropertyChanges { target: updateNotification; text: i18n.tr("Software is up to date")}
92+ PropertyChanges { target: updateNotification; visible: true}
93 PropertyChanges { target: updateList; visible: false}
94 PropertyChanges { target: installAllButton; visible: false}
95- PropertyChanges { target: checkForUpdatesArea; visible: false}
96 },
97 State {
98 name: "SYSTEMUPDATEFAILED"
99@@ -98,18 +97,31 @@
100 PropertyChanges { target: installingImageUpdate; visible: false}
101 PropertyChanges { target: installAllButton; visible: false}
102 PropertyChanges { target: checkForUpdatesArea; visible: false}
103- PropertyChanges { target: updatedNotification; visible: false}
104+ PropertyChanges { target: updateNotification; visible: false}
105 },
106 State {
107 name: "UPDATE"
108 PropertyChanges { target: notification; visible: false}
109 PropertyChanges { target: updateList; visible: true}
110 PropertyChanges { target: installAllButton; visible: true}
111- PropertyChanges { target: checkForUpdatesArea; visible: false}
112- PropertyChanges { target: updatedNotification; visible: false}
113+ PropertyChanges { target: updateNotification; visible: false}
114+ },
115+ State {
116+ name: "NOCREDENTIALS"
117+ PropertyChanges { target: notification; text: i18n.tr("Please log into your Ubuntu One account.")}
118+ PropertyChanges { target: notification; onClicked: root.open_online_accounts() }
119+ PropertyChanges { target: notification; progression: true}
120+ PropertyChanges { target: notification; visible: true}
121+ PropertyChanges { target: updateNotification; text: i18n.tr("Credentials not found")}
122+ PropertyChanges { target: updateNotification; visible: true}
123+ PropertyChanges { target: installAllButton; visible: false}
124 }
125 ]
126
127+ function open_online_accounts() {
128+ Qt.openUrlExternally("settings:///system/online-accounts");
129+ }
130+
131 UpdateManager {
132 id: updateManager
133 objectName: "updateManager"
134@@ -121,6 +133,7 @@
135
136 onUpdateAvailableFound: {
137 if (updateManager.model.length > 0) {
138+ root.updatesAvailable = updateManager.model.length;
139 root.state = "UPDATE";
140 root.installAll = downloading
141 } else {
142@@ -129,10 +142,21 @@
143 }
144
145 onUpdatesNotFound: {
146- root.state = "NOUPDATES";
147+ if (root.state != "NOCREDENTIALS") {
148+ root.state = "NOUPDATES";
149+ }
150+ }
151+
152+ onCheckFinished: {
153+ checkForUpdatesArea.visible = false;
154+ }
155+
156+ onCredentialsNotFound: {
157+ root.state = "NOCREDENTIALS";
158 }
159
160 onSystemUpdateDownloaded: {
161+ root.updatesAvailable -= 1;
162 PopupUtils.open(dialogInstallComponent);
163 }
164
165@@ -197,7 +221,7 @@
166 id: installAllButton
167 objectName: "installAllButton"
168
169- property string primaryText: i18n.tr("Install %1 update", "Install %1 updates", updateManager.model.length).arg(updateManager.model.length)
170+ property string primaryText: i18n.tr("Install %1 update", "Install %1 updates", root.updatesAvailable).arg(root.updatesAvailable)
171 property string secondaryText: i18n.tr("Pause All")
172 text: root.installAll ? secondaryText : primaryText
173 anchors {
174@@ -217,11 +241,15 @@
175 updateList.currentIndex = i;
176 var item = updateList.currentItem;
177 var modelItem = updateManager.model[i];
178- if (modelItem.updateState == !root.installAll) {
179+ if (modelItem.updateState != root.installAll && !modelItem.updateReady) {
180 item.actionButton.clicked();
181 }
182 }
183 }
184+ opacity: root.updatesAvailable > 0 ? 1 : 0
185+
186+ Behavior on opacity { PropertyAnimation { duration: UbuntuAnimation.SlowDuration
187+ easing: UbuntuAnimation.StandardEasing } }
188 }
189
190 ListView {
191@@ -290,16 +318,27 @@
192 updateManager.applySystemUpdate();
193 installingImageUpdate.visible = true;
194 } else if (modelData.updateState) {
195- updateManager.pauseDownload(modelData.packageName);
196+ if (modelData.systemUpdate) {
197+ updateManager.pauseDownload(modelData.packageName);
198+ } else {
199+ modelData.updateState = false;
200+ tracker.pause();
201+ }
202 } else {
203- modelData.selected = true;
204- updateManager.startDownload(modelData.packageName);
205+ if (!modelData.selected || modelData.systemUpdate) {
206+ modelData.selected = true;
207+ updateManager.startDownload(modelData.packageName);
208+ } else {
209+ modelData.updateState = true;
210+ tracker.resume();
211+ }
212 }
213 }
214 }
215
216 Label {
217 id: labelSize
218+ objectName: "labelSize"
219 text: convert_bytes_to_size(modelData.binaryFilesize)
220 anchors.bottom: labelVersion.bottom
221 anchors.right: parent.right
222@@ -309,20 +348,23 @@
223
224 Label {
225 id: labelTitle
226+ objectName: "labelTitle"
227 anchors {
228 top: parent.top
229 left: parent.left
230- right: buttonAppUpdate.right
231+ right: buttonAppUpdate.left
232 topMargin: units.gu(1)
233+ rightMargin: units.gu(1)
234 }
235 height: units.gu(3)
236 text: modelData.title
237 font.bold: true
238- elide: Text.ElideRight
239+ elide: buttonAppUpdate.visible ? Text.ElideRight : Text.ElideNone
240 }
241
242 Label {
243 id: labelUpdateStatus
244+ objectName: "labelUpdateStatus"
245 text: i18n.tr("Installing")
246 anchors.top: labelTitle.bottom
247 anchors.left: parent.left
248@@ -342,20 +384,41 @@
249 anchors.topMargin: units.gu(1)
250 anchors.right: parent.right
251 opacity: modelData.selected ? 1 : 0
252- value: modelData.downloadProgress
253+ value: modelData.systemUpdate ? modelData.downloadProgress : tracker.progress
254 minimumValue: 0
255 maximumValue: 100
256
257+ DownloadTracker {
258+ id: tracker
259+ objectName: "tracker"
260+ packageName: modelData.packageName
261+ clickToken: modelData.clickToken
262+ download: modelData.downloadUrl
263+
264+ onFinished: {
265+ progress.visible = false;
266+ buttonAppUpdate.visible = false;
267+ textArea.message = i18n.tr("Installed");
268+ root.updatesAvailable -= 1;
269+ }
270+
271+ onErrorFound: {
272+ modelData.updateState = false;
273+ textArea.message = error;
274+ }
275+ }
276+
277 Behavior on opacity { PropertyAnimation { duration: UbuntuAnimation.SleepyDuration } }
278 }
279
280 Label {
281 id: labelVersion
282+ objectName: "labelVersion"
283 anchors {
284 left: parent.left
285 right: buttonAppUpdate.right
286- top: progress.bottom
287- topMargin: units.gu(1)
288+ top: (!progress.visible || progress.opacity == 0) ? labelTitle.bottom : progress.bottom
289+ topMargin: (!progress.visible || progress.opacity == 0) ? 0 : units.gu(1)
290 bottom: parent.bottom
291 bottomMargin: units.gu(1)
292 }
293@@ -369,12 +432,14 @@
294
295 ListItem.Standard {
296 id: notification
297+ objectName: "notification"
298 visible: false
299 anchors.bottom: configuration.top
300 }
301
302 ListItem.SingleValue {
303 id: configuration
304+ objectName: "configuration"
305 anchors.bottom: parent.bottom
306 text: i18n.tr("Auto download")
307 value: {
308@@ -390,7 +455,8 @@
309 }
310
311 Rectangle {
312- id: updatedNotification
313+ id: updateNotification
314+ objectName: "updateNotification"
315 anchors {
316 left: parent.left
317 right: parent.right
318@@ -408,7 +474,7 @@
319 anchors.centerIn: parent
320
321 Label {
322- text: updatedNotification.text
323+ text: updateNotification.text
324 anchors.horizontalCenter: parent.horizontalCenter
325 fontSize: "large"
326 }
327@@ -417,6 +483,7 @@
328
329 Rectangle {
330 id: installingImageUpdate
331+ objectName: "installingImageUpdate"
332 anchors.fill: parent
333 visible: false
334
335@@ -454,7 +521,7 @@
336 result = bytes + i18n.tr(" bytes");
337 } else if (bytes < SIZE_IN_MIB) {
338 size = (bytes / SIZE_IN_KIB).toFixed(1);
339- result = bytes + i18n.tr(" KiB");
340+ result = size + i18n.tr(" KiB");
341 } else if (bytes < SIZE_IN_GIB) {
342 size = (bytes / SIZE_IN_MIB).toFixed(1);
343 result = size + i18n.tr(" MiB");
344
345=== added file 'plugins/system-update/download_tracker.cpp'
346--- plugins/system-update/download_tracker.cpp 1970-01-01 00:00:00 +0000
347+++ plugins/system-update/download_tracker.cpp 2014-03-27 13:16:48 +0000
348@@ -0,0 +1,137 @@
349+/*
350+ * Copyright (C) 2014 - Canonical Ltd.
351+ *
352+ * This program is free software: you can redistribute it and/or modify it
353+ * under the terms of the GNU Lesser General Public License, as
354+ * published by the Free Software Foundation; either version 2.1 or 3.0
355+ * of the License.
356+ *
357+ * This program is distributed in the hope that it will be useful, but
358+ * WITHOUT ANY WARRANTY; without even the implied warranties of
359+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
360+ * PURPOSE. See the applicable version of the GNU Lesser General Public
361+ * License for more details.
362+ *
363+ * You should have received a copy of both the GNU Lesser General Public
364+ * License along with this program. If not, see <http://www.gnu.org/licenses/>
365+ *
366+ * Authored by: Diego Sarmentero <diego.sarmentero@canonical.com>
367+ */
368+
369+#include "download_tracker.h"
370+#include "network/network.h"
371+#include <ubuntu/download_manager/download_struct.h>
372+#include <ubuntu/download_manager/error.h>
373+#include <QProcessEnvironment>
374+
375+#define DOWNLOAD_COMMAND "post-download-command"
376+#define APP_ID "app_id"
377+#define PKCON_COMMAND "pkcon"
378+
379+namespace UpdatePlugin {
380+
381+DownloadTracker::DownloadTracker(QObject *parent) :
382+ QObject(parent),
383+ m_clickToken(""),
384+ m_downloadUrl(""),
385+ m_download(nullptr),
386+ m_manager(nullptr),
387+ m_progress(0)
388+{
389+}
390+
391+void DownloadTracker::setDownload(const QString& url)
392+{
393+ if (url != "") {
394+ m_downloadUrl = url;
395+ startService();
396+ }
397+}
398+
399+void DownloadTracker::setClickToken(const QString& token)
400+{
401+ if (token != "") {
402+ m_clickToken = token;
403+ startService();
404+ }
405+}
406+
407+void DownloadTracker::setPackageName(const QString& package)
408+{
409+ if (package != "") {
410+ m_packageName = package;
411+ startService();
412+ }
413+}
414+
415+void DownloadTracker::startService()
416+{
417+ if (!m_clickToken.isEmpty() && !m_downloadUrl.isEmpty() && !m_packageName.isEmpty()) {
418+ if (m_manager == nullptr) {
419+ m_manager = Manager::createSessionManager("", this);
420+
421+ QObject::connect(m_manager, SIGNAL(downloadCreated(Download*)),
422+ this, SLOT(bindDownload(Download*)));
423+ }
424+ QVariantMap vmap;
425+ QStringList args;
426+ QString command = getPkconCommand();
427+ args << command << "-p" << "install-local" << "$file";
428+ vmap[DOWNLOAD_COMMAND] = args;
429+ vmap[APP_ID] = m_packageName;
430+ StringMap map;
431+ map[X_CLICK_TOKEN] = m_clickToken;
432+ DownloadStruct dstruct = DownloadStruct(m_downloadUrl, vmap, map);
433+ m_manager->createDownload(dstruct);
434+ }
435+}
436+
437+void DownloadTracker::bindDownload(Download* download)
438+{
439+ m_download = download;
440+ connect(m_download, SIGNAL(finished(const QString &)), this,
441+ SIGNAL(finished(const QString &)));
442+ connect(m_download, SIGNAL(error(Error*)), this,
443+ SLOT(registerError(Error*)));
444+ connect(m_download, SIGNAL(progress(qulonglong, qulonglong)), this,
445+ SLOT(setProgress(qulonglong, qulonglong)));
446+
447+ m_download->start();
448+}
449+
450+void DownloadTracker::registerError(Error* error)
451+{
452+ Q_EMIT errorFound(error->errorString());
453+}
454+
455+void DownloadTracker::pause()
456+{
457+ if (m_download != nullptr) {
458+ m_download->pause();
459+ }
460+}
461+
462+void DownloadTracker::resume()
463+{
464+ if (m_download != nullptr) {
465+ m_download->resume();
466+ }
467+}
468+
469+QString DownloadTracker::getPkconCommand()
470+{
471+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
472+ QString command = environment.value("PKCON_COMMAND", QString(PKCON_COMMAND));
473+ return command;
474+}
475+
476+void DownloadTracker::setProgress(qulonglong received, qulonglong total)
477+{
478+ if (total > 0) {
479+ qulonglong result = (received * 100);
480+ m_progress = static_cast<int>(result / total);
481+ emit progressChanged();
482+ }
483+}
484+
485+}
486
487=== added file 'plugins/system-update/download_tracker.h'
488--- plugins/system-update/download_tracker.h 1970-01-01 00:00:00 +0000
489+++ plugins/system-update/download_tracker.h 2014-03-27 13:16:48 +0000
490@@ -0,0 +1,87 @@
491+/*
492+ * Copyright (C) 2014 - Canonical Ltd.
493+ *
494+ * This program is free software: you can redistribute it and/or modify it
495+ * under the terms of the GNU Lesser General Public License, as
496+ * published by the Free Software Foundation; either version 2.1 or 3.0
497+ * of the License.
498+ *
499+ * This program is distributed in the hope that it will be useful, but
500+ * WITHOUT ANY WARRANTY; without even the implied warranties of
501+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
502+ * PURPOSE. See the applicable version of the GNU Lesser General Public
503+ * License for more details.
504+ *
505+ * You should have received a copy of both the GNU Lesser General Public
506+ * License along with this program. If not, see <http://www.gnu.org/licenses/>
507+ *
508+ * Authored by: Diego Sarmentero <diego.sarmentero@canonical.com>
509+ */
510+
511+
512+#ifndef DOWNLOADTRACKER_H
513+#define DOWNLOADTRACKER_H
514+
515+#include <QObject>
516+#include <QtQml>
517+#include <QList>
518+#include <QString>
519+#include <ubuntu/download_manager/download.h>
520+#include <ubuntu/download_manager/manager.h>
521+
522+using Ubuntu::DownloadManager::Download;
523+using Ubuntu::DownloadManager::Manager;
524+
525+namespace Ubuntu { namespace DownloadManager { class Error; } }
526+
527+namespace UpdatePlugin {
528+
529+class DownloadTracker : public QObject
530+{
531+ Q_OBJECT
532+ Q_PROPERTY(QString clickToken READ clickToken WRITE setClickToken)
533+ Q_PROPERTY(QString download READ download WRITE setDownload)
534+ Q_PROPERTY(QString packageName READ packageName WRITE setPackageName)
535+ Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
536+
537+public:
538+ explicit DownloadTracker(QObject *parent = 0);
539+ ~DownloadTracker() {}
540+
541+ Q_INVOKABLE void pause();
542+ Q_INVOKABLE void resume();
543+
544+ QString download() { return m_downloadUrl; }
545+ QString clickToken() { return m_clickToken; }
546+ QString packageName() { return m_packageName; }
547+ void setDownload(const QString& url);
548+ void setClickToken(const QString& token);
549+ void setPackageName(const QString& package);
550+ int progress() { return m_progress; }
551+
552+public Q_SLOTS:
553+ void bindDownload(Download* download);
554+ void setProgress(qulonglong received, qulonglong total);
555+ void registerError(Ubuntu::DownloadManager::Error* error);
556+
557+Q_SIGNALS:
558+ void error(const QString &errorMessage);
559+ void finished(const QString &path);
560+ void progressChanged();
561+ void errorFound(const QString &error);
562+
563+private:
564+ QString m_clickToken;
565+ QString m_downloadUrl;
566+ QString m_packageName;
567+ Download* m_download;
568+ Manager* m_manager;
569+ int m_progress;
570+
571+ void startService();
572+ QString getPkconCommand();
573+};
574+
575+}
576+
577+#endif // DOWNLOADTRACKER_H
578
579=== added directory 'plugins/system-update/network'
580=== added file 'plugins/system-update/network/network.cpp'
581--- plugins/system-update/network/network.cpp 1970-01-01 00:00:00 +0000
582+++ plugins/system-update/network/network.cpp 2014-03-27 13:16:48 +0000
583@@ -0,0 +1,165 @@
584+/*
585+ * Copyright 2013 Canonical Ltd.
586+ *
587+ * This library is free software; you can redistribute it and/or
588+ * modify it under the terms of version 3 of the GNU Lesser General Public
589+ * License as published by the Free Software Foundation.
590+ *
591+ * This program is distributed in the hope that it will be useful,
592+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
593+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
594+ * General Public License for more details.
595+ *
596+ * You should have received a copy of the GNU Lesser General Public
597+ * License along with this library; if not, write to the
598+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
599+ * Boston, MA 02110-1301, USA.
600+ */
601+
602+#include "network.h"
603+#include <QJsonDocument>
604+#include <QJsonObject>
605+#include <QJsonArray>
606+#include <QJsonValue>
607+#include <QByteArray>
608+#include <QUrl>
609+#include <QProcessEnvironment>
610+
611+#define URL_APPS "https://myapps.developer.ubuntu.com/dev/api/click-metadata/"
612+#define URL_PACKAGE "https://search.apps.ubuntu.com/api/v1/package/"
613+
614+namespace UpdatePlugin {
615+
616+Network::Network(QObject *parent) :
617+ QObject(parent),
618+ m_nam(this)
619+{
620+ m_request.setHeader(QNetworkRequest::ContentTypeHeader,
621+ "application/json");
622+
623+ QObject::connect(&m_nam, SIGNAL(finished(QNetworkReply*)),
624+ this, SLOT(onReply(QNetworkReply*)));
625+}
626+
627+void Network::checkForNewVersions(QHash<QString, Update*> &apps)
628+{
629+ m_apps = apps;
630+
631+ QJsonObject serializer;
632+ QJsonArray array;
633+ foreach(QString id, m_apps.keys()) {
634+ array.append(QJsonValue(m_apps.value(id)->getPackageName()));
635+ }
636+
637+ serializer.insert("name", array);
638+ QJsonDocument doc(serializer);
639+
640+ QByteArray content = doc.toJson();
641+
642+ QString urlApps = getUrlApps();
643+ m_request.setUrl(QUrl(urlApps));
644+ m_nam.post(m_request, content);
645+}
646+
647+
648+
649+QString Network::getUrlApps()
650+{
651+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
652+ QString command = environment.value("URL_APPS", QString(URL_APPS));
653+ return command;
654+}
655+
656+QString Network::getUrlPackage()
657+{
658+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
659+ QString command = environment.value("URL_PACKAGE", QString(URL_PACKAGE));
660+ return command;
661+}
662+
663+void Network::onReply(QNetworkReply *reply)
664+{
665+ QVariant statusAttr = reply->attribute(
666+ QNetworkRequest::HttpStatusCodeAttribute);
667+ if (!statusAttr.isValid()) {
668+ Q_EMIT errorOccurred();
669+ return;
670+ }
671+
672+ int httpStatus = statusAttr.toInt();
673+
674+ if (httpStatus == 200 || httpStatus == 201) {
675+ if (reply->hasRawHeader(X_CLICK_TOKEN)) {
676+ Update* app = qobject_cast<Update*>(
677+ reply->request().originatingObject());
678+ if (app != NULL) {
679+ QString header(reply->rawHeader(X_CLICK_TOKEN));
680+ Q_EMIT clickTokenObtained(app, header);
681+ }
682+ reply->deleteLater();
683+ return;
684+ }
685+
686+ QByteArray payload = reply->readAll();
687+ QJsonDocument document = QJsonDocument::fromJson(payload);
688+
689+ if (document.isArray()) {
690+ QJsonArray array = document.array();
691+ int i;
692+ bool updates = false;
693+ for (i = 0; i < array.size(); i++) {
694+ QJsonObject object = array.at(i).toObject();
695+ QString name = object.value("name").toString();
696+ QString version = object.value("version").toString();
697+ QString icon_url = object.value("icon_url").toString();
698+ int size = object.value("binary_filesize").toVariant().toInt();
699+ if (m_apps.contains(name)) {
700+ m_apps[name]->setRemoteVersion(version);
701+ if (m_apps[name]->updateRequired()) {
702+ m_apps[name]->setIconUrl(icon_url);
703+ m_apps[name]->setBinaryFilesize(size);
704+ updates = true;
705+ }
706+ }
707+ }
708+ if (updates) {
709+ Q_EMIT updatesFound();
710+ } else {
711+ Q_EMIT updatesNotFound();
712+ }
713+ } else if (document.isObject()) {
714+ QJsonObject object = document.object();
715+ QString url = object.value("download_url").toString();
716+ QString name = object.value("name").toString();
717+ Q_EMIT downloadUrlFound(name, url);
718+ } else {
719+ Q_EMIT errorOccurred();
720+ }
721+ } else {
722+ Q_EMIT errorOccurred();
723+ }
724+
725+ reply->deleteLater();
726+}
727+
728+void Network::getResourceUrl(const QString &packagename)
729+{
730+ QString urlPackage = getUrlPackage();
731+ m_request.setUrl(QUrl(urlPackage + packagename));
732+ m_nam.get(m_request);
733+}
734+
735+void Network::getClickToken(Update *app, const QString &url,
736+ const QString &authHeader)
737+{
738+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
739+ QString signUrl = environment.value("CLICK_TOKEN_URL", url);
740+ QUrl query(signUrl);
741+ query.setQuery(authHeader);
742+ QNetworkRequest request;
743+ request.setUrl(query);
744+ request.setOriginatingObject(app);
745+ m_nam.head(request);
746+}
747+
748+}
749
750=== added file 'plugins/system-update/network/network.h'
751--- plugins/system-update/network/network.h 1970-01-01 00:00:00 +0000
752+++ plugins/system-update/network/network.h 2014-03-27 13:16:48 +0000
753@@ -0,0 +1,64 @@
754+/*
755+ * Copyright 2013 Canonical Ltd.
756+ *
757+ * This library is free software; you can redistribute it and/or
758+ * modify it under the terms of version 3 of the GNU Lesser General Public
759+ * License as published by the Free Software Foundation.
760+ *
761+ * This program is distributed in the hope that it will be useful,
762+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
763+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
764+ * General Public License for more details.
765+ *
766+ * You should have received a copy of the GNU Lesser General Public
767+ * License along with this library; if not, write to the
768+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
769+ * Boston, MA 02110-1301, USA.
770+ */
771+
772+#ifndef NETWORK_H
773+#define NETWORK_H
774+
775+#include <QObject>
776+#include <QtNetwork/QNetworkAccessManager>
777+#include <QtNetwork/QNetworkReply>
778+#include <QHash>
779+#include "../update.h"
780+
781+#define X_CLICK_TOKEN "X-Click-Token"
782+
783+namespace UpdatePlugin {
784+
785+class Network : public QObject
786+{
787+ Q_OBJECT
788+public:
789+ explicit Network(QObject *parent = 0);
790+
791+ void checkForNewVersions(QHash<QString, Update*> &apps);
792+ void getResourceUrl(const QString &packagename);
793+ void getClickToken(Update *app, const QString &url,
794+ const QString &authHeader);
795+
796+Q_SIGNALS:
797+ void updatesFound();
798+ void updatesNotFound();
799+ void errorOccurred();
800+ void downloadUrlFound(const QString &packagename, const QString &url);
801+ void clickTokenObtained(Update *app, const QString &clickToken);
802+
803+private Q_SLOTS:
804+ void onReply(QNetworkReply*);
805+
806+private:
807+ QNetworkAccessManager m_nam;
808+ QNetworkRequest m_request;
809+ QHash<QString, Update*> m_apps;
810+
811+ QString getUrlApps();
812+ QString getUrlPackage();
813+};
814+
815+}
816+
817+#endif // NETWORK_H
818
819=== modified file 'plugins/system-update/plugin.cpp'
820--- plugins/system-update/plugin.cpp 2014-01-23 19:29:34 +0000
821+++ plugins/system-update/plugin.cpp 2014-03-27 13:16:48 +0000
822@@ -24,6 +24,7 @@
823 #include "update_manager.h"
824 #include "system_update.h"
825 #include "update.h"
826+#include "download_tracker.h"
827
828 using namespace UpdatePlugin;
829
830@@ -35,6 +36,7 @@
831 qmlRegisterType<UpdateManager>(uri, 1, 0, "UpdateManager");
832 qmlRegisterType<SystemUpdate>(uri, 1, 0, "SystemUpdate");
833 qmlRegisterType<Update>(uri, 1, 0, "Update");
834+ qmlRegisterType<DownloadTracker>(uri, 1, 0, "DownloadTracker");
835 }
836
837 void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
838
839=== modified file 'plugins/system-update/system_update.cpp'
840--- plugins/system-update/system_update.cpp 2014-01-27 18:43:14 +0000
841+++ plugins/system-update/system_update.cpp 2014-03-27 13:16:48 +0000
842@@ -165,7 +165,11 @@
843 update->setLastUpdateDate(lastUpdateDate);
844 update->setIconUrl(QString("file:///usr/share/ubuntu/settings/system/icons/distributor-logo.png"));
845
846- Q_EMIT this->updateAvailable(packageName, update);
847+ if (update->updateRequired()) {
848+ Q_EMIT updateAvailable(packageName, update);
849+ } else {
850+ Q_EMIT updateNotFound();
851+ }
852 }
853
854 void SystemUpdate::updateDownloadProgress(int percentage, double eta)
855
856=== modified file 'plugins/system-update/system_update.h'
857--- plugins/system-update/system_update.h 2014-01-27 10:14:51 +0000
858+++ plugins/system-update/system_update.h 2014-03-27 13:16:48 +0000
859@@ -56,6 +56,7 @@
860
861 Q_SIGNALS:
862 void updateAvailable(const QString& packageName, Update *update);
863+ void updateNotFound();
864 void updateProgress(int percentage, double eta);
865 void updatePaused(int percentage);
866 void updateDownloaded();
867
868=== modified file 'plugins/system-update/update.cpp'
869--- plugins/system-update/update.cpp 2014-01-27 18:43:14 +0000
870+++ plugins/system-update/update.cpp 2014-03-27 13:16:48 +0000
871@@ -21,12 +21,15 @@
872 #include "update.h"
873 #include <QStringList>
874 #include <apt-pkg/debversion.h>
875+#include <QProcessEnvironment>
876
877 namespace UpdatePlugin {
878
879 Update::Update(QObject *parent) :
880 QObject(parent),
881 m_binary_filesize(0),
882+ m_click_url(""),
883+ m_downloadUrl(""),
884 m_download_progress(0),
885 m_error(""),
886 m_icon_url(""),
887@@ -58,11 +61,14 @@
888 void Update::setRemoteVersion(QString& version)
889 {
890 m_remote_version = version;
891-
892- int result = debVS.CmpVersion(m_local_version.toUtf8().data(),
893- m_remote_version.toUtf8().data());
894-
895- m_update = result < 0;
896+ if (getIgnoreUpdates()) {
897+ int result = debVS.CmpVersion(m_local_version.toUtf8().data(),
898+ m_remote_version.toUtf8().data());
899+
900+ m_update = result < 0;
901+ } else {
902+ m_update = false;
903+ }
904 }
905
906 void Update::setError(QString error)
907@@ -120,4 +126,16 @@
908 Q_EMIT downloadProgressChanged();
909 }
910
911+void Update::setDownloadUrl(const QString &url) {
912+ m_downloadUrl = url;
913+ Q_EMIT downloadUrlChanged();
914+}
915+
916+bool Update::getIgnoreUpdates()
917+{
918+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
919+ QString value = environment.value("IGNORE_UPDATES", QString("NOT_IGNORE_UPDATES"));
920+ return value == "IGNORE_UPDATES";
921+}
922+
923 }
924
925=== modified file 'plugins/system-update/update.h'
926--- plugins/system-update/update.h 2014-01-27 10:14:51 +0000
927+++ plugins/system-update/update.h 2014-03-27 13:16:48 +0000
928@@ -54,6 +54,8 @@
929 NOTIFY lastUpdateDateChanged)
930 Q_PROPERTY(int downloadProgress READ downloadProgress
931 NOTIFY downloadProgressChanged)
932+ Q_PROPERTY(QString downloadUrl READ downloadUrl NOTIFY downloadUrlChanged)
933+ Q_PROPERTY(QString clickToken READ clickToken NOTIFY clickTokenChanged)
934
935 Q_SIGNALS:
936 void systemUpdateChanged();
937@@ -68,6 +70,8 @@
938 void errorChanged();
939 void downloadProgressChanged();
940 void lastUpdateDateChanged();
941+ void downloadUrlChanged();
942+ void clickTokenChanged();
943
944 public:
945 explicit Update(QObject *parent = 0);
946@@ -87,6 +91,9 @@
947 bool updateReady() { return m_update_ready; }
948 bool selected() { return m_selected; }
949 QString getError() { return m_error; }
950+ const QString& getClickUrl() const { return m_click_url; }
951+ QString downloadUrl() { return m_downloadUrl; }
952+ QString clickToken() { return m_clickToken; }
953
954 void setSystemUpdate(bool isSystem);
955 void initializeApplication(QString packagename, QString title,
956@@ -101,9 +108,15 @@
957 void setError(QString error);
958 void setUpdateAvailable(bool available) { m_update = available; }
959 void setLastUpdateDate(const QString date);
960+ void setClickUrl(const QString &url) { m_click_url = url; }
961+ void setDownloadUrl(const QString &url);
962+ void setClickToken(const QString &token) { m_clickToken = token; Q_EMIT clickTokenChanged(); }
963
964 private:
965 int m_binary_filesize;
966+ QString m_click_url;
967+ QString m_clickToken;
968+ QString m_downloadUrl;
969 int m_download_progress;
970 QString m_error;
971 QString m_icon_url;
972@@ -117,6 +130,8 @@
973 bool m_update;
974 bool m_update_ready;
975 bool m_update_state;
976+
977+ bool getIgnoreUpdates();
978 };
979
980 }
981
982=== modified file 'plugins/system-update/update_manager.cpp'
983--- plugins/system-update/update_manager.cpp 2014-02-26 11:39:47 +0000
984+++ plugins/system-update/update_manager.cpp 2014-03-27 13:16:48 +0000
985@@ -25,15 +25,46 @@
986 #include <QJsonObject>
987 #include <QJsonArray>
988 #include <QJsonValue>
989+#include <QProcessEnvironment>
990+
991+#define CLICK_COMMAND "click"
992
993 namespace UpdatePlugin {
994
995 UpdateManager::UpdateManager(QObject *parent):
996- QObject(parent)
997+ QObject(parent),
998+ m_systemCheckingUpdate(false),
999+ m_clickCheckingUpdate(false),
1000+ m_checkingUpdates(0)
1001 {
1002+ // SSO SERVICE
1003+ QObject::connect(&m_service, SIGNAL(credentialsFound(const Token&)),
1004+ this, SLOT(handleCredentialsFound(Token)));
1005+ QObject::connect(&m_service, SIGNAL(credentialsNotFound()),
1006+ this, SIGNAL(credentialsNotFound()));
1007+ QObject::connect(&m_service, SIGNAL(credentialsNotFound()),
1008+ this, SLOT(clickUpdateNotAvailable()));
1009+ // PROCESS
1010+ QObject::connect(&m_process, SIGNAL(finished(int)),
1011+ this, SLOT(processOutput()));
1012+ // NETWORK
1013+ QObject::connect(&m_network, SIGNAL(updatesFound()),
1014+ this, SLOT(processUpdates()));
1015+ QObject::connect(&m_network, SIGNAL(updatesNotFound()),
1016+ this, SLOT(clickUpdateNotAvailable()));
1017+ QObject::connect(&m_network, SIGNAL(errorOccurred()),
1018+ this, SIGNAL(errorFound()));
1019+ QObject::connect(&m_network,
1020+ SIGNAL(clickTokenObtained(Update*, const QString&)),
1021+ this, SLOT(clickTokenReceived(Update*, const QString&)));
1022+ QObject::connect(&m_network,
1023+ SIGNAL(downloadUrlFound(const QString&, const QString&)),
1024+ this, SLOT(downloadUrlObtained(const QString&, const QString&)));
1025 // SYSTEM UPDATE
1026 QObject::connect(&m_systemUpdate, SIGNAL(updateAvailable(const QString&, Update*)),
1027 this, SLOT(registerSystemUpdate(const QString&, Update*)));
1028+ QObject::connect(&m_systemUpdate, SIGNAL(updateNotFound()),
1029+ this, SLOT(systemUpdateNotAvailable()));
1030 QObject::connect(&m_systemUpdate, SIGNAL(downloadModeChanged()),
1031 SIGNAL(downloadModeChanged()));
1032 QObject::connect(&m_systemUpdate, SIGNAL(updateDownloaded()),
1033@@ -50,26 +81,127 @@
1034 {
1035 }
1036
1037+void UpdateManager::clickUpdateNotAvailable()
1038+{
1039+ m_clickCheckingUpdate = false;
1040+ reportCheckState();
1041+ updateNotAvailable();
1042+}
1043+
1044+void UpdateManager::systemUpdateNotAvailable()
1045+{
1046+ m_systemCheckingUpdate = false;
1047+ reportCheckState();
1048+ updateNotAvailable();
1049+}
1050+
1051+void UpdateManager::updateNotAvailable()
1052+{
1053+ m_checkingUpdates--;
1054+ if (m_checkingUpdates == 0 && m_model.count() == 0) {
1055+ Q_EMIT updatesNotFound();
1056+ }
1057+}
1058+
1059+void UpdateManager::reportCheckState()
1060+{
1061+ if (!m_clickCheckingUpdate && !m_systemCheckingUpdate) {
1062+ Q_EMIT checkFinished();
1063+ }
1064+}
1065+
1066 void UpdateManager::checkUpdates()
1067 {
1068+ m_systemCheckingUpdate = true;
1069+ m_clickCheckingUpdate = true;
1070+ m_checkingUpdates = 2;
1071 m_model.clear();
1072 m_apps.clear();
1073 Q_EMIT modelChanged();
1074- m_systemUpdate.checkForUpdate();
1075+ if (getCheckForCredentials()) {
1076+ m_systemUpdate.checkForUpdate();
1077+ m_service.getCredentials();
1078+ } else {
1079+ systemUpdateNotAvailable();
1080+ Token token("", "", "", "");
1081+ handleCredentialsFound(token);
1082+ }
1083+}
1084+
1085+void UpdateManager::handleCredentialsFound(Token token)
1086+{
1087+ m_token = token;
1088+ QStringList args("list");
1089+ args << "--manifest";
1090+ QString command = getClickCommand();
1091+ m_process.start(command, args);
1092+}
1093+
1094+QString UpdateManager::getClickCommand()
1095+{
1096+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
1097+ QString command = environment.value("CLICK_COMMAND", QString(CLICK_COMMAND));
1098+ return command;
1099+}
1100+
1101+bool UpdateManager::getCheckForCredentials()
1102+{
1103+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
1104+ QString value = environment.value("IGNORE_CREDENTIALS", QString("CHECK_CREDENTIALS"));
1105+ return value == "CHECK_CREDENTIALS";
1106+}
1107+
1108+void UpdateManager::processOutput()
1109+{
1110+ QString output(m_process.readAllStandardOutput());
1111+
1112+ QJsonDocument document = QJsonDocument::fromJson(output.toUtf8());
1113+
1114+ QJsonArray array = document.array();
1115+
1116+ int i;
1117+ for (i = 0; i < array.size(); i++) {
1118+ QJsonObject object = array.at(i).toObject();
1119+ QString name = object.value("name").toString();
1120+ QString title = object.value("title").toString();
1121+ QString version = object.value("version").toString();
1122+ Update *app = new Update();
1123+ app->initializeApplication(name, title, version);
1124+ m_apps[app->getPackageName()] = app;
1125+ }
1126+
1127+ m_network.checkForNewVersions(m_apps);
1128+}
1129+
1130+void UpdateManager::processUpdates()
1131+{
1132+ m_clickCheckingUpdate = false;
1133+ bool updateAvailable = false;
1134+ foreach (QString id, m_apps.keys()) {
1135+ Update *app = m_apps.value(id);
1136+ if(app->updateRequired()) {
1137+ updateAvailable = true;
1138+ m_model.append(QVariant::fromValue(app));
1139+ }
1140+ }
1141+
1142+ if (updateAvailable) {
1143+ Q_EMIT modelChanged();
1144+ Q_EMIT updateAvailableFound(false);
1145+ }
1146+ reportCheckState();
1147 }
1148
1149 void UpdateManager::registerSystemUpdate(const QString& packageName, Update *update)
1150 {
1151- if (update->updateRequired()) {
1152- if (!m_apps.contains(packageName)) {
1153- m_apps[packageName] = update;
1154- m_model.insert(0, QVariant::fromValue(update));
1155- Q_EMIT modelChanged();
1156- }
1157- Q_EMIT updateAvailableFound(update->updateState());
1158- } else {
1159- Q_EMIT updatesNotFound();
1160+ m_systemCheckingUpdate = false;
1161+ if (!m_apps.contains(packageName)) {
1162+ m_apps[packageName] = update;
1163+ m_model.insert(0, QVariant::fromValue(update));
1164+ Q_EMIT modelChanged();
1165 }
1166+ Q_EMIT updateAvailableFound(update->updateState());
1167+ reportCheckState();
1168 }
1169
1170 void UpdateManager::systemUpdatePaused(int value)
1171@@ -85,17 +217,25 @@
1172 void UpdateManager::startDownload(const QString &packagename)
1173 {
1174 m_apps[packagename]->setUpdateState(true);
1175- m_systemUpdate.downloadUpdate();
1176+ if (m_apps[packagename]->systemUpdate()) {
1177+ m_systemUpdate.downloadUpdate();
1178+ } else {
1179+ m_network.getResourceUrl(packagename);
1180+ }
1181 }
1182
1183 void UpdateManager::retryDownload(const QString &packagename)
1184 {
1185- Update *update = m_apps.take(packagename);
1186- m_systemUpdate.cancelUpdate();
1187- m_model.removeAt(0);
1188- update->deleteLater();
1189- Q_EMIT modelChanged();
1190- m_systemUpdate.checkForUpdate();
1191+ if (m_apps[packagename]->systemUpdate()) {
1192+ Update *update = m_apps.take(packagename);
1193+ m_systemUpdate.cancelUpdate();
1194+ m_model.removeAt(0);
1195+ update->deleteLater();
1196+ Q_EMIT modelChanged();
1197+ m_systemUpdate.checkForUpdate();
1198+ } else {
1199+ startDownload(packagename);
1200+ }
1201 }
1202
1203 void UpdateManager::pauseDownload(const QString &packagename)
1204@@ -104,4 +244,25 @@
1205 m_systemUpdate.pauseDownload();
1206 }
1207
1208+void UpdateManager::downloadUrlObtained(const QString &packagename,
1209+ const QString &url)
1210+{
1211+ if (m_token.isValid()) {
1212+ QString authHeader = m_token.signUrl(url, QStringLiteral("HEAD"), true);
1213+ Update *app = m_apps[packagename];
1214+ app->setClickUrl(url);
1215+ m_network.getClickToken(app, url, authHeader);
1216+ } else {
1217+ Update *app = m_apps[packagename];
1218+ app->setError("Invalid User Token");
1219+ }
1220+}
1221+
1222+void UpdateManager::clickTokenReceived(Update *app, const QString &clickToken)
1223+{
1224+ app->setError("");
1225+ app->setClickToken(clickToken);
1226+ app->setDownloadUrl(app->getClickUrl());
1227+}
1228+
1229 }
1230
1231=== modified file 'plugins/system-update/update_manager.h'
1232--- plugins/system-update/update_manager.h 2014-02-26 11:39:47 +0000
1233+++ plugins/system-update/update_manager.h 2014-03-27 13:16:48 +0000
1234@@ -29,6 +29,21 @@
1235 #include <QVariantList>
1236 #include "system_update.h"
1237 #include "update.h"
1238+#include <token.h>
1239+
1240+#ifdef TESTS
1241+#include "../../tests/plugins/system-update/fakeprocess.h"
1242+#include "../../tests/plugins/system-update/fakenetwork.h"
1243+#include "../../tests/plugins/system-update/fakessoservice.h"
1244+#include "../../tests/plugins/system-update/fakesystemupdate.h"
1245+#else
1246+#include <ssoservice.h>
1247+#include <QProcess>
1248+#include "network/network.h"
1249+#include "system_update.h"
1250+#endif
1251+
1252+using namespace UbuntuOne;
1253
1254 namespace UpdatePlugin {
1255
1256@@ -41,8 +56,10 @@
1257 Q_PROPERTY(int currentBuildNumber READ currentBuildNumber)
1258
1259 Q_SIGNALS:
1260+ void checkFinished();
1261 void modelChanged();
1262 void updatesNotFound();
1263+ void credentialsNotFound();
1264 void updateAvailableFound(bool downloading);
1265 void errorFound();
1266 void downloadModeChanged();
1267@@ -70,21 +87,49 @@
1268 QHash<QString, Update*> get_apps() { return m_apps; }
1269 QVariantList get_model() { return m_model; }
1270 int get_downloadMode() { return m_downloadMode; }
1271+ void set_token(Token& t) { m_token = t; }
1272+ Token get_token() { return m_token; }
1273 #endif
1274
1275 public Q_SLOTS:
1276 void registerSystemUpdate(const QString& packageName, Update *update);
1277
1278 private Q_SLOTS:
1279+ void clickUpdateNotAvailable();
1280+ void systemUpdateNotAvailable();
1281 void systemUpdatePaused(int value);
1282+ void processOutput();
1283+ void processUpdates();
1284+ void downloadUrlObtained(const QString &packagename, const QString &url);
1285+ void handleCredentialsFound(Token token);
1286+ void clickTokenReceived(Update *app, const QString &clickToken);
1287
1288 private:
1289+ bool m_systemCheckingUpdate;
1290+ bool m_clickCheckingUpdate;
1291+ int m_checkingUpdates;
1292 QHash<QString, Update*> m_apps;
1293 int m_downloadMode;
1294 QVariantList m_model;
1295+ Token m_token;
1296+
1297+#ifdef TESTS
1298+ FakeNetwork m_network;
1299+ FakeProcess m_process;
1300+ FakeSsoService m_service;
1301+ FakeSystemUpdate m_systemUpdate;
1302+#else
1303+ Network m_network;
1304+ QProcess m_process;
1305+ SSOService m_service;
1306 SystemUpdate m_systemUpdate;
1307+#endif
1308
1309 void checkForUpdates();
1310+ QString getClickCommand();
1311+ bool getCheckForCredentials();
1312+ void reportCheckState();
1313+ void updateNotAvailable();
1314 };
1315
1316 }
1317
1318=== modified file 'tests/autopilot/ubuntu_system_settings/tests/__init__.py'
1319--- tests/autopilot/ubuntu_system_settings/tests/__init__.py 2014-03-10 11:24:08 +0000
1320+++ tests/autopilot/ubuntu_system_settings/tests/__init__.py 2014-03-27 13:16:48 +0000
1321@@ -241,11 +241,9 @@
1322 button = self.main_view.select_single(
1323 objectName='entryComponent-system-update')
1324 self.assertThat(button, NotEquals(None))
1325- self.pointer.move_to_object(button)
1326- self.pointer.click()
1327+ self.scroll_to_and_click(button)
1328
1329 @property
1330 def updates_page(self):
1331 """ Return 'System Update' page """
1332- return self.main_view.select_single(
1333- objectName='entryComponent-system-update')
1334+ return self.main_view.select_single(objectName='systemUpdatesPage')
1335
1336=== modified file 'tests/autopilot/ubuntu_system_settings/tests/test_system_updates.py'
1337--- tests/autopilot/ubuntu_system_settings/tests/test_system_updates.py 2014-02-24 16:24:39 +0000
1338+++ tests/autopilot/ubuntu_system_settings/tests/test_system_updates.py 2014-03-27 13:16:48 +0000
1339@@ -5,8 +5,13 @@
1340 # under the terms of the GNU General Public License version 3, as published
1341 # by the Free Software Foundation.
1342
1343+from __future__ import absolute_import
1344+
1345+import os
1346+
1347 from autopilot.introspection.dbus import StateNotFoundError
1348-from testtools.matchers import NotEquals, raises
1349+from autopilot.matchers import Eventually
1350+from testtools.matchers import Equals, NotEquals, raises
1351
1352 from ubuntu_system_settings.tests import SystemUpdatesBaseTestCase
1353
1354@@ -20,6 +25,8 @@
1355
1356 def setUp(self):
1357 # Set environment variables
1358+ os.environ["IGNORE_CREDENTIALS"] = "True"
1359+ os.environ["IGNORE_UPDATES"] = "True"
1360 super(SystemUpdatesTestCases, self).setUp()
1361
1362 def test_show_updates(self):
1363@@ -27,10 +34,72 @@
1364 updates = self.updates_page
1365 self.assertThat(updates, NotEquals(None))
1366 # Move to text field
1367- self.pointer.move_to_object(updates)
1368- self.pointer.click()
1369+ self.scroll_to_and_click(updates)
1370
1371 def test_updates_not_in_main(self):
1372 """Check that the updates notification is shown in main."""
1373 self.assertThat(lambda: self.main_view.select_single(
1374 objectName='entryComponent-updates'), raises(StateNotFoundError))
1375+
1376+ def test_configuration(self):
1377+ """Check the configuration button."""
1378+ self.assertThat(lambda: self.main_view.select_single(
1379+ objectName='configurationPage'), raises(StateNotFoundError))
1380+ updates = self.updates_page
1381+ self.assertThat(updates, NotEquals(None))
1382+ configuration = updates.select_single(objectName='configuration')
1383+ self.assertThat(configuration, NotEquals(None))
1384+ self.scroll_to_and_click(configuration)
1385+
1386+ def test_check_for_updates_area(self):
1387+ """Check that the updates area is shown on opening."""
1388+ updates = self.updates_page
1389+ self.assertThat(updates, NotEquals(None))
1390+ checkForUpdatesArea = updates.select_single(
1391+ objectName='checkForUpdatesArea')
1392+ self.assertThat(checkForUpdatesArea, NotEquals(None))
1393+ self.assertThat(checkForUpdatesArea.visible, Equals(True))
1394+ self.assertThat(checkForUpdatesArea.visible,
1395+ Eventually(NotEquals(True)))
1396+
1397+ def test_searching_state(self):
1398+ """Check how the ui reacts to searching state."""
1399+ updates = self.updates_page
1400+ self.assertThat(updates, NotEquals(None))
1401+ updates.state.wait_for("SEARCHING")
1402+ self.assertThat(updates.state, Equals("SEARCHING"))
1403+ notification = updates.select_single(
1404+ objectName='notification')
1405+ self.assertThat(notification, NotEquals(None))
1406+ self.assertThat(notification.visible, Equals(False))
1407+ installAllButton = updates.select_single(
1408+ objectName='installAllButton')
1409+ self.assertThat(installAllButton, NotEquals(None))
1410+ self.assertThat(installAllButton.visible, Equals(False))
1411+ updateNotification = updates.select_single(
1412+ objectName='updateNotification')
1413+ self.assertThat(updateNotification, NotEquals(None))
1414+ self.assertThat(updateNotification.visible, Equals(False))
1415+ checkForUpdatesArea = updates.select_single(
1416+ objectName='checkForUpdatesArea')
1417+ self.assertThat(checkForUpdatesArea, NotEquals(None))
1418+ self.assertThat(checkForUpdatesArea.visible, Equals(True))
1419+
1420+ def test_no_updates_state(self):
1421+ """Check how the ui reacts to no updates state."""
1422+ updates = self.updates_page
1423+ self.assertThat(updates, NotEquals(None))
1424+ updates.state.wait_for("NOUPDATES")
1425+ self.assertThat(updates.state, Equals("NOUPDATES"))
1426+ updateList = updates.select_single(
1427+ objectName='updateList')
1428+ self.assertThat(updateList, NotEquals(None))
1429+ self.assertThat(updateList.visible, Equals(False))
1430+ installAllButton = updates.select_single(
1431+ objectName='installAllButton')
1432+ self.assertThat(installAllButton, NotEquals(None))
1433+ self.assertThat(installAllButton.visible, Equals(False))
1434+ updateNotification = updates.select_single(
1435+ objectName='updateNotification')
1436+ self.assertThat(updateNotification, NotEquals(None))
1437+ self.assertThat(updateNotification.visible, Equals(True))
1438
1439=== modified file 'tests/plugins/system-update/CMakeLists.txt'
1440--- tests/plugins/system-update/CMakeLists.txt 2014-02-03 11:10:35 +0000
1441+++ tests/plugins/system-update/CMakeLists.txt 2014-03-27 13:16:48 +0000
1442@@ -2,8 +2,20 @@
1443 include_directories(${CMAKE_CURRENT_BINARY_DIR} ../../../plugins/system-update)
1444 add_definitions(-DTESTS)
1445
1446+# Need to get libsignon/accounts here, as they get exposed in headers
1447+find_package (PkgConfig REQUIRED)
1448+pkg_check_modules(UBUNTUONEAUTH REQUIRED ubuntuoneauth-2.0)
1449+add_definitions(${UBUNTUONEAUTH_CFLAGS} ${UBUNTUONEAUTH_CFLAGS_OTHER})
1450+
1451 add_executable(tst-update-manager
1452 tst_updatemanager.cpp
1453+ fakeprocess.cpp
1454+ fakeprocess.h
1455+ fakenetwork.cpp
1456+ fakenetwork.h
1457+ fakessoservice.cpp
1458+ fakessoservice.h
1459+ fakesystemupdate.cpp
1460 ../../../plugins/system-update/update.cpp
1461 ../../../plugins/system-update/update_manager.cpp
1462 ../../../plugins/system-update/system_update.cpp
1463@@ -17,10 +29,15 @@
1464 # set the path to the library folder
1465 include_directories(/usr/include/apt-pkg/)
1466
1467-target_link_libraries(tst-update-manager apt-pkg)
1468+target_link_libraries(tst-update-manager apt-pkg ${UBUNTUONEAUTH_LDFLAGS})
1469 qt5_use_modules(tst-update-manager Qml Quick Core DBus Xml Network Test)
1470 add_test(NAME tst-update-manager COMMAND ${XVFB_CMD} ${CMAKE_CURRENT_BINARY_DIR}/tst-update-manager)
1471
1472 qt5_use_modules(tst-update Qml Quick Core DBus Xml Network Test)
1473 target_link_libraries(tst-update apt-pkg)
1474 add_test(NAME tst-update COMMAND ${XVFB_CMD} ${CMAKE_CURRENT_BINARY_DIR}/tst-update)
1475+
1476+add_custom_command(
1477+ TARGET tst-update-manager
1478+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/click.result ${CMAKE_CURRENT_BINARY_DIR}/click.result
1479+)
1480
1481=== added file 'tests/plugins/system-update/click.result'
1482--- tests/plugins/system-update/click.result 1970-01-01 00:00:00 +0000
1483+++ tests/plugins/system-update/click.result 2014-03-27 13:16:48 +0000
1484@@ -0,0 +1,26 @@
1485+[
1486+ {
1487+ "framework": "ubuntu-sdk-13.10",
1488+ "name": "com.ubuntu.dropping-letters",
1489+ "title": "Dropping Letters game",
1490+ "version": "0.1.2.2"
1491+ },
1492+ {
1493+ "framework": "ubuntu-sdk-13.10",
1494+ "name": "com.ubuntu.stock-ticker-mobile",
1495+ "title": "A stock trading app with charts, news, and management",
1496+ "version": "0.3.7ubuntu1"
1497+ },
1498+ {
1499+ "framework": "ubuntu-sdk-13.10",
1500+ "name": "com.ubuntu.sudoku",
1501+ "title": "Sudoku game for Ubuntu devices",
1502+ "version": "0.4.2ubuntu2"
1503+ },
1504+ {
1505+ "framework": "ubuntu-sdk-13.10",
1506+ "name": "com.ubuntu.developer.xda-app",
1507+ "title": "XDA Developers App",
1508+ "version": "0.4.2ubuntu2"
1509+ }
1510+]
1511
1512=== added file 'tests/plugins/system-update/fakenetwork.cpp'
1513--- tests/plugins/system-update/fakenetwork.cpp 1970-01-01 00:00:00 +0000
1514+++ tests/plugins/system-update/fakenetwork.cpp 2014-03-27 13:16:48 +0000
1515@@ -0,0 +1,51 @@
1516+/*
1517+ * Copyright 2013 Canonical Ltd.
1518+ *
1519+ * This library is free software; you can redistribute it and/or
1520+ * modify it under the terms of version 3 of the GNU Lesser General Public
1521+ * License as published by the Free Software Foundation.
1522+ *
1523+ * This program is distributed in the hope that it will be useful,
1524+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1525+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1526+ * General Public License for more details.
1527+ *
1528+ * You should have received a copy of the GNU Lesser General Public
1529+ * License along with this library; if not, write to the
1530+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
1531+ * Boston, MA 02110-1301, USA.
1532+ */
1533+
1534+#include "fakenetwork.h"
1535+
1536+namespace UpdatePlugin {
1537+
1538+FakeNetwork::FakeNetwork(QObject *parent) :
1539+ QObject(parent)
1540+{
1541+}
1542+
1543+void FakeNetwork::checkForNewVersions(QHash<QString, Update*> &apps)
1544+{
1545+ if(apps.contains("com.ubuntu.developer.xda-app")) {
1546+ Update* app = apps.value("com.ubuntu.developer.xda-app");
1547+ QString version("0.5.2ubuntu2");
1548+ app->setRemoteVersion(version);
1549+ emit this->updatesFound();
1550+ }
1551+}
1552+
1553+void FakeNetwork::getResourceUrl(const QString& packagename)
1554+{
1555+ emit this->downloadUrlFound(packagename, "http://canonical.com");
1556+}
1557+
1558+void FakeNetwork::getClickToken(Update* app, const QString& url, const QString& authHeader)
1559+{
1560+ Q_UNUSED(url);
1561+ Q_UNUSED(authHeader);
1562+ QString fakeHeader("x-click-token-header");
1563+ emit this->clickTokenObtained(app, fakeHeader);
1564+}
1565+
1566+}
1567
1568=== added file 'tests/plugins/system-update/fakenetwork.h'
1569--- tests/plugins/system-update/fakenetwork.h 1970-01-01 00:00:00 +0000
1570+++ tests/plugins/system-update/fakenetwork.h 2014-03-27 13:16:48 +0000
1571@@ -0,0 +1,49 @@
1572+/*
1573+ * Copyright 2013 Canonical Ltd.
1574+ *
1575+ * This library is free software; you can redistribute it and/or
1576+ * modify it under the terms of version 3 of the GNU Lesser General Public
1577+ * License as published by the Free Software Foundation.
1578+ *
1579+ * This program is distributed in the hope that it will be useful,
1580+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1581+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1582+ * General Public License for more details.
1583+ *
1584+ * You should have received a copy of the GNU Lesser General Public
1585+ * License along with this library; if not, write to the
1586+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
1587+ * Boston, MA 02110-1301, USA.
1588+ */
1589+
1590+#ifndef FAKENETWORK_H
1591+#define FAKENETWORK_H
1592+
1593+#include <QObject>
1594+#include <QHash>
1595+#include <QString>
1596+#include "update.h"
1597+
1598+namespace UpdatePlugin {
1599+
1600+class FakeNetwork : public QObject
1601+{
1602+ Q_OBJECT
1603+public:
1604+ explicit FakeNetwork(QObject *parent = 0);
1605+
1606+ void checkForNewVersions(QHash<QString, Update*> &apps);
1607+ void getResourceUrl(const QString& packagename);
1608+ void getClickToken(Update* app, const QString& url, const QString& authHeader);
1609+
1610+signals:
1611+ void updatesFound();
1612+ void updatesNotFound();
1613+ void errorOccurred();
1614+ void downloadUrlFound(const QString& packagename, const QString& url);
1615+ void clickTokenObtained(Update* app, const QString& clickToken);
1616+};
1617+
1618+}
1619+
1620+#endif // FAKENETWORK_H
1621
1622=== added file 'tests/plugins/system-update/fakeprocess.cpp'
1623--- tests/plugins/system-update/fakeprocess.cpp 1970-01-01 00:00:00 +0000
1624+++ tests/plugins/system-update/fakeprocess.cpp 2014-03-27 13:16:48 +0000
1625@@ -0,0 +1,63 @@
1626+/*
1627+ * Copyright 2013 Canonical Ltd.
1628+ *
1629+ * This library is free software; you can redistribute it and/or
1630+ * modify it under the terms of version 3 of the GNU Lesser General Public
1631+ * License as published by the Free Software Foundation.
1632+ *
1633+ * This program is distributed in the hope that it will be useful,
1634+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1635+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1636+ * General Public License for more details.
1637+ *
1638+ * You should have received a copy of the GNU Lesser General Public
1639+ * License along with this library; if not, write to the
1640+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
1641+ * Boston, MA 02110-1301, USA.
1642+ */
1643+
1644+#include "fakeprocess.h"
1645+#include <QFile>
1646+#include <QTextStream>
1647+#include <QDir>
1648+
1649+namespace UpdatePlugin {
1650+
1651+FakeProcess::FakeProcess(QObject *parent) :
1652+ QObject(parent)
1653+{
1654+}
1655+
1656+void FakeProcess::start(QString command, QStringList args)
1657+{
1658+ Q_UNUSED(args);
1659+ if(command == "click") {
1660+ QString path = QDir::currentPath();
1661+ path.append("/click.result");
1662+
1663+ this->m_content.clear();
1664+ QFile file(path);
1665+ file.open(QIODevice::ReadOnly);
1666+ QTextStream in(&file);
1667+ while(!in.atEnd()) {
1668+ QString line = in.readLine();
1669+ this->m_content.append(line);
1670+ }
1671+
1672+ file.close();
1673+ emit this->finished(0);
1674+ }
1675+}
1676+
1677+QString FakeProcess::readAllStandardOutput()
1678+{
1679+ return this->m_content;
1680+}
1681+
1682+void FakeProcess::startDetached(QString command, QStringList args)
1683+{
1684+ Q_UNUSED(command);
1685+ Q_UNUSED(args);
1686+}
1687+
1688+}
1689
1690=== added file 'tests/plugins/system-update/fakeprocess.h'
1691--- tests/plugins/system-update/fakeprocess.h 1970-01-01 00:00:00 +0000
1692+++ tests/plugins/system-update/fakeprocess.h 2014-03-27 13:16:48 +0000
1693@@ -0,0 +1,48 @@
1694+/*
1695+ * Copyright 2013 Canonical Ltd.
1696+ *
1697+ * This library is free software; you can redistribute it and/or
1698+ * modify it under the terms of version 3 of the GNU Lesser General Public
1699+ * License as published by the Free Software Foundation.
1700+ *
1701+ * This program is distributed in the hope that it will be useful,
1702+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1703+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1704+ * General Public License for more details.
1705+ *
1706+ * You should have received a copy of the GNU Lesser General Public
1707+ * License along with this library; if not, write to the
1708+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
1709+ * Boston, MA 02110-1301, USA.
1710+ */
1711+
1712+#ifndef FAKEPROCESS_H
1713+#define FAKEPROCESS_H
1714+
1715+#include <QObject>
1716+#include <QString>
1717+#include <QStringList>
1718+
1719+namespace UpdatePlugin {
1720+
1721+class FakeProcess : public QObject
1722+{
1723+ Q_OBJECT
1724+public:
1725+ explicit FakeProcess(QObject *parent = 0);
1726+
1727+ void start(QString command, QStringList args);
1728+ void startDetached(QString command, QStringList args);
1729+ QString readAllStandardOutput();
1730+
1731+signals:
1732+ void finished(int);
1733+
1734+private:
1735+ QString m_content;
1736+
1737+};
1738+
1739+}
1740+
1741+#endif // FAKEPROCESS_H
1742
1743=== added file 'tests/plugins/system-update/fakessoservice.cpp'
1744--- tests/plugins/system-update/fakessoservice.cpp 1970-01-01 00:00:00 +0000
1745+++ tests/plugins/system-update/fakessoservice.cpp 2014-03-27 13:16:48 +0000
1746@@ -0,0 +1,16 @@
1747+#include "fakessoservice.h"
1748+
1749+namespace UpdatePlugin {
1750+
1751+FakeSsoService::FakeSsoService(QObject *parent) :
1752+ QObject(parent)
1753+{
1754+}
1755+
1756+void FakeSsoService::getCredentials()
1757+{
1758+ Token token("token_key", "token_secret", "consumer_key", "consumer_secret");
1759+ emit this->credentialsFound(token);
1760+}
1761+
1762+}
1763
1764=== added file 'tests/plugins/system-update/fakessoservice.h'
1765--- tests/plugins/system-update/fakessoservice.h 1970-01-01 00:00:00 +0000
1766+++ tests/plugins/system-update/fakessoservice.h 2014-03-27 13:16:48 +0000
1767@@ -0,0 +1,27 @@
1768+#ifndef FAKESSOSERVICE_H
1769+#define FAKESSOSERVICE_H
1770+
1771+#include <QObject>
1772+#include <token.h>
1773+
1774+using namespace UbuntuOne;
1775+
1776+namespace UpdatePlugin {
1777+
1778+class FakeSsoService : public QObject
1779+{
1780+ Q_OBJECT
1781+public:
1782+ explicit FakeSsoService(QObject *parent = 0);
1783+
1784+ void getCredentials();
1785+
1786+signals:
1787+ void credentialsFound(const Token&);
1788+ void credentialsNotFound();
1789+
1790+};
1791+
1792+}
1793+
1794+#endif // FAKESSOSERVICE_H
1795
1796=== added file 'tests/plugins/system-update/fakesystemupdate.cpp'
1797--- tests/plugins/system-update/fakesystemupdate.cpp 1970-01-01 00:00:00 +0000
1798+++ tests/plugins/system-update/fakesystemupdate.cpp 2014-03-27 13:16:48 +0000
1799@@ -0,0 +1,28 @@
1800+/*
1801+ * Copyright 2014 Canonical Ltd.
1802+ *
1803+ * This library is free software; you can redistribute it and/or
1804+ * modify it under the terms of version 3 of the GNU Lesser General Public
1805+ * License as published by the Free Software Foundation.
1806+ *
1807+ * This program is distributed in the hope that it will be useful,
1808+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1809+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1810+ * General Public License for more details.
1811+ *
1812+ * You should have received a copy of the GNU Lesser General Public
1813+ * License along with this library; if not, write to the
1814+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
1815+ * Boston, MA 02110-1301, USA.
1816+ */
1817+
1818+#include "fakesystemupdate.h"
1819+
1820+namespace UpdatePlugin {
1821+
1822+FakeSystemUpdate::FakeSystemUpdate(QObject *parent) :
1823+ QObject(parent)
1824+{
1825+}
1826+
1827+}
1828
1829=== added file 'tests/plugins/system-update/fakesystemupdate.h'
1830--- tests/plugins/system-update/fakesystemupdate.h 1970-01-01 00:00:00 +0000
1831+++ tests/plugins/system-update/fakesystemupdate.h 2014-03-27 13:16:48 +0000
1832@@ -0,0 +1,58 @@
1833+/*
1834+ * Copyright 2014 Canonical Ltd.
1835+ *
1836+ * This library is free software; you can redistribute it and/or
1837+ * modify it under the terms of version 3 of the GNU Lesser General Public
1838+ * License as published by the Free Software Foundation.
1839+ *
1840+ * This program is distributed in the hope that it will be useful,
1841+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1842+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1843+ * General Public License for more details.
1844+ *
1845+ * You should have received a copy of the GNU Lesser General Public
1846+ * License along with this library; if not, write to the
1847+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
1848+ * Boston, MA 02110-1301, USA.
1849+ */
1850+
1851+#ifndef FAKESYSTEMUPDATE_H
1852+#define FAKESYSTEMUPDATE_H
1853+
1854+#include <QObject>
1855+#include "../../../plugins/system-update/update.h"
1856+
1857+namespace UpdatePlugin {
1858+
1859+class FakeSystemUpdate: public QObject
1860+{
1861+ Q_OBJECT
1862+public:
1863+ explicit FakeSystemUpdate(QObject *parent = 0);
1864+ ~FakeSystemUpdate() {}
1865+
1866+ int downloadMode() { return 0; }
1867+ void setDownloadMode(int) {}
1868+ int currentBuildNumber() { return 123;}
1869+
1870+ void checkForUpdate() {}
1871+ void downloadUpdate() {}
1872+ void applyUpdate() {}
1873+ void cancelUpdate() {}
1874+ void pauseDownload() {}
1875+
1876+Q_SIGNALS:
1877+ void updateAvailable(const QString& packageName, Update *update);
1878+ void updateNotFound();
1879+ void updateProgress(int percentage, double eta);
1880+ void updatePaused(int percentage);
1881+ void updateDownloaded();
1882+ void updateFailed(int consecutiveFailureCount, QString lastReason);
1883+ void downloadModeChanged();
1884+ void updateProcessFailed(const QString& reason);
1885+
1886+};
1887+
1888+}
1889+
1890+#endif // FAKESYSTEMUPDATE_H
1891
1892=== modified file 'tests/plugins/system-update/tst_updatemanager.cpp'
1893--- tests/plugins/system-update/tst_updatemanager.cpp 2014-02-26 11:39:47 +0000
1894+++ tests/plugins/system-update/tst_updatemanager.cpp 2014-03-27 13:16:48 +0000
1895@@ -33,6 +33,8 @@
1896 void testRegisterSystemUpdateNotRequired();
1897 void testStartDownload();
1898 void testPauseDownload();
1899+ void testCheckUpdatesModelSignal();
1900+ void testCheckUpdatesUpdateSignal();
1901
1902 private:
1903 Update* getUpdate();
1904@@ -120,6 +122,34 @@
1905 QTRY_COMPARE(update->updateState(), false);
1906 }
1907
1908+void UpdateManagerTest::testCheckUpdatesModelSignal()
1909+{
1910+ UpdateManager manager;
1911+ QSignalSpy spy(&manager, SIGNAL(modelChanged()));
1912+ QTRY_COMPARE(manager.get_apps().size(), 0);
1913+ manager.checkUpdates();
1914+ QTRY_COMPARE(manager.get_apps().size(), 4);
1915+ QTRY_COMPARE(manager.get_model().size(), 1);
1916+ Update* app = manager.get_model()[0].value<Update*>();
1917+ QTRY_COMPARE(app->getTitle(), QString("XDA Developers App"));
1918+ QTRY_COMPARE(app->updateRequired(), true);
1919+ QTRY_COMPARE(app->getPackageName(), QString("com.ubuntu.developer.xda-app"));
1920+}
1921+
1922+void UpdateManagerTest::testCheckUpdatesUpdateSignal()
1923+{
1924+ UpdateManager manager;
1925+ QSignalSpy spy(&manager, SIGNAL(updateAvailableFound()));
1926+ QTRY_COMPARE(manager.get_apps().size(), 0);
1927+ manager.checkUpdates();
1928+ QTRY_COMPARE(manager.get_apps().size(), 4);
1929+ QTRY_COMPARE(manager.get_model().size(), 1);
1930+ Update* app = manager.get_model()[0].value<Update*>();
1931+ QTRY_COMPARE(app->getTitle(), QString("XDA Developers App"));
1932+ QTRY_COMPARE(app->updateRequired(), true);
1933+ QTRY_COMPARE(app->getPackageName(), QString("com.ubuntu.developer.xda-app"));
1934+}
1935+
1936
1937 QTEST_MAIN(UpdateManagerTest);
1938 #include "tst_updatemanager.moc"

Subscribers

People subscribed via source and target branches