on xenial help sign in
Wizard: Update tests, remove app updates

The implementation of app updates in the Wizard is messy, difficult to test, and doesn't need to be carried any more. Because of that, I've removed the app update feature. I've also: * Added mock for SystemImage * Updated the System and NetworkingStatus mocks to match their upstreams * Moved around setup and teardown functions for each test * Removed tests for the location pages * Added a test for the system update page

commit9a7d5c0b23db1732562824fead37cd61bbd6ac59 authorDalton Durst <dalton@ubports.com> dateFri 10 May 2019, 14:40 (7 years ago) parent28c9aa3
14 files changed+161−1098
qml/Wizard/Pages/76-app-update.qml−241 qml/Wizard/Pages/ClickUpdateDelegate.qml−26 qml/Wizard/Pages/DownloadHandler.qml−204 qml/Wizard/Pages/GlobalUpdateControls.qml−138 qml/Wizard/Pages/UpdateDelegate.qml−389 tests/mocks/Ubuntu/Connectivity/networking-status.cpp+7−1 tests/mocks/Ubuntu/Connectivity/networking-status.h+5−3 tests/mocks/Ubuntu/SystemSettings/CMakeLists.txt+1 tests/mocks/Ubuntu/SystemSettings/Update/CMakeLists.txt+1 tests/mocks/Ubuntu/SystemSettings/Update/MockSystemImage.qml+30 tests/mocks/Ubuntu/SystemSettings/Update/qmldir+2 tests/mocks/Wizard/MockSystem.cpp+12−3 tests/mocks/Wizard/MockSystem.h+7−1 tests/qmltests/Wizard/tst_Wizard.qml+96−92
qml/Wizard/Pages/76-app-update.qmldeleted−241
@@ -1,241 +0,0 @@
1−/*
2− * Copyright (C) 2018 The UBports project
3− *
4− * Written by: Dalton Durst <dalton@ubports.com>
5− * Marius Gripsgard <marius@ubports.com>
6− *
7− * This program is free software: you can redistribute it and/or modify it
8− * under the terms of the GNU General Public License version 3, as published
9− * by the Free Software Foundation.
10− *
11− * This program is distributed in the hope that it will be useful, but
12− * WITHOUT ANY WARRANTY; without even the implied warranties of
13− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14− * PURPOSE. See the GNU General Public License for more details.
15− *
16− * You should have received a copy of the GNU General Public License along
17− * with this program. If not, see <http://www.gnu.org/licenses/>.
18− */
19−
20−import QMenuModel 0.1
21−import QtQuick 2.4
22−import QtQuick.Layouts 1.1
23−import Ubuntu.Components 1.3
24−import Ubuntu.Components.ListItems 1.3 as ListItem
25−import Ubuntu.Components.Popups 1.3
26−import Ubuntu.SystemSettings.Update 1.0
27−import Wizard 0.1
28−import ".." as LocalComponents
29−
30−LocalComponents.Page {
31− id: appUpdatePage
32− objectName: "appUpdatePage"
33−
34− title: i18n.tr("Update Apps")
35− forwardButtonSourceComponent: forwardButton
36−
37− property bool batchMode: false
38−
39− onlyOnUpdate: true
40−
41− property int updatesCount: {
42− var count = 0;
43− count += clickRepeater.count;
44− return count;
45− }
46−
47− function check(force) {
48− UpdateManager.check(UpdateManager.CheckClickIgnoreVersion);
49− }
50−
51− DownloadHandler {
52− id: downloadHandler
53− updateModel: UpdateManager.model
54− }
55−
56− Flickable {
57− id: scrollWidget
58− anchors {
59− fill: content
60− leftMargin: parent.leftMargin
61− rightMargin: parent.rightMargin
62− }
63− clip: true
64− contentHeight: scrollWidgetContent.height
65− boundsBehavior: (contentHeight > scrollWidgetContent.height) ?
66− Flickable.DragAndOvershootBounds :
67− Flickable.StopAtBounds
68− flickableDirection: Flickable.VerticalFlick
69−
70− Column {
71− id: scrollWidgetContent
72− anchors {
73− left: parent.left
74− right: parent.right
75− }
76−
77− GlobalUpdateControls {
78− id: g
79− objectName: "global"
80− anchors { left: parent.left; right: parent.right }
81−
82− height: hidden ? 0 : units.gu(8)
83− clip: true
84− status: UpdateManager.status
85− batchMode: appUpdatePage.batchMode
86− updatesCount: appUpdatePage.updatesCount
87− online: root.connected
88− onStop: UpdateManager.cancel()
89−
90− onRequestInstall: {
91− install();
92− }
93− onInstall: {
94− appUpdatePage.batchMode = true
95− postClickBatchHandler.target = appUpdatePage;
96− }
97− }
98−
99− Rectangle {
100− id: overlay
101− objectName: "overlay"
102− anchors {
103− left: parent.left
104− leftMargin: units.gu(2)
105− right: parent.right
106− rightMargin: units.gu(2)
107− }
108− visible: placeholder.text
109− color: theme.palette.normal.background
110− height: units.gu(10)
111−
112− Label {
113− id: placeholder
114− objectName: "overlayText"
115− anchors.fill: parent
116− verticalAlignment: Text.AlignVCenter
117− horizontalAlignment: Text.AlignHCenter
118− wrapMode: Text.WordWrap
119− text: {
120− var s = UpdateManager.status;
121− if (!root.connected) {
122− return i18n.tr("This device is not connected to the internet.") + " " +
123− i18n.tr("Use the OpenStore app to check for updates once connected.");
124− } else if (s === UpdateManager.StatusIdle && updatesCount === 0) {
125− return i18n.tr("Software is up to date");
126− } else if (s === UpdateManager.StatusServerError ||
127− s === UpdateManager.StatusNetworkError) {
128− return i18n.tr("The update server is not responding.") + " " +
129− i18n.tr("Use the OpenStore app to check for updates once connected.");
130− }
131− return "";
132− }
133− }
134− }
135−
136− Column {
137− id: clickUpdatesCol
138− objectName: "clickUpdates"
139− anchors { left: parent.left; right: parent.right }
140− visible: {
141− var s = UpdateManager.status;
142− var haveUpdates = clickRepeater.count > 0;
143− switch (s) {
144− case UpdateManager.StatusCheckingImageUpdates:
145− case UpdateManager.StatusIdle:
146− return haveUpdates && root.connected;
147− }
148− return false;
149− }
150−
151− Repeater {
152− id: clickRepeater
153− model: UpdateManager.clickUpdates
154−
155− delegate: ClickUpdateDelegate {
156− objectName: "clickUpdatesDelegate" + index
157− width: clickUpdatesCol.width
158− updateState: model.updateState
159− progress: model.progress
160− version: remoteVersion
161− size: model.size
162− name: title
163− iconUrl: model.iconUrl
164− kind: model.kind
165− error: model.error
166− signedUrl: signedDownloadUrl
167−
168− onInstall: downloadHandler.createDownload(model);
169− onPause: downloadHandler.pauseDownload(model)
170− onResume: downloadHandler.resumeDownload(model)
171− onRetry: {
172− /* This creates a new signed URL with which we can
173− retry the download. See onSignedUrlChanged. */
174− UpdateManager.retry(model.identifier,
175− model.revision);
176− }
177−
178− onSignedUrlChanged: {
179− // If we have a signedUrl, user intend to retry.
180− if (signedUrl) {
181− downloadHandler.retryDownload(model);
182− }
183− }
184−
185− Connections {
186− target: g
187− onInstall: install()
188− }
189−
190− /* If we a downloadId, we expect UDM to restore it
191− after some time. Workaround for lp:1603770. */
192− Timer {
193− id: downloadTimeout
194− interval: 30000
195− running: true
196− onTriggered: {
197− var s = updateState;
198− if (model.downloadId
199− || s === Update.StateQueuedForDownload
200− || s === Update.StateDownloading) {
201− downloadHandler.assertDownloadExist(model);
202− }
203− }
204− }
205− }
206− }
207− }
208− } // Column inside flickable
209− } // Flickable
210−
211− Connections {
212− id: postClickBatchHandler
213− ignoreUnknownSignals: true
214− target: null
215− onUpdatesCountChanged: {
216− if (target.updatesCount === 0) {
217− appUpdatePage.batchMode = false;
218− target = null;
219− }
220− }
221− }
222−
223− Component.onCompleted: check()
224−
225− Component {
226− id: forwardButton
227− LocalComponents.StackButton {
228− text: {
229− var s = UpdateManager.status;
230− if (s === UpdateManager.StatusIdle && updatesCount === 0) {
231− return i18n.tr("Next");
232− } else if (s === UpdateManager.StatusCheckingClickUpdates) {
233− return i18n.tr("Please wait...")
234− } else {
235− return i18n.tr("Skip");
236− }
237− }
238− onClicked: pageStack.next()
239− }
240− }
241−}
qml/Wizard/Pages/ClickUpdateDelegate.qmldeleted−26
@@ -1,26 +0,0 @@
1−/*
2− * This file is part of system-settings
3− *
4− * Copyright (C) 2016 Canonical Ltd.
5− *
6− * This program is free software: you can redistribute it and/or modify it
7− * under the terms of the GNU General Public License version 3, as published
8− * by the Free Software Foundation.
9− *
10− * This program is distributed in the hope that it will be useful, but
11− * WITHOUT ANY WARRANTY; without even the implied warranties of
12− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13− * PURPOSE. See the GNU General Public License for more details.
14− *
15− * You should have received a copy of the GNU General Public License along
16− * with this program. If not, see <http://www.gnu.org/licenses/>.
17− */
18−
19−import QtQuick 2.4
20−import Ubuntu.SystemSettings.Update 1.0
21−
22−UpdateDelegate {
23− id: update
24−
25− property string signedUrl
26−}
qml/Wizard/Pages/DownloadHandler.qmldeleted−204
@@ -1,204 +0,0 @@
1−/*
2− * This file is part of system-settings
3− *
4− * Copyright (C) 2016 Canonical Ltd.
5− *
6− * This program is free software: you can redistribute it and/or modify it
7− * under the terms of the GNU General Public License version 3, as published
8− * by the Free Software Foundation.
9− *
10− * This program is distributed in the hope that it will be useful, but
11− * WITHOUT ANY WARRANTY; without even the implied warranties of
12− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13− * PURPOSE. See the GNU General Public License for more details.
14− *
15− * You should have received a copy of the GNU General Public License along
16− * with this program. If not, see <http://www.gnu.org/licenses/>.
17− *
18− */
19−import QtQuick 2.4
20−import Ubuntu.DownloadManager 1.2
21−
22−Item {
23− id: downloadHandler
24− property var updateModel
25− property alias downloads: downloadManager.downloads
26− property var udm: DownloadManager {
27− id: downloadManager
28− onDownloadFinished: {
29− updateModel.setInstalled(download.metadata.custom.identifier,
30− download.metadata.custom.revision);
31− }
32− onDownloadPaused: {
33− updateModel.pauseUpdate(download.metadata.custom.identifier,
34− download.metadata.custom.revision)
35− }
36− onDownloadResumed: {
37− updateModel.resumeUpdate(download.metadata.custom.identifier,
38− download.metadata.custom.revision)
39− }
40− onDownloadCanceled: {
41− updateModel.cancelUpdate(download.metadata.custom.identifier,
42− download.metadata.custom.revision)
43− }
44− onErrorFound: {
45− updateModel.setError(download.metadata.custom.identifier,
46− download.metadata.custom.revision,
47− download.errorMessage)
48− }
49− onDownloadsChanged: restoreDownloads()
50− Component.onCompleted: restoreDownloads()
51− }
52−
53− function downloadAll() {
54−
55− }
56−
57− function restoreDownloads() {
58− var dl;
59− for (var i = 0; i<downloads.length; i++) {
60− dl = downloads[i];
61− if (!dl._bound) {
62− /* We only bind those signals here, that the UDM does not
63− receive, i.e. processing and progressChanged. */
64− dl.progressChanged.connect(onDownloadProgress.bind(dl));
65− dl.processing.connect(onDownloadProcessing.bind(dl));
66− dl._bound = true;
67− }
68− }
69− }
70−
71− function resumeDownload(click) {
72− var download = getDownloadFor(click);
73− if (download && !download.downloading && !download.isCompleted) {
74− download.resume();
75− }
76− }
77−
78− function pauseDownload(click) {
79− var download = getDownloadFor(click);
80− if (download && download.downloading) {
81− download.pause();
82− }
83− }
84−
85− // Return download for a click update.
86− function getDownloadFor(click) {
87− var cust;
88− var dl;
89−
90− for (var i = 0; i<downloads.length; i++) {
91− dl = downloads[i];
92− if (dl.errorMessage || dl.isCompleted) {
93− // Ignore failed and completed downloads.
94− continue;
95− }
96−
97− cust = downloads[i].metadata.custom;
98− if (cust.identifier === click.identifier && cust.revision === click.revision) {
99− return downloads[i];
100− }
101− }
102− return null;
103− }
104−
105− function hasExistingDownload(click) {
106− var existingDownload = getDownloadFor(click);
107− return (existingDownload !== null &&
108− !existingDownload.errorMessage &&
109− !existingDownload.isCompleted);
110− }
111−
112− function retryDownload(click) {
113− return createDownload(click, true);
114− }
115−
116− function createDownload(click, useSignedUrl) {
117− if (hasExistingDownload(click)) {
118− return null;
119− }
120− var downloadUrl = click.downloadUrl;
121− var metadata = {
122− "command": click.command,
123− "title": click.title,
124− "showInIndicator": false
125− };
126− var metadataObj = mdt.createObject(root, metadata);
127− metadataObj.custom = {
128− "identifier": click.identifier,
129− "package-name": click.identifier,
130− "revision": click.revision,
131− };
132−
133− var hdrs = {}
134− if (useSignedUrl) {
135− if (!click.signedDownloadUrl) {
136− console.warn('The signed download URL was empty.');
137− }
138− downloadUrl = click.signedDownloadUrl;
139− } else {
140− // If we're using an unsigned URL, we need to provide this header.
141− hdrs["X-Click-Token"] = click.token;
142− }
143−
144− var singleDownloadObj = sdl.createObject(root, {
145− "autoStart": true,
146− "hash": click.downloadHash,
147− "algorithm": "sha512",
148− "headers": hdrs,
149− "metadata": metadataObj,
150− "revision": click.revision,
151− "identifier": click.identifier
152− });
153− singleDownloadObj.download(downloadUrl);
154− return singleDownloadObj;
155− }
156−
157− function onDownloadProgress(progress) {
158− updateModel.setProgress(this.metadata.custom.identifier,
159− this.metadata.custom.revision,
160− this.progress);
161− }
162−
163− function onDownloadProcessing() {
164− updateModel.processUpdate(this.metadata.custom.identifier,
165− this.metadata.custom.revision);
166− }
167−
168− /* If a update's model has a downloadId, check if UDM knows it. If not,
169− treat this as a failure. Workaround for lp:1603770. */
170− function assertDownloadExist(click) {
171− if (!getDownloadFor(click)) {
172− updateModel.setError(
173− click.identifier, click.revision,
174− i18n.tr("Installation failed")
175− );
176− }
177− }
178−
179− Component {
180− id: sdl
181− SingleDownload {
182− id: download
183− objectName: "singleDownload"
184− property bool _bound: true
185−
186− onDownloadIdChanged: {
187− updateModel.queueUpdate(metadata.custom.identifier,
188− metadata.custom.revision,
189− downloadId);
190− }
191− onStarted: {
192− updateModel.startUpdate(metadata.custom.identifier,
193− metadata.custom.revision);
194− }
195− onProgressChanged: onDownloadProgress.call(download)
196− onProcessing: onDownloadProcessing.call(download)
197− }
198− }
199−
200− Component {
201− id: mdt
202− Metadata {}
203− }
204−}
qml/Wizard/Pages/GlobalUpdateControls.qmldeleted−138
@@ -1,138 +0,0 @@
1−/*
2− * This file is part of system-settings
3− *
4− * Copyright (C) 2016 Canonical Ltd.
5− *
6− * This program is free software: you can redistribute it and/or modify it
7− * under the terms of the GNU General Public License version 3, as published
8− * by the Free Software Foundation.
9− *
10− * This program is distributed in the hope that it will be useful, but
11− * WITHOUT ANY WARRANTY; without even the implied warranties of
12− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13− * PURPOSE. See the GNU General Public License for more details.
14− *
15− * You should have received a copy of the GNU General Public License along
16− * with this program. If not, see <http://www.gnu.org/licenses/>.
17− */
18−
19−import QtQuick 2.4
20−import QtQuick.Layouts 1.1
21−import Ubuntu.Components 1.3
22−import Ubuntu.Components.ListItems 1.3 as ListItems
23−import Ubuntu.SystemSettings.Update 1.0
24−
25−Item {
26− id: g
27−
28− property bool batchMode
29− property int status // A UpdateManager::Status
30− property bool requireRestart: false
31− property int updatesCount: 0
32− property bool online: false
33−
34− property bool hidden: {
35− switch (status) {
36− case UpdateManager.StatusNetworkError:
37− case UpdateManager.StatusServerError:
38− return true;
39− }
40− return !online ||
41− (updatesCount <= 1 && status === UpdateManager.StatusIdle) ||
42− batchMode
43− }
44−
45− signal stop()
46− signal requestInstall()
47− signal install()
48−
49− Behavior on height {
50− UbuntuNumberAnimation {}
51− }
52−
53− RowLayout {
54− id: checking
55− spacing: units.gu(2)
56− anchors {
57− fill: parent
58− margins: units.gu(2)
59− }
60−
61− Behavior on opacity {
62− UbuntuNumberAnimation {}
63− }
64−
65− opacity: visible ? 1 : 0
66− visible: {
67− switch (g.status) {
68− case UpdateManager.StatusCheckingClickUpdates:
69− case UpdateManager.StatusCheckingImageUpdates:
70− case UpdateManager.StatusCheckingAllUpdates:
71− return true;
72− }
73− return false;
74− }
75−
76− ActivityIndicator {
77− running: parent.visible
78− }
79−
80− Label {
81− objectName: "checkLabel"
82− Layout.fillWidth: true
83− elide: Text.ElideRight
84− text: i18n.tr("Checking for updates…")
85− }
86− }
87−
88− RowLayout {
89− id: install
90− anchors {
91− fill: parent
92− margins: units.gu(2)
93− }
94− spacing: units.gu(2)
95−
96− Behavior on opacity {
97− UbuntuNumberAnimation {}
98− }
99−
100− opacity: visible ? 1 : 0
101− visible: {
102− var canInstall = g.status === UpdateManager.StatusIdle;
103− return canInstall && updatesCount > 1;
104− }
105−
106− Label {
107− objectName: "updatesGlobalInfoLabel"
108− // // TRANSLATORS: %1 is number of software updates available.
109− text: i18n.tr("%1 update available",
110− "%1 updates available",
111− updatesCount).arg(updatesCount)
112− Layout.fillWidth: true
113− }
114−
115− Button {
116− objectName: "updatesGlobalInstallButton"
117− text: {
118− if (g.requireRestart === true) {
119− return i18n.tr("Update all…");
120− } else {
121− return i18n.tr("Update all");
122− }
123− }
124− onClicked: g.requestInstall()
125− color: theme.palette.normal.positive
126− strokeColor: "transparent"
127− }
128− }
129−
130− ListItems.ThinDivider {
131− visible: !g.hidden
132− anchors {
133− left: parent.left
134− right: parent.right
135− bottom: parent.bottom
136− }
137− }
138−}
qml/Wizard/Pages/UpdateDelegate.qmldeleted−389
@@ -1,389 +0,0 @@
1−/*
2− * This file is part of system-settings
3− *
4− * Copyright (C) 2016 Canonical Ltd.
5− *
6− * Contact: Jonas G. Drange <jonas.drange@canonical.com>
7− *
8− * This program is free software: you can redistribute it and/or modify it
9− * under the terms of the GNU General Public License version 3, as published
10− * by the Free Software Foundation.
11− *
12− * This program is distributed in the hope that it will be useful, but
13− * WITHOUT ANY WARRANTY; without even the implied warranties of
14− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15− * PURPOSE. See the GNU General Public License for more details.
16− *
17− * You should have received a copy of the GNU General Public License along
18− * with this program. If not, see <http://www.gnu.org/licenses/>.
19− *
20− */
21−
22−import QtQuick 2.4
23−import QtQuick.Layouts 1.1
24−import Ubuntu.Components 1.3
25−import Ubuntu.Components.ListItems 1.3 as ListItems
26−import Ubuntu.Components.Themes 1.3
27−import Ubuntu.SystemSettings.Update 1.0
28−
29−ListItem {
30− id: update
31−
32− property int updateState // This is an Update::State
33− property int kind // This is an Update::Kind
34− property real size
35− property string version
36− property string downloadId
37− property date updatedAt
38− property bool launchable: false
39−
40− property alias name: nameLabel.text
41− property alias error: errorElementDetail.text
42− property alias iconUrl: icon.source
43− property alias progress: progressBar.value
44−
45− signal retry()
46− signal download()
47− signal pause()
48− signal resume()
49− signal install()
50− signal launch()
51−
52− height: layout.height + (divider.visible ? divider.height : 0)
53− Behavior on height {
54− animation: UbuntuNumberAnimation {}
55− }
56−
57− SlotsLayout {
58− id: layout
59− mainSlot: ColumnLayout {
60− spacing: units.gu(1)
61− // Width the parent, minus the icon and some padding
62− width: parent.width - (icon.width + (layout.padding.top * 3))
63−
64− RowLayout {
65− spacing: units.gu(2)
66−
67− Label {
68− id: nameLabel
69− verticalAlignment: Text.AlignVCenter
70− elide: Text.ElideMiddle
71− Layout.fillWidth: true
72− Layout.fillHeight: true
73− }
74−
75− Button {
76− id: button
77− objectName: "updateButton"
78− Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
79− visible: {
80− switch (updateState) {
81− case Update.StateInstalled:
82− return launchable;
83− default:
84− return true;
85− }
86− }
87− enabled: {
88− switch(updateState) {
89− case Update.StateAvailable:
90− case Update.StateDownloading:
91− case Update.StateDownloadingAutomatically:
92− case Update.StateDownloadPaused:
93− case Update.StateAutomaticDownloadPaused:
94− case Update.StateInstallPaused:
95− case Update.StateDownloaded:
96− case Update.StateFailed:
97− return true;
98−
99− // Enabled if installed and a click app (can launch).
100− case Update.StateInstalled:
101− return kind === Update.KindClick;
102−
103− case Update.StateInstalling:
104− case Update.StateInstallingAutomatically:
105− case Update.StateUnavailable:
106− case Update.StateInstallFinished:
107− case Update.StateQueuedForDownload:
108− case Update.StateUnknown:
109− default:
110− return false;
111− }
112− }
113− text: {
114− switch(updateState) {
115− case Update.StateUnknown:
116− case Update.StateUnavailable:
117− case Update.StateFailed:
118− return i18n.tr("Retry");
119−
120− case Update.StateAvailable:
121− case Update.StateQueuedForDownload:
122− if (update.kind === Update.KindClick) {
123− return i18n.tr("Update");
124− } else {
125− return i18n.tr("Download");
126− }
127−
128− case Update.StateDownloadPaused:
129− case Update.StateAutomaticDownloadPaused:
130− case Update.StateInstallPaused:
131− return i18n.tr("Resume");
132−
133− case Update.StateDownloading:
134− case Update.StateDownloadingAutomatically:
135− case Update.StateInstalling:
136− case Update.StateInstallingAutomatically:
137− case Update.StateInstallFinished:
138− return i18n.tr("Pause");
139−
140− case Update.StateDownloaded:
141− if (kind === Update.KindImage) {
142− return i18n.tr("Install…");
143− } else {
144− return i18n.tr("Install");
145− }
146−
147− case Update.StateInstalled:
148− return i18n.tr("Open");
149−
150− default:
151− console.error("Unknown update state", updateState);
152− }
153− }
154−
155− onClicked: {
156− switch (updateState) {
157−
158− // Retries.
159− case Update.StateUnknown:
160− case Update.StateUnavailable:
161− case Update.StateFailed:
162− update.retry();
163− break;
164−
165− case Update.StateDownloadPaused:
166− case Update.StateAutomaticDownloadPaused:
167− case Update.StateInstallPaused:
168− update.resume();
169− break;
170−
171− case Update.StateAvailable:
172− if (kind === Update.KindClick) {
173− update.install();
174− } else {
175− update.download();
176− }
177− break;
178−
179− case Update.StateDownloaded:
180− update.install();
181− break;
182−
183− case Update.StateDownloading:
184− case Update.StateDownloadingAutomatically:
185− case Update.StateInstalling:
186− case Update.StateInstallingAutomatically:
187− update.pause();
188− break;
189−
190− case Update.StateInstalled:
191− update.launch();
192− break;
193− }
194− }
195− } // Button
196− } // Name/button RowLayout
197−
198− RowLayout {
199− spacing: units.gu(2)
200−
201− Label {
202− id: downloadLabel
203− objectName: "updateDownloadLabel"
204−
205− Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
206−
207− visible: text !== ""
208−
209− fontSize: "small"
210− text: {
211− switch (updateState) {
212−
213− case Update.StateInstalling:
214− case Update.StateInstallingAutomatically:
215− case Update.StateInstallPaused:
216− return i18n.tr("Installing");
217−
218− case Update.StateInstallPaused:
219− case Update.StateDownloadPaused:
220− return i18n.tr("Paused");
221−
222− case Update.StateQueuedForDownload:
223− return i18n.tr("Waiting to download");
224−
225− case Update.StateDownloading:
226− return i18n.tr("Downloading");
227−
228− default:
229− return "";
230− }
231− }
232− }
233−
234− Label {
235− id: statusLabel
236− objectName: "updateStatusLabel"
237−
238− visible: text !== ""
239− Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
240− Layout.fillWidth: true
241−
242− horizontalAlignment: Text.AlignRight
243− fontSize: "small"
244−
245− text: {
246−
247− function humanFileSize(bytes, si) {
248− var thresh = si ? 1000 : 1024;
249− if(Math.abs(bytes) < thresh) {
250− return bytes + ' B';
251− }
252− var units = si
253− ? ['kB','MB','GB','TB','PB','EB','ZB','YB']
254− : ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
255− var u = -1;
256− do {
257− bytes /= thresh;
258− ++u;
259− } while(Math.abs(bytes) >= thresh && u < units.length - 1);
260− return bytes.toFixed(1)+' '+units[u];
261− }
262−
263− switch (updateState) {
264− case Update.StateAvailable:
265− return humanFileSize(size, 1000);
266−
267− case Update.StateDownloading:
268− case Update.StateDownloadingAutomatically:
269− case Update.StateDownloadPaused:
270− case Update.StateAutomaticDownloadPaused:
271− var down = humanFileSize((size / 100) * progress, 1000);
272− var left = humanFileSize(size, 1000);
273− if (progress > 100) {
274− return left;
275− } else {
276− /* TRANSLATORS: %1 is the human readable amount
277− of bytes downloaded, and %2 is the total to be
278− downloaded. */
279− return i18n.tr("%1 of %2").arg(down).arg(left);
280− }
281−
282− case Update.StateDownloaded:
283− return i18n.tr("Downloaded");
284−
285− case Update.StateInstallFinished:
286− return i18n.tr("Installed");
287−
288− case Update.StateInstalled:
289− /* TRANSLATORS: %1 is the date at which this
290− update was applied. */
291− return i18n.tr("Updated %1").arg(
292− updatedAt.toLocaleDateString(Qt.locale(), "d MMMM")
293− );
294− default:
295− return "";
296− }
297− }
298−
299− /* Seems AlignRight causes a rendering issue that can be
300− fixed by doing an explicit doLayout. */
301− Component.onCompleted: doLayout()
302− }
303− }
304−
305− Column {
306− id: error
307− objectName: "updateError"
308− spacing: units.gu(1)
309− height: childrenRect.height
310− Layout.fillWidth: true
311− visible: errorElementDetail.text
312−
313− Label {
314− id: errorElementTitle
315− text: i18n.tr("Update failed")
316− color: UbuntuColors.red
317− }
318−
319− Label {
320− id: errorElementDetail
321− anchors { left: parent.left; right: parent.right }
322− fontSize: "small"
323− wrapMode: Text.WrapAnywhere
324− }
325− } // Error column
326−
327− ProgressBar {
328− id: progressBar
329− objectName: "updateProgressbar"
330−
331− visible: {
332− switch (updateState) {
333− case Update.StateQueuedForDownload:
334− case Update.StateDownloading:
335− case Update.StateDownloadPaused:
336− case Update.StateInstalling:
337− case Update.StateInstallPaused:
338− return true;
339−
340− default:
341− return false;
342− }
343− }
344− Layout.maximumHeight: units.gu(0.5)
345− Layout.fillWidth: true
346− indeterminate: update.progress < 0 || update.progress > 100
347− minimumValue: 0
348− maximumValue: 100
349− showProgressPercentage: false
350−
351− Behavior on value {
352− animation: UbuntuNumberAnimation {}
353− }
354− } // Progress bar
355− } // Layout for the rest of the stuff
356−
357− Item {
358− SlotsLayout.position: SlotsLayout.Leading;
359− width: units.gu(4)
360− height: width
361−
362− Image {
363− id: icon
364− visible: kind === Update.KindImage && !fallback.visible
365− anchors.fill: parent
366− asynchronous: true
367− smooth: true
368− mipmap: true
369− }
370−
371− UbuntuShape {
372− id: shape
373− visible: kind !== Update.KindImage && !fallback.visible
374− anchors.fill: parent
375− source: icon
376− }
377−
378− Image {
379− id: fallback
380− visible: icon.status === Image.Error
381− source : Qt.resolvedUrl("/usr/share/icons/suru/apps/scalable/ubuntu-logo-symbolic.svg")
382− anchors.fill: parent
383− asynchronous: true
384− smooth: true
385− mipmap: true
386− }
387− }
388− }
389−}
tests/mocks/Ubuntu/Connectivity/networking-status.cpp+7−1
@@ -39,7 +39,7 @@ NetworkingStatus::limitations() const
3939NetworkingStatus::Status
4040NetworkingStatus::status() const
4141{
42− return Status::Online;
42+ return m_status;
4343}
4444
4545bool
@@ -62,3 +62,9 @@ void NetworkingStatus::setLimitedBandwidth(bool limited)
6262 m_limitations.remove(m_limitations.indexOf(Limitations::Bandwith));
6363 }
6464}
65+
66+void NetworkingStatus::setStatus(Status status)
67+{
68+ m_status = status;
69+ Q_EMIT statusChanged(m_status);
70+}
tests/mocks/Ubuntu/Connectivity/networking-status.h+5−3
@@ -27,7 +27,7 @@ class Q_DECL_EXPORT NetworkingStatus : public QObject
2727 Q_DISABLE_COPY(NetworkingStatus)
2828
2929 Q_PROPERTY(QVector<Limitations> limitations READ limitations NOTIFY limitationsChanged)
30− Q_PROPERTY(Status status READ status NOTIFY statusChanged)
30+ Q_PROPERTY(Status status READ status WRITE setStatus NOTIFY statusChanged)
3131 Q_PROPERTY(bool online READ online NOTIFY onlineChanged)
3232 Q_PROPERTY(bool limitedBandwith READ limitedBandwith WRITE setLimitedBandwidth NOTIFY limitedBandwithChanged)
3333
@@ -48,21 +48,23 @@ public:
4848 Q_ENUM(Status)
4949
5050 QVector<Limitations> limitations() const;
51− Status status() const;
51+ NetworkingStatus::Status status() const;
5252 bool online() const;
5353 bool limitedBandwith() const;
5454
5555 // Only in the fake one
5656 void setLimitedBandwidth(bool limited);
57+ void setStatus(NetworkingStatus::Status status);
5758
5859Q_SIGNALS:
5960 void limitationsChanged();
60− void statusChanged(Status value);
61+ void statusChanged(NetworkingStatus::Status status);
6162 void onlineChanged(bool value);
6263 void limitedBandwithChanged(bool value);
6364
6465private:
6566 QVector<NetworkingStatus::Limitations> m_limitations;
67+ NetworkingStatus::Status m_status;
6668};
6769
6870Q_DECLARE_METATYPE(NetworkingStatus::Limitations)
tests/mocks/Ubuntu/SystemSettings/CMakeLists.txt+1
@@ -2,3 +2,4 @@ add_subdirectory(LanguagePlugin)
22add_subdirectory(SecurityPrivacy)
33add_subdirectory(TimeDate)
44add_subdirectory(Diagnostics)
5+add_subdirectory(Update)
tests/mocks/Ubuntu/SystemSettings/Update/CMakeLists.txtnew file+1
@@ -0,0 +1 @@
1+add_unity8_mock(Ubuntu.SystemSettings.Update 1.0 Ubuntu/SystemSettings/Update)
tests/mocks/Ubuntu/SystemSettings/Update/MockSystemImage.qmlnew file+30
@@ -0,0 +1,30 @@
1+/*
2+ * Copyright (C) 2019 The UBports Foundation
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+pragma Singleton
18+import QtQuick 2.0
19+
20+Item {
21+ id: root
22+
23+ property int targetBuildNumber: 0
24+ property bool checkingForUpdates: false
25+ property bool updateAvailable: false
26+
27+ signal updateDownloaded()
28+ signal applyUpdate()
29+ signal checkForUpdate()
30+}
tests/mocks/Ubuntu/SystemSettings/Update/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module Ubuntu.SystemSettings.Update
2+singleton SystemImage 1.0 MockSystemImage.qml
tests/mocks/Wizard/MockSystem.cpp+12−3
@@ -36,6 +36,17 @@ void MockSystem::setWizardEnabled(bool enabled)
3636 Q_EMIT wizardEnabledChanged();
3737}
3838
39+bool MockSystem::isUpdate() const
40+{
41+ return m_isUpdate;
42+}
43+
44+void MockSystem::setIsUpdate(bool update)
45+{
46+ m_isUpdate = update;
47+ Q_EMIT isUpdateChanged();
48+}
49+
3950void MockSystem::updateSessionLocale(const QString &locale)
4051{
4152 Q_EMIT updateSessionLocaleCalled(locale);
@@ -43,7 +54,5 @@ void MockSystem::updateSessionLocale(const QString &locale)
4354
4455void MockSystem::skipUntilFinishedPage()
4556{
46− QSettings settings;
47− settings.setValue(QStringLiteral("Wizard/SkipUntilFinishedPage"), true);
48− settings.sync();
57+ Q_EMIT wouldHaveSetSkipUntilFinish();
4958}
tests/mocks/Wizard/MockSystem.h+7−1
@@ -24,12 +24,15 @@ class MockSystem : public QObject
2424{
2525 Q_OBJECT
2626 Q_PROPERTY(bool wizardEnabled READ wizardEnabled WRITE setWizardEnabled NOTIFY wizardEnabledChanged)
27+ Q_PROPERTY(bool isUpdate READ isUpdate WRITE setIsUpdate NOTIFY isUpdateChanged)
2728
2829public:
2930 MockSystem();
3031
3132 bool wizardEnabled() const;
3233 void setWizardEnabled(bool enabled);
34+ bool isUpdate() const;
35+ void setIsUpdate(bool enabled); // only in mock
3336
3437public Q_SLOTS:
3538 void updateSessionLocale(const QString &locale);
@@ -40,12 +43,15 @@ public Q_SLOTS:
4043
4144Q_SIGNALS:
4245 void wizardEnabledChanged();
46+ void isUpdateChanged(); // only in mock
4347 void updateSessionLocaleCalled(const QString &locale); // only in mock
48+ void wouldHaveSetSkipUntilFinish(); // only in mock
4449
4550private:
4651 Q_DISABLE_COPY(MockSystem)
4752
48− bool m_wizardEnabled;
53+ bool m_wizardEnabled = true;
54+ bool m_isUpdate = false;
4955};
5056
5157#endif
tests/qmltests/Wizard/tst_Wizard.qml+96−92
@@ -20,7 +20,9 @@ import AccountsService 0.1
2020import MeeGo.QOfono 0.2
2121import QMenuModel 0.1
2222import Ubuntu.Components 1.3
23+import Ubuntu.Connectivity 1.0
2324import Ubuntu.SystemSettings.SecurityPrivacy 1.0
25+import Ubuntu.SystemSettings.Update 1.0
2426import Ubuntu.SystemSettings.TimeDate 1.1
2527import Unity.Test 0.1 as UT
2628import Wizard 0.1
@@ -86,34 +88,22 @@ Item {
8688 signalName: "keymapsChanged"
8789 }
8890
89− function setup() {
90− i18n.language = "en";
91− MockQOfono.setModems(["sim1"], [false], []);
92− MockQOfono.available = true;
93− MockQOfono.ready = true;
94− System.wizardEnabled = true;
95−
96− updateSessionLanguageSpy.clear();
97− setSecuritySpy.clear();
98− activateLocationSpy.clear();
99− activateGPSSpy.clear();
100− timezoneSpy.clear();
101− kbdLayoutSpy.clear();
102−
103− ActionData.data = {
104− "location-detection-enabled": {
105− 'valid': true,
106− 'state': false
107− },
108− "gps-detection-enabled": {
109− 'valid': true,
110− 'state': false
111− }
112− };
91+ SignalSpy {
92+ id: updateDownloadedSpy
93+ target: SystemImage
94+ signalName: "updateDownloaded"
11395 }
11496
115− Component.onCompleted: {
116− setup();
97+ SignalSpy {
98+ id: applyUpdateSpy
99+ target: SystemImage
100+ signalName: "applyUpdate"
101+ }
102+
103+ SignalSpy {
104+ id: skipUntilFinishedSpy
105+ target: System
106+ signalName: "wouldHaveSetSkipUntilFinish"
117107 }
118108
119109 UT.UnityTestCase {
@@ -123,6 +113,52 @@ Item {
123113
124114 property Item wizard: wizardLoader.status === Loader.Ready ? wizardLoader.item : null
125115
116+ function init() {
117+ i18n.language = "en";
118+ MockQOfono.setModems(["sim1"], [false], []);
119+ MockQOfono.available = true;
120+ MockQOfono.ready = true;
121+
122+ System.wizardEnabled = true;
123+ System.isUpdate = false;
124+
125+ SystemImage.updateAvailable = false;
126+ SystemImage.checkingForUpdates = false;
127+
128+ NetworkingStatus.status = NetworkingStatus.Offline;
129+
130+ updateSessionLanguageSpy.clear();
131+ setSecuritySpy.clear();
132+ activateLocationSpy.clear();
133+ activateGPSSpy.clear();
134+ timezoneSpy.clear();
135+ kbdLayoutSpy.clear();
136+ updateDownloadedSpy.clear();
137+ applyUpdateSpy.clear();
138+ skipUntilFinishedSpy.clear();
139+
140+ // reload our test subject to get it in a fresh state once again
141+ wizardLoader.active = true;
142+
143+ tryCompare(wizardLoader, "status", Loader.Ready);
144+
145+ var pages = findChild(wizard, "wizardPages")
146+ var security = findInvisibleChild(pages, "securityPrivacy");
147+ security.setSecurity("", "", UbuntuSecurityPrivacyPanel.Swipe);
148+ setSecuritySpy.target = security;
149+
150+ ActionData.data = {
151+ "location-detection-enabled": {
152+ 'valid': true,
153+ 'state': false
154+ },
155+ "gps-detection-enabled": {
156+ 'valid': true,
157+ 'state': false
158+ }
159+ };
160+ }
161+
126162 function cleanup() {
127163 wizardLoader.itemDestroyed = false;
128164
@@ -137,18 +173,6 @@ Item {
137173 // iteration to do its work. So to ensure the reload, we will wait until the
138174 // Shell instance gets destroyed.
139175 tryCompare(wizardLoader, "itemDestroyed", true);
140−
141− // reload our test subject to get it in a fresh state once again
142− wizardLoader.active = true;
143−
144− tryCompare(wizardLoader, "status", Loader.Ready);
145−
146− var pages = findChild(wizard, "wizardPages");
147− var security = findInvisibleChild(pages, "securityPrivacy");
148− security.setSecurity("", "", UbuntuSecurityPrivacyPanel.Swipe);
149− setSecuritySpy.target = security;
150−
151− setup();
152176 }
153177
154178 function waitForPage(name) {
@@ -166,13 +190,10 @@ Item {
166190 return stack.currentPage;
167191 }
168192
169− function goToPage(name, skipSim, skipLocation) {
193+ function goToPage(name, skipSim) {
170194 if (skipSim === undefined) {
171195 skipSim = false;
172196 }
173− if (skipLocation === undefined) {
174− skipLocation = false;
175− }
176197
177198 var page = waitForPage("languagePage");
178199 if (name === page.objectName) return page;
@@ -192,12 +213,6 @@ Item {
192213 if (name === page.objectName) return page;
193214 tap(findChild(page, "forwardButton"));
194215
195− if (!skipLocation) {
196− page = waitForPage("locationPage");
197− if (name === page.objectName) return page;
198− tap(findChild(page, "forwardButton"));
199− }
200−
201216 page = waitForPage("tzPage");
202217 if (name === page.objectName) return page;
203218 waitUntilTransitionsEnd(page);
@@ -211,10 +226,23 @@ Item {
211226 if (name === page.objectName) return page;
212227 tap(findChild(page, "nameInput"));
213228 typeString("foobar");
214− tap(findChild(page, "forwardButton"));
215229
216− page = waitForPage("passwdPage");
230+ if (name != "passwdPage") {
231+ // Skip the password prompt
232+ var pages = findChild(wizard, "wizardPages");
233+ var security = findInvisibleChild(pages, "securityPrivacy");
234+ security.setSecurity("", "", UbuntuSecurityPrivacyPanel.Passphrase);
235+ tap(findChild(page, "forwardButton"));
236+ } else {
237+ tap(findChild(page, "forwardButton"));
238+ page = waitForPage("passwdPage");
239+ if (name === page.objectName) return page;
240+ tap(findChild(page, "forwardButton"));
241+ }
242+
243+ page = waitForPage("systemUpdatePage");
217244 if (name === page.objectName) return page;
245+ waitUntilTransitionsEnd(page);
218246 tap(findChild(page, "forwardButton"));
219247
220248 page = waitForPage("finishedPage");
@@ -323,8 +351,6 @@ Item {
323351 enterPasscode("1111");
324352
325353 // now finish up
326− page = waitForPage("systemUpdatePage");
327− tap(findChild(page, "forwardButton"));
328354 page = waitForPage("finishedPage");
329355 waitUntilTransitionsEnd(page);
330356 tap(findChild(page, "finishButton"));
@@ -363,8 +389,6 @@ Item {
363389 tap(continueButton);
364390
365391 // now finish up
366− page = waitForPage("systemUpdatePage");
367− tap(findChild(page, "forwardButton"));
368392 page = waitForPage("finishedPage");
369393 waitUntilTransitionsEnd(page);
370394 tap(findChild(page, "finishButton"));
@@ -381,8 +405,6 @@ Item {
381405
382406 // now finish up
383407 tap(findChild(page, "forwardButton"));
384− page = waitForPage("systemUpdatePage");
385− tap(findChild(page, "forwardButton"));
386408 page = waitForPage("finishedPage");
387409 waitUntilTransitionsEnd(page);
388410 tap(findChild(page, "finishButton"));
@@ -390,42 +412,6 @@ Item {
390412 tryCompare(setSecuritySpy, "count", 0); // not called for swipe method
391413 }
392414
393− function test_locationGpsOnly() {
394− var page = goToPage("locationPage");
395− var gpsCheck = findChild(page, "gpsCheckLabel");
396− var nopeCheck = findChild(page, "nopeCheckLabel");
397−
398− var locationActionGroup = findInvisibleChild(page, "locationActionGroup");
399− activateLocationSpy.target = locationActionGroup.location;
400− activateGPSSpy.target = locationActionGroup.gps;
401−
402− tap(gpsCheck);
403− tryCompare(gpsCheck, "checked", true);
404− tryCompare(nopeCheck, "checked", false);
405−
406− tap(findChild(page, "forwardButton"));
407− tryCompare(activateLocationSpy, "count", 1)
408− tryCompare(activateGPSSpy, "count", 1)
409− }
410−
411− function test_locationNope() {
412− var page = goToPage("locationPage");
413− var gpsCheck = findChild(page, "gpsCheckLabel");
414− var nopeCheck = findChild(page, "nopeCheckLabel");
415−
416− var locationActionGroup = findInvisibleChild(page, "locationActionGroup");
417− activateLocationSpy.target = locationActionGroup.location;
418− activateGPSSpy.target = locationActionGroup.gps;
419−
420− tap(nopeCheck);
421− tryCompare(gpsCheck, "checked", false);
422− tryCompare(nopeCheck, "checked", true);
423−
424− tap(findChild(page, "forwardButton"));
425− tryCompare(activateLocationSpy, "count", 0);
426− tryCompare(activateGPSSpy, "count", 0);
427− }
428−
429415 function test_timezonePage() {
430416 var page = goToPage("tzPage");
431417 verify(page);
@@ -452,6 +438,24 @@ Item {
452438 compare(timezoneSpy.signalArguments[0][1], "Belfast");
453439 }
454440
441+ function test_systemUpdatePage() {
442+
443+ compare(updateDownloadedSpy.count, 0);
444+ SystemImage.updateAvailable = true;
445+ SystemImage.targetBuildNumber = 42;
446+ SystemImage.updateDownloaded();
447+ compare(updateDownloadedSpy.count, 1);
448+
449+ var page = goToPage("systemUpdatePage");
450+ var updateButton = findChild(page, "installButton");
451+
452+ compare(skipUntilFinishedSpy.count, 0);
453+ compare(applyUpdateSpy.count, 0);
454+ tap(updateButton);
455+ compare(skipUntilFinishedSpy.count, 1);
456+ compare(applyUpdateSpy.count, 1);
457+ }
458+
455459 function test_accountPage() {
456460 var page = goToPage("accountPage");
457461 var forwardButton = findChild(page, "forwardButton");