on fcccc72 help sign in
unity8/tests/qmltests/tst_ShellWithPin.qml
624 lines 23 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.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 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 UT.UnityTestCase {
132 id: testCase
133 name: "ShellWithPin"
134 when: windowShown
136 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
138 function init() {
139 tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
140 var greeter = findChild(shell, "greeter");
141 sessionSpy.target = greeter;
142 swipeAwayGreeter(true);
143 waitForLockscreen()
144 greeter.failedLoginsDelayAttempts = -1;
145 greeter.maxFailedLogins = -1;
146 }
148 function cleanup() {
149 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
151 shellLoader.itemDestroyed = false
153 shellLoader.active = false
155 tryCompare(shellLoader, "status", Loader.Null)
156 tryCompare(shellLoader, "item", null)
157 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
158 // item might still be alive. So if we set Loader.active back to true
159 // again right now we will get the very same Shell instance back. So no reload
160 // actually took place. Likely because Loader waits until the next event loop
161 // iteration to do its work. So to ensure the reload, we will wait until the
162 // Shell instance gets destroyed.
163 tryCompare(shellLoader, "itemDestroyed", true)
165 // kill all (fake) running apps
166 killApps()
168 AccountsService.enableLauncherWhileLocked = true
169 AccountsService.enableIndicatorsWhileLocked = true
170 AccountsService.demoEdges = false
171 callManager.foregroundCall = null
172 LightDM.Greeter.authenticate(""); // reset greeter
174 // reload our test subject to get it in a fresh state once again
175 shellLoader.active = true
177 tryCompare(shellLoader, "status", Loader.Ready)
178 removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item)
179 }
181 function killApps() {
182 while (ApplicationManager.count > 1) {
183 var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
184 ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
185 }
186 compare(ApplicationManager.count, 1)
187 }
189 function swipeAwayGreeter(waitForCoverPage) {
190 var greeter = findChild(shell, "greeter");
191 waitForRendering(greeter)
192 var coverPage = findChild(shell, "coverPage");
193 tryCompare(coverPage, "showProgress", 1);
195 var touchX = shell.width - (shell.edgeSize / 2);
196 var touchY = shell.height / 2;
197 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
199 if (waitForCoverPage) {
200 // wait until the animation has finished
201 var coverPage = findChild(shell, "coverPage");
202 tryCompare(coverPage, "showProgress", 0);
203 }
204 }
206 function waitForLockscreen() {
207 var lockscreen = findChild(shell, "lockscreen");
208 var pinPadLoader = findChild(lockscreen, "pinPadLoader");
209 tryCompare(pinPadLoader, "status", Loader.Ready)
210 waitForRendering(lockscreen)
211 }
213 function enterPin(pin) {
214 for (var i = 0; i < pin.length; ++i) {
215 var character = pin.charAt(i)
216 var button = findChild(shell, "pinPadButton" + character)
217 tryCompare(button, "enabled", true);
218 tap(button)
219 }
220 }
222 function confirmLockedApp(app) {
223 var greeter = findChild(shell, "greeter")
224 tryCompare(greeter, "shown", false)
225 tryCompare(greeter, "hasLockedApp", true)
226 tryCompare(greeter, "lockedApp", app)
227 tryCompare(LightDM.Greeter, "active", true)
228 tryCompare(ApplicationManager, "focusedApplicationId", app)
229 }
231 function test_greeterChangesIndicatorProfile() {
232 skip("Not supported yet, waiting on design for new settings panel");
234 var panel = findChild(shell, "panel");
235 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
237 LightDM.Greeter.hideGreeter();
238 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
240 LightDM.Greeter.showGreeter();
241 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
243 LightDM.Greeter.hideGreeter();
244 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
245 }
247 function test_login() {
248 sessionSpy.clear()
249 tryCompare(sessionSpy, "count", 0)
250 enterPin("1234")
251 tryCompare(sessionSpy, "count", 1)
252 }
254 function test_edgeDemoHidesLockscreen() {
255 LightDM.Greeter.showGreeter()
256 sessionSpy.clear()
257 var lockscreen = findChild(shell, "lockscreen")
259 tryCompare(lockscreen, "shown", true)
260 AccountsService.demoEdges = true
261 tryCompare(lockscreen, "shown", false)
263 var greeter = findChild(shell, "greeter");
264 swipeAwayGreeter(false);
265 tryCompare(greeter, "shown", false);
266 tryCompare(sessionSpy, "count", 1)
268 // Lockscreen is only hidden by the edge demo, so if we turn that
269 // off and show greeter again, lockscreen should appear
270 AccountsService.demoEdges = false
271 LightDM.Greeter.showGreeter()
272 lockscreen = findChild(shell, "lockscreen");
273 tryCompare(lockscreen, "shown", true)
274 }
276 function test_disabledEdges() {
277 var launcher = findChild(shell, "launcher")
278 tryCompare(launcher, "available", true)
279 AccountsService.enableLauncherWhileLocked = false
280 tryCompare(launcher, "available", false)
282 var indicators = findChild(shell, "indicators")
283 tryCompare(indicators, "available", true)
284 AccountsService.enableIndicatorsWhileLocked = false
285 tryCompare(indicators, "available", false)
286 }
288 function test_emergencyCall() {
289 var greeter = findChild(shell, "greeter")
290 var panel = findChild(shell, "panel")
291 var indicators = findChild(shell, "indicators")
292 var launcher = findChild(shell, "launcher")
293 var stage = findChild(shell, "stage")
295 tap(findChild(greeter, "emergencyCallLabel"));
297 tryCompare(greeter, "lockedApp", "dialer-app")
298 tryCompare(greeter, "hasLockedApp", true)
299 tryCompare(greeter, "shown", false);
300 tryCompare(panel, "fullscreenMode", true)
301 tryCompare(indicators, "available", false)
302 tryCompare(launcher, "available", false)
303 tryCompare(stage, "spreadEnabled", false)
305 // Cancel emergency mode, and go back to normal
306 waitForRendering(greeter)
307 LightDM.Greeter.showGreeter()
309 tryCompare(greeter, "shown", true)
310 tryCompare(greeter, "lockedApp", "")
311 tryCompare(greeter, "hasLockedApp", false)
312 tryCompare(greeter, "fullyShown", true);
313 tryCompare(panel, "fullscreenMode", false)
314 tryCompare(indicators, "available", true)
315 tryCompare(launcher, "available", true)
316 tryCompare(stage, "spreadEnabled", true)
317 }
319 function test_emergencyCallCrash() {
320 var greeter = findChild(shell, "greeter");
321 var emergencyButton = findChild(greeter, "emergencyCallLabel");
322 tap(emergencyButton)
324 tryCompare(greeter, "shown", false);
325 killApps() // kill dialer-app, as if it crashed
326 tryCompare(greeter, "shown", true);
327 tryCompare(findChild(greeter, "lockscreen"), "shown", true);
328 tryCompare(findChild(greeter, "coverPage"), "shown", false);
329 }
331 function test_emergencyCallAppLaunch() {
332 var greeter = findChild(shell, "greeter");
333 var emergencyButton = findChild(greeter, "emergencyCallLabel");
334 tap(emergencyButton)
336 tryCompare(greeter, "shown", false);
337 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
338 tryCompare(greeter, "shown", true);
339 }
341 function test_failedLoginsCount() {
342 AccountsService.failedLogins = 0
344 enterPin("1111")
345 tryCompare(AccountsService, "failedLogins", 1)
347 enterPin("1234")
348 tryCompare(AccountsService, "failedLogins", 0)
349 }
351 function test_wrongEntries() {
352 var greeter = findChild(shell, "greeter");
353 greeter.failedLoginsDelayAttempts = 3;
355 var placeHolder = findChild(shell, "wrongNoticeLabel")
356 tryCompare(placeHolder, "text", "")
358 enterPin("1111")
359 tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
361 enterPin("1111")
362 tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
364 var lockscreen = findChild(shell, "lockscreen")
365 tryCompare(lockscreen, "delayMinutes", 0)
366 enterPin("1111")
367 tryCompare(lockscreen, "delayMinutes", greeter.failedLoginsDelayMinutes);
368 }
370 function test_factoryReset() {
371 maxRetriesTextField.text = "3"
372 resetSpy.clear()
374 enterPin("1111")
375 enterPin("1111")
376 tryCompareFunction(function() {return findChild(root, "infoPopup") !== null}, true)
378 var dialog = findChild(root, "infoPopup")
379 var button = findChild(dialog, "infoPopupOkButton")
380 tap(button)
381 tryCompareFunction(function() {return findChild(root, "infoPopup")}, null)
383 tryCompare(resetSpy, "count", 0)
384 enterPin("1111")
385 tryCompare(resetSpy, "count", 1)
386 }
388 function test_emergencyDialerLockOut() {
389 // This is a theoretical attack on the lockscreen: Enter emergency
390 // dialer mode on a phone, then plug into a larger screen,
391 // switching to a tablet interface. This would in theory move the
392 // dialer to a side stage and give access to other apps. So just
393 // confirm that such an attack doesn't work.
395 var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
397 // We start in phone mode
398 compare(shell.usageScenario, "phone");
399 compare(applicationsDisplayLoader.usageScenario, "phone");
401 var lockscreen = findChild(shell, "lockscreen")
402 lockscreen.emergencyCall()
403 confirmLockedApp("dialer-app")
405 // OK, we're in. Now try (but fail) to switch to tablet mode
406 shell.usageScenario = "tablet";
407 compare(applicationsDisplayLoader.usageScenario, "phone");
409 // And when we kill the app, we go back to locked tablet mode
410 killApps()
411 var greeter = findChild(shell, "greeter")
412 tryCompare(greeter, "fullyShown", true)
413 compare(applicationsDisplayLoader.usageScenario, "tablet");
414 }
416 function test_emergencyDialerIncoming() {
417 callManager.foregroundCall = phoneCall
418 confirmLockedApp("dialer-app")
419 }
421 function test_emergencyDialerActiveCallPanel() {
422 // Make sure that the following sequence works:
423 // - Enter emergency mode call
424 // - Return to greeter
425 // - Click on active call panel
426 // - Should be back in emergency mode dialer
428 var greeter = findChild(shell, "greeter");
429 var lockscreen = findChild(shell, "lockscreen");
431 lockscreen.emergencyCall();
432 confirmLockedApp("dialer-app");
433 callManager.foregroundCall = phoneCall;
435 LightDM.Greeter.showGreeter();
436 tryCompare(lockscreen, "shown", true);
437 tryCompare(greeter, "hasLockedApp", false);
439 // simulate a callHint press, the real thing requires dialer: url support
440 ApplicationManager.requestFocusApplication("dialer-app");
442 confirmLockedApp("dialer-app");
443 }
445 function test_normalDialerActiveCallPanel() {
446 // Make sure that the following sequence works:
447 // - Log in
448 // - Start a call
449 // - Switch apps
450 // - Click on active call panel
451 // - Should be back in normal dialer
452 // (we've had a bug where we locked screen in this case)
454 var greeter = findChild(shell, "greeter");
455 var panel = findChild(shell, "panel");
457 enterPin("1234");
458 tryCompare(greeter, "shown", false);
459 tryCompare(LightDM.Greeter, "active", false);
461 ApplicationManager.startApplication("dialer-app", ApplicationManager.NoFlag);
462 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
463 callManager.foregroundCall = phoneCall;
465 ApplicationManager.requestFocusApplication("unity8-dash");
466 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
467 tryCompare(panel.callHint, "visible", true);
469 // simulate a callHint press, the real thing requires dialer: url support
470 ApplicationManager.requestFocusApplication("dialer-app");
472 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
473 tryCompare(greeter, "shown", false);
474 tryCompare(LightDM.Greeter, "active", false);
475 }
477 function test_focusRequestedHidesCoverPage() {
478 LightDM.Greeter.showGreeter();
480 var app = ApplicationManager.startApplication("gallery-app");
481 // wait until the app is fully loaded (ie, real surface replaces splash screen)
482 tryCompareFunction(function() { return app.session !== null && app.session.surface !== null }, true);
484 // New app hides coverPage?
485 var greeter = findChild(shell, "greeter");
486 var coverPage = testCase.findChild(shell, "coverPage");
487 tryCompare(coverPage, "showProgress", 0);
488 tryCompare(greeter, "fullyShown", true);
490 LightDM.Greeter.showGreeter();
491 tryCompare(coverPage, "showProgress", 1);
493 // Make sure focusing same app triggers same behavior
494 ApplicationManager.requestFocusApplication("gallery-app");
495 tryCompare(coverPage, "showProgress", 0);
496 tryCompare(greeter, "fullyShown", true);
497 }
499 function test_suspend() {
500 var greeter = findChild(shell, "greeter");
502 // Put it to sleep
503 Powerd.status = Powerd.Off;
505 // If locked, ApplicationManager.suspended should be true
506 tryCompare(ApplicationManager, "suspended", true);
508 // And wake up
509 Powerd.status = Powerd.On;
510 tryCompare(greeter, "fullyShown", true);
512 // Swipe away greeter to focus app
513 swipeAwayGreeter(true);
515 // We have a lockscreen, make sure we're still suspended
516 tryCompare(ApplicationManager, "suspended", true);
518 enterPin("1234")
520 // Now that the lockscreen has gone too, make sure we're waking up
521 tryCompare(ApplicationManager, "suspended", false);
523 }
525 /* We had a bug (1395075) where if a user kept swiping as the greeter
526 loaded, they would be able to get into the session before the
527 lockscreen appeared. Make sure that doesn't happen. */
528 function test_earlyDisable() {
529 // Kill current shell
530 shellLoader.itemDestroyed = false;
531 shellLoader.active = false;
532 tryCompare(shellLoader, "itemDestroyed", true);
533 LightDM.Greeter.authenticate(""); // reset greeter
535 // Create new shell
536 promptSpy.clear();
537 shellLoader.active = true;
538 tryCompareFunction(function() {return shell !== null}, true);
540 // Confirm that we start disabled
541 compare(promptSpy.count, 0);
542 verify(!shell.enabled);
544 // And that we only become enabled once the lockscreen is up
545 tryCompare(shell, "enabled", true);
546 verify(promptSpy.count > 0);
547 var lockscreen = findChild(shell, "lockscreen");
548 verify(lockscreen.shown);
549 }
551 /*
552 Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1393447
554 Do a left edge drag that is long enough to start displacing the greeter
555 but short engough so that the greeter comes back into place once the
556 finger is lifted.
558 In this situation the launcher should remaing fully shown and hide itself
559 only after its idle timeout is triggered.
560 */
561 function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
562 LightDM.Greeter.showGreeter();
563 var coverPage = testCase.findChild(shell, "coverPage");
564 tryCompare(coverPage, "showProgress", 1);
566 var launcher = testCase.findChild(shell, "launcher")
567 {
568 var dismissTimer = testCase.findInvisibleChild(launcher, "dismissTimer");
569 // effectively disable the dismiss timer
570 dismissTimer.interval = 24 * 60 * 60 * 1000 // 24 hours
571 }
572 var launcherPanel = testCase.findChild(launcher, "launcherPanel");
574 var toX = shell.width * 0.45;
575 touchFlick(shell,
576 1 /* fromX */, shell.height * 0.5 /* fromY */,
577 toX /* toX */, shell.height * 0.5 /* toY */,
578 true /* beginTouch */, false /* endTouch */,
579 50, 100);
581 // Launcher must be fully shown by now
582 tryCompare(launcherPanel, "x", 0);
584 // Greeter should be displaced
585 tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
587 touchRelease(shell, toX, shell.height * 0.5);
589 // Upon release the greeter should have slid back into full view
590 tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x === 0; }, true);
592 // And the launcher should stay fully shown
593 for (var i = 0; i < 10; ++i) {
594 wait(50);
595 compare(launcherPanel.x, 0);
596 }
597 }
599 function test_longLeftEdgeDrags() {
600 var coverPage = findChild(shell, "coverPage");
601 var lockscreen = findChild(shell, "lockscreen");
602 var launcher = findChild(shell, "launcherPanel");
603 ApplicationManager.startApplication("gallery-app");
604 tryCompare(ApplicationManager, "focusedApplicationId", "gallery-app");
606 // Show greeter
607 LightDM.Greeter.showGreeter();
608 tryCompare(coverPage, "showProgress", 1);
610 // Swipe cover page away
611 touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
612 tryCompare(launcher, "x", -launcher.width);
613 tryCompare(coverPage, "showProgress", 0);
614 compare(lockscreen.shown, true);
615 compare(ApplicationManager.focusedApplicationId, "gallery-app");
617 // Now attempt a swipe on lockscreen
618 touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
619 tryCompare(launcher, "x", 0);
620 compare(lockscreen.shown, true);
621 compare(ApplicationManager.focusedApplicationId, "gallery-app");
622 }
623 }