on xenial help sign in
Merge lp:~josharenson/unity8/qa_helpers
8a3b034Daniel d'Andrada
qml/Greeter/ShimGreeter.qmlnew file+45
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright (C) 2015 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.3
18+
19+/*
20+ * This is a shim greeter that is only used to provide a shell,
21+ * running without a greeter, the requisite information
22+ *
23+ */
24+
25+/* FIXME: this shuld be fine as a QtObject, but bug lp:1447391
26+ * dictates wrapping as an item instead
27+ */
28+Item {
29+ id: shimGreeter
30+
31+ readonly property bool active: false
32+ readonly property bool hasLockedApp: lockedApp !== ""
33+ readonly property bool locked: false
34+ readonly property bool shown: false
35+ readonly property bool waiting: false
36+
37+ property string lockedApp: ""
38+
39+ // Since ShimGreeter is never active, these can just return
40+ property var forceShow: (function() { return; })
41+ property var notifyAboutToFocusApp: (function(appId) { return; })
42+ property var notifyAppFocused: (function(appId) { return; })
43+ property var notifyShowingDashFromDrag: (function(appId) { return false; })
44+
45+}
qml/Shell.qml+47−34
@@ -111,6 +111,8 @@ Item {
111 // This is _only_ used to expose the property to autopilot tests
111 // This is _only_ used to expose the property to autopilot tests
112 readonly property string testShellMode: shellMode
112 readonly property string testShellMode: shellMode
113
113
114+ readonly property alias greeter: greeterLoader.item
115+
114 function activateApplication(appId) {
116 function activateApplication(appId) {
115 if (ApplicationManager.findApplication(appId)) {
117 if (ApplicationManager.findApplication(appId)) {
116 ApplicationManager.requestFocusApplication(appId);
118 ApplicationManager.requestFocusApplication(appId);
@@ -262,7 +264,9 @@ Item {
262 ? "phone"
264 ? "phone"
263 : shell.usageScenario
265 : shell.usageScenario
264 source: {
266 source: {
267+ if(shellMode === "greeter") {
268+ return "Stages/ShimStage.qml"
265− if (applicationsDisplayLoader.usageScenario === "phone") {
269+ } else if (applicationsDisplayLoader.usageScenario === "phone") {
266 return "Stages/PhoneStage.qml";
270 return "Stages/PhoneStage.qml";
267 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
271 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
268 return "Stages/TabletStage.qml";
272 return "Stages/TabletStage.qml";
@@ -414,50 +418,59 @@ Item {
414 }
418 }
415 }
419 }
416
420
417− Greeter {
421+ Loader {
418− id: greeter
422+ id: greeterLoader
419− objectName: "greeter"
420−
421− hides: [launcher, panel.indicators]
422− tabletMode: shell.usageScenario !== "phone"
423− launcherOffset: launcher.progress
424− forcedUnlock: tutorial.running
425− background: shell.background
426−
427 anchors.fill: parent
423 anchors.fill: parent
428 anchors.topMargin: panel.panelHeight
424 anchors.topMargin: panel.panelHeight
425+ sourceComponent: shellMode != "shell" ? integratedGreeter :
426+ Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
427+ onLoaded: {
428+ item.objectName = "greeter"
429+ }
430+ }
429
431
430− // avoid overlapping with Launcher's edge drag area
432+ Component {
431− // FIXME: Fix TouchRegistry & friends and remove this workaround
433+ id: integratedGreeter
432− // Issue involves launcher's DDA getting disabled on a long
434+ Greeter {
433− // left-edge drag
434− dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
435
435
436− onSessionStarted: {
436+ hides: [launcher, panel.indicators]
437− launcher.hide();
437+ tabletMode: shell.sideStageEnabled
438− }
438+ launcherOffset: launcher.progress
439+ forcedUnlock: tutorial.running
440+ background: shell.background
439
441
442+ // avoid overlapping with Launcher's edge drag area
443+ // FIXME: Fix TouchRegistry & friends and remove this workaround
444+ // Issue involves launcher's DDA getting disabled on a long
445+ // left-edge drag
446+ dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
447+
448+ onSessionStarted: {
449+ launcher.hide();
450+ }
451+
440− onTease: {
452+ onTease: {
441− if (!tutorial.running) {
453+ if (!tutorial.running) {
442− launcher.tease();
454+ launcher.tease();
455+ }
443 }
456 }
444− }
445
457
446− onEmergencyCall: startLockedApp("dialer-app")
458+ onEmergencyCall: startLockedApp("dialer-app")
447
459
448− Timer {
460+ Binding {
449− // See powerConnection for why this is useful
461+ target: ApplicationManager
450− id: showGreeterDelayed
462+ property: "suspended"
451− interval: 1
452− onTriggered: {
453− greeter.forceShow();
463+ value: greeter.shown
454 }
464 }
455 }
465 }
466+ }
456
467
457− Binding {
468+ Timer {
458− target: ApplicationManager
469+ // See powerConnection for why this is useful
459− property: "suspended"
470+ id: showGreeterDelayed
471+ interval: 1
472+ onTriggered: {
460− value: greeter.shown
473+ greeter.forceShow();
461 }
474 }
462 }
475 }
463
476
qml/Stages/ShimStage.qmlnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (C) 2015 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.3
18+import Ubuntu.Components 0.1
19+
20+Rectangle {
21+ id: shimStage
22+
23+ anchors.fill: parent
24+ color: UbuntuColors.lightAubergine
25+
26+ Text {
27+ id: greeterModeText
28+
29+ anchors.horizontalCenter: parent.horizontalCenter
30+ anchors.top: parent.top
31+ anchors.topMargin: units.gu(10)
32+ font.bold: true
33+ horizontalAlignment: Text.AlignHCenter
34+ text: "Shell is in \"greeter\" mode"
35+ }
36+
37+ Text {
38+ anchors.centerIn: parent
39+ color: UbuntuColors.orange
40+ font.pointSize: units.gu(8)
41+ horizontalAlignment: Text.AlignHCenter
42+ text: "Shim \nStage"
43+ }
44+}
tests/autopilot/unity8/application_lifecycle/tests/__init__.py+2−2
@@ -31,8 +31,8 @@ class ApplicationLifeCycleTestCase(tests.UnityTestCase):
31 super().setUp()
31 super().setUp()
32 self._qml_mock_enabled = False
32 self._qml_mock_enabled = False
33 self._data_dirs_mock_enabled = False
33 self._data_dirs_mock_enabled = False
34− unity_proxy = self.launch_unity()
34+ self.launch_unity()
35− process_helpers.unlock_unity(unity_proxy)
35+ process_helpers.unlock_unity()
36
36
37 def create_test_application(self):
37 def create_test_application(self):
38 desktop_file_dict = fixture_setup.DEFAULT_DESKTOP_FILE_DICT
38 desktop_file_dict = fixture_setup.DEFAULT_DESKTOP_FILE_DICT
tests/autopilot/unity8/greeter/__init__.py+52
@@ -0,0 +1,52 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+import dbus
21+
22+from autopilot.matchers import Eventually
23+from testtools.matchers import Equals
24+
25+
26+def hide_greeter_with_dbus():
27+ dbus_proxy = _get_greeter_dbus_proxy()
28+ if _is_greeter_active():
29+ dbus_proxy.HideGreeter()
30+
31+
32+def show_greeter_with_dbus():
33+ dbus_proxy = _get_greeter_dbus_proxy()
34+ if not _is_greeter_active():
35+ dbus_proxy.ShowGreeter()
36+
37+
38+def wait_for_greeter():
39+ Eventually(Equals(True), timeout=300).match(_is_greeter_active)
40+
41+
42+def _get_greeter_dbus_proxy():
43+ bus = dbus.SessionBus()
44+ return bus.get_object('com.canonical.UnityGreeter', '/')
45+
46+
47+def _is_greeter_active():
48+ try:
49+ dbus_proxy = _get_greeter_dbus_proxy()
50+ return dbus_proxy.Get('com.canonical.UnityGreeter', 'IsActive')
51+ except:
52+ return False
tests/autopilot/unity8/indicators/tests/__init__.py+1−1
@@ -31,7 +31,7 @@ class IndicatorTestCase(tests.UnityTestCase):
31 def setUp(self):
31 def setUp(self):
32 super().setUp()
32 super().setUp()
33 self.unity_proxy = self.launch_unity()
33 self.unity_proxy = self.launch_unity()
34− process_helpers.unlock_unity(self.unity_proxy)
34+ process_helpers.unlock_unity()
35
35
36
36
37class DeviceIndicatorTestCase(IndicatorTestCase):
37class DeviceIndicatorTestCase(IndicatorTestCase):
tests/autopilot/unity8/process_helpers.py+7−94
@@ -19,14 +19,11 @@
19
19
20import logging
20import logging
21import subprocess
21import subprocess
22−import dbus
23
22
24import ubuntuuitoolkit
23import ubuntuuitoolkit
25−from autopilot.exceptions import ProcessSearchError
26from autopilot.introspection import get_proxy_object_for_existing_process
24from autopilot.introspection import get_proxy_object_for_existing_process
27
25
28−from unity8.shell import emulators
26+from unity8 import greeter
29−from unity8.shell.emulators import main_window as main_window_emulator
30
27
31logger = logging.getLogger(__name__)
28logger = logging.getLogger(__name__)
32
29
@@ -39,104 +36,20 @@ class CannotAccessUnity(Exception):
39 pass
36 pass
40
37
41
38
42−def unlock_unity(unity_proxy_obj=None):
39+def unlock_unity():
43 """Helper function that attempts to unlock the unity greeter.
40 """Helper function that attempts to unlock the unity greeter.
44
41
45− If unity_proxy_object is None create a proxy object by querying for the
46− running unity process.
47− Otherwise re-use the passed proxy object.
48−
49− :raises RuntimeError: if the greeter attempts and fails to be unlocked.
50−
51− :raises RuntimeWarning: when the greeter cannot be found because it is
52− already unlocked.
53− :raises CannotAccessUnity: if unity is not introspectable or cannot be
54− found on dbus.
55− :raises CannotAccessUnity: if unity's upstart status is not "start" or the
56− upstart job cannot be found at all.
57−
58 """
42 """
59− if unity_proxy_obj is None:
43+ greeter.wait_for_greeter()
60− try:
44+ greeter.hide_greeter_with_dbus()
61− pid = get_unity_pid()
62− unity = _get_unity_proxy_object(pid)
63− main_window = unity.select_single(main_window_emulator.QQuickView)
64− except ProcessSearchError as e:
65− raise CannotAccessUnity(
66− "Cannot introspect unity, make sure that it has been started "
67− "with testability. Perhaps use the function "
68− "'restart_unity_with_testability' this module provides."
69− "(%s)"
70− % str(e)
71− )
72− else:
73− main_window = unity_proxy_obj.select_single(
74− main_window_emulator.QQuickView)
75−
76− greeter = main_window.get_greeter()
77− if greeter.created is False:
78− raise RuntimeWarning("Greeter appears to be already unlocked.")
79−
80− bus = dbus.SessionBus()
81− dbus_proxy = bus.get_object("com.canonical.UnityGreeter", "/")
82− try:
83− dbus_proxy.HideGreeter()
84− except dbus.DBusException:
85− logger.info("Failed to unlock greeter")
86− raise
87− else:
88− greeter.created.wait_for(False)
89− logger.info("Greeter unlocked, continuing.")
90
45
91
46
92−def lock_unity(unity_proxy_obj=None):
47+def lock_unity():
93 """Helper function that attempts to lock unity greeter.
48 """Helper function that attempts to lock unity greeter.
94
49
95− If unity_proxy_object is None create a proxy object by querying for the
96− running unity process.
97− Otherwise re-use the passed proxy object.
98−
99− :raises RuntimeError: if the greeter attempts and fails to be locked.
100−
101− :raises RuntimeWarning: when the greeter is found because it is
102− already locked.
103− :raises CannotAccessUnity: if unity is not introspectable or cannot be
104− found on dbus.
105− :raises CannotAccessUnity: if unity's upstart status is not "start" or the
106− upstart job cannot be found at all.
107−
108 """
50 """
109− if unity_proxy_obj is None:
51+ greeter.show_greeter_with_dbus()
110− try:
52+ greeter.wait_for_greeter()
111− pid = get_unity_pid()
112− unity = _get_unity_proxy_object(pid)
113− main_window = unity.select_single(main_window_emulator.QQuickView)
114− except ProcessSearchError as e:
115− raise CannotAccessUnity(
116− "Cannot introspect unity, make sure that it has been started "
117− "with testability. Perhaps use the function "
118− "'restart_unity_with_testability' this module provides."
119− "(%s)"
120− % str(e)
121− )
122− else:
123− main_window = unity_proxy_obj.select_single(
124− main_window_emulator.QQuickView)
125−
126− greeter = main_window.get_greeter()
127− if greeter.created is True:
128− raise RuntimeWarning("Greeter appears to be already locked.")
129−
130− bus = dbus.SessionBus()
131− dbus_proxy = bus.get_object("com.canonical.UnityGreeter", "/")
132− try:
133− dbus_proxy.ShowGreeter()
134− except dbus.DBusException:
135− logger.info("Failed to lock greeter")
136− raise
137− else:
138− greeter.created.wait_for(True)
139− logger.info("Greeter locked, continuing.")
140
53
141
54
142def restart_unity_with_testability(*args):
55def restart_unity_with_testability(*args):
tests/autopilot/unity8/shell/tests/test_emulators.py+2−2
@@ -41,8 +41,8 @@ class MainWindowTestCase(tests.UnityTestCase):
41
41
42 def setUp(self):
42 def setUp(self):
43 super().setUp()
43 super().setUp()
44− unity_proxy = self.launch_unity()
44+ self.launch_unity()
45− process_helpers.unlock_unity(unity_proxy)
45+ process_helpers.unlock_unity()
46
46
47
47
48class DashEmulatorTestCase(tests.DashBaseTestCase):
48class DashEmulatorTestCase(tests.DashBaseTestCase):
tests/autopilot/unity8/shell/tests/test_notifications.py+22−22
@@ -119,8 +119,8 @@ class InteractiveNotificationBase(NotificationsBase):
119
119
120 def test_interactive(self):
120 def test_interactive(self):
121 """Interactive notification must react upon click on itself."""
121 """Interactive notification must react upon click on itself."""
122− unity_proxy = self.launch_unity()
122+ self.launch_unity()
123− unlock_unity(unity_proxy)
123+ unlock_unity()
124
124
125 notify_list = self._get_notifications_list()
125 notify_list = self._get_notifications_list()
126
126
@@ -156,8 +156,8 @@ class InteractiveNotificationBase(NotificationsBase):
156 """Snap-decision with three actions should use
156 """Snap-decision with three actions should use
157 one-over two button layout.
157 one-over two button layout.
158 """
158 """
159− unity_proxy = self.launch_unity()
159+ self.launch_unity()
160− unlock_unity(unity_proxy)
160+ unlock_unity()
161
161
162 summary = "Theatre at Ferria Stadium"
162 summary = "Theatre at Ferria Stadium"
163 body = "at Ferria Stadium in Bilbao, Spain\n07578545317"
163 body = "at Ferria Stadium in Bilbao, Spain\n07578545317"
@@ -197,8 +197,8 @@ class InteractiveNotificationBase(NotificationsBase):
197 """Snap-decision should block input to shell
197 """Snap-decision should block input to shell
198 without greeter/lockscreen.
198 without greeter/lockscreen.
199 """
199 """
200− unity_proxy = self.launch_unity()
200+ self.launch_unity()
201− unlock_unity(unity_proxy)
201+ unlock_unity()
202
202
203 summary = "Incoming file"
203 summary = "Incoming file"
204 body = "Frank would like to send you the file: essay.pdf"
204 body = "Frank would like to send you the file: essay.pdf"
@@ -421,8 +421,8 @@ class EphemeralNotificationsTests(NotificationsBase):
421
421
422 def test_icon_summary_body(self):
422 def test_icon_summary_body(self):
423 """Notification must display the expected summary and body text."""
423 """Notification must display the expected summary and body text."""
424− unity_proxy = self.launch_unity()
424+ self.launch_unity()
425− unlock_unity(unity_proxy)
425+ unlock_unity()
426
426
427 notify_list = self._get_notifications_list()
427 notify_list = self._get_notifications_list()
428
428
@@ -458,8 +458,8 @@ class EphemeralNotificationsTests(NotificationsBase):
458 def test_icon_summary(self):
458 def test_icon_summary(self):
459 """Notification must display the expected summary and secondary
459 """Notification must display the expected summary and secondary
460 icon."""
460 icon."""
461− unity_proxy = self.launch_unity()
461+ self.launch_unity()
462− unlock_unity(unity_proxy)
462+ unlock_unity()
463
463
464 notify_list = self._get_notifications_list()
464 notify_list = self._get_notifications_list()
465
465
@@ -491,8 +491,8 @@ class EphemeralNotificationsTests(NotificationsBase):
491 def test_urgency_order(self):
491 def test_urgency_order(self):
492 """Notifications must be displayed in order according to their
492 """Notifications must be displayed in order according to their
493 urgency."""
493 urgency."""
494− unity_proxy = self.launch_unity()
494+ self.launch_unity()
495− unlock_unity(unity_proxy)
495+ unlock_unity()
496
496
497 notify_list = self._get_notifications_list()
497 notify_list = self._get_notifications_list()
498
498
@@ -578,8 +578,8 @@ class EphemeralNotificationsTests(NotificationsBase):
578
578
579 def test_summary_and_body(self):
579 def test_summary_and_body(self):
580 """Notification must display the expected summary- and body-text."""
580 """Notification must display the expected summary- and body-text."""
581− unity_proxy = self.launch_unity()
581+ self.launch_unity()
582− unlock_unity(unity_proxy)
582+ unlock_unity()
583
583
584 notify_list = self._get_notifications_list()
584 notify_list = self._get_notifications_list()
585
585
@@ -603,8 +603,8 @@ class EphemeralNotificationsTests(NotificationsBase):
603
603
604 def test_summary_only(self):
604 def test_summary_only(self):
605 """Notification must display only the expected summary-text."""
605 """Notification must display only the expected summary-text."""
606− unity_proxy = self.launch_unity()
606+ self.launch_unity()
607− unlock_unity(unity_proxy)
607+ unlock_unity()
608
608
609 notify_list = self._get_notifications_list()
609 notify_list = self._get_notifications_list()
610
610
@@ -621,8 +621,8 @@ class EphemeralNotificationsTests(NotificationsBase):
621 def test_update_notification_same_layout(self):
621 def test_update_notification_same_layout(self):
622 """Notification must allow updating its contents while being
622 """Notification must allow updating its contents while being
623 displayed."""
623 displayed."""
624− unity_proxy = self.launch_unity()
624+ self.launch_unity()
625− unlock_unity(unity_proxy)
625+ unlock_unity()
626
626
627 notify_list = self._get_notifications_list()
627 notify_list = self._get_notifications_list()
628
628
@@ -660,8 +660,8 @@ class EphemeralNotificationsTests(NotificationsBase):
660 def test_update_notification_layout_change(self):
660 def test_update_notification_layout_change(self):
661 """Notification must allow updating its contents and layout while
661 """Notification must allow updating its contents and layout while
662 being displayed."""
662 being displayed."""
663− unity_proxy = self.launch_unity()
663+ self.launch_unity()
664− unlock_unity(unity_proxy)
664+ unlock_unity()
665
665
666 notify_list = self._get_notifications_list()
666 notify_list = self._get_notifications_list()
667
667
@@ -709,8 +709,8 @@ class EphemeralNotificationsTests(NotificationsBase):
709 """ use the create notification script to get a notification dialog.
709 """ use the create notification script to get a notification dialog.
710 Check that the arguments passed to the script match the fields. """
710 Check that the arguments passed to the script match the fields. """
711
711
712− unity_proxy = self.launch_unity()
712+ self.launch_unity()
713− unlock_unity(unity_proxy)
713+ unlock_unity()
714
714
715 summary = 'Helper summary'
715 summary = 'Helper summary'
716 body = 'Helper body'
716 body = 'Helper body'
tests/qmltests/Tutorial/tst_Tutorial.qml+1
@@ -73,6 +73,7 @@ Item {
73 sourceComponent: Component {
73 sourceComponent: Component {
74 Shell {
74 Shell {
75 property string indicatorProfile: "phone"
75 property string indicatorProfile: "phone"
76+ property string shellMode: "full-greeter" /* default */
76
77
77 Component.onDestruction: {
78 Component.onDestruction: {
78 shellLoader.itemDestroyed = true;
79 shellLoader.itemDestroyed = true;
tests/qmltests/tst_Shell.qml+2
@@ -81,6 +81,8 @@ Rectangle {
81 property bool itemDestroyed: false
81 property bool itemDestroyed: false
82 sourceComponent: Component {
82 sourceComponent: Component {
83 Shell {
83 Shell {
84+ property string shellMode: "full-greeter" /* default */
85+
84 usageScenario: "phone"
86 usageScenario: "phone"
85 orientation: Qt.PortraitOrientation
87 orientation: Qt.PortraitOrientation
86 primaryOrientation: Qt.PortraitOrientation
88 primaryOrientation: Qt.PortraitOrientation
tests/qmltests/tst_ShellWithPin.qml+2
@@ -69,6 +69,8 @@ Item {
69 property bool itemDestroyed: false
69 property bool itemDestroyed: false
70 sourceComponent: Component {
70 sourceComponent: Component {
71 Shell {
71 Shell {
72+ property string shellMode: "full-greeter" /* default */
73+
72 Component.onDestruction: {
74 Component.onDestruction: {
73 shellLoader.itemDestroyed = true
75 shellLoader.itemDestroyed = true
74 }
76 }