on xenial help sign in
Merge
035d9deAlbert Astals
Components/CrossFadeImage.qml+3−1
@@ -85,7 +85,7 @@ Item {
85 Connections {
85 Connections {
86 target: __nextImage
86 target: __nextImage
87 onStatusChanged: {
87 onStatusChanged: {
88− if (__nextImage.status == Image.Ready) {
88+ if (__nextImage.status == Image.Ready || __nextImage.status == Image.Error) {
89 swapImages();
89 swapImages();
90 }
90 }
91 }
91 }
@@ -96,6 +96,7 @@ Item {
96 anchors.fill: parent
96 anchors.fill: parent
97 cache: false
97 cache: false
98 asynchronous: true
98 asynchronous: true
99+ fillMode: Image.PreserveAspectCrop
99 z: 1
100 z: 1
100 }
101 }
101
102
@@ -104,6 +105,7 @@ Item {
104 anchors.fill: parent
105 anchors.fill: parent
105 cache: false
106 cache: false
106 asynchronous: true
107 asynchronous: true
108+ fillMode: Image.PreserveAspectCrop
107 z: 0
109 z: 0
108 }
110 }
109
111
Greeter/Greeter.qml+2−3
@@ -34,7 +34,7 @@ Showable {
34
34
35 property alias dragHandleWidth: dragHandle.width
35 property alias dragHandleWidth: dragHandle.width
36 property alias model: greeterContentLoader.model
36 property alias model: greeterContentLoader.model
37− property bool locked: shown && multiUser && !greeterContentLoader.promptless
37+ property bool locked: shown && !LightDM.Greeter.promptless
38
38
39 readonly property bool narrowMode: !multiUser && width <= units.gu(60)
39 readonly property bool narrowMode: !multiUser && width <= units.gu(60)
40 readonly property bool multiUser: LightDM.Users.count > 1
40 readonly property bool multiUser: LightDM.Users.count > 1
@@ -59,7 +59,6 @@ Showable {
59 anchors.fill: parent
59 anchors.fill: parent
60 property var model: LightDM.Users
60 property var model: LightDM.Users
61 property int currentIndex: 0
61 property int currentIndex: 0
62− property bool promptless: item ? item.promptless : false
63 property var infographicModel: LightDM.Infographic
62 property var infographicModel: LightDM.Infographic
64
63
65 source: required ? "GreeterContent.qml" : ""
64 source: required ? "GreeterContent.qml" : ""
@@ -113,7 +112,7 @@ Showable {
113 // UI element)
112 // UI element)
114 distanceThreshold: 0
113 distanceThreshold: 0
115
114
116− enabled: !greeter.locked
115+ enabled: greeter.narrowMode || !greeter.locked
117
116
118 direction: Direction.Leftwards
117 direction: Direction.Leftwards
119 }
118 }
Greeter/GreeterContent.qml−2
@@ -23,7 +23,6 @@ MouseArea {
23 id: root
23 id: root
24 anchors.fill: parent
24 anchors.fill: parent
25
25
26− property bool promptless: loginLoader.status == Loader.Ready && LightDM.Greeter.promptless
27 property bool ready: wallpaper.status == Image.Ready
26 property bool ready: wallpaper.status == Image.Ready
28 property bool leftTeaserPressed: teasingMouseArea.pressed &&
27 property bool leftTeaserPressed: teasingMouseArea.pressed &&
29 teasingMouseArea.mouseX < teasingMouseArea.width / 2
28 teasingMouseArea.mouseX < teasingMouseArea.width / 2
@@ -38,7 +37,6 @@ MouseArea {
38
37
39 source: shell.background
38 source: shell.background
40 anchors.fill: parent
39 anchors.fill: parent
41− crossFade: false
42 fadeInFirst: false
40 fadeInFirst: false
43 }
41 }
44
42
Shell.qml+54−32
@@ -15,10 +15,12 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import GSettings 1.0
18import Ubuntu.Application 0.1
19import Ubuntu.Application 0.1
19import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
20import Ubuntu.Gestures 0.1
21import Ubuntu.Gestures 0.1
21import LightDM 0.1 as LightDM
22import LightDM 0.1 as LightDM
23+import Powerd 0.1
22import "Dash"
24import "Dash"
23import "Greeter"
25import "Greeter"
24import "Launcher"
26import "Launcher"
@@ -40,8 +42,8 @@ FocusScope {
40 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
42 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
41
43
42 property real edgeSize: units.gu(2)
44 property real edgeSize: units.gu(2)
43− property url default_background: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
45+ property url defaultBackground: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
44− property url background: default_background
46+ property url background: backgroundSettings.pictureUri
45 readonly property real panelHeight: panel.panelHeight
47 readonly property real panelHeight: panel.panelHeight
46
48
47 property bool dashShown: dash.shown
49 property bool dashShown: dash.shown
@@ -114,6 +116,11 @@ FocusScope {
114 }
116 }
115 }
117 }
116
118
119+ GSettings {
120+ id: backgroundSettings
121+ schema.id: "org.gnome.desktop.background"
122+ }
123+
117 VolumeControl {
124 VolumeControl {
118 id: volumeControl
125 id: volumeControl
119 }
126 }
@@ -121,12 +128,6 @@ FocusScope {
121 Keys.onVolumeUpPressed: volumeControl.volumeUp()
128 Keys.onVolumeUpPressed: volumeControl.volumeUp()
122 Keys.onVolumeDownPressed: volumeControl.volumeDown()
129 Keys.onVolumeDownPressed: volumeControl.volumeDown()
123
130
124− Keys.onReleased: {
125− if (event.key == Qt.Key_PowerOff) {
126− greeter.show()
127− }
128− }
129−
130 Item {
131 Item {
131 id: underlay
132 id: underlay
132 objectName: "underlay"
133 objectName: "underlay"
@@ -144,12 +145,16 @@ FocusScope {
144 // through the translucent parts of the shell surface.
145 // through the translucent parts of the shell surface.
145 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
146 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
146
147
147− Image {
148+ CrossFadeImage {
148 id: backgroundImage
149 id: backgroundImage
150+ objectName: "backgroundImage"
149 source: shell.background
151 source: shell.background
150− sourceSize.width: parent.width
151− sourceSize.height: parent.height
152 anchors.fill: parent
152 anchors.fill: parent
153+ onStatusChanged: {
154+ if (status == Image.Error) {
155+ backgroundSettings.pictureUri = shell.defaultBackground
156+ }
157+ }
153 }
158 }
154
159
155 Rectangle {
160 Rectangle {
@@ -194,7 +199,6 @@ FocusScope {
194 }
199 }
195 }
200 }
196
201
197−
198 Item {
202 Item {
199 id: stagesOuterContainer
203 id: stagesOuterContainer
200
204
@@ -259,7 +263,6 @@ FocusScope {
259 ignoreUnknownSignals: true
263 ignoreUnknownSignals: true
260 }
264 }
261
265
262−
263 Stage {
266 Stage {
264 id: mainStage
267 id: mainStage
265
268
@@ -420,9 +423,6 @@ FocusScope {
420
423
421 dragHandleWidth: shell.edgeSize
424 dragHandleWidth: shell.edgeSize
422
425
423− property var previousMainApp: null
424− property var previousSideApp: null
425−
426 onShownChanged: {
426 onShownChanged: {
427 if (shown) {
427 if (shown) {
428 lockscreen.reset();
428 lockscreen.reset();
@@ -432,27 +432,13 @@ FocusScope {
432 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
432 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
433 }
433 }
434 greeter.forceActiveFocus();
434 greeter.forceActiveFocus();
435− // FIXME: *FocusedApplication are not updated when unfocused, hence the need to check whether
436− // the stage was actually shown
437− if (mainStage.fullyShown) greeter.previousMainApp = applicationManager.mainStageFocusedApplication;
438− if (sideStage.fullyShown) greeter.previousSideApp = applicationManager.sideStageFocusedApplication;
439− applicationManager.unfocusCurrentApplication();
440− } else {
441− if (greeter.previousMainApp) {
442− applicationManager.focusApplication(greeter.previousMainApp);
443− greeter.previousMainApp = null;
444− }
445− if (greeter.previousSideApp) {
446− applicationManager.focusApplication(greeter.previousSideApp);
447− greeter.previousSideApp = null;
448− }
449 }
435 }
450 }
436 }
451
437
452 onUnlocked: greeter.hide()
438 onUnlocked: greeter.hide()
453 onSelected: {
439 onSelected: {
454 var bgPath = greeter.model.data(uid, LightDM.UserRoles.BackgroundPathRole)
440 var bgPath = greeter.model.data(uid, LightDM.UserRoles.BackgroundPathRole)
455− shell.background = bgPath ? bgPath : default_background
441+ shell.background = bgPath ? bgPath : defaultBackground
456 }
442 }
457
443
458 onLeftTeaserPressedChanged: {
444 onLeftTeaserPressedChanged: {
@@ -467,6 +453,42 @@ FocusScope {
467 blockInput: greeter.shown || lockscreen.shown
453 blockInput: greeter.shown || lockscreen.shown
468 }
454 }
469
455
456+ Connections {
457+ id: powerConnection
458+ target: Powerd
459+
460+ property var previousMainApp: null
461+ property var previousSideApp: null
462+
463+ function setFocused(focused) {
464+ if (!focused) {
465+ // FIXME: *FocusedApplication are not updated when unfocused, hence the need to check whether
466+ // the stage was actually shown
467+ if (mainStage.fullyShown) powerConnection.previousMainApp = applicationManager.mainStageFocusedApplication;
468+ if (sideStage.fullyShown) powerConnection.previousSideApp = applicationManager.sideStageFocusedApplication;
469+ applicationManager.unfocusCurrentApplication();
470+ } else {
471+ if (powerConnection.previousMainApp) {
472+ applicationManager.focusApplication(powerConnection.previousMainApp);
473+ powerConnection.previousMainApp = null;
474+ }
475+ if (powerConnection.previousSideApp) {
476+ applicationManager.focusApplication(powerConnection.previousSideApp);
477+ powerConnection.previousSideApp = null;
478+ }
479+ }
480+ }
481+
482+ onPowerStateChange: {
483+ if (state == 0) { // suspend
484+ powerConnection.setFocused(false);
485+ greeter.show();
486+ } else if (state == 1) { // active
487+ powerConnection.setFocused(true);
488+ }
489+ }
490+ }
491+
470 Item {
492 Item {
471 id: overlay
493 id: overlay
472
494
@@ -552,7 +574,7 @@ FocusScope {
552 anchors.bottom: parent.bottom
574 anchors.bottom: parent.bottom
553 width: parent.width
575 width: parent.width
554 dragAreaWidth: shell.edgeSize
576 dragAreaWidth: shell.edgeSize
555− available: !greeter.locked
577+ available: !greeter.shown || greeter.narrowMode
556 onDashItemSelected: {
578 onDashItemSelected: {
557 greeter.hide()
579 greeter.hide()
558 // Animate if moving between application and dash
580 // Animate if moving between application and dash
debian/control+3−1
@@ -56,8 +56,10 @@ Description: Indicators client test application
56
56
57Package: unity8
57Package: unity8
58Architecture: any
58Architecture: any
59+Depends: gsettings-desktop-schemas,
59−Depends: qmenumodel-qml,
60+ qmenumodel-qml,
60 qtdeclarative5-dee-plugin,
61 qtdeclarative5-dee-plugin,
62+ qtdeclarative5-gsettings1.0,
61 qtdeclarative5-ubuntu-ui-toolkit-plugin,
63 qtdeclarative5-ubuntu-ui-toolkit-plugin,
62 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
64 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
63 qtdeclarative5-xmllistmodel-plugin,
65 qtdeclarative5-xmllistmodel-plugin,
debian/unity8-private.install+1
@@ -1,6 +1,7 @@
1usr/lib/*/unity8/qml/HudClient
1usr/lib/*/unity8/qml/HudClient
2usr/lib/*/unity8/qml/LightDM
2usr/lib/*/unity8/qml/LightDM
3usr/lib/*/unity8/qml/ListViewWithPageHeader
3usr/lib/*/unity8/qml/ListViewWithPageHeader
4+usr/lib/*/unity8/qml/Powerd
4usr/lib/*/unity8/qml/Ubuntu
5usr/lib/*/unity8/qml/Ubuntu
5usr/lib/*/unity8/qml/Unity
6usr/lib/*/unity8/qml/Unity
6usr/lib/*/unity8/qml/Utils
7usr/lib/*/unity8/qml/Utils
plugins/CMakeLists.txt+1
@@ -4,3 +4,4 @@ add_subdirectory(Unity)
4add_subdirectory(HudClient)
4add_subdirectory(HudClient)
5add_subdirectory(LightDM)
5add_subdirectory(LightDM)
6add_subdirectory(ListViewWithPageHeader)
6add_subdirectory(ListViewWithPageHeader)
7+add_subdirectory(Powerd)
plugins/Powerd/CMakeLists.txtnew file+13
@@ -0,0 +1,13 @@
1+include(Plugins)
2+
3+add_definitions(-DSM_BUSNAME=systemBus)
4+
5+add_library(Powerd-qml MODULE
6+ plugin.cpp
7+ Powerd.cpp
8+ )
9+
10+qt5_use_modules(Powerd-qml DBus Qml)
11+
12+export_qmlfiles(Powerd Powerd)
13+export_qmlplugin(Powerd 0.1 Powerd TARGETS Powerd-qml)
plugins/Powerd/Powerd.cppnew file+36
@@ -0,0 +1,36 @@
1+/*
2+ * Copyright (C) 2013 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+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "Powerd.h"
20+
21+Powerd::Powerd(QObject* parent)
22+ : QObject(parent),
23+ powerd(NULL)
24+{
25+ powerd = new QDBusInterface("com.canonical.powerd",
26+ "/com/canonical/powerd",
27+ "com.canonical.powerd",
28+ QDBusConnection::SM_BUSNAME(), this);
29+
30+ powerd->connection().connect("com.canonical.powerd",
31+ "/com/canonical/powerd",
32+ "com.canonical.powerd",
33+ "SysPowerStateChange",
34+ this,
35+ SIGNAL(powerStateChange(int)));
36+}
plugins/Powerd/Powerd.hnew file+40
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright (C) 2012,2013 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+ * Authors: Gerry Boland <gerry.boland@canonical.com>
17+ * Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef UNITY_POWERD_H
21+#define UNITY_POWERD_H
22+
23+#include <QtCore/QObject>
24+#include <QtDBus/QDBusInterface>
25+
26+class Powerd: public QObject
27+{
28+ Q_OBJECT
29+
30+public:
31+ explicit Powerd(QObject *parent = 0);
32+
33+Q_SIGNALS:
34+ void powerStateChange(int state);
35+
36+private:
37+ QDBusInterface *powerd;
38+};
39+
40+#endif
plugins/Powerd/plugin.cppnew file+36
@@ -0,0 +1,36 @@
1+/*
2+ * Copyright (C) 2012,2013 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+ * Authors: Gerry Boland <gerry.boland@canonical.com>
17+ * Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "plugin.h"
21+#include "Powerd.h"
22+
23+#include <QtQml/QtQml>
24+
25+static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
26+{
27+ Q_UNUSED(engine)
28+ Q_UNUSED(scriptEngine)
29+ return new Powerd();
30+}
31+
32+void PowerdPlugin::registerTypes(const char *uri)
33+{
34+ Q_ASSERT(uri == QLatin1String("Powerd"));
35+ qmlRegisterSingletonType<Powerd>(uri, 0, 1, "Powerd", powerd_provider);
36+}
plugins/Powerd/plugin.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2012,2013 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+ * Authors: Gerry Boland <gerry.boland@canonical.com>
17+ * Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef POWER_PLUGIN_H
21+#define POWER_PLUGIN_H
22+
23+#include <QtQml/QQmlEngine>
24+#include <QtQml/QQmlExtensionPlugin>
25+
26+class PowerdPlugin : public QQmlExtensionPlugin
27+{
28+ Q_OBJECT
29+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
30+
31+public:
32+ void registerTypes(const char *uri);
33+};
34+
35+#endif
plugins/Powerd/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Powerd
2+plugin Powerd-qml
3+typeinfo plugin.qmltypes
tests/autopilot/unity8/shell/emulators/greeter.py+4−1
@@ -25,7 +25,7 @@ class Greeter(UnityEmulatorBase):
25
25
26 """An emulator that understands the greeter screen."""
26 """An emulator that understands the greeter screen."""
27
27
28− def unlock(self):
28+ def swipe(self):
29 """Swipe the greeter screen away."""
29 """Swipe the greeter screen away."""
30 self.created.wait_for(True)
30 self.created.wait_for(True)
31 touch = Touch.create()
31 touch = Touch.create()
@@ -38,3 +38,6 @@ class Greeter(UnityEmulatorBase):
38 touch.drag(start_x, start_y, stop_x, stop_y)
38 touch.drag(start_x, start_y, stop_x, stop_y)
39
39
40 self.created.wait_for(False)
40 self.created.wait_for(False)
41+
42+ def get_prompt(self):
43+ return self.select_single("TextField", objectName="passwordInput")
tests/autopilot/unity8/shell/tests/test_hud.py+5−5
@@ -37,7 +37,7 @@ class TestHud(UnityTestCase):
37
37
38 """
38 """
39 self.launch_unity()
39 self.launch_unity()
40− self.main_window.get_greeter().unlock()
40+ self.main_window.get_greeter().swipe()
41 window = self.main_window.get_qml_view()
41 window = self.main_window.get_qml_view()
42 hud_show_button = self.main_window.get_hud_show_button()
42 hud_show_button = self.main_window.get_hud_show_button()
43 hud = self.main_window.get_hud()
43 hud = self.main_window.get_hud()
@@ -58,7 +58,7 @@ class TestHud(UnityTestCase):
58
58
59 """
59 """
60 self.launch_unity()
60 self.launch_unity()
61− self.main_window.get_greeter().unlock()
61+ self.main_window.get_greeter().swipe()
62 window = self.main_window.get_qml_view()
62 window = self.main_window.get_qml_view()
63 hud_show_button = self.main_window.get_hud_show_button()
63 hud_show_button = self.main_window.get_hud_show_button()
64 hud = self.main_window.get_hud()
64 hud = self.main_window.get_hud()
@@ -81,7 +81,7 @@ class TestHud(UnityTestCase):
81 def test_hide_hud_click(self):
81 def test_hide_hud_click(self):
82 """Tapping the close button of the Hud must dismiss it."""
82 """Tapping the close button of the Hud must dismiss it."""
83 self.launch_unity()
83 self.launch_unity()
84− self.main_window.get_greeter().unlock()
84+ self.main_window.get_greeter().swipe()
85 hud = self.main_window.get_hud()
85 hud = self.main_window.get_hud()
86
86
87 self._launch_test_app_from_app_screen()
87 self._launch_test_app_from_app_screen()
@@ -97,7 +97,7 @@ class TestHud(UnityTestCase):
97
97
98 """
98 """
99 self.launch_unity()
99 self.launch_unity()
100− self.main_window.get_greeter().unlock()
100+ self.main_window.get_greeter().swipe()
101 hud = self.main_window.get_hud()
101 hud = self.main_window.get_hud()
102 window = self.main_window.get_qml_view()
102 window = self.main_window.get_qml_view()
103
103
@@ -114,7 +114,7 @@ class TestHud(UnityTestCase):
114 def test_launcher_hides_hud(self):
114 def test_launcher_hides_hud(self):
115 """Opening the Launcher while the Hud is active must close the Hud."""
115 """Opening the Launcher while the Hud is active must close the Hud."""
116 self.launch_unity()
116 self.launch_unity()
117− self.main_window.get_greeter().unlock()
117+ self.main_window.get_greeter().swipe()
118 hud = self.main_window.get_hud()
118 hud = self.main_window.get_hud()
119 launcher = self.main_window.get_launcher()
119 launcher = self.main_window.get_launcher()
120
120
tests/autopilot/unity8/shell/tests/test_lock_screen.py+58−26
@@ -40,52 +40,68 @@ class TestLockscreen(UnityTestCase):
40 """Must be able to unlock the PIN entry lock screen."""
40 """Must be able to unlock the PIN entry lock screen."""
41 self.launch_unity()
41 self.launch_unity()
42 greeter = self.main_window.get_greeter()
42 greeter = self.main_window.get_greeter()
43− greeter.unlock()
44
43
44+ if greeter.narrowMode:
45+ greeter.swipe()
45− lockscreen = self._wait_for_lockscreen()
46+ lockscreen = self._wait_for_lockscreen()
46− self._enter_pincode("1234")
47+ self._enter_pincode("1234")
47−
48+ self.assertThat(lockscreen.shown, Eventually(Equals(False)))
49+ else:
50+ self._enter_prompt_passphrase("1234")
48− self.assertThat(lockscreen.shown, Eventually(Equals(False)))
51+ self.assertThat(greeter.shown, Eventually(Equals(False)))
49
52
50 @with_lightdm_mock("single-passphrase")
53 @with_lightdm_mock("single-passphrase")
51 def test_can_unlock_passphrase_screen(self):
54 def test_can_unlock_passphrase_screen(self):
52 """Must be able to unlock the passphrase entry screen."""
55 """Must be able to unlock the passphrase entry screen."""
53 self.launch_unity()
56 self.launch_unity()
54 greeter = self.main_window.get_greeter()
57 greeter = self.main_window.get_greeter()
55− greeter.unlock()
56−
57− lockscreen = self._wait_for_lockscreen()
58− self._enter_passphrase("password")
59
58
59+ if greeter.narrowMode:
60+ greeter.swipe()
61+ lockscreen = self._wait_for_lockscreen()
62+ self._enter_pin_passphrase("password")
60− self.assertThat(lockscreen.shown, Eventually(Equals(False)))
63+ self.assertThat(lockscreen.shown, Eventually(Equals(False)))
64+ else:
65+ self._enter_prompt_passphrase("password")
66+ self.assertThat(greeter.shown, Eventually(Equals(False)))
61
67
62 @with_lightdm_mock("single-pin")
68 @with_lightdm_mock("single-pin")
63 def test_pin_screen_wrong_code(self):
69 def test_pin_screen_wrong_code(self):
64 """Entering the wrong pin code must not dismiss the lock screen."""
70 """Entering the wrong pin code must not dismiss the lock screen."""
65 self.launch_unity()
71 self.launch_unity()
66 greeter = self.main_window.get_greeter()
72 greeter = self.main_window.get_greeter()
67− greeter.unlock()
68−
69− lockscreen = self._wait_for_lockscreen()
70− self._enter_pincode("4321")
71
73
74+ if greeter.narrowMode:
75+ greeter.swipe()
76+ lockscreen = self._wait_for_lockscreen()
77+ self._enter_pincode("4321")
72− pinentryField = self.main_window.get_pinentryField()
78+ pinentryField = self.main_window.get_pinentryField()
73− self.assertThat(pinentryField.text, Eventually(Equals("")))
79+ self.assertThat(pinentryField.text, Eventually(Equals("")))
80+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
81+ else:
82+ self._enter_prompt_passphrase("4231")
83+ prompt = self.main_window.get_greeter().get_prompt()
84+ self.assertThat(prompt.text, Eventually(Equals("")))
74− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
85+ self.assertThat(greeter.shown, Eventually(Equals(True)))
75
86
76 @with_lightdm_mock("single-passphrase")
87 @with_lightdm_mock("single-passphrase")
77 def test_passphrase_screen_wrong_password(self):
88 def test_passphrase_screen_wrong_password(self):
78 """Entering the wrong password must not dismiss the lock screen."""
89 """Entering the wrong password must not dismiss the lock screen."""
79 self.launch_unity()
90 self.launch_unity()
80 greeter = self.main_window.get_greeter()
91 greeter = self.main_window.get_greeter()
81− greeter.unlock()
82
92
93+ if greeter.narrowMode:
94+ greeter.swipe()
83− lockscreen = self._wait_for_lockscreen()
95+ lockscreen = self._wait_for_lockscreen()
84− self._enter_passphrase("foobar")
96+ self._enter_pin_passphrase("foobar")
85−
86− pinentryField = self.main_window.get_pinentryField()
97+ pinentryField = self.main_window.get_pinentryField()
87− self.assertThat(pinentryField.text, Eventually(Equals("")))
98+ self.assertThat(pinentryField.text, Eventually(Equals("")))
99+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
100+ else:
101+ self._enter_prompt_passphrase("foobar")
102+ prompt = self.main_window.get_greeter().get_prompt()
103+ self.assertThat(prompt.text, Eventually(Equals("")))
88− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
104+ self.assertThat(greeter.shown, Eventually(Equals(True)))
89
105
90 def _wait_for_lockscreen(self):
106 def _wait_for_lockscreen(self):
91 """Wait for the lock screen to load, and return it."""
107 """Wait for the lock screen to load, and return it."""
@@ -114,9 +130,9 @@ class TestLockscreen(UnityTestCase):
114 )
130 )
115 self.touch.tap_object(self.main_window.get_pinPadButton(int(num)))
131 self.touch.tap_object(self.main_window.get_pinPadButton(int(num)))
116
132
117− def _enter_passphrase(self, passphrase):
133+ def _enter_pin_passphrase(self, passphrase):
118 """Enter the password specified in 'passphrase' into the password entry
134 """Enter the password specified in 'passphrase' into the password entry
119− field.
135+ field of the pin lock screen.
120
136
121 :param passphrase: The string you want to enter.
137 :param passphrase: The string you want to enter.
122 :raises: TypeError if passphrase is not a string.
138 :raises: TypeError if passphrase is not a string.
@@ -129,3 +145,19 @@ class TestLockscreen(UnityTestCase):
129 self.touch.tap_object(pinentryField)
145 self.touch.tap_object(pinentryField)
130 self.keyboard.type(passphrase)
146 self.keyboard.type(passphrase)
131 self.keyboard.type("\n")
147 self.keyboard.type("\n")
148+
149+ def _enter_prompt_passphrase(self, passphrase):
150+ """Enter the password specified in 'passphrase' into the password entry
151+ field of the main user list's prompt.
152+
153+ :param passphrase: The string you want to enter.
154+ :raises: TypeError if passphrase is not a string.
155+
156+ """
157+ if not isinstance(passphrase, basestring):
158+ raise TypeError("'passphrase' parameter must be a string.")
159+
160+ prompt = self.main_window.get_greeter().get_prompt()
161+ self.touch.tap_object(prompt)
162+ self.keyboard.type(passphrase)
163+ self.keyboard.type("\n")
tests/data/unity/backgrounds/blue.pngnew file

Binary file not shown.

tests/data/unity/backgrounds/red.pngnew file

Binary file not shown.

tests/mocks/CMakeLists.txt+1
@@ -1,3 +1,4 @@
1+add_subdirectory(GSettings)
1add_subdirectory(libusermetrics)
2add_subdirectory(libusermetrics)
2add_subdirectory(LightDM)
3add_subdirectory(LightDM)
3add_subdirectory(QMenuModel)
4add_subdirectory(QMenuModel)
tests/mocks/GSettings/CMakeLists.txtnew file+31
@@ -0,0 +1,31 @@
1+set(CMAKE_AUTOMOC ON)
2+
3+include(FindPkgConfig)
4+find_package(Qt5Core REQUIRED)
5+find_package(Qt5Quick REQUIRED)
6+
7+include_directories(
8+ ${CMAKE_CURRENT_BINARY_DIR}
9+ ${Qt5Core_INCLUDE_DIRS}
10+ ${Qt5Quick_INCLUDE_DIRS}
11+)
12+
13+add_definitions(-DQT_NO_KEYWORDS)
14+
15+set(GSettingsQML_SOURCES
16+ fake_gsettings.cpp
17+ plugin.cpp
18+)
19+
20+add_library(FakeGSettingsQml MODULE ${GSettingsQML_SOURCES})
21+target_link_libraries(FakeGSettingsQml
22+ ${Qt5Core_LIBRARIES}
23+ ${Qt5Quick_LIBRARIES}
24+)
25+
26+qt5_use_modules(FakeGSettingsQml Qml)
27+
28+# copy qmldir file into build directory for shadow builds
29+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
30+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
31+ )
tests/mocks/GSettings/fake_gsettings.cppnew file+102
@@ -0,0 +1,102 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "fake_gsettings.h"
18+
19+#include <QList>
20+
21+GSettingsControllerQml* GSettingsControllerQml::s_controllerInstance = 0;
22+
23+GSettingsControllerQml::GSettingsControllerQml() {
24+}
25+
26+GSettingsControllerQml::~GSettingsControllerQml() {
27+ s_controllerInstance = 0;
28+}
29+
30+GSettingsControllerQml* GSettingsControllerQml::instance() {
31+ if(!s_controllerInstance) {
32+ s_controllerInstance = new GSettingsControllerQml();
33+ }
34+ return s_controllerInstance;
35+}
36+
37+void GSettingsControllerQml::registerSettingsObject(GSettingsQml *obj) {
38+ m_registeredGSettings.append(obj);
39+}
40+
41+void GSettingsControllerQml::unRegisterSettingsObject(GSettingsQml *obj) {
42+ m_registeredGSettings.removeOne(obj);
43+}
44+
45+void GSettingsControllerQml::setPictureUri(const QString &str) {
46+ Q_FOREACH (GSettingsQml *obj, m_registeredGSettings) {
47+ obj->setPictureUri(str);
48+ }
49+}
50+
51+GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {
52+}
53+
54+QByteArray GSettingsSchemaQml::id() const {
55+ return m_id;
56+}
57+
58+void GSettingsSchemaQml::setId(const QByteArray &id) {
59+ if (m_id.isEmpty()) {
60+ qWarning("GSettings.schema.id may only be set on construction");
61+ return;
62+ }
63+
64+ m_id = id;
65+}
66+
67+QByteArray GSettingsSchemaQml::path() const {
68+ return m_path;
69+}
70+
71+void GSettingsSchemaQml::setPath(const QByteArray &path) {
72+ if (m_path.isEmpty()) {
73+ qWarning("GSettings.schema.path may only be set on construction");
74+ return;
75+ }
76+
77+ m_path = path;
78+}
79+
80+GSettingsQml::GSettingsQml(QObject *parent): QObject(parent) {
81+ m_schema = new GSettingsSchemaQml(this);
82+ GSettingsControllerQml::instance()->registerSettingsObject(this);
83+}
84+
85+GSettingsQml::~GSettingsQml() {
86+ GSettingsControllerQml::instance()->unRegisterSettingsObject(this);
87+}
88+
89+GSettingsSchemaQml * GSettingsQml::schema() const {
90+ return m_schema;
91+}
92+
93+QString GSettingsQml::pictureUri() const {
94+ return m_pictureUri;
95+}
96+
97+void GSettingsQml::setPictureUri(const QString &str) {
98+ if (str != m_pictureUri) {
99+ m_pictureUri = str;
100+ Q_EMIT pictureUriChanged(m_pictureUri);
101+ }
102+}
tests/mocks/GSettings/fake_gsettings.hnew file+90
@@ -0,0 +1,90 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FAKE_GSETTINGS_H
18+#define FAKE_GSETTINGS_H
19+
20+#include <QList>
21+#include <QtQml>
22+
23+class GSettingsSchemaQml: public QObject
24+{
25+ Q_OBJECT
26+
27+ Q_PROPERTY(QByteArray id READ id WRITE setId)
28+
29+public:
30+ GSettingsSchemaQml(QObject *parent = NULL);
31+
32+ QByteArray id() const;
33+ void setId(const QByteArray &id);
34+
35+ QByteArray path() const;
36+ void setPath(const QByteArray &path);
37+
38+private:
39+ QByteArray m_id;
40+ QByteArray m_path;
41+};
42+
43+class GSettingsQml: public QObject
44+{
45+ Q_OBJECT
46+
47+ Q_PROPERTY(GSettingsSchemaQml* schema READ schema NOTIFY schemaChanged)
48+ Q_PROPERTY(QString pictureUri READ pictureUri WRITE setPictureUri NOTIFY pictureUriChanged)
49+
50+public:
51+ GSettingsQml(QObject *parent = NULL);
52+ ~GSettingsQml();
53+
54+ GSettingsSchemaQml * schema() const;
55+ QString pictureUri() const;
56+
57+ void setPictureUri(const QString &str);
58+
59+Q_SIGNALS:
60+ void schemaChanged();
61+ void pictureUriChanged(const QString&);
62+
63+private:
64+ GSettingsSchemaQml* m_schema;
65+
66+ QString m_pictureUri;
67+
68+ friend class GSettingsSchemaQml;
69+};
70+
71+class GSettingsControllerQml: public QObject
72+{
73+ Q_OBJECT
74+
75+public:
76+ static GSettingsControllerQml* instance();
77+ ~GSettingsControllerQml();
78+
79+ void registerSettingsObject(GSettingsQml *obj);
80+ void unRegisterSettingsObject(GSettingsQml *obj);
81+ Q_INVOKABLE void setPictureUri(const QString &str);
82+
83+private:
84+ GSettingsControllerQml();
85+
86+ static GSettingsControllerQml* s_controllerInstance;
87+ QList<GSettingsQml *> m_registeredGSettings;
88+};
89+
90+#endif // FAKE_GSETTINGS_H
tests/mocks/GSettings/plugin.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 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+#include "plugin.h"
18+#include "fake_gsettings.h"
19+
20+#include <QtQml>
21+
22+static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
23+{
24+ return GSettingsControllerQml::instance();
25+}
26+
27+void FakeGSettingsQmlPlugin::registerTypes(const char *uri)
28+{
29+ qmlRegisterSingletonType<GSettingsControllerQml>(uri, 1, 0, "GSettingsController", controllerProvider);
30+ qmlRegisterType<GSettingsQml>(uri, 1, 0, "GSettings");
31+ qmlRegisterUncreatableType<GSettingsSchemaQml>(uri, 1, 0, "GSettingsSchema",
32+ "GSettingsSchema can only be used inside of a GSettings component");
33+}
tests/mocks/GSettings/plugin.hnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2013 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+#ifndef PLUGIN_H
18+#define PLUGIN_H
19+
20+#include <QtQml/QQmlExtensionPlugin>
21+
22+class FakeGSettingsQmlPlugin : public QQmlExtensionPlugin
23+{
24+ Q_OBJECT
25+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26+
27+public:
28+ void registerTypes(const char *uri);
29+};
30+
31+#endif // PLUGIN_H
tests/mocks/GSettings/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module GSettings
2+plugin FakeGSettingsQml
tests/mocks/LightDM/single/GreeterPrivate.cpp+2−4
@@ -34,10 +34,8 @@ void GreeterPrivate::handleAuthenticate()
34{
34{
35 Q_Q(Greeter);
35 Q_Q(Greeter);
36
36
37− if (authenticationUser == "one") {
38− authenticated = true;
37+ authenticated = true;
39− Q_EMIT q->authenticationComplete();
38+ Q_EMIT q->authenticationComplete();
40− }
41}
39}
42
40
43void GreeterPrivate::handleRespond(QString const &response)
41void GreeterPrivate::handleRespond(QString const &response)
tests/qmltests/CMakeLists.txt+1−1
@@ -26,7 +26,7 @@ set(qmltest_DEFAULT_TARGETS qmluitests)
26set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
26set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
27set(qmltest_DEFAULT_PROPERTIES "")
27set(qmltest_DEFAULT_PROPERTIES "")
28
28
29−add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
29+add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
30 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
30 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
31add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
31add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
32add_qml_test(Components DraggingArea)
32add_qml_test(Components DraggingArea)
tests/qmltests/Dash/tst_FilterGrids.qml+1−1
@@ -118,7 +118,7 @@ Rectangle {
118 gridLoader.source = "../../../Dash/" + data.component;
118 gridLoader.source = "../../../Dash/" + data.component;
119
119
120 // Wait until the FilterGrid is loaded by the loader
120 // Wait until the FilterGrid is loaded by the loader
121− tryCompare(gridLoader, "status", Loader.Ready, "Loader couldn't load " + data.component);
121+ tryCompare(gridLoader, "status", Loader.Ready);
122
122
123 // Wait until the GridView inside the model has finished rendering
123 // Wait until the GridView inside the model has finished rendering
124 var gridView = findChild(gridLoader, "responsiveGridViewGrid");
124 var gridView = findChild(gridLoader, "responsiveGridViewGrid");
tests/qmltests/tst_Shell.qml+14
@@ -19,6 +19,7 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import QtTest 1.0
21import QtTest 1.0
22+import GSettings 1.0
22import Ubuntu.Application 0.1
23import Ubuntu.Application 0.1
23import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
24
25
@@ -277,5 +278,18 @@ Item {
277 }
278 }
278 } while (!isStill);
279 } while (!isStill);
279 }
280 }
281+
282+ function test_wallpaper_data() {
283+ return [
284+ {tag: "red", url: "tests/data/unity/backgrounds/red.png"},
285+ {tag: "blue", url: "tests/data/unity/backgrounds/blue.png"}
286+ ]
287+ }
288+
289+ function test_wallpaper(data) {
290+ var backgroundImage = findChild(shell, "backgroundImage")
291+ GSettingsController.setPictureUri(data.url)
292+ tryCompareFunction(function() { return backgroundImage.source.toString().indexOf(data.url) !== -1; }, true)
293+ }
280 }
294 }
281}
295}