on 8a93021 help sign in
unity8/tests/qmltests/tst_ShellWithPin.qml
628 lines 24 kB history raw
1/*
2 * Copyright (C) 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 */
17import QtQuick 2.0
18import Ubuntu.Components 1.0
19import QtTest 1.0
20import AccountsService 0.1
21import GSettings 1.0
22import LightDM 0.1 as LightDM
23import Ubuntu.SystemImage 0.1
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Application 0.1
26import Unity.Test 0.1 as UT
27import Powerd 0.1
29import "../../qml"
31Item {
32 id: root
33 width: contentRow.width
34 height: contentRow.height
36 Component.onCompleted: {
37 // must set the mock mode before loading the Shell
38 LightDM.Greeter.mockMode = "single-pin";
39 LightDM.Users.mockMode = "single-pin";
40 shellLoader.active = true;
41 }
43 QtObject {
44 id: applicationArguments
46 function hasGeometry() {
47 return false;
48 }
50 function width() {
51 return 0;
52 }
54 function height() {
55 return 0;
56 }
57 }
59 Row {
60 id: contentRow
62 Loader {
63 id: shellLoader
64 active: false
66 width: units.gu(40)
67 height: units.gu(71)
69 property bool itemDestroyed: false
70 sourceComponent: Component {
71 Shell {
72 property string indicatorProfile: "phone"
74 Component.onDestruction: {
75 shellLoader.itemDestroyed = true
76 }
77 }
78 }
79 }
81 Rectangle {
82 color: "white"
83 width: units.gu(30)
84 height: shellLoader.height
86 Column {
87 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
88 spacing: units.gu(1)
89 Button {
90 anchors { left: parent.left; right: parent.right }
91 text: "Show Greeter"
92 onClicked: LightDM.Greeter.showGreeter()
93 }
95 Label {
96 text: "Max retries:"
97 color: "black"
98 }
99 TextField {
100 id: maxRetriesTextField
101 text: "-1"
102 onTextChanged: {
103 var greeter = testCase.findChild(shellLoader.item, "greeter");
104 greeter.maxFailedLogins = text;
105 }
106 }
107 }
108 }
109 }
111 SignalSpy {
112 id: sessionSpy
113 signalName: "sessionStarted"
114 }
116 SignalSpy {
117 id: resetSpy
118 target: SystemImage
119 signalName: "resettingDevice"
120 }
122 SignalSpy {
123 id: promptSpy
124 target: LightDM.Greeter
125 signalName: "showPrompt"
126 }
128 Telephony.CallEntry {
129 id: phoneCall
130 phoneNumber: "+447812221111"
131 }
133 UT.UnityTestCase {
134 id: testCase
135 name: "ShellWithPin"
136 when: windowShown
138 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
140 function init() {
141 tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
142 var greeter = findChild(shell, "greeter");
143 sessionSpy.target = greeter;
144 swipeAwayGreeter(true);
145 waitForLockscreen()
146 greeter.failedLoginsDelayAttempts = -1;
147 greeter.maxFailedLogins = -1;
148 }
150 function cleanup() {
151 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
153 shellLoader.itemDestroyed = false
155 shellLoader.active = false
157 tryCompare(shellLoader, "status", Loader.Null)
158 tryCompare(shellLoader, "item", null)
159 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
160 // item might still be alive. So if we set Loader.active back to true
161 // again right now we will get the very same Shell instance back. So no reload
162 // actually took place. Likely because Loader waits until the next event loop
163 // iteration to do its work. So to ensure the reload, we will wait until the
164 // Shell instance gets destroyed.
165 tryCompare(shellLoader, "itemDestroyed", true)
167 // kill all (fake) running apps
168 killApps()
170 AccountsService.enableLauncherWhileLocked = true
171 AccountsService.enableIndicatorsWhileLocked = true
172 AccountsService.demoEdges = false
173 callManager.foregroundCall = null
174 LightDM.Greeter.authenticate(""); // reset greeter
176 // reload our test subject to get it in a fresh state once again
177 shellLoader.active = true
179 tryCompare(shellLoader, "status", Loader.Ready)
180 removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item)
181 }
183 function killApps() {
184 while (ApplicationManager.count > 1) {
185 var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
186 ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
187 }
188 compare(ApplicationManager.count, 1)
189 }
191 function swipeAwayGreeter(waitForCoverPage) {
192 var greeter = findChild(shell, "greeter");
193 waitForRendering(greeter)
194 var coverPage = findChild(shell, "coverPage");
195 tryCompare(coverPage, "showProgress", 1);
197 var touchX = shell.width - (shell.edgeSize / 2);
198 var touchY = shell.height / 2;
199 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
201 if (waitForCoverPage) {
202 // wait until the animation has finished
203 var coverPage = findChild(shell, "coverPage");
204 tryCompare(coverPage, "showProgress", 0);
205 }
206 }
208 function waitForLockscreen() {
209 var lockscreen = findChild(shell, "lockscreen");
210 var pinPadLoader = findChild(lockscreen, "pinPadLoader");
211 tryCompare(pinPadLoader, "status", Loader.Ready)
212 waitForRendering(lockscreen)
213 }
215 function enterPin(pin) {
216 for (var i = 0; i < pin.length; ++i) {
217 var character = pin.charAt(i)
218 var button = findChild(shell, "pinPadButton" + character)
219 tryCompare(button, "enabled", true);
220 tap(button)
221 }
222 }
224 function confirmLockedApp(app) {
225 var greeter = findChild(shell, "greeter")
226 tryCompare(greeter, "shown", false)
227 tryCompare(greeter, "hasLockedApp", true)
228 tryCompare(greeter, "lockedApp", app)
229 tryCompare(LightDM.Greeter, "active", true)
230 tryCompare(ApplicationManager, "focusedApplicationId", app)
231 }
233 function test_greeterChangesIndicatorProfile() {
234 skip("Not supported yet, waiting on design for new settings panel");
236 var panel = findChild(shell, "panel");
237 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
239 LightDM.Greeter.hideGreeter();
240 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
242 LightDM.Greeter.showGreeter();
243 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
245 LightDM.Greeter.hideGreeter();
246 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
247 }
249 function test_login() {
250 sessionSpy.clear()
251 tryCompare(sessionSpy, "count", 0)
252 enterPin("1234")
253 tryCompare(sessionSpy, "count", 1)
254 }
256 function test_edgeDemoHidesLockscreen() {
257 LightDM.Greeter.showGreeter()
258 sessionSpy.clear()
259 var lockscreen = findChild(shell, "lockscreen")
261 tryCompare(lockscreen, "shown", true)
262 AccountsService.demoEdges = true
263 tryCompare(lockscreen, "shown", false)
265 var greeter = findChild(shell, "greeter");
266 swipeAwayGreeter(false);
267 tryCompare(greeter, "shown", false);
268 tryCompare(sessionSpy, "count", 1)
270 // Lockscreen is only hidden by the edge demo, so if we turn that
271 // off and show greeter again, lockscreen should appear
272 AccountsService.demoEdges = false
273 LightDM.Greeter.showGreeter()
274 lockscreen = findChild(shell, "lockscreen");
275 tryCompare(lockscreen, "shown", true)
276 }
278 function test_disabledEdges() {
279 var launcher = findChild(shell, "launcher")
280 tryCompare(launcher, "available", true)
281 AccountsService.enableLauncherWhileLocked = false
282 tryCompare(launcher, "available", false)
284 var indicators = findChild(shell, "indicators")
285 tryCompare(indicators, "available", true)
286 AccountsService.enableIndicatorsWhileLocked = false
287 tryCompare(indicators, "available", false)
288 }
290 function test_emergencyCall() {
291 var greeter = findChild(shell, "greeter")
292 var panel = findChild(shell, "panel")
293 var indicators = findChild(shell, "indicators")
294 var launcher = findChild(shell, "launcher")
295 var stage = findChild(shell, "stage")
297 tap(findChild(greeter, "emergencyCallLabel"));
299 tryCompare(greeter, "lockedApp", "dialer-app")
300 tryCompare(greeter, "hasLockedApp", true)
301 tryCompare(greeter, "shown", false);
302 tryCompare(panel, "fullscreenMode", true)
303 tryCompare(indicators, "available", false)
304 tryCompare(launcher, "available", false)
305 tryCompare(stage, "spreadEnabled", false)
307 // Cancel emergency mode, and go back to normal
308 waitForRendering(greeter)
309 LightDM.Greeter.showGreeter()
311 tryCompare(greeter, "shown", true)
312 tryCompare(greeter, "lockedApp", "")
313 tryCompare(greeter, "hasLockedApp", false)
314 tryCompare(greeter, "fullyShown", true);
315 tryCompare(panel, "fullscreenMode", false)
316 tryCompare(indicators, "available", true)
317 tryCompare(launcher, "available", true)
318 tryCompare(stage, "spreadEnabled", true)
319 }
321 function test_emergencyCallCrash() {
322 var greeter = findChild(shell, "greeter");
323 var emergencyButton = findChild(greeter, "emergencyCallLabel");
324 tap(emergencyButton)
326 tryCompare(greeter, "shown", false);
327 killApps() // kill dialer-app, as if it crashed
328 tryCompare(greeter, "shown", true);
329 tryCompare(findChild(greeter, "lockscreen"), "shown", true);
330 tryCompare(findChild(greeter, "coverPage"), "shown", false);
331 }
333 function test_emergencyCallAppLaunch() {
334 var greeter = findChild(shell, "greeter");
335 var emergencyButton = findChild(greeter, "emergencyCallLabel");
336 tap(emergencyButton)
338 tryCompare(greeter, "shown", false);
339 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
340 tryCompare(greeter, "shown", true);
341 }
343 function test_failedLoginsCount() {
344 AccountsService.failedLogins = 0
346 enterPin("1111")
347 tryCompare(AccountsService, "failedLogins", 1)
349 enterPin("1234")
350 tryCompare(AccountsService, "failedLogins", 0)
351 }
353 function test_wrongEntries() {
354 var greeter = findChild(shell, "greeter");
355 greeter.failedLoginsDelayAttempts = 3;
357 var placeHolder = findChild(shell, "wrongNoticeLabel")
358 tryCompare(placeHolder, "text", "")
360 enterPin("1111")
361 tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
363 enterPin("1111")
364 tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
366 var lockscreen = findChild(shell, "lockscreen")
367 tryCompare(lockscreen, "delayMinutes", 0)
368 enterPin("1111")
369 tryCompare(lockscreen, "delayMinutes", greeter.failedLoginsDelayMinutes);
370 }
372 function test_factoryReset() {
373 maxRetriesTextField.text = "3"
374 resetSpy.clear()
376 enterPin("1111")
377 enterPin("1111")
378 tryCompareFunction(function() {return findChild(root, "infoPopup") !== null}, true)
380 var dialog = findChild(root, "infoPopup")
381 var button = findChild(dialog, "infoPopupOkButton")
382 tap(button)
383 tryCompareFunction(function() {return findChild(root, "infoPopup")}, null)
385 tryCompare(resetSpy, "count", 0)
386 enterPin("1111")
387 tryCompare(resetSpy, "count", 1)
388 }
390 function test_emergencyDialerLockOut() {
391 // This is a theoretical attack on the lockscreen: Enter emergency
392 // dialer mode on a phone, then plug into a larger screen,
393 // switching to a tablet interface. This would in theory move the
394 // dialer to a side stage and give access to other apps. So just
395 // confirm that such an attack doesn't work.
397 var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
399 // We start in phone mode
400 tryCompare(shell, "sideStageEnabled", false)
401 tryCompare(applicationsDisplayLoader, "tabletMode", false)
403 var lockscreen = findChild(shell, "lockscreen")
404 lockscreen.emergencyCall()
405 confirmLockedApp("dialer-app")
407 // OK, we're in. Now try (but fail) to switch to tablet mode
408 shell.tablet = true
409 tryCompare(shell, "sideStageEnabled", true)
410 tryCompare(applicationsDisplayLoader, "tabletMode", false)
412 // And when we kill the app, we go back to locked tablet mode
413 killApps()
414 var greeter = findChild(shell, "greeter")
415 tryCompare(greeter, "fullyShown", true)
416 tryCompare(shell, "sideStageEnabled", true)
417 tryCompare(applicationsDisplayLoader, "tabletMode", true)
418 }
420 function test_emergencyDialerIncoming() {
421 callManager.foregroundCall = phoneCall
422 confirmLockedApp("dialer-app")
423 }
425 function test_emergencyDialerActiveCallPanel() {
426 // Make sure that the following sequence works:
427 // - Enter emergency mode call
428 // - Return to greeter
429 // - Click on active call panel
430 // - Should be back in emergency mode dialer
432 var greeter = findChild(shell, "greeter");
433 var lockscreen = findChild(shell, "lockscreen");
435 lockscreen.emergencyCall();
436 confirmLockedApp("dialer-app");
437 callManager.foregroundCall = phoneCall;
439 LightDM.Greeter.showGreeter();
440 tryCompare(lockscreen, "shown", true);
441 tryCompare(greeter, "hasLockedApp", false);
443 // simulate a callHint press, the real thing requires dialer: url support
444 ApplicationManager.requestFocusApplication("dialer-app");
446 confirmLockedApp("dialer-app");
447 }
449 function test_normalDialerActiveCallPanel() {
450 // Make sure that the following sequence works:
451 // - Log in
452 // - Start a call
453 // - Switch apps
454 // - Click on active call panel
455 // - Should be back in normal dialer
456 // (we've had a bug where we locked screen in this case)
458 var greeter = findChild(shell, "greeter");
459 var panel = findChild(shell, "panel");
461 enterPin("1234");
462 tryCompare(greeter, "shown", false);
463 tryCompare(LightDM.Greeter, "active", false);
465 ApplicationManager.startApplication("dialer-app", ApplicationManager.NoFlag);
466 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
467 callManager.foregroundCall = phoneCall;
469 ApplicationManager.requestFocusApplication("unity8-dash");
470 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
471 tryCompare(panel.callHint, "visible", true);
473 // simulate a callHint press, the real thing requires dialer: url support
474 ApplicationManager.requestFocusApplication("dialer-app");
476 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
477 tryCompare(greeter, "shown", false);
478 tryCompare(LightDM.Greeter, "active", false);
479 }
481 function test_focusRequestedHidesCoverPage() {
482 LightDM.Greeter.showGreeter();
484 var app = ApplicationManager.startApplication("gallery-app");
485 // wait until the app is fully loaded (ie, real surface replaces splash screen)
486 tryCompareFunction(function() { return app.session !== null && app.session.surface !== null }, true);
488 // New app hides coverPage?
489 var greeter = findChild(shell, "greeter");
490 var coverPage = testCase.findChild(shell, "coverPage");
491 tryCompare(coverPage, "showProgress", 0);
492 tryCompare(greeter, "fullyShown", true);
494 LightDM.Greeter.showGreeter();
495 tryCompare(coverPage, "showProgress", 1);
497 // Make sure focusing same app triggers same behavior
498 ApplicationManager.requestFocusApplication("gallery-app");
499 tryCompare(coverPage, "showProgress", 0);
500 tryCompare(greeter, "fullyShown", true);
501 }
503 function test_suspend() {
504 var greeter = findChild(shell, "greeter");
506 // Put it to sleep
507 Powerd.status = Powerd.Off;
509 // If locked, ApplicationManager.suspended should be true
510 tryCompare(ApplicationManager, "suspended", true);
512 // And wake up
513 Powerd.status = Powerd.On;
514 tryCompare(greeter, "fullyShown", true);
516 // Swipe away greeter to focus app
517 swipeAwayGreeter(true);
519 // We have a lockscreen, make sure we're still suspended
520 tryCompare(ApplicationManager, "suspended", true);
522 enterPin("1234")
524 // Now that the lockscreen has gone too, make sure we're waking up
525 tryCompare(ApplicationManager, "suspended", false);
527 }
529 /* We had a bug (1395075) where if a user kept swiping as the greeter
530 loaded, they would be able to get into the session before the
531 lockscreen appeared. Make sure that doesn't happen. */
532 function test_earlyDisable() {
533 // Kill current shell
534 shellLoader.itemDestroyed = false;
535 shellLoader.active = false;
536 tryCompare(shellLoader, "itemDestroyed", true);
537 LightDM.Greeter.authenticate(""); // reset greeter
539 // Create new shell
540 promptSpy.clear();
541 shellLoader.active = true;
542 tryCompareFunction(function() {return shell !== null}, true);
544 // Confirm that we start disabled
545 compare(promptSpy.count, 0);
546 verify(!shell.enabled);
548 // And that we only become enabled once the lockscreen is up
549 tryCompare(shell, "enabled", true);
550 verify(promptSpy.count > 0);
551 var lockscreen = findChild(shell, "lockscreen");
552 verify(lockscreen.shown);
553 }
555 /*
556 Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1393447
558 Do a left edge drag that is long enough to start displacing the greeter
559 but short engough so that the greeter comes back into place once the
560 finger is lifted.
562 In this situation the launcher should remaing fully shown and hide itself
563 only after its idle timeout is triggered.
564 */
565 function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
566 LightDM.Greeter.showGreeter();
567 var coverPage = testCase.findChild(shell, "coverPage");
568 tryCompare(coverPage, "showProgress", 1);
570 var launcher = testCase.findChild(shell, "launcher")
571 {
572 var dismissTimer = testCase.findInvisibleChild(launcher, "dismissTimer");
573 // effectively disable the dismiss timer
574 dismissTimer.interval = 24 * 60 * 60 * 1000 // 24 hours
575 }
576 var launcherPanel = testCase.findChild(launcher, "launcherPanel");
578 var toX = shell.width * 0.45;
579 touchFlick(shell,
580 1 /* fromX */, shell.height * 0.5 /* fromY */,
581 toX /* toX */, shell.height * 0.5 /* toY */,
582 true /* beginTouch */, false /* endTouch */,
583 50, 100);
585 // Launcher must be fully shown by now
586 tryCompare(launcherPanel, "x", 0);
588 // Greeter should be displaced
589 tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
591 touchRelease(shell, toX, shell.height * 0.5);
593 // Upon release the greeter should have slid back into full view
594 tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x === 0; }, true);
596 // And the launcher should stay fully shown
597 for (var i = 0; i < 10; ++i) {
598 wait(50);
599 compare(launcherPanel.x, 0);
600 }
601 }
603 function test_longLeftEdgeDrags() {
604 var coverPage = findChild(shell, "coverPage");
605 var lockscreen = findChild(shell, "lockscreen");
606 var launcher = findChild(shell, "launcherPanel");
607 ApplicationManager.startApplication("gallery-app");
608 tryCompare(ApplicationManager, "focusedApplicationId", "gallery-app");
610 // Show greeter
611 LightDM.Greeter.showGreeter();
612 tryCompare(coverPage, "showProgress", 1);
614 // Swipe cover page away
615 touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
616 tryCompare(launcher, "x", -launcher.width);
617 tryCompare(coverPage, "showProgress", 0);
618 compare(lockscreen.shown, true);
619 compare(ApplicationManager.focusedApplicationId, "gallery-app");
621 // Now attempt a swipe on lockscreen
622 touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
623 tryCompare(launcher, "x", 0);
624 compare(lockscreen.shown, true);
625 compare(ApplicationManager.focusedApplicationId, "gallery-app");
626 }
627 }