on bbbdccd help sign in
unity8/tests/qmltests/tst_ShellWithPin.qml
639 lines 24 kB history raw
1/*
2 * Copyright (C) 2014-2015 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.4
18import Ubuntu.Components 1.3
19import QtTest 1.0
20import AccountsService 0.1
21import GSettings 1.0
22import IntegratedLightDM 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 Component.onDestruction: {
73 shellLoader.itemDestroyed = true
74 }
75 }
76 }
77 }
79 Rectangle {
80 color: "white"
81 width: units.gu(30)
82 height: shellLoader.height
84 Column {
85 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
86 spacing: units.gu(1)
87 Button {
88 anchors { left: parent.left; right: parent.right }
89 text: "Show Greeter"
90 onClicked: LightDM.Greeter.showGreeter()
91 }
93 Label {
94 text: "Max retries:"
95 color: "black"
96 }
97 TextField {
98 id: maxRetriesTextField
99 text: "-1"
100 onTextChanged: {
101 var greeter = testCase.findChild(shellLoader.item, "greeter");
102 greeter.maxFailedLogins = text;
103 }
104 }
105 }
106 }
107 }
109 SignalSpy {
110 id: sessionSpy
111 signalName: "sessionStarted"
112 }
114 SignalSpy {
115 id: resetSpy
116 target: SystemImage
117 signalName: "resettingDevice"
118 }
120 SignalSpy {
121 id: promptSpy
122 target: LightDM.Greeter
123 signalName: "showPrompt"
124 }
126 Telephony.CallEntry {
127 id: phoneCall
128 phoneNumber: "+447812221111"
129 }
131 Item {
132 id: fakeDismissTimer
133 property bool running: false
134 signal triggered
136 function stop() {
137 running = false;
138 }
140 function restart() {
141 running = true;
142 }
143 }
145 UT.UnityTestCase {
146 id: testCase
147 name: "ShellWithPin"
148 when: windowShown
150 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
152 function init() {
153 tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
154 var greeter = findChild(shell, "greeter");
155 sessionSpy.target = greeter;
156 swipeAwayGreeter(true);
157 waitForLockscreen()
158 greeter.failedLoginsDelayAttempts = -1;
159 greeter.maxFailedLogins = -1;
161 var launcher = findChild(shell, "launcher");
162 var panel = findChild(launcher, "launcherPanel");
163 verify(!!panel);
164 panel.dismissTimer = fakeDismissTimer;
165 }
167 function cleanup() {
168 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
170 shellLoader.itemDestroyed = false
172 shellLoader.active = false
174 tryCompare(shellLoader, "status", Loader.Null)
175 tryCompare(shellLoader, "item", null)
176 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
177 // item might still be alive. So if we set Loader.active back to true
178 // again right now we will get the very same Shell instance back. So no reload
179 // actually took place. Likely because Loader waits until the next event loop
180 // iteration to do its work. So to ensure the reload, we will wait until the
181 // Shell instance gets destroyed.
182 tryCompare(shellLoader, "itemDestroyed", true)
184 // kill all (fake) running apps
185 killApps()
187 AccountsService.enableLauncherWhileLocked = true
188 AccountsService.enableIndicatorsWhileLocked = true
189 AccountsService.demoEdges = false
190 callManager.foregroundCall = null
191 LightDM.Greeter.authenticate(""); // reset greeter
193 // reload our test subject to get it in a fresh state once again
194 shellLoader.active = true
196 tryCompare(shellLoader, "status", Loader.Ready)
197 removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item)
198 }
200 function killApps() {
201 while (ApplicationManager.count > 1) {
202 var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
203 ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
204 }
205 compare(ApplicationManager.count, 1)
206 }
208 function swipeAwayGreeter(waitForCoverPage) {
209 var greeter = findChild(shell, "greeter");
210 waitForRendering(greeter)
211 var coverPage = findChild(shell, "coverPage");
212 tryCompare(coverPage, "showProgress", 1);
214 var touchX = shell.width - (shell.edgeSize / 2);
215 var touchY = shell.height / 2;
216 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
218 if (waitForCoverPage) {
219 // wait until the animation has finished
220 var coverPage = findChild(shell, "coverPage");
221 tryCompare(coverPage, "showProgress", 0);
222 }
223 }
225 function waitForLockscreen() {
226 var lockscreen = findChild(shell, "lockscreen");
227 var pinPadLoader = findChild(lockscreen, "pinPadLoader");
228 tryCompare(pinPadLoader, "status", Loader.Ready)
229 waitForRendering(lockscreen)
230 }
232 function enterPin(pin) {
233 for (var i = 0; i < pin.length; ++i) {
234 var character = pin.charAt(i)
235 var button = findChild(shell, "pinPadButton" + character)
236 tryCompare(button, "enabled", true);
237 tap(button)
238 }
239 }
241 function confirmLockedApp(app) {
242 var greeter = findChild(shell, "greeter")
243 tryCompare(greeter, "shown", false)
244 tryCompare(greeter, "hasLockedApp", true)
245 tryCompare(greeter, "lockedApp", app)
246 tryCompare(LightDM.Greeter, "active", true)
247 tryCompare(ApplicationManager, "focusedApplicationId", app)
248 }
250 function test_greeterChangesIndicatorProfile() {
251 skip("Not supported yet, waiting on design for new settings panel");
253 var panel = findChild(shell, "panel");
254 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
256 LightDM.Greeter.hideGreeter();
257 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
259 LightDM.Greeter.showGreeter();
260 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
262 LightDM.Greeter.hideGreeter();
263 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
264 }
266 function test_login() {
267 sessionSpy.clear()
268 tryCompare(sessionSpy, "count", 0)
269 enterPin("1234")
270 tryCompare(sessionSpy, "count", 1)
271 }
273 function test_edgeDemoHidesLockscreen() {
274 LightDM.Greeter.showGreeter()
275 sessionSpy.clear()
276 var lockscreen = findChild(shell, "lockscreen")
278 tryCompare(lockscreen, "shown", true)
279 AccountsService.demoEdges = true
280 tryCompare(lockscreen, "shown", false)
282 var greeter = findChild(shell, "greeter");
283 swipeAwayGreeter(false);
284 tryCompare(greeter, "shown", false);
285 tryCompare(sessionSpy, "count", 1)
287 // Lockscreen is only hidden by the edge demo, so if we turn that
288 // off and show greeter again, lockscreen should appear
289 AccountsService.demoEdges = false
290 LightDM.Greeter.showGreeter()
291 lockscreen = findChild(shell, "lockscreen");
292 tryCompare(lockscreen, "shown", true)
293 }
295 function test_disabledEdges() {
296 var launcher = findChild(shell, "launcher")
297 tryCompare(launcher, "available", true)
298 AccountsService.enableLauncherWhileLocked = false
299 tryCompare(launcher, "available", false)
301 var indicators = findChild(shell, "indicators")
302 tryCompare(indicators, "available", true)
303 AccountsService.enableIndicatorsWhileLocked = false
304 tryCompare(indicators, "available", false)
305 }
307 function test_emergencyCall() {
308 var greeter = findChild(shell, "greeter")
309 var panel = findChild(shell, "panel")
310 var indicators = findChild(shell, "indicators")
311 var launcher = findChild(shell, "launcher")
312 var stage = findChild(shell, "stage")
314 tap(findChild(greeter, "emergencyCallLabel"));
316 tryCompare(greeter, "lockedApp", "dialer-app")
317 tryCompare(greeter, "hasLockedApp", true)
318 tryCompare(greeter, "shown", false);
319 tryCompare(panel, "fullscreenMode", true)
320 tryCompare(indicators, "available", false)
321 tryCompare(launcher, "available", false)
322 tryCompare(stage, "spreadEnabled", false)
324 // Cancel emergency mode, and go back to normal
325 waitForRendering(greeter)
326 LightDM.Greeter.showGreeter()
328 tryCompare(greeter, "shown", true)
329 tryCompare(greeter, "lockedApp", "")
330 tryCompare(greeter, "hasLockedApp", false)
331 tryCompare(greeter, "fullyShown", true);
332 tryCompare(panel, "fullscreenMode", false)
333 tryCompare(indicators, "available", true)
334 tryCompare(launcher, "available", true)
335 tryCompare(stage, "spreadEnabled", false)
336 }
338 function test_emergencyCallCrash() {
339 var greeter = findChild(shell, "greeter");
340 var emergencyButton = findChild(greeter, "emergencyCallLabel");
341 tap(emergencyButton)
343 tryCompare(greeter, "shown", false);
344 killApps() // kill dialer-app, as if it crashed
345 tryCompare(greeter, "shown", true);
346 tryCompare(findChild(greeter, "lockscreen"), "shown", true);
347 tryCompare(findChild(greeter, "coverPage"), "shown", false);
348 }
350 function test_emergencyCallAppLaunch() {
351 var greeter = findChild(shell, "greeter");
352 var emergencyButton = findChild(greeter, "emergencyCallLabel");
353 tap(emergencyButton)
355 tryCompare(greeter, "shown", false);
356 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
357 tryCompare(greeter, "shown", true);
358 }
360 function test_failedLoginsCount() {
361 AccountsService.failedLogins = 0
363 enterPin("1111")
364 tryCompare(AccountsService, "failedLogins", 1)
366 enterPin("1234")
367 tryCompare(AccountsService, "failedLogins", 0)
368 }
370 function test_wrongEntries() {
371 var greeter = findChild(shell, "greeter");
372 greeter.failedLoginsDelayAttempts = 3;
374 var placeHolder = findChild(shell, "wrongNoticeLabel")
375 tryCompare(placeHolder, "text", "")
377 enterPin("1111")
378 tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
380 enterPin("1111")
381 tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
383 var lockscreen = findChild(shell, "lockscreen")
384 tryCompare(lockscreen, "delayMinutes", 0)
385 enterPin("1111")
386 tryCompare(lockscreen, "delayMinutes", greeter.failedLoginsDelayMinutes);
387 }
389 function test_factoryReset() {
390 maxRetriesTextField.text = "3"
391 resetSpy.clear()
393 enterPin("1111")
394 enterPin("1111")
395 tryCompareFunction(function() {return findChild(root, "infoPopup") !== null}, true)
397 var dialog = findChild(root, "infoPopup")
398 var button = findChild(dialog, "infoPopupOkButton")
399 tap(button)
400 tryCompareFunction(function() {return findChild(root, "infoPopup")}, null)
402 tryCompare(resetSpy, "count", 0)
403 enterPin("1111")
404 tryCompare(resetSpy, "count", 1)
405 }
407 function test_emergencyDialerLockOut() {
408 // This is a theoretical attack on the lockscreen: Enter emergency
409 // dialer mode on a phone, then plug into a larger screen,
410 // switching to a tablet interface. This would in theory move the
411 // dialer to a side stage and give access to other apps. So just
412 // confirm that such an attack doesn't work.
414 var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
416 // We start in phone mode
417 compare(shell.usageScenario, "phone");
418 compare(applicationsDisplayLoader.usageScenario, "phone");
420 var lockscreen = findChild(shell, "lockscreen")
421 lockscreen.emergencyCall()
422 confirmLockedApp("dialer-app")
424 // OK, we're in. Now try (but fail) to switch to tablet mode
425 shell.usageScenario = "tablet";
426 compare(applicationsDisplayLoader.usageScenario, "phone");
428 // And when we kill the app, we go back to locked tablet mode
429 killApps()
430 var greeter = findChild(shell, "greeter")
431 tryCompare(greeter, "fullyShown", true)
432 compare(applicationsDisplayLoader.usageScenario, "tablet");
433 }
435 function test_emergencyDialerIncoming() {
436 callManager.foregroundCall = phoneCall
437 confirmLockedApp("dialer-app")
438 }
440 function test_emergencyDialerActiveCallPanel() {
441 // Make sure that the following sequence works:
442 // - Enter emergency mode call
443 // - Return to greeter
444 // - Click on active call panel
445 // - Should be back in emergency mode dialer
447 var greeter = findChild(shell, "greeter");
448 var lockscreen = findChild(shell, "lockscreen");
450 lockscreen.emergencyCall();
451 confirmLockedApp("dialer-app");
452 callManager.foregroundCall = phoneCall;
454 LightDM.Greeter.showGreeter();
455 tryCompare(lockscreen, "shown", true);
456 tryCompare(greeter, "hasLockedApp", false);
458 // simulate a callHint press, the real thing requires dialer: url support
459 ApplicationManager.requestFocusApplication("dialer-app");
461 confirmLockedApp("dialer-app");
462 }
464 function test_normalDialerActiveCallPanel() {
465 // Make sure that the following sequence works:
466 // - Log in
467 // - Start a call
468 // - Switch apps
469 // - Click on active call panel
470 // - Should be back in normal dialer
471 // (we've had a bug where we locked screen in this case)
473 var greeter = findChild(shell, "greeter");
474 var panel = findChild(shell, "panel");
476 enterPin("1234");
477 tryCompare(greeter, "shown", false);
478 tryCompare(LightDM.Greeter, "active", false);
480 ApplicationManager.startApplication("dialer-app", ApplicationManager.NoFlag);
481 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
482 callManager.foregroundCall = phoneCall;
484 ApplicationManager.requestFocusApplication("unity8-dash");
485 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
486 tryCompare(panel.callHint, "visible", true);
488 // simulate a callHint press, the real thing requires dialer: url support
489 ApplicationManager.requestFocusApplication("dialer-app");
491 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
492 tryCompare(greeter, "shown", false);
493 tryCompare(LightDM.Greeter, "active", false);
494 }
496 function test_focusRequestedHidesCoverPage() {
497 LightDM.Greeter.showGreeter();
499 var app = ApplicationManager.startApplication("gallery-app");
500 // wait until the app is fully loaded (ie, real surface replaces splash screen)
501 tryCompareFunction(function() { return app.session !== null && app.session.lastSurface !== null }, true);
503 // New app hides coverPage?
504 var greeter = findChild(shell, "greeter");
505 var coverPage = testCase.findChild(shell, "coverPage");
506 tryCompare(coverPage, "showProgress", 0);
507 tryCompare(greeter, "fullyShown", true);
509 LightDM.Greeter.showGreeter();
510 tryCompare(coverPage, "showProgress", 1);
512 // Make sure focusing same app triggers same behavior
513 ApplicationManager.requestFocusApplication("gallery-app");
514 tryCompare(coverPage, "showProgress", 0);
515 tryCompare(greeter, "fullyShown", true);
516 }
518 function test_suspend() {
519 var greeter = findChild(shell, "greeter");
520 var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
522 // Put it to sleep
523 Powerd.setStatus(Powerd.Off, Powerd.Unknown);
525 // If locked, applicationsDisplayLoader.item.suspended should be true
526 tryCompare(applicationsDisplayLoader.item, "suspended", true);
528 // And wake up
529 Powerd.setStatus(Powerd.On, Powerd.Unknown);
530 tryCompare(greeter, "fullyShown", true);
532 // Swipe away greeter to focus app
533 swipeAwayGreeter(true);
535 // We have a lockscreen, make sure we're still suspended
536 tryCompare(applicationsDisplayLoader.item, "suspended", true);
538 enterPin("1234")
540 // Now that the lockscreen has gone too, make sure we're waking up
541 tryCompare(applicationsDisplayLoader.item, "suspended", false);
543 }
545 /* We had a bug (1395075) where if a user kept swiping as the greeter
546 loaded, they would be able to get into the session before the
547 lockscreen appeared. Make sure that doesn't happen. */
548 function test_earlyDisable() {
549 // Kill current shell
550 shellLoader.itemDestroyed = false;
551 shellLoader.active = false;
552 tryCompare(shellLoader, "itemDestroyed", true);
553 LightDM.Greeter.authenticate(""); // reset greeter
555 // Create new shell
556 promptSpy.clear();
557 shellLoader.active = true;
558 tryCompareFunction(function() {return shell !== null}, true);
560 // Confirm that we start disabled
561 compare(promptSpy.count, 0);
562 verify(!shell.enabled);
564 // And that we only become enabled once the lockscreen is up
565 tryCompare(shell, "enabled", true);
566 verify(promptSpy.count > 0);
567 var lockscreen = findChild(shell, "lockscreen");
568 verify(lockscreen.shown);
569 }
571 /*
572 Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1393447
574 Do a left edge drag that is long enough to start displacing the greeter
575 but short engough so that the greeter comes back into place once the
576 finger is lifted.
578 In this situation the launcher should remaing fully shown and hide itself
579 only after its idle timeout is triggered.
580 */
581 function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
582 LightDM.Greeter.showGreeter();
583 var coverPage = testCase.findChild(shell, "coverPage");
584 tryCompare(coverPage, "showProgress", 1);
586 var launcher = testCase.findChild(shell, "launcher")
587 var launcherPanel = testCase.findChild(launcher, "launcherPanel");
589 var toX = shell.width * 0.45;
590 touchFlick(shell,
591 1 /* fromX */, shell.height * 0.5 /* fromY */,
592 toX /* toX */, shell.height * 0.5 /* toY */,
593 true /* beginTouch */, false /* endTouch */,
594 50, 100);
596 // Launcher must be fully shown by now
597 tryCompare(launcherPanel, "x", 0);
599 // Greeter should be displaced
600 tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
602 touchRelease(shell, toX, shell.height * 0.5);
604 // Upon release the greeter should have slid back into full view
605 tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x === 0; }, true);
607 // And the launcher should stay fully shown
608 for (var i = 0; i < 10; ++i) {
609 wait(50);
610 compare(launcherPanel.x, 0);
611 }
612 }
614 function test_longLeftEdgeDrags() {
615 var coverPage = findChild(shell, "coverPage");
616 var lockscreen = findChild(shell, "lockscreen");
617 var launcher = findChild(shell, "launcherPanel");
618 ApplicationManager.startApplication("gallery-app");
619 tryCompare(ApplicationManager, "focusedApplicationId", "gallery-app");
621 // Show greeter
622 LightDM.Greeter.showGreeter();
623 tryCompare(coverPage, "showProgress", 1);
625 // Swipe cover page away
626 touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
627 tryCompare(launcher, "x", -launcher.width);
628 tryCompare(coverPage, "showProgress", 0);
629 compare(lockscreen.shown, true);
630 compare(ApplicationManager.focusedApplicationId, "gallery-app");
632 // Now attempt a swipe on lockscreen
633 touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
634 tryCompare(launcher, "x", 0);
635 compare(lockscreen.shown, true);
636 compare(ApplicationManager.focusedApplicationId, "gallery-app");
637 }
638 }