@@ -0,0 +1,362 @@
1+/*
2+ * Copyright (C) 2013,2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import AccountsService 0.1
20+import GSettings 1.0
21+import LightDM 0.1 as LightDM
22+import Ubuntu.Components 1.1
23+import Ubuntu.Telephony 0.1 as Telephony
24+import Unity.Application 0.1
25+import Unity.Connectivity 0.1
26+import Unity.Test 0.1
27+import Powerd 0.1
28+import Utils 0.1
29+
30+import "../../qml"
31+
32+Item {
33+ id: root
34+ width: units.gu(140)
35+ height: units.gu(90)
36+
37+ Binding {
38+ target: MouseTouchAdaptor
39+ property: "enabled"
40+ value: false
41+ }
42+
43+ Component.onCompleted: {
44+ // must set the mock mode before loading the Shell
45+ LightDM.Greeter.mockMode = "full";
46+ LightDM.Users.mockMode = "full";
47+
48+ // ensures apps which are tested decorations are in view.
49+ WindowStateStorage.geometry = {
50+ 'unity8-dash': Qt.rect(0, units.gu(3), units.gu(50), units.gu(40)),
51+ 'dialer-app': Qt.rect(units.gu(51), units.gu(3), units.gu(50), units.gu(40)),
52+ 'camera-app': Qt.rect(0, units.gu(44), units.gu(50), units.gu(40)),
53+ }
54+
55+ shellLoader.active = true;
56+ }
57+
58+ QtObject {
59+ id: applicationArguments
60+
61+ function hasGeometry() {
62+ return false
63+ }
64+
65+ function width() {
66+ return 0
67+ }
68+
69+ function height() {
70+ return 0
71+ }
72+ }
73+
74+ Row {
75+ anchors.fill: parent
76+ Loader {
77+ id: shellLoader
78+
79+ active: false
80+ width: units.gu(110)
81+ height: parent.height
82+
83+ property bool itemDestroyed: false
84+ sourceComponent: Component {
85+ Shell {
86+ usageMode: "Windowed"
87+ property string indicatorProfile: "desktop"
88+
89+ Component.onDestruction: {
90+ shellLoader.itemDestroyed = true
91+ }
92+ }
93+ }
94+ }
95+
96+ Rectangle {
97+ color: "white"
98+ width: units.gu(30)
99+ height: parent.height
100+
101+ Column {
102+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
103+ spacing: units.gu(1)
104+ Button {
105+ text: "Show Greeter"
106+ onClicked: {
107+ if (shellLoader.status !== Loader.Ready)
108+ return
109+
110+ var greeter = testCase.findChild(shellLoader.item, "greeter")
111+ if (!greeter.shown) {
112+ greeter.show()
113+ }
114+ }
115+ }
116+
117+ Repeater {
118+ id: apps
119+ model: ApplicationManager.availableApplications()
120+ anchors {
121+ left: parent.left
122+ right: parent.right
123+ }
124+ height: childrenRect.height
125+
126+ Row {
127+ id: appColumn
128+ enabled: modelData != "unity8-dash"
129+ property var application: null
130+
131+ Component.onCompleted: {
132+ application = ApplicationManager.findApplication(modelData);
133+ }
134+
135+ Connections {
136+ target: ApplicationManager
137+ onCountChanged: {
138+ appColumn.application = ApplicationManager.findApplication(modelData);
139+ }
140+ }
141+
142+ CheckBox {
143+ id: appCheckBox
144+ onTriggered: {
145+ if (checked) ApplicationManager.startApplication(modelData);
146+ else ApplicationManager.stopApplication(modelData);
147+ }
148+ Binding {
149+ target: appCheckBox
150+ property: "checked"
151+ value: appColumn.application != null
152+ }
153+ }
154+
155+ Label {
156+ text: appColumn.application ? appColumn.application.name : modelData
157+ color: "black"
158+ }
159+ }
160+ }
161+ }
162+ }
163+ }
164+
165+ SignalSpy {
166+ id: sessionSpy
167+ signalName: "sessionStarted"
168+ }
169+
170+ SignalSpy {
171+ id: dashCommunicatorSpy
172+ signalName: "setCurrentScopeCalled"
173+ }
174+
175+ SignalSpy {
176+ id: unlockAllModemsSpy
177+ target: Connectivity
178+ signalName: "unlockingAllModems"
179+ }
180+
181+ UnityTestCase {
182+ id: testCase
183+ name: "DesktopShell"
184+ when: windowShown
185+
186+ property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
187+
188+ function init() {
189+ tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
190+ var userList = findChild(shell, "userList");
191+ tryCompare(userList, "movingInternally", false);
192+ sessionSpy.clear()
193+ sessionSpy.target = findChild(shell, "greeter")
194+ dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator")
195+ }
196+
197+ function cleanup() {
198+ tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
199+
200+ shellLoader.itemDestroyed = false
201+
202+ shellLoader.active = false
203+
204+ tryCompare(shellLoader, "status", Loader.Null)
205+ tryCompare(shellLoader, "item", null)
206+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
207+ // item might still be alive. So if we set Loader.active back to true
208+ // again right now we will get the very same Shell instance back. So no reload
209+ // actually took place. Likely because Loader waits until the next event loop
210+ // iteration to do its work. So to ensure the reload, we will wait until the
211+ // Shell instance gets destroyed.
212+ tryCompare(shellLoader, "itemDestroyed", true)
213+
214+ // kill all (fake) running apps
215+ killApps()
216+
217+ unlockAllModemsSpy.clear()
218+ AccountsService.demoEdges = false
219+ LightDM.Greeter.authenticate(""); // reset greeter
220+
221+ // reload our test subject to get it in a fresh state once again
222+ shellLoader.active = true
223+
224+ tryCompare(shellLoader, "status", Loader.Ready)
225+ removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item)
226+ }
227+
228+ function killApps() {
229+ while (ApplicationManager.count > 1) {
230+ var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
231+ ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId)
232+ }
233+ compare(ApplicationManager.count, 1)
234+ }
235+
236+ function selectIndex(i) {
237+ // We could be anywhere in list; find target index to know which direction
238+ var greeter = findChild(shell, "greeter")
239+ var userlist = findChild(greeter, "userList")
240+ if (userlist.currentIndex == i)
241+ keyClick(Qt.Key_Escape) // Reset state if we're not moving
242+ while (userlist.currentIndex != i) {
243+ var next = userlist.currentIndex + 1
244+ if (userlist.currentIndex > i) {
245+ next = userlist.currentIndex - 1
246+ }
247+ tap(findChild(greeter, "username"+next));
248+ tryCompare(userlist, "currentIndex", next)
249+ tryCompare(userlist, "movingInternally", false)
250+ }
251+ }
252+
253+ function selectUser(name) {
254+ // Find index of user with the right name
255+ for (var i = 0; i < LightDM.Users.count; i++) {
256+ if (LightDM.Users.data(i, LightDM.UserRoles.NameRole) == name) {
257+ break
258+ }
259+ }
260+ if (i == LightDM.Users.count) {
261+ fail("Didn't find name")
262+ return -1
263+ }
264+ selectIndex(i)
265+ return i
266+ }
267+
268+ function clickPasswordInput(isButton) {
269+ var greeter = findChild(shell, "greeter")
270+ tryCompare(greeter, "fullyShown", true);
271+
272+ var passwordMouseArea = findChild(shell, "passwordMouseArea")
273+ tryCompare(passwordMouseArea, "enabled", isButton)
274+
275+ var passwordInput = findChild(shell, "passwordInput")
276+ mouseClick(passwordInput)
277+ }
278+
279+ function confirmLoggedIn(loggedIn) {
280+ var greeter = findChild(shell, "greeter");
281+ tryCompare(greeter, "shown", loggedIn ? false : true);
282+ verify(loggedIn ? sessionSpy.count > 0 : sessionSpy.count === 0);
283+ }
284+
285+ function swipeFromLeftEdge(swipeLength) {
286+ var touchStartX = 2
287+ var touchStartY = shell.height / 2
288+ touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY)
289+ }
290+
291+ function waitUntilAppSurfaceShowsUp(appId) {
292+ var appWindow = findChild(shell, "appWindow_" + appId);
293+ verify(appWindow);
294+ var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
295+ verify(appWindowStates);
296+ tryCompare(appWindowStates, "state", "surface");
297+ }
298+
299+ function startApplication(appId) {
300+ var app = ApplicationManager.findApplication(appId);
301+ if (!app) {
302+ app = ApplicationManager.startApplication(appId);
303+ }
304+ verify(app);
305+ waitUntilAppSurfaceShowsUp(appId);
306+ verify(app.session.surface);
307+ return app;
308+ }
309+
310+ function test_appFocusSwitch_data() {
311+ return [
312+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
313+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
314+ ]
315+ }
316+
317+ function test_appFocusSwitch(data) {
318+ selectUser("no-password");
319+ clickPasswordInput(true)
320+ confirmLoggedIn(true);
321+
322+ var i;
323+ for (i = 0; i < data.apps.length; i++) {
324+ startApplication(data.apps[i]);
325+ }
326+
327+ ApplicationManager.requestFocusApplication(data.apps[data.focusfrom]);
328+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
329+
330+ ApplicationManager.requestFocusApplication(data.apps[data.focusTo]);
331+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
332+ }
333+
334+ function test_decorationPressFocusesApplication_data() {
335+ return [
336+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
337+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
338+ ]
339+ }
340+
341+ function test_decorationPressFocusesApplication(data) {
342+ selectUser("no-password");
343+ clickPasswordInput(true)
344+ confirmLoggedIn(true);
345+
346+ var i;
347+ for (i = 0; i < data.apps.length; i++) {
348+ startApplication(data.apps[i]);
349+ }
350+
351+ var fromAppDecoration = findChild(shell, "appWindowDecoration_" + data.apps[data.focusfrom]);
352+ verify(fromAppDecoration);
353+ mouseClick(fromAppDecoration);
354+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
355+
356+ var toAppDecoration = findChild(shell, "appWindowDecoration_" + data.apps[data.focusTo]);
357+ verify(toAppDecoration);
358+ mouseClick(toAppDecoration);
359+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
360+ }
361+ }
362+}