Merge lp:~dandrader/unity8/lp1116207 into lp:unity8

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Daniel d'Andrada
Approved revision: 141
Merged at revision: 133
Proposed branch: lp:~dandrader/unity8/lp1116207
Merge into: lp:unity8
Diff against target: 693 lines (+403/-30)
17 files modified
Components/Stage.qml (+14/-0)
Dash/DashHome.qml (+1/-0)
Shell.qml (+25/-9)
main.cpp (+1/-1)
tests/mocks/CMakeLists.txt (+1/-0)
tests/mocks/QMenuModel/CMakeLists.txt (+11/-0)
tests/mocks/QMenuModel/QDBusActionGroup.qml (+27/-0)
tests/mocks/QMenuModel/qmldir (+2/-0)
tests/mocks/Ubuntu/Application/ApplicationManager.cpp (+6/-2)
tests/mocks/Unity/CMakeLists.txt (+1/-0)
tests/mocks/Unity/Notifications/CMakeLists.txt (+11/-0)
tests/mocks/Unity/fake_scopes.cpp (+2/-2)
tests/qmltests/CMakeLists.txt (+3/-0)
tests/qmltests/Components/tst_Stage.qml (+1/-0)
tests/qmltests/Dash/tst_Dash.qml (+4/-4)
tests/qmltests/Dash/tst_DashContent.qml (+12/-12)
tests/qmltests/tst_Shell.qml (+281/-0)
To merge this branch: bzr merge lp:~dandrader/unity8/lp1116207
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Michał Sawicz Needs Fixing
Nick Dedekind (community) Approve
Review via email: mp+175163@code.launchpad.net

Commit message

Give a visual feedback on right-edge drag with no running apps (LP: #1116207)

Description of the change

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

Hmm....

> VolumeControl.qml:18,1: module "QMenuModel" is not installed

Nick is preparing a removal of Overview.qml and friends, so that this won't be an issue in the end... But then instantiating the whole Shell.qml means we're loading everything down the tree, and with real plugins for most.

This will also most probably conflict with lp:~unity-team/unity8/refactor-wm-and-test/ which was done in parts so that we abstract window management for easier testing...

I wonder if this should be an autopilot test instead.

review: Needs Information
Revision history for this message
Michał Sawicz (saviq) wrote :

Ah, BTW couldn't you use Nick's export_qml* macros for the mock Unity plugin?

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> Hmm....
>
> > VolumeControl.qml:18,1: module "QMenuModel" is not installed
>
> Nick is preparing a removal of Overview.qml and friends, so that this won't be
> an issue in the end... But then instantiating the whole Shell.qml means we're
> loading everything down the tree, and with real plugins for most.

Now, with fake plugins for all of the troublesome ones (e.g.: Ubuntu.Gestures is the real one as there's no sense in using a fake of it).

Have you seem how it looks like?
$ make tryShell

>
> This will also most probably conflict with lp:~unity-team/unity8/refactor-wm-
> and-test/ which was done in parts so that we abstract window management for
> easier testing...

Everything will change some day. If I wait until the final versions of things I will be out of work. I also consulted Gerry before starting this work.

>
> I wonder if this should be an autopilot test instead.

Please god nooooo!!!!!
I made this test also as a showcase of the many things you can do without the need for autopilot.
I'm testing the Shell component. I'm testing the code inside it, how it wires the many components together. Not the whole live system (with real, live, plugins, etc) and the logic in all components.
There's really absolutely no need for autopilot to test this bug fix. There's not a single advantage in using it for this test. We are only getting its drawbacks: Slowness. Not easy to run by the developer (he normally doesn't run it). Prone to be brittle and unreliable as you're bringing in a whole lot of variables not completely under your control or easily predictable (IPC, multiple processes, etc).

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> [...]
> Now, with fake plugins for all of the troublesome ones (e.g.: Ubuntu.Gestures
> is the real one as there's no sense in using a fake of it).
> [...]

s/Now/No

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> Ah, BTW couldn't you use Nick's export_qml* macros for the mock Unity plugin?

I tried, but it puts things in a path different from the one I'm using here.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

I still don't understand why we need the QMenuModel mock? It's not part of unity, so why we need to mock?

We've removed the overview now in any case.

Revision history for this message
Nick Dedekind (nick-dedekind) :
review: Needs Information
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

181 - createMainStageComponent();
182 - createSideStageComponent();

190 + if (!m_mainStageComponent)
191 + createMainStageComponent();

200 + if (!m_sideStageComponent)
201 + createSideStageComponent();

Why the change?

We should probably also have a test which verifies that having an open app switches to the app rather than this new behaviour. And also that we will still have this new behaviour after closing all the apps

review: Needs Fixing
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

232 +install(FILES ${QMLFILES}
233 + DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Unity/Notifications
234 + )

I don't think we want to install notification mock do we? Otherwise we wont have the real deal in autopilot tests. Will still work on build tests.

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> I still don't understand why we need the QMenuModel mock? It's not part of
> unity, so why we need to mock?

Because the VolumeControl component used in Shell.qml uses QMenuModel and jenkins does not install it before running qmluitests as it's not a build dependency.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> 232 +install(FILES ${QMLFILES}
> 233 + DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Unity/Notifications
> 234 + )
>
> I don't think we want to install notification mock do we? Otherwise we wont
> have the real deal in autopilot tests. Will still work on build tests.

Right, there's no need to install it for the Shell qmluitest to work.
Removed those linex.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> 181 - createMainStageComponent();
> 182 - createSideStageComponent();
>
> 190 + if (!m_mainStageComponent)
> 191 + createMainStageComponent();
>
> 200 + if (!m_sideStageComponent)
> 201 + createSideStageComponent();
>
> Why the change?

Because in the Shell test (i.e. uqmlscene binary) ApplicationManager was being created *before* the QQuickView (unlike in the unity8 binary) and therefore the assumptions made in createMainStageComponent() and createSideStageComponent() did not hold and caused a crash.
By creating them lazily, only once they're needed (called from QML code), we ensure that those assumptions hold as those QML functions (ApplicationManager.focusApplication) get called only once the QML is running inside a QQuickView.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> We should probably also have a test which verifies that having an open app
> switches to the app rather than this new behaviour. And also that we will
> still have this new behaviour after closing all the apps

Done.

Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

> > I still don't understand why we need the QMenuModel mock? It's not part of
> > unity, so why we need to mock?
>
> Because the VolumeControl component used in Shell.qml uses QMenuModel and
> jenkins does not install it before running qmluitests as it's not a build
> dependency.

Right, well can we just add it to the build-deps then if it's needed.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> > > I still don't understand why we need the QMenuModel mock? It's not part of
> > > unity, so why we need to mock?
> >
> > Because the VolumeControl component used in Shell.qml uses QMenuModel and
> > jenkins does not install it before running qmluitests as it's not a build
> > dependency.
>
> Right, well can we just add it to the build-deps then if it's needed.

Done.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> > > > I still don't understand why we need the QMenuModel mock? It's not part
> of
> > > > unity, so why we need to mock?
> > >
> > > Because the VolumeControl component used in Shell.qml uses QMenuModel and
> > > jenkins does not install it before running qmluitests as it's not a build
> > > dependency.
> >
> > Right, well can we just add it to the build-deps then if it's needed.
>
> Done.

Commit rolled back as per discussion on IRC.

Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

LGTM

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

Test failures:
    qmltestrunner.Dash::test_set_current_scope
    qmltestrunner.Dash::test_show_scope_on_load
    qmltestrunner.DashContent::test_movement_started_signal
    qmltestrunner.DashContent::test_positioned_at_beginning_signal
    qmltestrunner.DashContent::test_scope_mapping

review: Needs Fixing
lp:~dandrader/unity8/lp1116207 updated
138. By Daniel d'Andrada

Fix whitespace issue.

139. By Daniel d'Andrada

Update Dash tests

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> Test failures:
> qmltestrunner.Dash::test_set_current_scope
> qmltestrunner.Dash::test_show_scope_on_load
> qmltestrunner.DashContent::test_movement_started_signal
> qmltestrunner.DashContent::test_positioned_at_beginning_signal
> qmltestrunner.DashContent::test_scope_mapping

Done.

lp:~dandrader/unity8/lp1116207 updated
140. By Daniel d'Andrada

Update DashContent test

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~dandrader/unity8/lp1116207 updated
141. By Daniel d'Andrada

Make Shell test a bit more robust

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Components/Stage.qml'
2--- Components/Stage.qml 2013-06-28 19:32:16 +0000
3+++ Components/Stage.qml 2013-07-25 14:56:25 +0000
4@@ -39,6 +39,7 @@
5 type == ApplicationInfo.MainStage ? applicationManager.mainStageApplications
6 : applicationManager.sideStageApplications
7 property bool fullyShown: false
8+ property bool fullyHidden: true
9 readonly property var focusedApplication:
10 type == ApplicationInfo.MainStage ? applicationManager.mainStageFocusedApplication
11 : applicationManager.sideStageFocusedApplication
12@@ -249,6 +250,19 @@
13 }
14 }
15
16+ Connections {
17+ target: stage.applications
18+ onCountChanged: { __discardObsoleteScreenshots(); }
19+ ignoreUnknownSignals: true
20+ }
21+ onFullyHiddenChanged: { __discardObsoleteScreenshots(); }
22+ function __discardObsoleteScreenshots() {
23+ if (stage.fullyHidden && stage.applications && stage.applications.count == 0) {
24+ stage.focusedApplicationWhenUsingScreenshots = undefined;
25+ stage.__hideScreenshots();
26+ }
27+ }
28+
29
30 SequentialAnimation {
31 id: showStartingApplicationAnimation
32
33=== modified file 'Dash/DashHome.qml'
34--- Dash/DashHome.qml 2013-07-09 11:44:08 +0000
35+++ Dash/DashHome.qml 2013-07-25 14:56:25 +0000
36@@ -26,6 +26,7 @@
37
38 ScopeView {
39 id: root
40+ objectName: "DashHome"
41
42 onMovementStarted: listView.showHeader()
43
44
45=== modified file 'Shell.qml'
46--- Shell.qml 2013-07-19 16:22:50 +0000
47+++ Shell.qml 2013-07-25 14:56:25 +0000
48@@ -36,8 +36,8 @@
49
50 // this is only here to select the width / height of the window if not running fullscreen
51 property bool tablet: false
52- width: tablet ? units.gu(160) : ApplicationArguments.hasGeometry() ? ApplicationArguments.width() : units.gu(40)
53- height: tablet ? units.gu(100) : ApplicationArguments.hasGeometry() ? ApplicationArguments.height() : units.gu(71)
54+ width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
55+ height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
56
57 property real edgeSize: units.gu(2)
58 property url default_background: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
59@@ -129,11 +129,20 @@
60
61 Item {
62 id: underlay
63+ objectName: "underlay"
64 anchors.fill: parent
65- visible: !(panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth)
66- && (stages.fullyHidden
67- || (stages.fullyShown && mainStage.usingScreenshots)
68- || !stages.fullyShown && (mainStage.usingScreenshots || (sideStage.shown && sideStage.usingScreenshots)))
69+
70+ // Whether the underlay is fully covered by opaque UI elements.
71+ property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
72+
73+ // Whether the user should see the topmost application surface (if there's one at all).
74+ property bool applicationSurfaceShouldBeSeen:
75+ (mainStage.applications && mainStage.applications.count > 0)
76+ && (!stages.fullyHidden && !mainStage.usingScreenshots)
77+
78+ // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
79+ // through the translucent parts of the shell surface.
80+ visible: !fullyCovered && !applicationSurfaceShouldBeSeen
81
82 Image {
83 id: backgroundImage
84@@ -151,6 +160,7 @@
85
86 Dash {
87 id: dash
88+ objectName: "dash"
89
90 available: !greeter.shown && !lockscreen.shown
91 hides: [stages, launcher, panel.indicators]
92@@ -178,7 +188,7 @@
93 }
94 }
95
96- // FIXME: only necessary because stagesRevealer.animatedProgress and
97+ // FIXME: only necessary because stagesOuterContainer.showProgress and
98 // greeterRevealer.animatedProgress are not animated
99 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
100 }
101@@ -198,6 +208,7 @@
102
103 Showable {
104 id: stages
105+ objectName: "stages"
106
107 x: width
108
109@@ -254,6 +265,7 @@
110
111 anchors.fill: parent
112 fullyShown: stages.fullyShown
113+ fullyHidden: stages.fullyHidden
114 shouldUseScreenshots: !fullyShown
115 rightEdgeEnabled: !sideStage.enabled
116
117@@ -338,7 +350,7 @@
118 }
119
120 DragHandle {
121- id: stagesRevealer
122+ id: stagesDragHandle
123
124 anchors.top: parent.top
125 anchors.bottom: parent.bottom
126@@ -346,7 +358,11 @@
127
128 width: shell.edgeSize
129 direction: Direction.Leftwards
130- enabled: mainStage.applications.count > 0 || sideStage.applications.count > 0
131+ property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
132+
133+ maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
134+ // Make autocompletion impossible when !haveApps
135+ edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
136 }
137 }
138 }
139
140=== modified file 'main.cpp'
141--- main.cpp 2013-07-19 16:22:50 +0000
142+++ main.cpp 2013-07-25 14:56:25 +0000
143@@ -113,7 +113,7 @@
144 view->setResizeMode(QQuickView::SizeRootObjectToView);
145 view->setTitle("Qml Phone Shell");
146 view->engine()->setBaseUrl(QUrl::fromLocalFile(::shellAppDirectory()));
147- view->rootContext()->setContextProperty("ApplicationArguments", &qmlArgs);
148+ view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
149 if (args.contains(QLatin1String("-frameless"))) {
150 view->setFlags(Qt::FramelessWindowHint);
151 }
152
153=== modified file 'tests/mocks/CMakeLists.txt'
154--- tests/mocks/CMakeLists.txt 2013-07-04 11:45:01 +0000
155+++ tests/mocks/CMakeLists.txt 2013-07-25 14:56:25 +0000
156@@ -1,5 +1,6 @@
157 add_subdirectory(libusermetrics)
158 add_subdirectory(LightDM)
159+add_subdirectory(QMenuModel)
160 add_subdirectory(Ubuntu)
161 add_subdirectory(Unity)
162 add_subdirectory(HudClient)
163
164=== added directory 'tests/mocks/QMenuModel'
165=== added file 'tests/mocks/QMenuModel/CMakeLists.txt'
166--- tests/mocks/QMenuModel/CMakeLists.txt 1970-01-01 00:00:00 +0000
167+++ tests/mocks/QMenuModel/CMakeLists.txt 2013-07-25 14:56:25 +0000
168@@ -0,0 +1,11 @@
169+file(GLOB QMLFILES
170+ *.qml
171+ *.js
172+ qmldir
173+)
174+
175+# copy files into build directory for shadow builds
176+add_custom_target(QMenuModelQmlFiles ALL
177+ COMMAND cp ${QMLFILES} ${CMAKE_CURRENT_BINARY_DIR}
178+ DEPENDS ${QMLFILES}
179+)
180
181=== added file 'tests/mocks/QMenuModel/QDBusActionGroup.qml'
182--- tests/mocks/QMenuModel/QDBusActionGroup.qml 1970-01-01 00:00:00 +0000
183+++ tests/mocks/QMenuModel/QDBusActionGroup.qml 2013-07-25 14:56:25 +0000
184@@ -0,0 +1,27 @@
185+/*
186+ * Copyright (C) 2013 Canonical, Ltd.
187+ *
188+ * Authors:
189+ * Daniel d'Andrada <daniel.dandrada@canonical.com>
190+ *
191+ * This program is free software; you can redistribute it and/or modify
192+ * it under the terms of the GNU General Public License as published by
193+ * the Free Software Foundation; version 3.
194+ *
195+ * This program is distributed in the hope that it will be useful,
196+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
197+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
198+ * GNU General Public License for more details.
199+ *
200+ * You should have received a copy of the GNU General Public License
201+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
202+ */
203+
204+import QtQuick 2.0
205+
206+QtObject {
207+ property int busType
208+ property string busName
209+ property string objectPath
210+ function start() {}
211+}
212
213=== added file 'tests/mocks/QMenuModel/qmldir'
214--- tests/mocks/QMenuModel/qmldir 1970-01-01 00:00:00 +0000
215+++ tests/mocks/QMenuModel/qmldir 2013-07-25 14:56:25 +0000
216@@ -0,0 +1,2 @@
217+module QMenuModel
218+QDBusActionGroup 0.1 QDBusActionGroup.qml
219
220=== modified file 'tests/mocks/Ubuntu/Application/ApplicationManager.cpp'
221--- tests/mocks/Ubuntu/Application/ApplicationManager.cpp 2013-07-16 20:45:13 +0000
222+++ tests/mocks/Ubuntu/Application/ApplicationManager.cpp 2013-07-25 14:56:25 +0000
223@@ -37,8 +37,6 @@
224 , m_sideStage(0)
225 {
226 buildListOfAvailableApplications();
227- createMainStageComponent();
228- createSideStageComponent();
229 }
230
231 ApplicationManager::~ApplicationManager()
232@@ -403,6 +401,9 @@
233
234 void ApplicationManager::createMainStage()
235 {
236+ if (!m_mainStageComponent)
237+ createMainStageComponent();
238+
239 // The assumptions I make here really should hold.
240 QQuickView *quickView =
241 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
242@@ -437,6 +438,9 @@
243
244 void ApplicationManager::createSideStage()
245 {
246+ if (!m_sideStageComponent)
247+ createSideStageComponent();
248+
249 // The assumptions I make here really should hold.
250 QQuickView *quickView =
251 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
252
253=== modified file 'tests/mocks/Unity/CMakeLists.txt'
254--- tests/mocks/Unity/CMakeLists.txt 2013-07-11 09:09:20 +0000
255+++ tests/mocks/Unity/CMakeLists.txt 2013-07-25 14:56:25 +0000
256@@ -44,3 +44,4 @@
257 )
258
259 add_subdirectory(Launcher)
260+add_subdirectory(Notifications)
261
262=== added file 'tests/mocks/Unity/Notifications/CMakeLists.txt'
263--- tests/mocks/Unity/Notifications/CMakeLists.txt 1970-01-01 00:00:00 +0000
264+++ tests/mocks/Unity/Notifications/CMakeLists.txt 2013-07-25 14:56:25 +0000
265@@ -0,0 +1,11 @@
266+file(GLOB QMLFILES
267+ *.qml
268+ *.js
269+ qmldir
270+)
271+
272+# copy files into build directory for shadow builds
273+add_custom_target(NotificationsQmlFiles ALL
274+ COMMAND cp ${QMLFILES} ${CMAKE_CURRENT_BINARY_DIR}
275+ DEPENDS ${QMLFILES}
276+)
277
278=== modified file 'tests/mocks/Unity/fake_scopes.cpp'
279--- tests/mocks/Unity/fake_scopes.cpp 2013-06-12 15:03:07 +0000
280+++ tests/mocks/Unity/fake_scopes.cpp 2013-07-25 14:56:25 +0000
281@@ -52,8 +52,8 @@
282 clear();
283 addScope(new Scope("MockScope1", "People", true, this));
284 addScope(new Scope("MockScope2", "Music", false, this));
285- addScope(new Scope("MockScope3", "Home", true, this));
286- addScope(new Scope("MockScope4", "Applications", true, this));
287+ addScope(new Scope("home.scope", "Home", true, this));
288+ addScope(new Scope("applications.scope", "Applications", true, this));
289 addScope(new Scope("MockScope5", "Videos", true, this));
290
291 if (!m_loaded) {
292
293=== modified file 'tests/qmltests/CMakeLists.txt'
294--- tests/qmltests/CMakeLists.txt 2013-07-17 13:21:55 +0000
295+++ tests/qmltests/CMakeLists.txt 2013-07-25 14:56:25 +0000
296@@ -25,6 +25,9 @@
297 set(qmltest_DEFAULT_TARGETS qmluitests)
298 set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
299 set(qmltest_DEFAULT_PROPERTIES "")
300+
301+add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
302+ ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
303 add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
304 add_qml_test(Components DraggingArea)
305 add_qml_test(Components FilterGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
306
307=== modified file 'tests/qmltests/Components/tst_Stage.qml'
308--- tests/qmltests/Components/tst_Stage.qml 2013-06-19 08:29:34 +0000
309+++ tests/qmltests/Components/tst_Stage.qml 2013-07-25 14:56:25 +0000
310@@ -161,6 +161,7 @@
311 applicationManager: fakeAppManager
312 rightEdgeDraggingAreaWidth: units.gu(2)
313 fullyShown: true
314+ fullyHidden: false
315 newApplicationScreenshot: FakeApplicationScreenshot {
316 id: newAppScreenshot
317 parent: stage
318
319=== modified file 'tests/qmltests/Dash/tst_Dash.qml'
320--- tests/qmltests/Dash/tst_Dash.qml 2013-06-13 09:27:02 +0000
321+++ tests/qmltests/Dash/tst_Dash.qml 2013-07-25 14:56:25 +0000
322@@ -38,8 +38,8 @@
323 scopeDelegateMapping: {
324 "MockScope1": "../tests/qmltests/Dash/qml/fake_scopeView1.qml",
325 "MockScope2": "../tests/qmltests/Dash/qml/fake_scopeView2.qml",
326- "MockScope3": "../tests/qmltests/Dash/qml/fake_scopeView3.qml",
327- "MockScope4": "../tests/qmltests/Dash/qml/fake_scopeView4.qml"
328+ "home.scope": "../tests/qmltests/Dash/qml/fake_scopeView3.qml",
329+ "applications.scope": "../tests/qmltests/Dash/qml/fake_scopeView4.qml"
330 }
331 genericScope: "../tests/qmltests/Dash/qml/fake_generic_scopeView.qml"
332 }
333@@ -69,8 +69,8 @@
334 return [
335 { tag: "MockScope1", visualIndex: 0, shouldBeVisible: true },
336 { tag: "MockScope2", visualIndex: -1, shouldBeVisible: false },
337- { tag: "MockScope3", visualIndex: 1, shouldBeVisible: true },
338- { tag: "MockScope4", visualIndex: 2, shouldBeVisible: true },
339+ { tag: "home.scope", visualIndex: 1, shouldBeVisible: true },
340+ { tag: "applications.scope", visualIndex: 2, shouldBeVisible: true },
341 { tag: "MockScope5", visualIndex: 3, shouldBeVisible: true },
342 ]
343 }
344
345=== modified file 'tests/qmltests/Dash/tst_DashContent.qml'
346--- tests/qmltests/Dash/tst_DashContent.qml 2013-06-13 15:26:39 +0000
347+++ tests/qmltests/Dash/tst_DashContent.qml 2013-07-25 14:56:25 +0000
348@@ -31,8 +31,8 @@
349 property var scopeStatus: {
350 'MockScope1': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
351 'MockScope2': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
352- 'MockScope3': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
353- 'MockScope4': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
354+ 'home.scope': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
355+ 'applications.scope': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
356 'MockScope5': { 'movementStarted': 0, 'positionedAtBeginning': 0 }
357 }
358
359@@ -55,8 +55,8 @@
360 scopeDelegateMapping: {
361 "MockScope1": "../tests/qmltests/Dash/qml/fake_scopeView1.qml",
362 "MockScope2": "../tests/qmltests/Dash/qml/fake_scopeView2.qml",
363- "MockScope3": "../tests/qmltests/Dash/qml/fake_scopeView3.qml",
364- "MockScope4": "../tests/qmltests/Dash/qml/fake_scopeView4.qml"
365+ "home.scope": "../tests/qmltests/Dash/qml/fake_scopeView3.qml",
366+ "applications.scope": "../tests/qmltests/Dash/qml/fake_scopeView4.qml"
367 }
368 genericScope: "../tests/qmltests/Dash/qml/fake_generic_scopeView.qml"
369 }
370@@ -68,11 +68,11 @@
371 scopeStatus["MockScope2"].movementStarted = 0;
372 scopeStatus["MockScope2"].positionedAtBeginning = 0;
373
374- scopeStatus["MockScope3"].movementStarted = 0;
375- scopeStatus["MockScope3"].positionedAtBeginning = 0;
376+ scopeStatus["home.scope"].movementStarted = 0;
377+ scopeStatus["home.scope"].positionedAtBeginning = 0;
378
379- scopeStatus["MockScope4"].movementStarted = 0;
380- scopeStatus["MockScope4"].positionedAtBeginning = 0;
381+ scopeStatus["applications.scope"].movementStarted = 0;
382+ scopeStatus["applications.scope"].positionedAtBeginning = 0;
383
384 scopeStatus["MockScope5"].movementStarted = 0;
385 scopeStatus["MockScope5"].positionedAtBeginning = 0;
386@@ -127,8 +127,8 @@
387 compare(movementStartedSpy.count, 1, "DashContent should have emitted movementStarted signal when content list did.");
388 compare(scopeStatus["MockScope1"].movementStarted, 1, "MockScope1 should have emitted movementStarted signal when content list did.");
389 compare(scopeStatus["MockScope2"].movementStarted, 1, "MockScope2 should have emitted movementStarted signal when content list did.");
390- compare(scopeStatus["MockScope3"].movementStarted, 1, "MockScope3 should have emitted movementStarted signal when content list did.");
391- compare(scopeStatus["MockScope4"].movementStarted, 1, "MockScope4 should have emitted movementStarted signal when content list did.");
392+ compare(scopeStatus["home.scope"].movementStarted, 1, "home.scope should have emitted movementStarted signal when content list did.");
393+ compare(scopeStatus["applications.scope"].movementStarted, 1, "applications.scope should have emitted movementStarted signal when content list did.");
394 compare(scopeStatus["MockScope5"].movementStarted, 1, "MockScope5 should have emitted movementStarted signal when content list did.");
395 }
396
397@@ -140,8 +140,8 @@
398 dashContent.positionedAtBeginning();
399 compare(scopeStatus["MockScope1"].positionedAtBeginning, 1, "MockScope1 should have emitted positionedAtBeginning signal when DashContent did.");
400 compare(scopeStatus["MockScope2"].positionedAtBeginning, 1, "MockScope2 should have emitted positionedAtBeginning signal when DashContent did.");
401- compare(scopeStatus["MockScope3"].positionedAtBeginning, 1, "MockScope3 should have emitted positionedAtBeginning signal when DashContent did.");
402- compare(scopeStatus["MockScope4"].positionedAtBeginning, 1, "MockScope4 should have emitted positionedAtBeginning signal when DashContent did.");
403+ compare(scopeStatus["home.scope"].positionedAtBeginning, 1, "home.scope should have emitted positionedAtBeginning signal when DashContent did.");
404+ compare(scopeStatus["applications.scope"].positionedAtBeginning, 1, "applications.scope should have emitted positionedAtBeginning signal when DashContent did.");
405 compare(scopeStatus["MockScope5"].positionedAtBeginning, 1, "MockScope5 should have emitted positionedAtBeginning signal when DashContent did.");
406 }
407
408
409=== added file 'tests/qmltests/tst_Shell.qml'
410--- tests/qmltests/tst_Shell.qml 1970-01-01 00:00:00 +0000
411+++ tests/qmltests/tst_Shell.qml 2013-07-25 14:56:25 +0000
412@@ -0,0 +1,281 @@
413+/*
414+ * Copyright (C) 2013 Canonical, Ltd.
415+ *
416+ * Authors:
417+ * Daniel d'Andrada <daniel.dandrada@canonical.com>
418+ *
419+ * This program is free software; you can redistribute it and/or modify
420+ * it under the terms of the GNU General Public License as published by
421+ * the Free Software Foundation; version 3.
422+ *
423+ * This program is distributed in the hope that it will be useful,
424+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
425+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
426+ * GNU General Public License for more details.
427+ *
428+ * You should have received a copy of the GNU General Public License
429+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
430+ */
431+
432+import QtQuick 2.0
433+import QtTest 1.0
434+import Ubuntu.Application 0.1
435+import Unity.Test 0.1 as UT
436+
437+import "../.."
438+
439+Item {
440+ width: shell.width
441+ height: shell.height
442+
443+ QtObject {
444+ id: applicationArguments
445+
446+ function hasGeometry() {
447+ return false;
448+ }
449+
450+ function width() {
451+ return 0;
452+ }
453+
454+ function height() {
455+ return 0;
456+ }
457+ }
458+
459+ Shell {
460+ id: shell
461+ }
462+
463+ UT.UnityTestCase {
464+ name: "Shell"
465+ when: windowShown
466+
467+ function initTestCase() {
468+ // swipe away the greeter/lockscreen
469+ var touchX = shell.width - (shell.edgeSize / 2);
470+ var touchY = shell.height / 2;
471+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
472+
473+ var dash = findChild(shell, "dash");
474+ // wait until the animation has finished
475+ tryCompare(dash, "contentScale", 1.0);
476+ tryCompare(dash, "opacity", 1.0);
477+ }
478+
479+ function cleanup() {
480+ // kill all (fake) running apps
481+ ApplicationManager.mainStageApplications.clear();
482+ ApplicationManager.sideStageApplications.clear();
483+
484+ var dashHome = findChild(shell, "DashHome");
485+ swipeUntilScopeViewIsReached(dashHome);
486+ }
487+
488+ /*
489+ Test the effect of a right-edge drag on the dash in 3 situations:
490+ 1 - when no application has been launched yet
491+ 2 - when there's a minimized application
492+ 3 - after the last running application has been closed/stopped
493+
494+ The behavior of Dash on 3 should be the same as on 1.
495+ */
496+ function test_rightEdgeDrag() {
497+ checkRightEdgeDragWithNoRunningApps();
498+
499+ dragLauncherIntoView();
500+
501+ // Launch an app from the launcher
502+ tapOnAppIconInLauncher();
503+ waitUntilApplicationWindowIsFullyVisible();
504+
505+ // Minimize the application we just launched
506+ swipeFromLeftEdge();
507+
508+ waitForUIToSettle();
509+
510+ checkRightEdgeDragWithMinimizedApp();
511+
512+ // Minimize that application once again
513+ swipeFromLeftEdge();
514+
515+ // kill it
516+ ApplicationManager.mainStageApplications.clear();
517+
518+ waitForUIToSettle();
519+
520+ // Right edge behavior should now be the same as before that app was launched
521+ checkRightEdgeDragWithNoRunningApps();
522+ }
523+
524+ /*
525+ Perform a right-edge drag when the Dash is being show and there are
526+ no running/minimized apps to be restored.
527+
528+ The expected behavior is that an animation should be played to hint the
529+ user that his right-edge drag gesture has been successfully recognized
530+ but there is no application to be brought to foreground.
531+ */
532+ function checkRightEdgeDragWithNoRunningApps() {
533+ var touchX = shell.width - (shell.edgeSize / 2);
534+ var touchY = shell.height / 2;
535+
536+ var dash = findChild(shell, "dash");
537+ // check that dash has normal scale and opacity
538+ compare(dash.contentScale, 1.0);
539+ compare(dash.opacity, 1.0);
540+
541+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
542+ true /* beginTouch */, false /* endTouch */);
543+
544+ // check that Dash has been scaled down and had its opacity reduced
545+ tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
546+ tryCompareFunction(function() { return dash.opacity <= 0.5; }, true);
547+
548+ touchRelease(shell, shell.width * 0.1, touchY);
549+
550+ // and now everything should have gone back to normal
551+ tryCompare(dash, "contentScale", 1.0);
552+ tryCompare(dash, "opacity", 1.0);
553+ }
554+
555+ /*
556+ Perform a right-edge drag when the Dash is being show and there is
557+ a running/minimized app to be restored.
558+
559+ The expected behavior is that the dash should fade away and ultimately be
560+ made invisible once the gesture is finished as the restored app will now
561+ be on foreground.
562+ */
563+ function checkRightEdgeDragWithMinimizedApp() {
564+ var touchX = shell.width - (shell.edgeSize / 2);
565+ var touchY = shell.height / 2;
566+
567+ var dash = findChild(shell, "dash");
568+ // check that dash has normal scale and opacity
569+ tryCompare(dash, "contentScale", 1.0);
570+ tryCompare(dash, "opacity", 1.0);
571+
572+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
573+ true /* beginTouch */, false /* endTouch */);
574+
575+ // check that Dash has been scaled down and had its opacity reduced
576+ tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
577+ tryCompareFunction(function() { return dash.opacity <= 0.5; }, true);
578+
579+ touchRelease(shell, shell.width * 0.1, touchY);
580+
581+ // dash should have gone away, now that the app is on foreground
582+ tryCompare(dash, "visible", false);
583+ }
584+
585+ // Wait for the whole UI to settle down
586+ function waitForUIToSettle() {
587+ waitUntilApplicationWindowIsFullyHidden();
588+ var dashContentList = findChild(shell, "dashContentList");
589+ tryCompare(dashContentList, "moving", false);
590+ }
591+
592+ function dragLauncherIntoView() {
593+ var launcherPanel = findChild(shell, "launcherPanel");
594+ verify(launcherPanel.x = - launcherPanel.width);
595+ swipeFromLeftEdge();
596+ tryCompare(launcherPanel, "x", 0);
597+ }
598+
599+ function tapOnAppIconInLauncher() {
600+ var launcherPanel = findChild(shell, "launcherPanel");
601+
602+ // pick the first icon, the one at the bottom.
603+ var appIcon = findChild(launcherPanel, "launcherDelegate0")
604+
605+ // Swipe upwards over the launcher to ensure that this icon
606+ // at the bottom is not folded and faded away.
607+ var touchStartX = launcherPanel.width / 2;
608+ var touchStartY = launcherPanel.height / 2;
609+ touchFlick(launcherPanel, touchStartX, touchStartY, touchStartX, 0);
610+ tryCompare(launcherPanel, "moving", false);
611+
612+ // NB tapping (i.e., using touch events) doesn't activate the icon... go figure...
613+ mouseClick(appIcon, appIcon.width / 2, appIcon.height / 2);
614+ }
615+
616+ function waitUntilApplicationWindowIsFullyVisible() {
617+ var underlay = findChild(shell, "underlay");
618+ tryCompare(underlay, "visible", false);
619+ }
620+
621+ function waitUntilApplicationWindowIsFullyHidden() {
622+ var stages = findChild(shell, "stages");
623+ tryCompare(stages, "fullyHidden", true);
624+ }
625+
626+ function swipeUntilScopeViewIsReached(scopeView) {
627+ while (!itemIsOnScreen(scopeView)) {
628+ if (itemIsToLeftOfScreen(scopeView)) {
629+ swipeRightFromCenter();
630+ } else {
631+ swipeLeftFromCenter();
632+ }
633+ waitUntilItemStopsMoving(scopeView);
634+ }
635+ }
636+
637+ function swipeFromLeftEdge() {
638+ var touchStartX = 2;
639+ var touchStartY = shell.height / 2;
640+ touchFlick(shell, touchStartX, touchStartY, shell.width * 0.75, touchStartY);
641+ }
642+
643+ function swipeLeftFromCenter() {
644+ var touchStartX = shell.width / 2;
645+ var touchStartY = shell.height / 2;
646+ touchFlick(shell, touchStartX, touchStartY, 0, touchStartY);
647+ }
648+
649+ function swipeRightFromCenter() {
650+ var touchStartX = shell.width / 2;
651+ var touchStartY = shell.height / 2;
652+ touchFlick(shell, touchStartX, touchStartY, shell.width, touchStartY);
653+ }
654+
655+ function swipeUpFromCenter() {
656+ var touchStartX = shell.width / 2;
657+ var touchStartY = shell.height / 2;
658+ touchFlick(shell, touchStartX, touchStartY, touchStartX, 0);
659+ }
660+
661+ function itemIsOnScreen(item) {
662+ var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
663+
664+ return itemRectInShell.x >= 0
665+ && itemRectInShell.y >= 0
666+ && itemRectInShell.x + itemRectInShell.width <= shell.width
667+ && itemRectInShell.y + itemRectInShell.height <= shell.height;
668+ }
669+
670+ function itemIsToLeftOfScreen(item) {
671+ var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
672+ return itemRectInShell.x < 0;
673+ }
674+
675+ function waitUntilItemStopsMoving(item) {
676+ var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
677+ var previousX = itemRectInShell.x;
678+ var previousY = itemRectInShell.y;
679+ var isStill = false;
680+
681+ do {
682+ wait(100);
683+ itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
684+ if (itemRectInShell.x == previousX && itemRectInShell.y == previousY) {
685+ isStill = true;
686+ } else {
687+ previousX = itemRectInShell.x;
688+ previousY = itemRectInShell.y;
689+ }
690+ } while (!isStill);
691+ }
692+ }
693+}

Subscribers

People subscribed via source and target branches