on xenial help sign in
Implement an optional system update feature during OOBE wizard (LP: #1580785)

Approved by: Michał Sawicz

commit926c95150c6eccd7c216686021d57b6edaca6319 authorLukáš Tinkl <lukas.tinkl@canonical.com> dateThu 11 Aug 2016, 06:25 (10 years ago) parent1d28d6ba2042d7
17 files changed+477−29
plugins/Ubuntu/SystemImage/CMakeLists.txt+5 plugins/Ubuntu/SystemImage/SystemImage.cpp+127−5 plugins/Ubuntu/SystemImage/SystemImage.h+52−2 plugins/Ubuntu/SystemImage/plugin.cpp+3−9 plugins/Ubuntu/SystemImage/plugin.h+1−1 plugins/Wizard/PageList.cpp+16−1 plugins/Wizard/System.cpp+9−1 plugins/Wizard/System.h+5−1 qml/Wizard/Page.qml+1−1 qml/Wizard/Pages/30-wifi.qml+8−4 qml/Wizard/Pages/77-system-update.qml+156 tests/mocks/Ubuntu/SystemImage/MockSystemImage.cpp+24−1 tests/mocks/Ubuntu/SystemImage/MockSystemImage.h+22−2 tests/mocks/Wizard/MockSystem.cpp+8 tests/mocks/Wizard/MockSystem.h+4 tests/plugins/Wizard/tst_pagelist.cpp+30−1 tests/qmltests/Wizard/tst_Wizard.qml+6
plugins/Ubuntu/SystemImage/CMakeLists.txt+5
@@ -1,3 +1,7 @@
1+include_directories(
2+ ${GLIB_INCLUDE_DIRS}
3+)
4+
15set(SYSTEMIMAGE_SOURCES
26 plugin.cpp
37 SystemImage.cpp
@@ -6,5 +10,6 @@ set(SYSTEMIMAGE_SOURCES
610add_library(SystemImage MODULE ${SYSTEMIMAGE_SOURCES})
711
812qt5_use_modules(SystemImage Qml DBus Core)
13+target_link_libraries(SystemImage ${GLIB_LIBRARIES})
914
1015add_unity8_plugin(Ubuntu.SystemImage 0.1 Ubuntu/SystemImage TARGETS SystemImage)
plugins/Ubuntu/SystemImage/SystemImage.cpp+127−5
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,20 +14,142 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−#include "SystemImage.h"
1817#include <QDBusConnection>
1918#include <QDBusPendingCall>
19+#include <QDBusReply>
20+#include <QDebug>
21+
22+#include <glib.h>
23+
24+#include "SystemImage.h"
25+
26+#define SYSTEMIMAGE_SERVICE QStringLiteral("com.canonical.SystemImage")
27+#define SYSTEMIMAGE_PATH QStringLiteral("/Service")
28+#define SYSTEMIMAGE_IFACE QStringLiteral("com.canonical.SystemImage")
29+
30+Q_LOGGING_CATEGORY(SYSTEMIMAGEPLUGIN, "unity8.systemimage", QtWarningMsg)
31+
32+#define DEBUG_MSG qCDebug(SYSTEMIMAGEPLUGIN).nospace().noquote() << Q_FUNC_INFO
33+#define WARNING_MSG qCWarning(SYSTEMIMAGEPLUGIN).nospace().noquote() << Q_FUNC_INFO
2034
2135SystemImage::SystemImage(QObject *parent)
2236 : QObject(parent)
2337{
38+ QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("UpdateAvailableStatus"),
39+ this, SLOT(onUpdateAvailableStatus(bool,bool,QString,int,QString,QString)));
40+ QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("UpdateDownloaded"),
41+ this, SLOT(onUpdateDownloaded()));
42+ QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("UpdateFailed"),
43+ this, SLOT(onUpdateFailed(int,QString)));
44+ QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("Applied"),
45+ this, SLOT(onUpdateApplied(bool)));
46+ QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("Rebooting"),
47+ this, SLOT(onRebooting(bool)));
48+}
49+
50+void SystemImage::checkForUpdate()
51+{
52+ DEBUG_MSG << "Checking for update";
53+ const QDBusMessage msg = QDBusMessage::createMethodCall(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE,
54+ QStringLiteral("CheckForUpdate"));
55+ QDBusConnection::systemBus().asyncCall(msg);
56+}
57+
58+void SystemImage::applyUpdate()
59+{
60+ DEBUG_MSG << "Applying update";
61+ setUpdateApplying(true);
62+
63+ const QDBusMessage msg = QDBusMessage::createMethodCall(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE,
64+ QStringLiteral("ApplyUpdate"));
65+ QDBusConnection::systemBus().asyncCall(msg);
2466}
2567
2668void SystemImage::factoryReset()
2769{
28− const QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("com.canonical.SystemImage"),
29− QStringLiteral("/Service"),
30− QStringLiteral("com.canonical.SystemImage"),
70+ const QDBusMessage msg = QDBusMessage::createMethodCall(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE,
3171 QStringLiteral("FactoryReset"));
3272 QDBusConnection::systemBus().asyncCall(msg);
3373}
74+
75+void SystemImage::onUpdateAvailableStatus(bool is_available, bool downloading, const QString &available_version, int update_size,
76+ const QString &last_update_date, const QString &error_reason)
77+{
78+ DEBUG_MSG << "A new update is " << (is_available ? "" : "NOT") << "available";
79+
80+ if (is_available == m_updateAvailable) {
81+ return;
82+ }
83+
84+ m_updateAvailable = is_available;
85+ m_downloading = downloading;
86+ m_availableVersion = available_version;
87+ m_updateSize = formatSize(update_size);
88+ m_lastUpdateDate = last_update_date;
89+ m_errorReason = error_reason;
90+ Q_EMIT updateAvailableStatus();
91+
92+ DEBUG_MSG << "Downloading: " << downloading << ", version: " << available_version << ", last update: " << last_update_date <<
93+ ", size: " << m_updateSize;
94+}
95+
96+void SystemImage::onUpdateDownloaded()
97+{
98+ DEBUG_MSG << "Update downloaded";
99+
100+ m_downloaded = true;
101+ Q_EMIT updateDownloadedChanged();
102+}
103+
104+void SystemImage::onUpdateFailed(int /*consecutive_failure_count*/, const QString &last_reason)
105+{
106+ WARNING_MSG << "System Update failed: " << last_reason;
107+ setUpdateApplying(false);
108+}
109+
110+void SystemImage::onUpdateApplied(bool applied)
111+{
112+ DEBUG_MSG << "System Update applied with status: " << applied;
113+ setUpdateApplying(false);
114+ if (applied) {
115+ resetUpdateStatus();
116+ Q_EMIT updateAvailableStatus();
117+ }
118+}
119+
120+void SystemImage::onRebooting(bool status)
121+{
122+ setUpdateApplying(false);
123+ DEBUG_MSG << "Rebooting: " << status;
124+}
125+
126+void SystemImage::setUpdateApplying(bool status)
127+{
128+ if (status != m_updateApplying) {
129+ m_updateApplying = status;
130+ Q_EMIT updateApplyingChanged();
131+ }
132+}
133+
134+void SystemImage::resetUpdateStatus()
135+{
136+ m_updateAvailable = false;
137+ m_updateApplying = false;
138+ m_downloading = false;
139+ m_downloaded = false;
140+ m_availableVersion.clear();
141+ m_updateSize.clear();
142+ m_lastUpdateDate.clear();
143+ m_errorReason.clear();
144+}
145+
146+QString SystemImage::formatSize(quint64 size) const
147+{
148+ guint64 g_size = size;
149+
150+ gchar * formatted_size = g_format_size(g_size);
151+ QString q_formatted_size = QString::fromLocal8Bit(formatted_size);
152+ g_free(formatted_size);
153+
154+ return q_formatted_size;
155+}
plugins/Ubuntu/SystemImage/SystemImage.h+52−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -18,17 +18,67 @@
1818#define SYSTEMIMAGE_H
1919
2020#include <QObject>
21+#include <QLoggingCategory>
22+
23+Q_DECLARE_LOGGING_CATEGORY(SYSTEMIMAGEPLUGIN)
2124
2225class SystemImage : public QObject
2326{
2427 Q_OBJECT
2528 Q_DISABLE_COPY(SystemImage)
2629
30+ Q_PROPERTY(bool updateAvailable READ updateAvailable NOTIFY updateAvailableStatus)
31+ Q_PROPERTY(bool updateDownloading READ updateDownloading NOTIFY updateAvailableStatus)
32+ Q_PROPERTY(QString availableVersion READ availableVersion NOTIFY updateAvailableStatus)
33+ Q_PROPERTY(QString updateSize READ updateSize NOTIFY updateAvailableStatus)
34+ Q_PROPERTY(bool updateApplying READ updateApplying NOTIFY updateApplyingChanged)
35+ Q_PROPERTY(bool updateDownloaded READ updateDownloaded NOTIFY updateDownloadedChanged)
36+
2737public:
28− explicit SystemImage(QObject *parent = 0);
38+ explicit SystemImage(QObject *parent = nullptr);
2939 ~SystemImage() = default;
3040
41+ bool updateAvailable() const { return m_updateAvailable; }
42+ bool updateDownloading() const { return m_downloading; }
43+ QString availableVersion() const { return m_availableVersion; }
44+ QString updateSize() const { return m_updateSize; }
45+ bool updateApplying() const { return m_updateApplying; }
46+ bool updateDownloaded() const { return m_downloaded; }
47+
48+public Q_SLOTS:
49+ Q_INVOKABLE void checkForUpdate();
50+ Q_INVOKABLE void applyUpdate();
3151 Q_INVOKABLE void factoryReset();
52+
53+private Q_SLOTS:
54+ void onUpdateAvailableStatus(bool is_available, bool updateDownloading, const QString &available_version,
55+ int update_size, const QString &last_update_date, const QString &error_reason);
56+ void onUpdateDownloaded();
57+ void onUpdateFailed(int consecutive_failure_count, const QString & last_reason);
58+ void onUpdateApplied(bool applied);
59+ void onRebooting(bool status);
60+
61+Q_SIGNALS:
62+ void updateAvailableStatus();
63+ void updateDownloadedChanged();
64+ void updateApplyingChanged();
65+
66+private Q_SLOTS:
67+ void setUpdateApplying(bool status);
68+
69+private:
70+ void resetUpdateStatus();
71+ QString formatSize(quint64 size) const;
72+
73+private:
74+ bool m_updateAvailable = false;
75+ bool m_updateApplying = false;
76+ bool m_downloading = false;
77+ bool m_downloaded = false;
78+ QString m_availableVersion;
79+ QString m_updateSize;
80+ QString m_lastUpdateDate;
81+ QString m_errorReason;
3282};
3383
3484#endif // SYSTEMIMAGE_H
plugins/Ubuntu/SystemImage/plugin.cpp+3−9
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -19,16 +19,10 @@
1919
2020#include <QtQml>
2121
22−static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
23−{
24− Q_UNUSED(engine)
25− Q_UNUSED(scriptEngine)
26− return new SystemImage();
27−}
28−
2922void BackendPlugin::registerTypes(const char *uri)
3023{
3124 Q_ASSERT(uri == QLatin1String("Ubuntu.SystemImage"));
3225
33− qmlRegisterSingletonType<SystemImage>(uri, 0, 1, "SystemImage", service_provider);
26+ qmlRegisterSingletonType<SystemImage>(uri, 0, 1, "SystemImage",
27+ [](QQmlEngine*, QJSEngine*) -> QObject* { return new SystemImage; });
3428}
plugins/Ubuntu/SystemImage/plugin.h+1−1
@@ -22,7 +22,7 @@
2222class BackendPlugin : public QQmlExtensionPlugin
2323{
2424 Q_OBJECT
25− Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
2626
2727public:
2828 void registerTypes(const char *uri) override;
plugins/Wizard/PageList.cpp+16−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical Ltd.
2+ * Copyright (C) 2014-2016 Canonical Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it
55 * under the terms of the GNU General Public License version 3, as published
@@ -34,6 +34,7 @@
3434#include <QDir>
3535#include <QSet>
3636#include <QStandardPaths>
37+#include <QSettings>
3738
3839PageList::PageList(QObject *parent)
3940 : QObject(parent),
@@ -66,6 +67,20 @@ PageList::PageList(QObject *parent)
6667 Q_FOREACH(const QString &page, disabledPages) {
6768 m_pages.remove(page);
6869 }
70+
71+ // If there was a system update installed, skip until the last page to just greet the user
72+ QSettings settings;
73+ if (settings.value(QStringLiteral("Wizard/SkipUntilFinishedPage")).toBool()) {
74+ const QString lastPage = m_pages.lastKey();
75+ Q_FOREACH(const QString &page, m_pages.keys()) {
76+ if (Q_UNLIKELY(page != lastPage)) {
77+ m_pages.remove(page);
78+ }
79+ }
80+
81+ // ... and reset it again for the next run
82+ settings.remove(QStringLiteral("Wizard/SkipUntilFinishedPage"));
83+ }
6984}
7085
7186QStringList PageList::entries() const
plugins/Wizard/System.cpp+9−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014-2015 Canonical Ltd.
2+ * Copyright (C) 2014-2016 Canonical Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it
55 * under the terms of the GNU General Public License version 3, as published
@@ -26,6 +26,7 @@
2626#include <QMap>
2727#include <QProcess>
2828#include <QDebug>
29+#include <QSettings>
2930
3031System::System()
3132 : QObject()
@@ -110,3 +111,10 @@ void System::updateSessionLocale(const QString &locale)
110111 initctl restart --no-wait indicator-messages; \
111112 initctl restart --no-wait unity8-dash\""));
112113}
114+
115+void System::skipUntilFinishedPage()
116+{
117+ QSettings settings;
118+ settings.setValue(QStringLiteral("Wizard/SkipUntilFinishedPage"), true);
119+ settings.sync();
120+}
plugins/Wizard/System.h+5−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014-2015 Canonical Ltd.
2+ * Copyright (C) 2014-2016 Canonical Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it
55 * under the terms of the GNU General Public License version 3, as published
@@ -35,6 +35,10 @@ public:
3535
3636public Q_SLOTS:
3737 void updateSessionLocale(const QString &locale);
38+ /**
39+ * Mark the wizard to skip all the pages and just show the last (welcome to ubuntu) page
40+ */
41+ void skipUntilFinishedPage();
3842
3943Q_SIGNALS:
4044 void wizardEnabledChanged();
qml/Wizard/Page.qml+1−1
@@ -21,7 +21,7 @@ import Wizard 0.1
2121Item {
2222 readonly property real maximumContentWidth: units.gu(50)
2323 readonly property bool wideMode: width > units.gu(90) && width > height
24− readonly property bool contentAnimationRunning: contentAnimation.running
24+ readonly property alias contentAnimationRunning: contentAnimation.running
2525
2626 readonly property real buttonMargin: units.gu(3)
2727 readonly property real buttonWidth: (width - buttonMargin * 2) / 2 -
qml/Wizard/Pages/30-wifi.qml+8−4
@@ -20,6 +20,7 @@ import QMenuModel 0.1 as QMenuModel
2020import Ubuntu.Components 1.3
2121import Wizard 0.1
2222import Ubuntu.Connectivity 1.0
23+import Ubuntu.SystemImage 0.1
2324import ".." as LocalComponents
2425
2526LocalComponents.Page {
@@ -158,7 +159,7 @@ LocalComponents.Page {
158159 anchors.right: parent.right
159160 anchors.leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
160161 font.weight: Font.Light
161− color: "#68064d"
162+ color: textColor
162163 wrapMode: Text.Wrap
163164 text: listview.count > 0 ? i18n.tr("Available Wi-Fi networks")
164165 : i18n.tr("No available Wi-Fi networks")
@@ -174,8 +175,6 @@ LocalComponents.Page {
174175 Layout.fillHeight: true
175176
176177 delegate: Loader {
177− id: loader
178−
179178 readonly property bool isAccessPoint: model.type === "unity.widgets.systemsettings.tablet.accesspoint"
180179 readonly property bool isConnected: item && item.menuData && item.menuData.actionState
181180 readonly property bool isEnterprise: item && item.isEnterprise
@@ -205,7 +204,12 @@ LocalComponents.Page {
205204 id: forwardButton
206205 LocalComponents.StackButton {
207206 text: (connected || listview.count === 0) ? i18n.tr("Next") : i18n.tr("Skip")
208− onClicked: pageStack.next()
207+ onClicked: {
208+ if (connected) {
209+ SystemImage.checkForUpdate(); // initiate the background check for System Update
210+ }
211+ pageStack.next();
212+ }
209213 }
210214 }
211215}
qml/Wizard/Pages/77-system-update.qmlnew file+156
@@ -0,0 +1,156 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
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+import QtQuick 2.4
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.3
20+import Ubuntu.SystemImage 0.1
21+import Wizard 0.1
22+import ".." as LocalComponents
23+
24+LocalComponents.Page {
25+ id: systemUpdatePage
26+ objectName: "systemUpdatePage"
27+
28+ title: i18n.tr("Update Device")
29+ forwardButtonSourceComponent: forwardButton
30+
31+ skip: !SystemImage.updateDownloaded // skip the page when the system update is not ready to install
32+
33+ Column {
34+ id: column
35+ anchors {
36+ fill: content
37+ leftMargin: systemUpdatePage.leftMargin
38+ rightMargin: systemUpdatePage.rightMargin
39+ topMargin: systemUpdatePage.customMargin
40+ }
41+ spacing: units.gu(3)
42+ opacity: spinner.running ? 0.5 : 1
43+ Behavior on opacity {
44+ UbuntuNumberAnimation {}
45+ }
46+
47+ Label {
48+ anchors.left: parent.left
49+ anchors.right: parent.right
50+ anchors.leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
51+ font.weight: Font.Light
52+ color: textColor
53+ wrapMode: Text.Wrap
54+ fontSize: "small"
55+ text: i18n.tr("There is a system update available and ready to install. Afterwards, the device will automatically restart.")
56+ }
57+
58+ GridLayout {
59+ rows: 3
60+ columns: 2
61+ rowSpacing: units.gu(1)
62+ columnSpacing: units.gu(2)
63+
64+ Image {
65+ Layout.rowSpan: 3
66+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
67+ sourceSize: Qt.size(units.gu(3), units.gu(3))
68+ fillMode: Image.PreserveAspectFit
69+ source: "image://theme/distributor-logo"
70+ }
71+
72+ Label {
73+ color: textColor
74+ font.weight: Font.Normal
75+ fontSize: "small"
76+ text: i18n.ctr("string identifying name of the update", "Ubuntu system")
77+ }
78+
79+ Label {
80+ font.weight: Font.Light
81+ fontSize: "small"
82+ color: textColor
83+ text: i18n.ctr("version of the system update", "Version %1").arg(SystemImage.availableVersion)
84+ }
85+
86+ Label {
87+ font.weight: Font.Light
88+ fontSize: "small"
89+ color: textColor
90+ text: SystemImage.updateSize
91+ }
92+ }
93+
94+ Label {
95+ anchors.left: parent.left
96+ anchors.right: parent.right
97+ anchors.leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
98+ font.weight: Font.Light
99+ color: textColor
100+ wrapMode: Text.Wrap
101+ fontSize: "small"
102+ text: i18n.tr("This could take a few minutes...")
103+ }
104+
105+ Rectangle {
106+ anchors.left: parent.left
107+ anchors.leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
108+ color: theme.palette.normal.foreground
109+ radius: units.dp(4)
110+ width: buttonLabel.paintedWidth + units.gu(3)
111+ height: buttonLabel.paintedHeight + units.gu(1.8)
112+
113+ Label {
114+ id: buttonLabel
115+ color: textColor
116+ text: i18n.tr("Install and restart now")
117+ font.weight: Font.Light
118+ anchors.centerIn: parent
119+ }
120+
121+ AbstractButton {
122+ id: button
123+ objectName: "installButton"
124+ anchors.fill: parent
125+ onClicked: {
126+ System.skipUntilFinishedPage();
127+ SystemImage.applyUpdate();
128+ }
129+ }
130+
131+ transformOrigin: Item.Top
132+ scale: button.pressed ? 0.98 : 1.0
133+ Behavior on scale {
134+ ScaleAnimator {
135+ duration: UbuntuAnimation.SnapDuration
136+ easing.type: Easing.Linear
137+ }
138+ }
139+ }
140+ }
141+
142+ ActivityIndicator {
143+ id: spinner
144+ anchors.centerIn: systemUpdatePage
145+ running: SystemImage.updateApplying
146+ visible: running
147+ }
148+
149+ Component {
150+ id: forwardButton
151+ LocalComponents.StackButton {
152+ text: i18n.tr("Skip")
153+ onClicked: pageStack.next()
154+ }
155+ }
156+}
tests/mocks/Ubuntu/SystemImage/MockSystemImage.cpp+24−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,6 +14,9 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17+#include <QDebug>
18+#include <QTimer>
19+
1720#include "MockSystemImage.h"
1821
1922MockSystemImage::MockSystemImage(QObject *parent)
@@ -21,7 +24,27 @@ MockSystemImage::MockSystemImage(QObject *parent)
2124{
2225}
2326
27+void MockSystemImage::checkForUpdate()
28+{
29+ qDebug() << "Doing a fake system update check";
30+}
31+
32+void MockSystemImage::applyUpdate()
33+{
34+ qDebug() << "Applying a fake system update";
35+ setUpdateApplying(true);
36+ QTimer::singleShot(3000, [this] {setUpdateApplying(false);});
37+}
38+
2439void MockSystemImage::factoryReset()
2540{
2641 Q_EMIT resettingDevice();
2742}
43+
44+void MockSystemImage::setUpdateApplying(bool status)
45+{
46+ if (status != m_updateApplying) {
47+ m_updateApplying = status;
48+ Q_EMIT updateApplyingChanged();
49+ }
50+}
tests/mocks/Ubuntu/SystemImage/MockSystemImage.h+22−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -24,13 +24,33 @@ class MockSystemImage : public QObject
2424 Q_OBJECT
2525 Q_DISABLE_COPY(MockSystemImage)
2626
27+ Q_PROPERTY(bool updateDownloaded READ updateDownloaded CONSTANT)
28+ Q_PROPERTY(QString availableVersion READ availableVersion CONSTANT)
29+ Q_PROPERTY(QString updateSize READ updateSize CONSTANT)
30+ Q_PROPERTY(bool updateApplying READ updateApplying NOTIFY updateApplyingChanged)
31+
2732public:
28− explicit MockSystemImage(QObject *parent = 0);
33+ explicit MockSystemImage(QObject *parent = nullptr);
2934
35+ Q_INVOKABLE void checkForUpdate();
36+ Q_INVOKABLE void applyUpdate();
3037 Q_INVOKABLE void factoryReset();
3138
39+ bool updateApplying() const { return m_updateApplying; }
40+ // these are const only in mock
41+ bool updateDownloaded() const { return true; }
42+ QString availableVersion() const { return QStringLiteral("42"); }
43+ QString updateSize() const { return QStringLiteral("4.2 MB"); }
44+
3245Q_SIGNALS:
3346 void resettingDevice(); // only for mock
47+ void updateApplyingChanged();
48+
49+private Q_SLOTS:
50+ void setUpdateApplying(bool status);
51+
52+private:
53+ bool m_updateApplying = false;
3454};
3555
3656#endif // MOCK_SYSTEMIMAGE_H
tests/mocks/Wizard/MockSystem.cpp+8
@@ -15,6 +15,7 @@
1515 */
1616
1717#include <QDebug>
18+#include <QSettings>
1819
1920#include "MockSystem.h"
2021
@@ -39,3 +40,10 @@ void MockSystem::updateSessionLocale(const QString &locale)
3940{
4041 Q_EMIT updateSessionLocaleCalled(locale);
4142}
43+
44+void MockSystem::skipUntilFinishedPage()
45+{
46+ QSettings settings;
47+ settings.setValue(QStringLiteral("Wizard/SkipUntilFinishedPage"), true);
48+ settings.sync();
49+}
tests/mocks/Wizard/MockSystem.h+4
@@ -33,6 +33,10 @@ public:
3333
3434public Q_SLOTS:
3535 void updateSessionLocale(const QString &locale);
36+ /**
37+ * Mark the wizard to skip all the pages and just show the last (welcome to ubuntu) page
38+ */
39+ void skipUntilFinishedPage();
3640
3741Q_SIGNALS:
3842 void wizardEnabledChanged();
tests/plugins/Wizard/tst_pagelist.cpp+30−1
@@ -20,6 +20,7 @@
2020#include <QObject>
2121#include <QTemporaryDir>
2222#include <QTest>
23+#include <QSettings>
2324
2425#define PAGES_PATH "Wizard/Pages"
2526
@@ -28,7 +29,7 @@ class PageListTest: public QObject
2829 Q_OBJECT
2930
3031public:
31− PageListTest() {};
32+ PageListTest() {}
3233
3334private Q_SLOTS:
3435 void testCollect();
@@ -37,6 +38,7 @@ private Q_SLOTS:
3738 void testIgnoreNonQml();
3839 void testIgnoreDuplicates();
3940 void testDisabled();
41+ void testSkipUntilLastPage();
4042
4143private:
4244 void fillRoot(const QTemporaryDir &root);
@@ -150,5 +152,32 @@ void PageListTest::testDisabled()
150152 QCOMPARE(pageList.entries(), QStringList() << "4.qml");
151153}
152154
155+void PageListTest::testSkipUntilLastPage() {
156+ PageList * pageList = nullptr;
157+ QTemporaryDir root;
158+ fillRoot(root);
159+
160+ makeFile(root, "a", "1.qml");
161+ makeFile(root, "a", "2.qml");
162+ makeFile(root, "a", "3.qml");
163+
164+ // normal run
165+ pageList = new PageList;
166+ QCOMPARE(pageList->numPages(), 3);
167+ delete pageList; pageList = nullptr;
168+
169+ // after system update had been installed, have the last page only
170+ QSettings settings;
171+ settings.setValue(QStringLiteral("Wizard/SkipUntilFinishedPage"), true);
172+ pageList = new PageList;
173+ QCOMPARE(pageList->entries(), {QStringLiteral("3.qml")}); // only the last page should be in the list
174+ delete pageList; pageList = nullptr;
175+
176+ // normal run again
177+ pageList = new PageList;
178+ QCOMPARE(pageList->numPages(), 3);
179+ delete pageList; pageList = nullptr;
180+}
181+
153182QTEST_MAIN(PageListTest)
154183#include "tst_pagelist.moc"
tests/qmltests/Wizard/tst_Wizard.qml+6
@@ -319,6 +319,8 @@ Item {
319319 // now finish up
320320 page = waitForPage("reportingPage");
321321 tap(findChild(page, "forwardButton"));
322+ page = waitForPage("systemUpdatePage");
323+ tap(findChild(page, "forwardButton"));
322324 page = waitForPage("finishedPage");
323325 waitUntilTransitionsEnd(page);
324326 tap(findChild(page, "finishButton"));
@@ -359,6 +361,8 @@ Item {
359361 // now finish up
360362 page = waitForPage("reportingPage");
361363 tap(findChild(page, "forwardButton"));
364+ page = waitForPage("systemUpdatePage");
365+ tap(findChild(page, "forwardButton"));
362366 page = waitForPage("finishedPage");
363367 waitUntilTransitionsEnd(page);
364368 tap(findChild(page, "finishButton"));
@@ -377,6 +381,8 @@ Item {
377381 tap(findChild(page, "forwardButton"));
378382 page = waitForPage("reportingPage");
379383 tap(findChild(page, "forwardButton"));
384+ page = waitForPage("systemUpdatePage");
385+ tap(findChild(page, "forwardButton"));
380386 page = waitForPage("finishedPage");
381387 waitUntilTransitionsEnd(page);
382388 tap(findChild(page, "finishButton"));