on xenial help sign in
Merge
commitd08ddb5c5264b429df2e7c99f19a67a1d3b685db authorAlbert Astals <albert.astals@canonical.com> dateWed 16 Apr 2014, 10:29 (13 years ago) parentaa864a18981aba
28 files changed+604−222
debian/changelog+65 debian/unity8-autopilot.install+1 po/ast.po+1−1 po/br.po+164 po/fi.po+5−22 po/gl.po+5−22 po/ug.po+7−23 qml/Components/FilterGrid.qml+1 qml/Components/ResponsiveGridView.qml+1 qml/Dash/CardFilterGrid.qml+1 qml/Dash/DashRenderer.qml+2 qml/Dash/GenericScopeView.qml+8−8 qml/Notifications/NotificationMenuItemFactory.qml+2−1 qml/Shell.qml+5−12 tests/autopilot/unity8/application_lifecycle/tests/__init__.py+49 tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+56−72 tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py+8−39 tests/autopilot/unity8/indicators/tests/__init__.py+17 tests/autopilot/unity8/indicators/tests/test_indicators.py+69−13 tests/autopilot/unity8/process_helpers.py+16 tests/autopilot/unity8/shell/emulators/__init__.py+2−6 tests/autopilot/unity8/shell/emulators/dash.py+15 tests/autopilot/unity8/shell/emulators/main_window.py+32−2 tests/autopilot/unity8/shell/tests/test_emulators.py+16 tests/autopilot/unity8/shell/tests/test_system_integration.py+1−1 tests/qmltests/tst_Shell.qml+11 tools/CMakeLists.txt+2 tools/unlock-device+42
debian/changelog+65
@@ -1,3 +1,68 @@
1+unity8 (7.85+14.04.20140415.2-0ubuntu1) trusty; urgency=low
2+
3+ [ Michael Terry ]
4+ * When an application requests focus, handle it in Shell.qml by hiding
5+ the greeter and stopping any edge demo. (LP: #1227753)
6+
7+ [ Leo Arias ]
8+ * Use subprocess.check_call when caling url-dispatcher, so an error
9+ will be raised if it fails.
10+ * Test application life cycle with fake apps, instead of messaging and
11+ address book.
12+
13+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 15 Apr 2014 12:47:11 +0000
14+
15+unity8 (7.85+14.04.20140410.1-0ubuntu1) trusty; urgency=medium
16+
17+ [ Didier Roche ]
18+ * Resync trunk with previous revert upload
19+
20+ [ Michał Sawicz ]
21+ * Set the Qt.ImhNoPredictiveText flag on wifi password field, fixes
22+ lp:1291575 (LP: #1291575)
23+
24+ [ Albert Astals ]
25+ * Take into account the originY when specifying the delegate ranges
26+ Fixes bug #1300302 (LP: #1300302)
27+
28+ [ CI bot ]
29+ * Resync trunk
30+
31+ [ Allan LeSage ]
32+ * Swiping open an indicator must show its correct title--protect
33+ against lp:1253804 . (LP: #1253804)
34+
35+ [ Alexander Sack ]
36+ * Fix TypeError: issue seen in system_integration autopilot test on
37+ image 279. (LP: #1303685)
38+
39+ [ Bill Filler ]
40+ * Set the Qt.ImhNoPredictiveText flag on wifi password field, fixes
41+ lp:1291575 (LP: #1291575)
42+
43+ [ Leo Arias ]
44+ * Added a search autopilot helper.
45+
46+ [ Michael Terry ]
47+ * Provide a all-in-one script for getting a device to an unlocked
48+ state.
49+
50+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 10 Apr 2014 10:03:31 +0000
51+
52+unity8 (7.85+14.04.20140404.is.7.85+14.04.20140403.1-0ubuntu1) trusty; urgency=medium
53+
54+ * Revert to previous version as it's linked to latest sdk change which
55+ is making gallery-app AP tests failing on the CI dashboard
56+
57+ -- Didier Roche <didrocks@ubuntu.com> Tue, 08 Apr 2014 13:53:47 +0200
58+
59+unity8 (7.85+14.04.20140404-0ubuntu1) trusty; urgency=low
60+
61+ [ Albert Astals ]
62+ * Adapt to new TabBar
63+
64+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 04 Apr 2014 15:03:00 +0000
65+
166unity8 (7.85+14.04.20140403.1-0ubuntu1) trusty; urgency=low
267
368 [ Michael Terry ]
debian/unity8-autopilot.install+1
@@ -1 +1,2 @@
11usr/lib/python*/*/unity*
2+usr/share/unity8/unlock-device
po/ast.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-04-04 07:12+0000\n"
17+"X-Launchpad-Export-Date: 2014-04-08 07:13+0000\n"
1818"X-Generator: Launchpad (build 16976)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
po/br.ponew file+164
@@ -0,0 +1,164 @@
1+# Breton translation for unity8
2+# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2014-03-25 16:48+0100\n"
11+"PO-Revision-Date: 2014-04-15 18:15+0000\n"
12+"Last-Translator: Rouxel <vincent.rouxel@mailoo.org>\n"
13+"Language-Team: Breton <br@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"X-Launchpad-Export-Date: 2014-04-16 07:12+0000\n"
18+"X-Generator: Launchpad (build 16976)\n"
19+
20+#: plugins/Unity/Launcher/launcheritem.cpp:43
21+#: plugins/Unity/Launcher/launcheritem.cpp:73
22+msgid "Lock to Launcher"
23+msgstr ""
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:73
26+msgid "Unlock from Launcher"
27+msgstr ""
28+
29+#: qml/Components/EdgeDemoOverlay.qml:151
30+msgid "Skip intro"
31+msgstr ""
32+
33+#: qml/Components/EdgeDemo.qml:113
34+msgid "Right edge"
35+msgstr ""
36+
37+#: qml/Components/EdgeDemo.qml:114
38+msgid "Try swiping from the right edge to unlock the phone"
39+msgstr ""
40+
41+#: qml/Components/EdgeDemo.qml:145
42+msgid "Top edge"
43+msgstr ""
44+
45+#: qml/Components/EdgeDemo.qml:146
46+msgid "Try swiping from the top edge to access the indicators"
47+msgstr ""
48+
49+#: qml/Components/EdgeDemo.qml:171
50+msgid "Close"
51+msgstr "Serriñ"
52+
53+#: qml/Components/EdgeDemo.qml:172
54+msgid "Swipe up again to close the settings screen"
55+msgstr ""
56+
57+#: qml/Components/EdgeDemo.qml:199
58+msgid "Left edge"
59+msgstr ""
60+
61+#: qml/Components/EdgeDemo.qml:200
62+msgid "Swipe from the left to reveal the launcher for quick access to apps"
63+msgstr ""
64+
65+#: qml/Components/EdgeDemo.qml:227
66+msgid "Well done"
67+msgstr ""
68+
69+#: qml/Components/EdgeDemo.qml:228
70+msgid ""
71+"You have now mastered the edge gestures and can start using the "
72+"phone<br><br>Tap on the screen to start"
73+msgstr ""
74+
75+#: qml/Components/PageHeader.qml:365
76+msgid "Recent searches"
77+msgstr ""
78+
79+#: qml/Components/SeeMore.qml:35
80+msgid "See more"
81+msgstr ""
82+
83+#: qml/Components/SeeMore.qml:58
84+msgid "See less"
85+msgstr ""
86+
87+#: qml/Dash/DashApps.qml:38
88+msgid "Recent"
89+msgstr ""
90+
91+#: qml/Dash/PreviewListView.qml:52
92+#, qt-format
93+msgid "%1 Preview"
94+msgstr ""
95+
96+#: qml/Dash/Previews/PreviewActionCombo.qml:34
97+msgid "More..."
98+msgstr ""
99+
100+#: qml/Dash/Previews/PreviewActionCombo.qml:34
101+msgid "Less..."
102+msgstr ""
103+
104+#: qml/Dash/Previews/PreviewRatingInput.qml:81
105+msgid "Rate this"
106+msgstr ""
107+
108+#: qml/Dash/Previews/PreviewRatingInput.qml:126
109+msgid "Add a review"
110+msgstr ""
111+
112+#: qml/Dash/Previews/PreviewRatingInput.qml:166
113+msgid "Send"
114+msgstr ""
115+
116+#: qml/Greeter/Lockscreen.qml:149
117+msgid "Emergency Call"
118+msgstr ""
119+
120+#: qml/Greeter/PassphraseLockscreen.qml:59
121+#, qt-format
122+msgid "Hello %1"
123+msgstr ""
124+
125+#: qml/Greeter/PassphraseLockscreen.qml:59
126+msgid "Hello"
127+msgstr ""
128+
129+#: qml/Hud/HudParametrizedActionsPage.qml:132
130+msgid "Confirm"
131+msgstr ""
132+
133+#: qml/Hud/Hud.qml:61 qml/Hud/Hud.qml:99 qml/Hud/Hud.qml:372
134+msgid "Type or say a command"
135+msgstr ""
136+
137+#: qml/Hud/Hud.qml:84
138+msgid "Loading. Please Wait..."
139+msgstr ""
140+
141+#: qml/Hud/Hud.qml:89
142+msgid "Speak Now..."
143+msgstr ""
144+
145+#: qml/Hud/Hud.qml:94
146+msgid "Speaking..."
147+msgstr ""
148+
149+#: qml/Notifications/NotificationMenuItemFactory.qml:91
150+msgid "Show password"
151+msgstr ""
152+
153+#: qml/Notifications/NotificationMenuItemFactory.qml:103
154+msgid "Please enter SIM PIN"
155+msgstr ""
156+
157+#: qml/Panel/SearchIndicator.qml:27
158+msgid "Search"
159+msgstr ""
160+
161+#: qml/Shell.qml:417
162+#, qt-format
163+msgid "Please enter %1"
164+msgstr ""
po/fi.po+5−22
@@ -8,15 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-03-25 16:48+0100\n"
11−"PO-Revision-Date: 2014-03-09 19:22+0000\n"
11+"PO-Revision-Date: 2014-04-05 09:57+0000\n"
1212"Last-Translator: Jiri Grönroos <Unknown>\n"
1313"Language-Team: Finnish <fi@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2014-03-28 06:48+0000\n"
19−"X-Generator: Launchpad (build 16967)\n"
17+"X-Launchpad-Export-Date: 2014-04-08 07:13+0000\n"
18+"X-Generator: Launchpad (build 16976)\n"
2019
2120#: plugins/Unity/Launcher/launcheritem.cpp:43
2221#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -89,12 +88,12 @@ msgstr "Näytä vähemmän"
8988
9089#: qml/Dash/DashApps.qml:38
9190msgid "Recent"
92−msgstr ""
91+msgstr "Viimeisimmät"
9392
9493#: qml/Dash/PreviewListView.qml:52
9594#, qt-format
9695msgid "%1 Preview"
97−msgstr ""
96+msgstr "%1 esikatselu"
9897
9998#: qml/Dash/Previews/PreviewActionCombo.qml:34
10099msgid "More..."
@@ -165,19 +164,3 @@ msgstr "Hae"
165164#, qt-format
166165msgid "Please enter %1"
167166msgstr "Kirjoita %1"
168−
169−#~ msgid "Comments:"
170−#~ msgstr "Kommentit:"
171−
172−#~ msgid "Review"
173−#~ msgstr "Arvostelu"
174−
175−#, qt-format
176−#~ msgid "%1 review"
177−#~ msgid_plural "%1 reviews"
178−#~ msgstr[0] "%1 arvostelu"
179−#~ msgstr[1] "%1 arvostelua"
180−
181−#, qt-format
182−#~ msgid "(%1)"
183−#~ msgstr "(%1)"
po/gl.po+5−22
@@ -8,15 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-03-25 16:48+0100\n"
11−"PO-Revision-Date: 2014-03-08 06:58+0000\n"
11+"PO-Revision-Date: 2014-04-08 07:04+0000\n"
1212"Last-Translator: Marcos Lans <Unknown>\n"
1313"Language-Team: Galician <gl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2014-03-28 06:48+0000\n"
19−"X-Generator: Launchpad (build 16967)\n"
17+"X-Launchpad-Export-Date: 2014-04-09 07:31+0000\n"
18+"X-Generator: Launchpad (build 16976)\n"
2019
2120#: plugins/Unity/Launcher/launcheritem.cpp:43
2221#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -89,12 +88,12 @@ msgstr "Ver menos"
8988
9089#: qml/Dash/DashApps.qml:38
9190msgid "Recent"
92−msgstr ""
91+msgstr "Recentes"
9392
9493#: qml/Dash/PreviewListView.qml:52
9594#, qt-format
9695msgid "%1 Preview"
97−msgstr ""
96+msgstr "%1 previsualización"
9897
9998#: qml/Dash/Previews/PreviewActionCombo.qml:34
10099msgid "More..."
@@ -165,19 +164,3 @@ msgstr "Buscar"
165164#, qt-format
166165msgid "Please enter %1"
167166msgstr "Introduza %1"
168−
169−#, qt-format
170−#~ msgid "(%1)"
171−#~ msgstr "(%1)"
172−
173−#~ msgid "Comments:"
174−#~ msgstr "Comentarios:"
175−
176−#~ msgid "Review"
177−#~ msgstr "Opinión"
178−
179−#, qt-format
180−#~ msgid "%1 review"
181−#~ msgid_plural "%1 reviews"
182−#~ msgstr[0] "%1 opinión"
183−#~ msgstr[1] "%1 opinións"
po/ug.po+7−23
@@ -8,15 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-03-25 16:48+0100\n"
11−"PO-Revision-Date: 2014-02-15 12:09+0000\n"
12−"Last-Translator: Gheyret T.Kenji <Unknown>\n"
11+"PO-Revision-Date: 2014-04-14 09:31+0000\n"
12+"Last-Translator: volkhan <Unknown>\n"
1313"Language-Team: Uyghur <ug@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2014-03-28 06:49+0000\n"
19−"X-Generator: Launchpad (build 16967)\n"
17+"X-Launchpad-Export-Date: 2014-04-15 07:57+0000\n"
18+"X-Generator: Launchpad (build 16976)\n"
2019
2120#: plugins/Unity/Launcher/launcheritem.cpp:43
2221#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -89,7 +88,7 @@ msgstr "ئازراق كۆرۈش"
8988
9089#: qml/Dash/DashApps.qml:38
9190msgid "Recent"
92−msgstr ""
91+msgstr "يېقىنقى"
9392
9493#: qml/Dash/PreviewListView.qml:52
9594#, qt-format
@@ -98,11 +97,11 @@ msgstr ""
9897
9998#: qml/Dash/Previews/PreviewActionCombo.qml:34
10099msgid "More..."
101−msgstr ""
100+msgstr "تېخىمۇ كۆپ…"
102101
103102#: qml/Dash/Previews/PreviewActionCombo.qml:34
104103msgid "Less..."
105−msgstr ""
104+msgstr "ئازراق..."
106105
107106#: qml/Dash/Previews/PreviewRatingInput.qml:81
108107msgid "Rate this"
@@ -165,18 +164,3 @@ msgstr "ئىزدە"
165164#, qt-format
166165msgid "Please enter %1"
167166msgstr "%1 نى كىرگۈزۈڭ"
168−
169−#, qt-format
170−#~ msgid "%1 review"
171−#~ msgid_plural "%1 reviews"
172−#~ msgstr[0] "%1 باھا"
173−
174−#, qt-format
175−#~ msgid "(%1)"
176−#~ msgstr "(%1)"
177−
178−#~ msgid "Comments:"
179−#~ msgstr "ئىنكاس:"
180−
181−#~ msgid "Review"
182−#~ msgstr "تەكشۈر"
qml/Components/FilterGrid.qml+1
@@ -59,6 +59,7 @@ Item {
5959 property alias cellHeight: iconTileGrid.cellHeight
6060 property alias delegateCreationBegin: iconTileGrid.delegateCreationBegin
6161 property alias delegateCreationEnd: iconTileGrid.delegateCreationEnd
62+ readonly property alias originY: iconTileGrid.originY
6263 readonly property alias flicking: iconTileGrid.flicking
6364 readonly property alias moving: iconTileGrid.moving
6465 readonly property alias pressDelay: iconTileGrid.pressDelay
qml/Components/ResponsiveGridView.qml+1
@@ -40,6 +40,7 @@ Item {
4040 readonly property alias flicking: gridView.flicking
4141 readonly property alias moving: gridView.moving
4242 readonly property alias pressDelay: gridView.pressDelay
43+ readonly property alias originY: gridView.originY
4344 property alias delegateCreationBegin: gridView.delegateCreationBegin
4445 property alias delegateCreationEnd: gridView.delegateCreationEnd
4546 property alias highlightIndex: gridView.highlightIndex
qml/Dash/CardFilterGrid.qml+1
@@ -24,6 +24,7 @@ DashRenderer {
2424 collapsedHeight: filterGrid.collapsedHeight
2525 margins: filterGrid.margins
2626 uncollapsedHeight: filterGrid.uncollapsedHeight
27+ originY: filterGrid.originY
2728 verticalSpacing: units.gu(1)
2829 currentItem: filterGrid.currentItem
2930 height: filterGrid.height
qml/Dash/DashRenderer.qml+2
@@ -33,6 +33,8 @@ Item {
3333
3434 property var delegateCreationEnd: undefined
3535
36+ property real originY: 0
37+
3638 property real verticalSpacing: 0
3739
3840 // The current item of the renderer
qml/Dash/GenericScopeView.qml+8−8
@@ -247,16 +247,16 @@ FocusScope {
247247
248248 if (item && item.hasOwnProperty("delegateCreationBegin")) {
249249 if (baseItem.y + baseItem.height <= 0) {
250− // Not visible (item at top of the list)
251− item.delegateCreationBegin = baseItem.height
252− item.delegateCreationEnd = baseItem.height
250+ // Not visible (item at top of the list viewport)
251+ item.delegateCreationBegin = item.originY + baseItem.height
252+ item.delegateCreationEnd = item.originY + baseItem.height
253253 } else if (baseItem.y >= categoryView.height) {
254− // Not visible (item at bottom of the list)
255− item.delegateCreationBegin = 0
256− item.delegateCreationEnd = 0
254+ // Not visible (item at bottom of the list viewport)
255+ item.delegateCreationBegin = item.originY
256+ item.delegateCreationEnd = item.originY
257257 } else {
258− item.delegateCreationBegin = Math.max(-baseItem.y, 0)
259− item.delegateCreationEnd = Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
258+ item.delegateCreationBegin = item.originY + Math.max(-baseItem.y, 0)
259+ item.delegateCreationEnd = item.originY + Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
260260 }
261261 }
262262 }
qml/Notifications/NotificationMenuItemFactory.qml+2−1
@@ -66,7 +66,8 @@ Loader {
6666 TextField {
6767 id: textfield
6868
69− inputMethodHints: Qt.ImhNoAutoUppercase
69+ // TODO using Qt.ImhNoPredictiveText here until lp #1291575 is fixed for ubuntu-ui-toolkit
70+ inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
7071 anchors.left: parent.left; anchors.right: parent.right
7172 echoMode: checkBox.checked ? TextInput.Normal : TextInput.Password
7273 height: units.gu(5)
qml/Shell.qml+5−12
@@ -240,6 +240,8 @@ FocusScope {
240240 function show(focusApp) {
241241 shown = true;
242242 panel.indicators.hide();
243+ edgeDemo.stopDemo();
244+ greeter.hide();
243245 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
244246 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
245247 }
@@ -267,12 +269,6 @@ FocusScope {
267269 stages.hide();
268270 }
269271 }
270−
271− // If any app is focused when greeter is open, it's due to a user action
272− // like a snap decision (say, an incoming call).
273− // TODO: these should be protected to only unlock for certain applications / certain usecases
274− // potentially only in connection with a notification.
275− greeter.hide()
276272 }
277273
278274 onApplicationAdded: {
@@ -559,12 +555,9 @@ FocusScope {
559555 }
560556 }
561557 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
562− onLauncherApplicationSelected:{
563− if (edgeDemo.running)
564− return;
565−
566− greeter.hide()
567− shell.activateApplication(appId)
558+ onLauncherApplicationSelected: {
559+ if (!edgeDemo.running)
560+ shell.activateApplication(appId)
568561 }
569562 onShownChanged: {
570563 if (shown) {
tests/autopilot/unity8/application_lifecycle/tests/__init__.py+49
@@ -0,0 +1,49 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2014 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+from autopilot.matchers import Eventually
21+from testtools.matchers import Equals
22+from ubuntuuitoolkit import fixture_setup
23+
24+from unity8 import process_helpers
25+from unity8.shell import tests
26+
27+
28+class ApplicationLifeCycleTestCase(tests.UnityTestCase):
29+
30+ def setUp(self):
31+ super(ApplicationLifeCycleTestCase, self).setUp()
32+ self._qml_mock_enabled = False
33+ self._data_dirs_mock_enabled = False
34+ unity_proxy = self.launch_unity()
35+ process_helpers.unlock_unity(unity_proxy)
36+
37+ def create_test_application(self):
38+ desktop_file_dict = fixture_setup.DEFAULT_DESKTOP_FILE_DICT
39+ desktop_file_dict.update({'X-Ubuntu-Single-Instance': 'true'})
40+ fake_application = fixture_setup.FakeApplication(
41+ desktop_file_dict=desktop_file_dict)
42+ self.useFixture(fake_application)
43+ return (
44+ fake_application.qml_file_path, fake_application.desktop_file_path)
45+
46+ def assert_current_focused_application(self, application_name):
47+ self.assertThat(
48+ self.main_window.get_current_focused_app_id,
49+ Eventually(Equals(application_name)))
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+56−72
@@ -21,27 +21,26 @@
2121
2222from __future__ import absolute_import
2323
24−from unity8.process_helpers import unlock_unity
25−from unity8.shell import disable_qml_mocking
26−from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
24+import logging
25+import os
2726
2827from autopilot.matchers import Eventually
2928from autopilot.platform import model
30−from testtools.matchers import Equals, NotEquals
29+from testtools.matchers import Equals
3130
32−import logging
31+from unity8 import process_helpers
32+from unity8.application_lifecycle import tests
3333
34−logger = logging.getLogger(__name__)
3534
35+logger = logging.getLogger(__name__)
3636
37−class ApplicationLifecycleTests(UnityTestCase):
3837
39− scenarios = _get_device_emulation_scenarios()
38+class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
4039
4140 def setUp(self):
41+ if model() == 'Desktop':
42+ self.skipTest('Test cannot be run on the desktop.')
4243 super(ApplicationLifecycleTests, self).setUp()
43− if model() == "Desktop":
44− self.skipTest("Test cannot be run on the desktop.")
4544
4645 def swipe_screen_from_right(self):
4746 width = self.main_window.width
@@ -54,80 +53,65 @@ class ApplicationLifecycleTests(UnityTestCase):
5453 logger.info("Swiping screen from the right edge")
5554 self.touch.drag(start_x, start_y, end_x, end_y)
5655
57− def _launch_app(self, app_name):
58− """Launches the application *app_name*
59−
60− Assumes that the desktop file resides at:
61− /usr/share/applications/{app_name}.desktop
56+ def launch_fake_app(self):
57+ _, desktop_file_path = self.create_test_application()
58+ desktop_file_name = os.path.basename(desktop_file_path)
59+ application_name, _ = os.path.splitext(desktop_file_name)
60+ self.launch_upstart_application(application_name)
61+ return application_name
6262
63− """
64− desktop_file = "--desktop_file_hint="\
65− "/usr/share/applications/{app_name}.desktop".format(
66− app_name=app_name
67− )
68− return self.launch_test_application(
69− app_name,
70− desktop_file,
71− "--stage_hint=main_stage",
72− app_type='qt'
73− )
74−
75− @disable_qml_mocking
7663 def test_can_launch_application(self):
7764 """Must be able to launch an application."""
78− unity_proxy = self.launch_unity()
79− unlock_unity(unity_proxy)
80−
81− app = self._launch_app("messaging-app")
65+ application_name = self.launch_fake_app()
66+ self.assert_current_focused_application(application_name)
8267
83− self.assertThat(app, NotEquals(None))
84− self.assertThat(
85− self.main_window.get_current_focused_app_id(),
86− Eventually(Equals("messaging-app"))
87− )
88−
89− @disable_qml_mocking
9068 def test_can_launch_multiple_applications(self):
9169 """A second application launched must be focused."""
92− unity_proxy = self.launch_unity()
93− unlock_unity(unity_proxy)
94−
95− self._launch_app("messaging-app")
96− self.assertThat(
97− self.main_window.get_current_focused_app_id(),
98− Eventually(Equals("messaging-app"))
99− )
100−
101− self._launch_app("address-book-app")
102− self.assertThat(
103− self.main_window.get_current_focused_app_id(),
104− Eventually(Equals("address-book-app"))
105− )
106−
107− @disable_qml_mocking
70+ application1_name = self.launch_fake_app()
71+ self.assert_current_focused_application(application1_name)
72+
73+ application2_name = self.launch_fake_app()
74+ self.assertFalse(application1_name == application2_name)
75+ self.assert_current_focused_application(application2_name)
76+
10877 def test_app_moves_from_unfocused_to_focused(self):
10978 """An application that is in the unfocused state must be able to be
11079 brought back to the focused state.
11180
11281 """
113− unity_proxy = self.launch_unity()
114− unlock_unity(unity_proxy)
115−
116− self._launch_app("messaging-app")
117− self.assertThat(
118− self.main_window.get_current_focused_app_id(),
119− Eventually(Equals("messaging-app"))
120− )
82+ application1_name = self.launch_fake_app()
83+ self.assert_current_focused_application(application1_name)
12184
122− self._launch_app("address-book-app")
123− self.assertThat(
124− self.main_window.get_current_focused_app_id(),
125− Eventually(Equals("address-book-app"))
126− )
85+ application2_name = self.launch_fake_app()
86+ self.assertFalse(application1_name == application2_name)
87+ self.assert_current_focused_application(application2_name)
12788
12889 self.swipe_screen_from_right()
12990
130− self.assertThat(
131− self.main_window.get_current_focused_app_id(),
132− Eventually(Equals("messaging-app"))
133− )
91+ self.assert_current_focused_application(application1_name)
92+
93+ def test_greeter_hides_on_app_open(self):
94+ """Greeter should hide when an app is opened"""
95+ process_helpers.lock_unity()
96+ greeter = self.main_window.get_greeter()
97+ self.assertThat(greeter.created, Eventually(Equals(True)))
98+
99+ application_name = self.launch_fake_app()
100+ self.assertThat(greeter.created, Eventually(Equals(False)))
101+ self.assert_current_focused_application(application_name)
102+
103+ def test_greeter_hides_on_app_focus(self):
104+ """Greeter should hide when an app is re-focused"""
105+ application_name = self.launch_fake_app()
106+ self.assert_current_focused_application(application_name)
107+
108+ self.main_window.show_dash_swiping()
109+ self.assert_current_focused_application('')
110+
111+ process_helpers.lock_unity()
112+ greeter = self.main_window.get_greeter()
113+ self.assertThat(greeter.created, Eventually(Equals(True)))
114+
115+ self.launch_upstart_application(application_name)
116+ self.assertThat(greeter.created, Eventually(Equals(False)))
117+ self.assert_current_focused_application(application_name)
tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py+8−39
@@ -20,48 +20,19 @@
2020"""Test the integration with the URL dispatcher service."""
2121
2222import os
23+import subprocess
2324
2425from autopilot import platform
25−from autopilot.matchers import Eventually
26−from testtools.matchers import Equals
27−from ubuntuuitoolkit import fixture_setup
2826
29−from unity8 import process_helpers
30−from unity8.shell import tests
27+from unity8.application_lifecycle import tests
3128
3229
33−class URLDispatcherTestCase(tests.UnityTestCase):
34−
35− scenarios = tests._get_device_emulation_scenarios()
36−
37− test_qml = ("""
38−import QtQuick 2.0
39−import Ubuntu.Components 0.1
40−
41−MainView {
42− width: units.gu(48)
43− height: units.gu(60)
44−
45− Label {
46− text: 'Test application.'
47− }
48−}
49−""")
30+class URLDispatcherTestCase(tests.ApplicationLifeCycleTestCase):
5031
5132 def setUp(self):
5233 if platform.model() == 'Desktop':
5334 self.skipTest("URL dispatcher doesn't work on the desktop.")
5435 super(URLDispatcherTestCase, self).setUp()
55− self._qml_mock_enabled = False
56− self._data_dirs_mock_enabled = False
57− unity_proxy = self.launch_unity()
58− process_helpers.unlock_unity(unity_proxy)
59−
60− def create_test_application(self):
61− fake_application = fixture_setup.FakeApplication(self.test_qml)
62− self.useFixture(fake_application)
63− return (
64− fake_application.qml_file_path, fake_application.desktop_file_path)
6536
6637 def test_swipe_out_application_started_by_url_dispatcher(self):
6738 _, desktop_file_path = self.create_test_application()
@@ -70,12 +41,10 @@ MainView {
7041
7142 self.assertEqual('', self.main_window.get_current_focused_app_id())
7243 self.addCleanup(os.system, 'pkill qmlscene')
73− os.system('url-dispatcher application:///{}'.format(desktop_file_name))
74− self.assertThat(
75− self.main_window.get_current_focused_app_id,
76− Eventually(Equals(application_name)))
44+
45+ subprocess.check_call(
46+ ['url-dispatcher', 'application:///{}'.format(desktop_file_name)])
47+ self.assert_current_focused_application(application_name)
7748
7849 self.main_window.show_dash_swiping()
79− self.assertThat(
80− self.main_window.get_current_focused_app_id,
81− Eventually(Equals('')))
50+ self.assert_current_focused_application('')
tests/autopilot/unity8/indicators/tests/__init__.py+17
@@ -0,0 +1,17 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity - Indicators Autopilot Test Suite
4+# Copyright (C) 2013, 2014 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/>.
tests/autopilot/unity8/indicators/tests/test_indicators.py+69−13
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
3−# Unity - Indicators Autopilot Test Suite
4−# Copyright (C) 2013 Canonical
3+# Unity Indicators Autopilot Test Suite
4+# Copyright (C) 2013, 2014 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -15,20 +15,32 @@
1515#
1616# You should have received a copy of the GNU General Public License
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
18−#
1918
2019from __future__ import absolute_import
2120
21+from testscenarios import multiply_scenarios
22+
2223from autopilot import platform
23−from testtools import skipIf
2424
2525from unity8.process_helpers import unlock_unity
26−from unity8.shell.tests import UnityTestCase
26+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
2727
2828
2929class IndicatorTestCase(UnityTestCase):
3030
31− scenarios = [
31+ device_emulation_scenarios = _get_device_emulation_scenarios()
32+
33+ def setUp(self):
34+ if platform.model() == 'Desktop':
35+ self.skipTest('Test cannot be run on the desktop.')
36+ super(IndicatorTestCase, self).setUp()
37+ self.unity_proxy = self.launch_unity()
38+ unlock_unity(self.unity_proxy)
39+
40+
41+class IndicatorExistsTestCase(IndicatorTestCase):
42+
43+ indicator_scenarios = [
3244 ('Bluetooth', dict(indicator_name='indicator-bluetooth')),
3345 ('Datetime', dict(indicator_name='indicator-datetime')),
3446 ('Location', dict(indicator_name='indicator-location')),
@@ -37,14 +49,58 @@ class IndicatorTestCase(UnityTestCase):
3749 ('Power', dict(indicator_name='indicator-power')),
3850 ('Sound', dict(indicator_name='indicator-sound')),
3951 ]
52+ scenarios = multiply_scenarios(
53+ indicator_scenarios,
54+ IndicatorTestCase.device_emulation_scenarios
55+ )
4056
41− @skipIf(platform.image_codename() == 'Desktop',
42− "Unity8/phablet-only test!")
4357 def setUp(self):
44− super(IndicatorTestCase, self).setUp()
58+ super(IndicatorExistsTestCase, self).setUp()
59+ if (platform.model() == 'Nexus 10' and
60+ self.indicator_name == 'indicator-bluetooth'):
61+ self.skipTest('Nexus 10 does not have bluetooth at the moment.')
4562
4663 def test_indicator_exists(self):
47− unity_proxy = self.launch_unity()
48− unlock_unity(unity_proxy)
49− indicator = self.main_window.get_indicator(self.indicator_name)
50− self.assertIsNotNone(indicator)
64+ self.main_window._get_indicator_widget(
65+ self.indicator_name
66+ )
67+
68+
69+class IndicatorPageTitleMatchesWidgetTestCase(IndicatorTestCase):
70+
71+ indicator_scenarios = [
72+ ('Bluetooth', dict(indicator_name='indicator-bluetooth',
73+ title='Bluetooth')),
74+ ('Datetime', dict(indicator_name='indicator-datetime',
75+ title='Upcoming')),
76+ ('Location', dict(indicator_name='indicator-location',
77+ title='Location')),
78+ ('Messaging', dict(indicator_name='indicator-messages',
79+ title='Incoming')),
80+ ('Network', dict(indicator_name='indicator-network',
81+ title='Network')),
82+ ('Power', dict(indicator_name='indicator-power',
83+ title='Battery')),
84+ ('Sound', dict(indicator_name='indicator-sound',
85+ title='Sound')),
86+ ]
87+ scenarios = multiply_scenarios(
88+ indicator_scenarios,
89+ IndicatorTestCase.device_emulation_scenarios
90+ )
91+
92+ def setUp(self):
93+ super(IndicatorPageTitleMatchesWidgetTestCase, self).setUp()
94+ if (platform.model() == 'Nexus 10' and
95+ self.indicator_name == 'indicator-bluetooth'):
96+ self.skipTest('Nexus 10 does not have bluetooth at the moment.')
97+
98+ def test_indicator_page_title_matches_widget(self):
99+ """Swiping open an indicator must show its correct title.
100+
101+ See https://bugs.launchpad.net/ubuntu-ux/+bug/1253804 .
102+ """
103+ indicator_page = self.main_window.open_indicator_page(
104+ self.indicator_name)
105+ self.assertTrue(indicator_page.visible)
106+ self.assertEqual(indicator_page.title, self.title)
tests/autopilot/unity8/process_helpers.py+16
@@ -89,6 +89,22 @@ def unlock_unity(unity_proxy_obj=None):
8989 break
9090
9191
92+def lock_unity(unity_proxy_obj=None):
93+ """Helper function that attempts to lock the unity greeter."""
94+ import evdev, time
95+ uinput = evdev.UInput(name='unity8-autopilot-power-button',
96+ devnode='/dev/autopilot-uinput')
97+ # One press and release to turn screen off (locking unity)
98+ uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 1)
99+ uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 0)
100+ uinput.syn()
101+ time.sleep(1)
102+ # And another press and release to turn screen back on
103+ uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 1)
104+ uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 0)
105+ uinput.syn()
106+
107+
92108def restart_unity_with_testability(*args):
93109 """Restarts (or starts) unity with testability enabled.
94110
tests/autopilot/unity8/shell/emulators/__init__.py+2−6
@@ -17,16 +17,12 @@
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818#
1919
20−from autopilot import input, introspection
20+from ubuntuuitoolkit import emulators as toolkit_emulators
2121
2222
23−class UnityEmulatorBase(introspection.CustomEmulatorBase):
23+class UnityEmulatorBase(toolkit_emulators.UbuntuUIToolkitEmulatorBase):
2424 """A base class for all unity emulators."""
2525
26− def __init__(self, *args):
27− super(UnityEmulatorBase, self).__init__(*args)
28− self.pointing_device = input.Pointer(device=input.Touch.create())
29−
3026
3127class UnityEmulatorException(Exception):
3228 """Exception raised when there is an error with the Unity emulators."""
tests/autopilot/unity8/shell/emulators/dash.py+15
@@ -23,6 +23,8 @@ from unity8.shell import emulators
2323
2424from autopilot import logging as autopilot_logging
2525from autopilot.introspection import dbus
26+from testtools.matchers import MatchesAny, Equals
27+from ubuntuuitoolkit import emulators as toolkit_emulators
2628
2729
2830logger = logging.getLogger(__name__)
@@ -132,6 +134,19 @@ class Dash(emulators.UnityEmulatorBase):
132134 self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
133135 self.dash_content_list.currentIndex.wait_for(original_index + 1)
134136
137+ def enter_search_query(self, query):
138+ search_text_field = self._get_search_text_field()
139+ search_text_field.write(query)
140+ search_text_field.state.wait_for('idle')
141+
142+ def _get_search_text_field(self):
143+ page_header = self._get_page_header()
144+ search_container = page_header.select_single(
145+ 'QQuickItem', objectName='searchContainer')
146+ search_container.state.wait_for(
147+ MatchesAny(Equals('narrowActive'), Equals('active')))
148+ return search_container.select_single(toolkit_emulators.TextField)
149+
135150
136151class GenericScopeView(emulators.UnityEmulatorBase):
137152 """Autopilot emulator for generic scopes."""
tests/autopilot/unity8/shell/emulators/main_window.py+32−2
@@ -20,6 +20,7 @@
2020import logging
2121
2222from autopilot import logging as autopilot_logging
23+from autopilot import input
2324
2425from unity8.shell import emulators
2526from unity8.shell.emulators.greeter import Greeter
@@ -87,12 +88,32 @@ class QQuickView(emulators.UnityEmulatorBase):
8788 def get_pinentryField(self):
8889 return self.select_single(objectName="pinentryField")
8990
90− def get_indicator(self, indicator_name):
91− return self.wait_select_single(
91+ def _get_indicator_widget(self, indicator_name):
92+ return self.select_single(
9293 'DefaultIndicatorWidget',
9394 objectName=indicator_name+'-widget'
9495 )
9596
97+ def _get_indicator_page(self, indicator_name):
98+ return self.select_single(
99+ 'DefaultIndicatorPage',
100+ objectName=indicator_name+'-page'
101+ )
102+
103+ @autopilot_logging.log_action(logger.info)
104+ def open_indicator_page(self, indicator_name):
105+ """Swipe to open the indicator, wait until it's open.
106+
107+ :returns: The indicator page.
108+ """
109+ widget = self._get_indicator_widget(indicator_name)
110+ start_x, start_y = input.get_center_point(widget)
111+ end_x = start_x
112+ end_y = self.height
113+ self.pointing_device.drag(start_x, start_y, end_x, end_y)
114+ self.wait_select_single('Indicators', fullyOpened=True)
115+ return self._get_indicator_page(indicator_name)
116+
96117 def get_shell_background(self):
97118 return self.select_single(
98119 "CrossFadeImage", objectName="backgroundImage")
@@ -113,3 +134,12 @@ class QQuickView(emulators.UnityEmulatorBase):
113134 def get_current_focused_app_id(self):
114135 """Return the id of the focused application."""
115136 return self.select_single('Shell').focusedApplicationId
137+
138+ @autopilot_logging.log_action(logger.info)
139+ def search(self, query):
140+ search_indicator = self._get_search_indicator()
141+ self.pointing_device.click_object(search_indicator)
142+ self.get_dash().enter_search_query(query)
143+
144+ def _get_search_indicator(self):
145+ return self.select_single('SearchIndicator', objectName='search')
tests/autopilot/unity8/shell/tests/test_emulators.py+16
@@ -39,6 +39,22 @@ from unity8.shell import emulators, fixture_setup, tests
3939from unity8.shell.emulators import dash as dash_emulators
4040
4141
42+class MainWindowTestCase(tests.UnityTestCase):
43+
44+ scenarios = tests._get_device_emulation_scenarios()
45+
46+ def setUp(self):
47+ super(MainWindowTestCase, self).setUp()
48+ unity_proxy = self.launch_unity()
49+ process_helpers.unlock_unity(unity_proxy)
50+
51+ def test_search(self):
52+ self.main_window.search('Test')
53+ text_field = self.main_window.get_dash()._get_search_text_field()
54+ self.assertEqual(text_field.text, 'Test')
55+ self.assertEqual(text_field.state, 'idle')
56+
57+
4258class DashBaseTestCase(tests.UnityTestCase):
4359
4460 scenarios = tests._get_device_emulation_scenarios()
tests/autopilot/unity8/shell/tests/test_system_integration.py+1−1
@@ -37,5 +37,5 @@ class SystemIntegrationTests(UnityTestCase):
3737 self.launch_unity()
3838
3939 # invoke policykit to check permissions
40− pid = subprocess.check_output("pidof -s unity8", shell=True)
40+ pid = subprocess.check_output(["pidof", "-s", "unity8"], universal_newlines=True)
4141 subprocess.check_call("pkcheck --action-id org.freedesktop.NetworkManager.enable-disable-network --process " + pid, shell=True)
tests/qmltests/tst_Shell.qml+11
@@ -554,5 +554,16 @@ Item {
554554
555555 tryCompare(searchIndicator, "opacity", 0);
556556 }
557+
558+ function test_focusRequestedHidesGreeter() {
559+ var greeter = findChild(shell, "greeter")
560+
561+ greeter.show()
562+ tryCompare(greeter, "showProgress", 1)
563+
564+ ApplicationManager.focusRequested("notes-app")
565+ tryCompare(greeter, "showProgress", 0)
566+ waitUntilApplicationWindowIsFullyVisible()
567+ }
557568 }
558569}
tools/CMakeLists.txt+2
@@ -9,3 +9,5 @@ qt5_use_modules(${SCOPE_TOOL} Qml Quick)
99install(TARGETS ${SCOPE_TOOL}
1010 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
1111 )
12+
13+install(FILES unlock-device DESTINATION ${SHELL_APP_DIR})
tools/unlock-devicenew file+42
@@ -0,0 +1,42 @@
1+#!/bin/bash
2+# -*- Mode: sh; indent-tabs-mode: nil; tab-width: 4 -*-
3+
4+# Our goal here is to get an Ubuntu Touch device into a testable unlocked state.
5+# This will include a reboot.
6+
7+WAIT_COMMAND="sleep 20"
8+
9+while getopts s:w: opt; do
10+ case $opt in
11+ s)
12+ export ANDROID_SERIAL=$OPTARG
13+ ;;
14+ w)
15+ WAIT_COMMAND=$OPTARG
16+ ;;
17+ esac
18+done
19+
20+UNLOCK_SCRIPT='
21+import dbus, logging;
22+from unity8 import process_helpers as helpers;
23+logging.basicConfig(level=logging.INFO);
24+bus = dbus.SystemBus().get_object("com.canonical.powerd", "/com/canonical/powerd");
25+cookie = bus.requestSysState("unlock-device-hold", 1, dbus_interface="com.canonical.powerd");
26+helpers.restart_unity_with_testability();
27+bus.clearSysState(cookie, dbus_interface="com.canonical.powerd");
28+helpers.unlock_unity()
29+'
30+
31+adb reboot
32+sleep 5
33+adb "wait-for-device"
34+eval "$WAIT_COMMAND"
35+UNLOCK_OUTPUT=$(adb shell "sudo -u phablet -i python3 -c '$UNLOCK_SCRIPT'" 2>&1)
36+if echo "$UNLOCK_OUTPUT" | grep 'Greeter unlocked' >/dev/null; then
37+ echo "I: Unlock passed"
38+ exit 0
39+else
40+ echo "I: Unlock failed, script output: '$UNLOCK_OUTPUT'"
41+ exit 1
42+fi