on xenial help sign in
Merge lp:unity8
commit28836cbabda2e1e199850a9e6260a5157e2a5f22 authorAlbert Astals Cid <albert.astals@canonical.com> dateTue 23 Jun 2015, 09:48 (11 years ago) parent4379f2abeb7168
26 files changed+337−211
debian/changelog+24−31 plugins/Dash/CardCreator.js+8 po/de.po+2−2 po/el.po+11−11 po/hu.po+4−4 po/it.po+7−7 qml/Components/PinLockscreen.qml+1−5 qml/Components/ZoomableImage.qml+1 qml/Dash/Previews/PreviewHeader.qml+6 qml/Dash/Previews/PreviewImageGallery.qml+14 qml/Dash/Previews/PreviewZoomableImage.qml+13 qml/Stages/PhoneStage.qml+7 qml/Stages/TabletStage.qml+6 src/MouseTouchAdaptor.cpp+6−6 tests/autopilot/unity8/application_lifecycle/tests/__init__.py+2−2 tests/autopilot/unity8/greeter/__init__.py+52 tests/autopilot/unity8/indicators/tests/__init__.py+1−1 tests/autopilot/unity8/process_helpers.py+7−94 tests/autopilot/unity8/shell/tests/test_emulators.py+2−2 tests/autopilot/unity8/shell/tests/test_notifications.py+22−22 tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+30 tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+21−1 tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+27−6 tests/qmltests/Dash/tst_Card.qml+30 tests/qmltests/Stages/tst_PhoneStage.qml+19 tests/qmltests/tst_Shell.qml+14−17
debian/changelog+24−31
@@ -1,47 +1,40 @@
1−unity8 (8.10+15.10.20150616.1-0ubuntu1) wily; urgency=medium
1+unity8 (8.10+15.10.20150618-0ubuntu1) wily; urgency=medium
22
33 [ Albert Astals Cid ]
4− * Bind running to the proper object.visible
5− * Do not use template in generated card creator code
6− * Fix regression introduced by the concierge change
7− * Take into account the extra margin on top when calculating the
8− display margin
9− * workaround oxide 1.7 issue (LP: #1459142)
4+ * Refactor QmlTest.cmake module so that all tests can go through it.
5+ Also a bit of cleanup around tests.
6+ * Support fallback images for dash card and preview widgets (LP:
7+ #1324142)
108
119 [ CI Train Bot ]
1210 * New rebuild forced.
11+ * Resync trunk.
1312
14− [ Federico Gimenez ]
15− * On the autopilot helpers, swipe if the item to open from the dash is
16− not visible. unity8-autopilot dependencies now require ubuntu-ui-
17− toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
18−
19− [ Florian Boucault ]
20− * Notifications: shaped icon was stretching the icon instead of
21− respecting its aspect ratio and filling the whole shape. (LP:
22− #1450229)
13+ [ Daniel d'Andrada ]
14+ * Fix Shell tests
2315
2416 [ Josh Arenson ]
25− * Implement bare-bones shellMode=greeter and shellMode=shell
17+ * Refactor greeter emulator to unlock the greeter via dbus
2618
27− [ Leo Arias ]
28− * On the autopilot helpers, swipe if the item to open from the dash is
29− not visible. unity8-autopilot dependencies now require ubuntu-ui-
30− toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
31−
32− [ Leonardo Arias Fonseca ]
33− * On the autopilot helpers, swipe if the item to open from the dash is
34− not visible. unity8-autopilot dependencies now require ubuntu-ui-
35− toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
19+ [ Lukáš Tinkl ]
20+ * respect target window's devicePixelRatio in MouseTouchAdaptor
3621
3722 [ Michael Zanetti ]
38− * add info on how to run a single function in make test
23+ * Refactor QmlTest.cmake module so that all tests can go through it.
24+ Also a bit of cleanup around tests.
25+
26+ [ Michał Sawicz ]
27+ * Refactor QmlTest.cmake module so that all tests can go through it.
28+ Also a bit of cleanup around tests.
3929
40− [ Richard Huddie ]
41− * Slow down the speed of autopilot helpers used to swipe to a
42− different scope to improve the reliability. (LP: #1461059, #1457047)
30+ [ handsome_feng ]
31+ * Forbid closing apps during the edge gesture. (LP: #1445572)
32+ * Removed the horizonal rule on pin unlock screen. (LP: #1368798)
33+
34+ [ handsome_feng<445865575@qq.com> ]
35+ * Forbid closing apps during the edge gesture. (LP: #1445572)
4336
44− -- CI Train Bot <ci-train-bot@canonical.com> Tue, 16 Jun 2015 15:12:59 +0000
37+ -- CI Train Bot <ci-train-bot@canonical.com> Thu, 18 Jun 2015 19:40:06 +0000
4538
4639unity8 (8.10+15.10.20150612-0ubuntu2) wily; urgency=medium
4740
plugins/Dash/CardCreator.js+8
@@ -389,6 +389,10 @@ function cardString(template, components) {
389389 }
390390
391391 code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode);
392+ var fallback = components["art"] && components["art"]["fallback"] || "";
393+ if (fallback !== "") {
394+ code += 'Connections { target: artShapeLoader.item ? artShapeLoader.item.image : null; onStatusChanged: if (artShapeLoader.item.image.status === Image.Error) artShapeLoader.item.image.source = "%1"; } \n'.arg(fallback);
395+ }
392396 } else {
393397 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
394398 }
@@ -468,6 +472,10 @@ function cardString(template, components) {
468472
469473 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
470474 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
475+ var fallback = components["mascot"] && components["mascot"]["fallback"] || "";
476+ if (fallback !== "") {
477+ code += 'Connections { target: mascotImage.image; onStatusChanged: if (mascotImage.image.status === Image.Error) mascotImage.source = "%1"; } \n'.arg(fallback);
478+ }
471479 }
472480
473481 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white")';
po/de.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-06-18 05:07+0000\n"
19−"X-Generator: Launchpad (build 17570)\n"
18+"X-Launchpad-Export-Date: 2015-06-23 05:32+0000\n"
19+"X-Generator: Launchpad (build 17572)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/el.po+11−11
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-22 09:03+0000\n"
11+"PO-Revision-Date: 2015-06-18 19:05+0000\n"
1212"Last-Translator: Simos Xenitellis <Unknown>\n"
1313"Language-Team: Greek <el@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-06-23 05:33+0000\n"
19+"X-Generator: Launchpad (build 17572)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -117,7 +117,7 @@ msgstr "Ναι"
117117#: qml/Components/Dialogs.qml:152
118118msgctxt "Title: Power off/Restart dialog"
119119msgid "Power"
120−msgstr ""
120+msgstr "Τερματισμός"
121121
122122#: qml/Components/Dialogs.qml:153
123123msgid ""
@@ -165,17 +165,17 @@ msgstr "Αναζήτηση"
165165#: qml/Dash/PageHeader.qml:269
166166msgctxt "Button: Open the Ubuntu Store"
167167msgid "Store"
168−msgstr ""
168+msgstr "Κατάστημα"
169169
170170#: qml/Dash/PageHeader.qml:276
171171msgctxt "Button: Start a search in the current dash scope"
172172msgid "Search"
173−msgstr ""
173+msgstr "Αναζήτηση"
174174
175175#: qml/Dash/PageHeader.qml:286
176176msgctxt "Button: Show the current dash scope settings"
177177msgid "Settings"
178−msgstr ""
178+msgstr "Ρυθμίσεις"
179179
180180#: qml/Dash/PageHeader.qml:293
181181msgid "Remove from Favorites"
@@ -378,7 +378,7 @@ msgstr "Πατήστε εδώ για συνέχεια."
378378
379379#: qml/Tutorial/TutorialContent.qml:81
380380msgid "Click here to finish."
381−msgstr ""
381+msgstr "Πατήστε εδώ για τέλος."
382382
383383#: qml/Tutorial/TutorialLeftFinish.qml:24
384384msgid "These are the shortcuts to favorite apps"
@@ -510,12 +510,12 @@ msgstr "Επιτρέψετε το τηλέφωνο να εντοπίσει τη
510510
511511#: qml/Wizard/Pages/50-location.qml:69
512512msgid "Using GPS only (less accurate)"
513−msgstr "Χρήση μόνο GPS (λιγότερο ακριβή)"
513+msgstr "Χρήση μόνο GPS (λιγότερο ακριβής)"
514514
515515#: qml/Wizard/Pages/50-location.qml:86
516516msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
517517msgstr ""
518−"Χρήση GPS, ανωνυνοποιημένα δίκτυα Wi-Fi και πληροφορίες δικτύων κινητής "
518+"Χρήση GPS, ανωνυμοποιημένων δικτύων Wi-Fi και πληροφορίες δικτύων κινητής "
519519"τηλεφωνίας (συνιστάται)"
520520
521521#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
@@ -546,7 +546,7 @@ msgid ""
546546"partners, the makers of the operating system."
547547msgstr ""
548548"Το τηλέφωνό σας είναι ρυθμισμένο να στέλνει αυτόματα αναφορές σφαλμάτων στην "
549−"Canonical και τους συνεργάτες της, τους δημιουργούς τους λειτουργικού "
549+"Canonical και τους συνεργάτες της, τους δημιουργούς του λειτουργικού "
550550"συστήματος."
551551
552552#: qml/Wizard/Pages/60-reporting.qml:43
po/hu.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-05-04 18:02+0000\n"
11+"PO-Revision-Date: 2015-06-19 09:54+0000\n"
1212"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-05-05 05:47+0000\n"
19−"X-Generator: Launchpad (build 17474)\n"
18+"X-Launchpad-Export-Date: 2015-06-23 05:33+0000\n"
19+"X-Generator: Launchpad (build 17572)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -113,7 +113,7 @@ msgstr "Igen"
113113#: qml/Components/Dialogs.qml:152
114114msgctxt "Title: Power off/Restart dialog"
115115msgid "Power"
116−msgstr "Főkapcsoló"
116+msgstr "Menü"
117117
118118#: qml/Components/Dialogs.qml:153
119119msgid ""
po/it.po+7−7
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-06-18 05:07+0000\n"
19−"X-Generator: Launchpad (build 17570)\n"
18+"X-Launchpad-Export-Date: 2015-06-23 05:33+0000\n"
19+"X-Generator: Launchpad (build 17572)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -640,11 +640,6 @@ msgstr "%1 〉"
640640#~ msgstr ""
641641#~ "Provare a far scorrere dal bordo in alto per accedere agli indicatori"
642642
643−#~ msgid "Swipe up again to close the settings screen"
644−#~ msgstr ""
645−#~ "Far scorrere verso l'alto nuovamente per chiudere la schermata delle "
646−#~ "impostazioni"
647−
648643#~ msgid "Swipe from the left to reveal the launcher for quick access to apps"
649644#~ msgstr ""
650645#~ "Far scorrere il bordo sinistro per mostrare il Launcher e avere un rapido "
@@ -698,3 +693,8 @@ msgstr "%1 〉"
698693
699694#~ msgid "Manage Scopes"
700695#~ msgstr "Gestisci Scope"
696+
697+#~ msgid "Swipe up again to close the settings screen"
698+#~ msgstr ""
699+#~ "Far scorrere nuovamente verso l'alto per chiudere la schermata delle "
700+#~ "impostazioni"
qml/Components/PinLockscreen.qml+1−5
@@ -52,7 +52,7 @@ Item {
5252 verticalCenter: parent.verticalCenter;
5353 verticalCenterOffset: Math.max(-units.gu(10), -(root.height - height) / 2) + units.gu(4)
5454 }
55− spacing: units.gu(2)
55+ spacing: units.gu(4)
5656
5757 Column {
5858 id: shakeContainer
@@ -154,10 +154,6 @@ Item {
154154 }
155155 }
156156
157− ThinDivider {
158− anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
159− }
160−
161157 Grid {
162158 id: numbersGrid
163159 objectName: "numbersGrid"
qml/Components/ZoomableImage.qml+1
@@ -30,6 +30,7 @@ Item {
3030 property var zoomable: false
3131 property alias imageStatus: imageRenderer.status
3232 property alias asynchronous: imageRenderer.asynchronous
33+ readonly property alias status: imageRenderer.status
3334
3435 Flickable {
3536 id: flickable
qml/Dash/Previews/PreviewHeader.qml+6
@@ -22,6 +22,7 @@ import "../"
2222/*! This preview widget shows a header
2323 * The title comes in widgetData["title"]
2424 * The mascot comes in widgetData["mascot"]
25+ * The mascot fall back image comes in widgetData["fallback"]
2526 * The subtitle comes in widgetData["subtitle"]
2627 * The attributes comes in widgetData["attributes"]
2728 */
@@ -35,11 +36,15 @@ PreviewWidget {
3536 id: headerRoot
3637 objectName: "innerPreviewHeader"
3738 readonly property url mascot: root.widgetData["mascot"] || ""
39+ readonly property url fallback: root.widgetData["fallback"] || ""
3840 readonly property string title: root.widgetData["title"] || ""
3941 readonly property string subtitle: root.widgetData["subtitle"] || ""
4042 readonly property var attributes: root.widgetData["attributes"] || null
4143 readonly property color fontColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
4244
45+ // Rewire the source since we may have unwired it on onStatusChanged
46+ onMascotChanged: if (mascotShapeLoader.item) mascotShapeLoader.item.image.source = mascot;
47+
4348 implicitHeight: row.height + row.margins * 2
4449 width: parent.width
4550
@@ -82,6 +87,7 @@ PreviewWidget {
8287 fillMode: Image.PreserveAspectCrop
8388 horizontalAlignment: Image.AlignHCenter
8489 verticalAlignment: Image.AlignVCenter
90+ onStatusChanged: if (status === Image.Error) source = headerRoot.fallback;
8591 }
8692 }
8793 }
qml/Dash/Previews/PreviewImageGallery.qml+14
@@ -20,6 +20,7 @@ import "../../Components"
2020
2121/*! This preview widget shows a horizontal list of images.
2222 * The URIs for the images should be an array in widgetData["sources"].
23+ * Images fall back to widgetData["fallback"] if loading fails
2324 */
2425
2526PreviewWidget {
@@ -72,6 +73,14 @@ PreviewWidget {
7273 overlay.show();
7374 }
7475 }
76+
77+ Connections {
78+ target: sourceImage
79+ // If modelData would change after failing to load it would not be
80+ // reloaded since the source binding is destroyed by the source = fallback
81+ // But at the moment the model never changes
82+ onStatusChanged: if (sourceImage.status === Image.Error) sourceImage.source = widgetData["fallback"];
83+ }
7584 }
7685 }
7786
@@ -122,6 +131,11 @@ PreviewWidget {
122131 source: modelData ? modelData : ""
123132 fillMode: Image.PreserveAspectFit
124133 sourceSize { width: screenshot.width; height: screenshot.height }
134+
135+ // If modelData would change after failing to load it would not be
136+ // reloaded since the source binding is destroyed by the source = fallback
137+ // But at the moment the model never changes
138+ onStatusChanged: if (status === Image.Error) source = widgetData["fallback"];
125139 }
126140
127141 MouseArea {
qml/Dash/Previews/PreviewZoomableImage.qml+13
@@ -21,6 +21,7 @@ import "../../Components"
2121/*! \brief Preview widget for image.
2222
2323 This widget shows image contained in widgetData["source"],
24+ and falls back to widgetData["fallback"] if loading fails
2425 can be zoomable accordingly with widgetData["zoomable"].
2526 */
2627
@@ -53,6 +54,14 @@ PreviewWidget {
5354 overlay.show();
5455 }
5556 }
57+
58+ Connections {
59+ target: lazyImage.sourceImage
60+ // If modelData would change after failing to load it would not be
61+ // reloaded since the source binding is destroyed by the source = fallback
62+ // But at the moment the model never changes
63+ onStatusChanged: if (lazyImage.sourceImage.status === Image.Error) lazyImage.sourceImage.source = widgetData["fallback"];
64+ }
5665 }
5766
5867 PreviewOverlay {
@@ -67,6 +76,10 @@ PreviewWidget {
6776 anchors.fill: parent
6877 source: widgetData["source"]
6978 zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
79+ // If modelData would change after failing to load it would not be
80+ // reloaded since the source binding is destroyed by the source = fallback
81+ // But at the moment the model never changes
82+ onStatusChanged: if (status === Image.Error) source = widgetData["fallback"];
7083 }
7184 }
7285}
qml/Stages/PhoneStage.qml+7
@@ -548,6 +548,13 @@ Rectangle {
548548 }
549549 }
550550
551+ //eat touch events during the right edge gesture
552+ MouseArea {
553+ objectName: "eventEaterArea"
554+ anchors.fill: parent
555+ enabled: spreadDragArea.dragging
556+ }
557+
551558 DirectionalDragArea {
552559 id: spreadDragArea
553560 objectName: "spreadDragArea"
qml/Stages/TabletStage.qml+6
@@ -724,6 +724,12 @@ Rectangle {
724724 }
725725 }
726726
727+ //eat touch events during the right edge gesture
728+ MouseArea {
729+ anchors.fill: parent
730+ enabled: spreadDragArea.dragging
731+ }
732+
727733 DirectionalDragArea {
728734 id: spreadDragArea
729735 objectName: "spreadDragArea"
src/MouseTouchAdaptor.cpp+6−6
@@ -104,10 +104,10 @@ bool MouseTouchAdaptor::handleButtonPress(xcb_button_press_event_t *pressEvent)
104104 if (button != Qt::LeftButton)
105105 return true;
106106
107− QPoint windowPos(pressEvent->event_x, pressEvent->event_y);
108−
109107 QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(pressEvent->event));
110108
109+ QPoint windowPos(pressEvent->event_x / targetWindow->devicePixelRatio(), pressEvent->event_y / targetWindow->devicePixelRatio());
110+
111111 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
112112 false /* autoCommit */);
113113 touchEvent.press(0 /* touchId */, windowPos);
@@ -125,10 +125,10 @@ bool MouseTouchAdaptor::handleButtonRelease(xcb_button_release_event_t *releaseE
125125 if (button != Qt::LeftButton)
126126 return true;
127127
128− QPoint windowPos(releaseEvent->event_x, releaseEvent->event_y);
129−
130128 QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(releaseEvent->event));
131129
130+ QPoint windowPos(releaseEvent->event_x / targetWindow->devicePixelRatio(), releaseEvent->event_y / targetWindow->devicePixelRatio());
131+
132132 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
133133 false /* autoCommit */);
134134 touchEvent.release(0 /* touchId */, windowPos);
@@ -144,10 +144,10 @@ bool MouseTouchAdaptor::handleMotionNotify(xcb_motion_notify_event_t *event)
144144 return true;
145145 }
146146
147− QPoint windowPos(event->event_x, event->event_y);
148−
149147 QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(event->event));
150148
149+ QPoint windowPos(event->event_x / targetWindow->devicePixelRatio(), event->event_y / targetWindow->devicePixelRatio());
150+
151151 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
152152 false /* autoCommit */);
153153 touchEvent.move(0 /* touchId */, windowPos);
tests/autopilot/unity8/application_lifecycle/tests/__init__.py+2−2
@@ -31,8 +31,8 @@ class ApplicationLifeCycleTestCase(tests.UnityTestCase):
3131 super().setUp()
3232 self._qml_mock_enabled = False
3333 self._data_dirs_mock_enabled = False
34− unity_proxy = self.launch_unity()
35− process_helpers.unlock_unity(unity_proxy)
34+ self.launch_unity()
35+ process_helpers.unlock_unity()
3636
3737 def create_test_application(self):
3838 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):
3131 def setUp(self):
3232 super().setUp()
3333 self.unity_proxy = self.launch_unity()
34− process_helpers.unlock_unity(self.unity_proxy)
34+ process_helpers.unlock_unity()
3535
3636
3737class DeviceIndicatorTestCase(IndicatorTestCase):
tests/autopilot/unity8/process_helpers.py+7−94
@@ -19,14 +19,11 @@
1919
2020import logging
2121import subprocess
22−import dbus
2322
2423import ubuntuuitoolkit
25−from autopilot.exceptions import ProcessSearchError
2624from autopilot.introspection import get_proxy_object_for_existing_process
2725
28−from unity8.shell import emulators
29−from unity8.shell.emulators import main_window as main_window_emulator
26+from unity8 import greeter
3027
3128logger = logging.getLogger(__name__)
3229
@@ -39,104 +36,20 @@ class CannotAccessUnity(Exception):
3936 pass
4037
4138
42−def unlock_unity(unity_proxy_obj=None):
39+def unlock_unity():
4340 """Helper function that attempts to unlock the unity greeter.
4441
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−
5842 """
59− if unity_proxy_obj is None:
60− try:
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.")
43+ greeter.wait_for_greeter()
44+ greeter.hide_greeter_with_dbus()
9045
9146
92−def lock_unity(unity_proxy_obj=None):
47+def lock_unity():
9348 """Helper function that attempts to lock unity greeter.
9449
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−
10850 """
109− if unity_proxy_obj is None:
110− try:
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.")
51+ greeter.show_greeter_with_dbus()
52+ greeter.wait_for_greeter()
14053
14154
14255def restart_unity_with_testability(*args):
tests/autopilot/unity8/shell/tests/test_emulators.py+2−2
@@ -41,8 +41,8 @@ class MainWindowTestCase(tests.UnityTestCase):
4141
4242 def setUp(self):
4343 super().setUp()
44− unity_proxy = self.launch_unity()
45− process_helpers.unlock_unity(unity_proxy)
44+ self.launch_unity()
45+ process_helpers.unlock_unity()
4646
4747
4848class DashEmulatorTestCase(tests.DashBaseTestCase):
tests/autopilot/unity8/shell/tests/test_notifications.py+22−22
@@ -119,8 +119,8 @@ class InteractiveNotificationBase(NotificationsBase):
119119
120120 def test_interactive(self):
121121 """Interactive notification must react upon click on itself."""
122− unity_proxy = self.launch_unity()
123− unlock_unity(unity_proxy)
122+ self.launch_unity()
123+ unlock_unity()
124124
125125 notify_list = self._get_notifications_list()
126126
@@ -156,8 +156,8 @@ class InteractiveNotificationBase(NotificationsBase):
156156 """Snap-decision with three actions should use
157157 one-over two button layout.
158158 """
159− unity_proxy = self.launch_unity()
160− unlock_unity(unity_proxy)
159+ self.launch_unity()
160+ unlock_unity()
161161
162162 summary = "Theatre at Ferria Stadium"
163163 body = "at Ferria Stadium in Bilbao, Spain\n07578545317"
@@ -197,8 +197,8 @@ class InteractiveNotificationBase(NotificationsBase):
197197 """Snap-decision should block input to shell
198198 without greeter/lockscreen.
199199 """
200− unity_proxy = self.launch_unity()
201− unlock_unity(unity_proxy)
200+ self.launch_unity()
201+ unlock_unity()
202202
203203 summary = "Incoming file"
204204 body = "Frank would like to send you the file: essay.pdf"
@@ -421,8 +421,8 @@ class EphemeralNotificationsTests(NotificationsBase):
421421
422422 def test_icon_summary_body(self):
423423 """Notification must display the expected summary and body text."""
424− unity_proxy = self.launch_unity()
425− unlock_unity(unity_proxy)
424+ self.launch_unity()
425+ unlock_unity()
426426
427427 notify_list = self._get_notifications_list()
428428
@@ -458,8 +458,8 @@ class EphemeralNotificationsTests(NotificationsBase):
458458 def test_icon_summary(self):
459459 """Notification must display the expected summary and secondary
460460 icon."""
461− unity_proxy = self.launch_unity()
462− unlock_unity(unity_proxy)
461+ self.launch_unity()
462+ unlock_unity()
463463
464464 notify_list = self._get_notifications_list()
465465
@@ -491,8 +491,8 @@ class EphemeralNotificationsTests(NotificationsBase):
491491 def test_urgency_order(self):
492492 """Notifications must be displayed in order according to their
493493 urgency."""
494− unity_proxy = self.launch_unity()
495− unlock_unity(unity_proxy)
494+ self.launch_unity()
495+ unlock_unity()
496496
497497 notify_list = self._get_notifications_list()
498498
@@ -578,8 +578,8 @@ class EphemeralNotificationsTests(NotificationsBase):
578578
579579 def test_summary_and_body(self):
580580 """Notification must display the expected summary- and body-text."""
581− unity_proxy = self.launch_unity()
582− unlock_unity(unity_proxy)
581+ self.launch_unity()
582+ unlock_unity()
583583
584584 notify_list = self._get_notifications_list()
585585
@@ -603,8 +603,8 @@ class EphemeralNotificationsTests(NotificationsBase):
603603
604604 def test_summary_only(self):
605605 """Notification must display only the expected summary-text."""
606− unity_proxy = self.launch_unity()
607− unlock_unity(unity_proxy)
606+ self.launch_unity()
607+ unlock_unity()
608608
609609 notify_list = self._get_notifications_list()
610610
@@ -621,8 +621,8 @@ class EphemeralNotificationsTests(NotificationsBase):
621621 def test_update_notification_same_layout(self):
622622 """Notification must allow updating its contents while being
623623 displayed."""
624− unity_proxy = self.launch_unity()
625− unlock_unity(unity_proxy)
624+ self.launch_unity()
625+ unlock_unity()
626626
627627 notify_list = self._get_notifications_list()
628628
@@ -660,8 +660,8 @@ class EphemeralNotificationsTests(NotificationsBase):
660660 def test_update_notification_layout_change(self):
661661 """Notification must allow updating its contents and layout while
662662 being displayed."""
663− unity_proxy = self.launch_unity()
664− unlock_unity(unity_proxy)
663+ self.launch_unity()
664+ unlock_unity()
665665
666666 notify_list = self._get_notifications_list()
667667
@@ -709,8 +709,8 @@ class EphemeralNotificationsTests(NotificationsBase):
709709 """ use the create notification script to get a notification dialog.
710710 Check that the arguments passed to the script match the fields. """
711711
712− unity_proxy = self.launch_unity()
713− unlock_unity(unity_proxy)
712+ self.launch_unity()
713+ unlock_unity()
714714
715715 summary = 'Helper summary'
716716 body = 'Helper body'
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+30
@@ -38,6 +38,22 @@ Rectangle {
3838 "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
3939 }
4040
41+ property var brokenheaderjson: {
42+ "title": "THE TITLE",
43+ "subtitle": "Something catchy",
44+ "mascot": "bad_path",
45+ "fallback": "",
46+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
47+ }
48+
49+ property var fallbackheaderjson: {
50+ "title": "THE TITLE",
51+ "subtitle": "Something catchy",
52+ "mascot": "bad_path2",
53+ "fallback": "../graphics/play_button.png",
54+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
55+ }
56+
4157 PreviewHeader {
4258 id: previewHeader
4359 widgetData: headerjson
@@ -121,5 +137,19 @@ Rectangle {
121137 compare(innerPreviewHeader.subtitle, "Something catchy");
122138 compare(innerPreviewHeader.mascot.toString().slice(-24), "graphics/play_button.png");
123139 }
140+
141+ function test_fallback() {
142+ previewHeader.widgetData = brokenheaderjson;
143+ tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
144+ var mascot = findChild(previewHeader, "mascotShape");
145+ compare(mascot.visible, false);
146+
147+ previewHeader.widgetData = {};
148+ previewHeader.widgetData = fallbackheaderjson;
149+ tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
150+ var mascot = findChild(previewHeader, "mascotShape");
151+ tryCompare(mascot, "visible", true);
152+ tryCompare(mascot.image, "status", Image.Ready);
153+ }
124154 }
125155}
tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+21−1
@@ -33,10 +33,21 @@ Rectangle {
3333 "sources": [
3434 "../../graphics/phone_background.jpg",
3535 "../../graphics/tablet_background.jpg",
36− "../../graphics/clock@18.png"
36+ "../../graphics/clock@18.png",
37+ "../../graphics/borked"
3738 ]
3839 }
3940
41+ property var sourcesModel1WithFallback: {
42+ "sources": [
43+ "../../graphics/phone_background.jpg",
44+ "../../graphics/tablet_background.jpg",
45+ "../../graphics/clock@18.png",
46+ "../../graphics/borked"
47+ ]
48+ , "fallback": "../../graphics/clock@18.png"
49+ }
50+
4051 PreviewImageGallery {
4152 id: imageGallery
4253 width: parent.width
@@ -103,5 +114,14 @@ Rectangle {
103114 tryCompare(overlayListView, "currentIndex", data.index);
104115 verify(image.source === overlayListView.currentItem.source);
105116 }
117+
118+ function test_fallback() {
119+ var image3 = findChild(imageGallery, "previewImage3");
120+ tryCompare(image3, "state", "error");
121+ imageGallery.widgetData = sourcesModel0;
122+ imageGallery.widgetData = sourcesModel1WithFallback;
123+ image3 = findChild(imageGallery, "previewImage3");
124+ tryCompare(image3, "state", "ready");
125+ }
106126 }
107127}
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+27−6
@@ -26,7 +26,7 @@ Rectangle {
2626 color: "lightgrey"
2727
2828 property var widgetData0: {
29− "source": Qt.resolvedUrl("../artwork/checkers.png"),
29+ "source": "../../graphics/phone_background.jpg",
3030 "zoomable": false
3131 }
3232
@@ -34,21 +34,35 @@ Rectangle {
3434 "source": ""
3535 }
3636
37− PreviewZoomableImage {
38− id: zoomableImage
37+ property var widgetData2: {
38+ "source": "fadsasf",
39+ "fallback": "../../graphics/phone_background.jpg"
40+ }
41+
42+ Loader {
43+ id: loader
3944 anchors.centerIn: parent
40− widgetData: widgetData0
45+ sourceComponent: PreviewZoomableImage {
46+ widgetData: widgetData0
47+ }
4148 }
4249
50+ property alias zoomableImage: loader.item
51+
4352 UT.UnityTestCase {
4453 name: "PreviewZoomableImageTest"
4554 when: windowShown
4655
47− property Item lazyImage: findChild(zoomableImage, "lazyImage");
48− property Item overlay: findChild(zoomableImage.rootItem, "overlay");
56+ property Item lazyImage
57+ property Item overlay
4958
5059 function init() {
60+ // Use a loader so we start from scratch each time
61+ loader.active = false;
62+ loader.active = true;
63+ lazyImage = findChild(zoomableImage, "lazyImage");
5164 waitForRendering(zoomableImage);
65+ overlay = findChild(zoomableImage.rootItem, "overlay");
5266 waitForRendering(overlay);
5367 }
5468
@@ -79,5 +93,12 @@ Rectangle {
7993 mouseClick(overlayCloseButton);
8094 tryCompare(overlay, "visible", false);
8195 }
96+
97+ function test_fallback() {
98+ zoomableImage.widgetData = widgetData2;
99+ waitForRendering(zoomableImage);
100+ waitForRendering(lazyImage);
101+ tryCompare(lazyImage, "state", "ready");
102+ }
82103 }
83104}
tests/qmltests/Dash/tst_Card.qml+30
@@ -223,6 +223,7 @@ Rectangle {
223223 property Item mascotImage: findChild(card, "mascotImage");
224224
225225 function init() {
226+ cardTool.components = Qt.binding(function() { return Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['components']; });
226227 loader.visible = true;
227228 }
228229
@@ -423,6 +424,7 @@ Rectangle {
423424 ];
424425 }
425426
427+
426428 function test_background(data) {
427429 selector.selectedIndex = data.index;
428430 waitForRendering(selector);
@@ -453,6 +455,34 @@ Rectangle {
453455 }
454456 }
455457
458+ function test_fallback() {
459+ selector.selectedIndex = 0;
460+ waitForRendering(card);
461+
462+ card.cardData["art"] = "somethingbroken";
463+ card.cardDataChanged();
464+ waitForRendering(card);
465+ tryCompare(art, "visible", false);
466+
467+ cardTool.components["art"]["fallback"] = Qt.resolvedUrl("artwork/emblem.png");
468+ cardTool.componentsChanged();
469+ card.cardData["art"] = "somethingbroken";
470+ card.cardDataChanged();
471+ waitForRendering(card);
472+ tryCompare(art, "visible", true);
473+
474+ card.cardData["mascot"] = "somethingbroken2";
475+ card.cardDataChanged();
476+ compare(mascotImage.image.status, Image.Error);
477+
478+ cardTool.components["mascot"] = {"fallback": Qt.resolvedUrl("artwork/emblem.png")};
479+ cardTool.componentsChanged();
480+ card.cardData["mascot"] = "somethingbroken2";
481+ card.cardDataChanged();
482+ waitForRendering(card);
483+ tryCompare(mascotImage.image, "status", Image.Ready);
484+ }
485+
456486 function test_font_weights_data() {
457487 return [
458488 { tag: "Title only", index: 8, weight: Font.Normal },
tests/qmltests/Stages/tst_PhoneStage.qml+19
@@ -346,6 +346,25 @@ Item {
346346 tryCompare(spreadView, "contentX", -spreadView.shift)
347347 }
348348
349+ function test_cantAccessPhoneStageWhileRightEdgeGesture() {
350+ var spreadView = findChild(phoneStage, "spreadView");
351+ var eventEaterArea = findChild(phoneStage, "eventEaterArea")
352+
353+ var startX = phoneStage.width - 2;
354+ var startY = phoneStage.height / 2;
355+ var endY = startY;
356+ var endX = phoneStage.width / 2;
357+
358+ touchFlick(phoneStage, startX, startY, endX, endY,
359+ true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
360+
361+ compare(eventEaterArea.enabled, true);
362+
363+ touchRelease(phoneStage, endX, endY);
364+
365+ compare(eventEaterArea.enabled, false);
366+ }
367+
349368 function test_leftEdge_data() {
350369 return [
351370 { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(5), shouldMoveApp: true },
tests/qmltests/tst_Shell.qml+14−17
@@ -83,7 +83,7 @@ Rectangle {
8383 Shell {
8484 property string shellMode: "full-greeter" /* default */
8585
86− usageScenario: "phone"
86+ usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
8787 orientation: Qt.PortraitOrientation
8888 primaryOrientation: Qt.PortraitOrientation
8989 nativeOrientation: Qt.PortraitOrientation
@@ -151,14 +151,11 @@ Rectangle {
151151 }
152152 }
153153 ListItem.ItemSelector {
154− id: usageModeSelector
154+ id: usageScenarioSelector
155155 anchors { left: parent.left; right: parent.right }
156156 activeFocusOnPress: false
157− text: "Usage mode"
158− model: ["Staged", "Windowed"]
159− onSelectedIndexChanged: {
160− GSettingsController.setUsageMode(model[selectedIndex]);
161− }
157+ text: "Usage scenario"
158+ model: ["phone", "tablet", "desktop"]
162159 }
163160 MouseTouchEmulationCheckbox {
164161 id: mouseEmulation
@@ -725,7 +722,7 @@ Rectangle {
725722
726723 tryCompare(webApp.session.surface, "activeFocus", true);
727724
728− GSettingsController.setUsageMode("Windowed");
725+ shell.usageScenario = "desktop";
729726
730727 // check that the desktop stage and window have been loaded
731728 {
@@ -735,7 +732,7 @@ Rectangle {
735732
736733 tryCompare(webApp.session.surface, "activeFocus", true);
737734
738− GSettingsController.setUsageMode("Staged");
735+ shell.usageScenario = "tablet";
739736
740737 // check that the tablet stage and app surface delegate have been loaded
741738 {
@@ -1205,30 +1202,30 @@ Rectangle {
12051202
12061203 function test_stageLoader_data() {
12071204 return [
1208− {tag: "phone", source: "Stages/PhoneStage.qml", formFactor: "phone", usageMode: "Staged"},
1209− {tag: "tablet", source: "Stages/TabletStage.qml", formFactor: "tablet", usageMode: "Staged"},
1210− {tag: "desktop", source: "Stages/DesktopStage.qml", formFactor: "tablet", usageMode: "Windowed"}
1205+ {tag: "phone", source: "Stages/PhoneStage.qml", formFactor: "phone", usageScenario: "phone"},
1206+ {tag: "tablet", source: "Stages/TabletStage.qml", formFactor: "tablet", usageScenario: "tablet"},
1207+ {tag: "desktop", source: "Stages/DesktopStage.qml", formFactor: "tablet", usageScenario: "desktop"}
12111208 ]
12121209 }
12131210
12141211 function test_stageLoader(data) {
1215− GSettingsController.setUsageMode(data.usageMode);
12161212 loadShell(data.formFactor);
1213+ shell.usageScenario = data.usageScenario;
12171214 var stageLoader = findChild(shell, "applicationsDisplayLoader");
12181215 verify(String(stageLoader.source).indexOf(data.source) >= 0);
12191216 }
12201217
12211218 function test_launcherInverted_data() {
12221219 return [
1223− {tag: "phone", formFactor: "phone", usageMode: "Staged", launcherInverted: true},
1224− {tag: "tablet", formFactor: "tablet", usageMode: "Staged", launcherInverted: true},
1225− {tag: "desktop", formFactor: "tablet", usageMode: "Windowed", launcherInverted: false}
1220+ {tag: "phone", formFactor: "phone", usageScenario: "phone", launcherInverted: true},
1221+ {tag: "tablet", formFactor: "tablet", usageScenario: "tablet", launcherInverted: true},
1222+ {tag: "desktop", formFactor: "tablet", usageScenario: "desktop", launcherInverted: false}
12261223 ]
12271224 }
12281225
12291226 function test_launcherInverted(data) {
1230− GSettingsController.setUsageMode(data.usageMode);
12311227 loadShell(data.formFactor);
1228+ shell.usageScenario = data.usageScenario;
12321229
12331230 var launcher = findChild(shell, "launcher");
12341231 compare(launcher.inverted, data.launcherInverted);