1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.authentication.domain.interactor 18 19 import android.app.admin.DevicePolicyManager 20 import androidx.test.filters.SmallTest 21 import com.android.systemui.SysuiTestCase 22 import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel 23 import com.android.systemui.authentication.data.repository.AuthenticationRepository 24 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository 25 import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel 26 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate 27 import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel 28 import com.android.systemui.coroutines.collectLastValue 29 import com.android.systemui.scene.SceneTestUtils 30 import com.android.systemui.scene.shared.model.SceneKey 31 import com.android.systemui.scene.shared.model.SceneModel 32 import com.google.common.truth.Truth.assertThat 33 import kotlin.time.Duration.Companion.milliseconds 34 import kotlin.time.Duration.Companion.seconds 35 import kotlinx.coroutines.ExperimentalCoroutinesApi 36 import kotlinx.coroutines.test.TestScope 37 import kotlinx.coroutines.test.advanceTimeBy 38 import kotlinx.coroutines.test.runCurrent 39 import kotlinx.coroutines.test.runTest 40 import org.junit.Test 41 import org.junit.runner.RunWith 42 import org.junit.runners.JUnit4 43 44 @OptIn(ExperimentalCoroutinesApi::class) 45 @SmallTest 46 @RunWith(JUnit4::class) 47 class AuthenticationInteractorTest : SysuiTestCase() { 48 49 private val utils = SceneTestUtils(this) 50 private val testScope = utils.testScope 51 private val repository: AuthenticationRepository = utils.authenticationRepository() 52 private val sceneInteractor = utils.sceneInteractor() 53 private val underTest = 54 utils.authenticationInteractor( 55 repository = repository, 56 sceneInteractor = sceneInteractor, 57 ) 58 59 @Test 60 fun authenticationMethod() = 61 testScope.runTest { 62 val authMethod by collectLastValue(underTest.authenticationMethod) 63 runCurrent() 64 assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Pin) 65 assertThat(underTest.getAuthenticationMethod()) 66 .isEqualTo(DomainLayerAuthenticationMethodModel.Pin) 67 68 utils.authenticationRepository.setAuthenticationMethod( 69 DataLayerAuthenticationMethodModel.Password 70 ) 71 72 assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Password) 73 assertThat(underTest.getAuthenticationMethod()) 74 .isEqualTo(DomainLayerAuthenticationMethodModel.Password) 75 } 76 77 @Test 78 fun authenticationMethod_noneTreatedAsSwipe_whenLockscreenEnabled() = 79 testScope.runTest { 80 val authMethod by collectLastValue(underTest.authenticationMethod) 81 runCurrent() 82 83 utils.authenticationRepository.apply { 84 setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) 85 setLockscreenEnabled(true) 86 } 87 88 assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Swipe) 89 assertThat(underTest.getAuthenticationMethod()) 90 .isEqualTo(DomainLayerAuthenticationMethodModel.Swipe) 91 } 92 93 @Test 94 fun authenticationMethod_none_whenLockscreenDisabled() = 95 testScope.runTest { 96 val authMethod by collectLastValue(underTest.authenticationMethod) 97 runCurrent() 98 99 utils.authenticationRepository.apply { 100 setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) 101 setLockscreenEnabled(false) 102 } 103 104 assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.None) 105 assertThat(underTest.getAuthenticationMethod()) 106 .isEqualTo(DomainLayerAuthenticationMethodModel.None) 107 } 108 109 @Test 110 fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() = 111 testScope.runTest { 112 val isUnlocked by collectLastValue(underTest.isUnlocked) 113 utils.authenticationRepository.apply { 114 setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) 115 setLockscreenEnabled(false) 116 // Toggle isUnlocked, twice. 117 // 118 // This is done because the underTest.isUnlocked flow doesn't receive values from 119 // just changing the state above; the actual isUnlocked state needs to change to 120 // cause the logic under test to "pick up" the current state again. 121 // 122 // It is done twice to make sure that we don't actually change the isUnlocked state 123 // from what it originally was. 124 setUnlocked(!utils.authenticationRepository.isUnlocked.value) 125 runCurrent() 126 setUnlocked(!utils.authenticationRepository.isUnlocked.value) 127 runCurrent() 128 } 129 130 assertThat(isUnlocked).isTrue() 131 } 132 133 @Test 134 fun isUnlocked_whenAuthMethodIsNoneAndLockscreenEnabled_isTrue() = 135 testScope.runTest { 136 utils.authenticationRepository.apply { 137 setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) 138 setLockscreenEnabled(true) 139 } 140 141 val isUnlocked by collectLastValue(underTest.isUnlocked) 142 assertThat(isUnlocked).isTrue() 143 } 144 145 @Test 146 fun canSwipeToDismiss_onLockscreenWithSwipe_isTrue() = 147 testScope.runTest { 148 utils.authenticationRepository.apply { 149 setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) 150 setLockscreenEnabled(true) 151 } 152 switchToScene(SceneKey.Lockscreen) 153 154 val canSwipeToDismiss by collectLastValue(underTest.canSwipeToDismiss) 155 assertThat(canSwipeToDismiss).isTrue() 156 } 157 158 @Test 159 fun canSwipeToDismiss_onLockscreenWithPin_isFalse() = 160 testScope.runTest { 161 utils.authenticationRepository.apply { 162 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 163 setLockscreenEnabled(true) 164 } 165 switchToScene(SceneKey.Lockscreen) 166 167 val canSwipeToDismiss by collectLastValue(underTest.canSwipeToDismiss) 168 assertThat(canSwipeToDismiss).isFalse() 169 } 170 171 @Test 172 fun canSwipeToDismiss_afterLockscreenDismissedInSwipeMode_isFalse() = 173 testScope.runTest { 174 utils.authenticationRepository.apply { 175 setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) 176 setLockscreenEnabled(true) 177 } 178 switchToScene(SceneKey.Lockscreen) 179 switchToScene(SceneKey.Gone) 180 181 val canSwipeToDismiss by collectLastValue(underTest.canSwipeToDismiss) 182 assertThat(canSwipeToDismiss).isFalse() 183 } 184 185 @Test 186 fun isAuthenticationRequired_lockedAndSecured_true() = 187 testScope.runTest { 188 utils.authenticationRepository.apply { 189 setUnlocked(false) 190 runCurrent() 191 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Password) 192 } 193 194 assertThat(underTest.isAuthenticationRequired()).isTrue() 195 } 196 197 @Test 198 fun isAuthenticationRequired_lockedAndNotSecured_false() = 199 testScope.runTest { 200 utils.authenticationRepository.apply { 201 setUnlocked(false) 202 runCurrent() 203 setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) 204 } 205 206 assertThat(underTest.isAuthenticationRequired()).isFalse() 207 } 208 209 @Test 210 fun isAuthenticationRequired_unlockedAndSecured_false() = 211 testScope.runTest { 212 utils.authenticationRepository.apply { 213 setUnlocked(true) 214 runCurrent() 215 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Password) 216 } 217 218 assertThat(underTest.isAuthenticationRequired()).isFalse() 219 } 220 221 @Test 222 fun isAuthenticationRequired_unlockedAndNotSecured_false() = 223 testScope.runTest { 224 utils.authenticationRepository.apply { 225 setUnlocked(true) 226 runCurrent() 227 setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) 228 } 229 230 assertThat(underTest.isAuthenticationRequired()).isFalse() 231 } 232 233 @Test 234 fun authenticate_withCorrectPin_returnsTrue() = 235 testScope.runTest { 236 val isThrottled by collectLastValue(underTest.isThrottled) 237 utils.authenticationRepository.setAuthenticationMethod( 238 DataLayerAuthenticationMethodModel.Pin 239 ) 240 assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() 241 assertThat(isThrottled).isFalse() 242 } 243 244 @Test 245 fun authenticate_withIncorrectPin_returnsFalse() = 246 testScope.runTest { 247 utils.authenticationRepository.setAuthenticationMethod( 248 DataLayerAuthenticationMethodModel.Pin 249 ) 250 assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4))).isFalse() 251 } 252 253 @Test(expected = IllegalArgumentException::class) 254 fun authenticate_withEmptyPin_throwsException() = 255 testScope.runTest { 256 utils.authenticationRepository.setAuthenticationMethod( 257 DataLayerAuthenticationMethodModel.Pin 258 ) 259 underTest.authenticate(listOf()) 260 } 261 262 @Test 263 fun authenticate_withCorrectMaxLengthPin_returnsTrue() = 264 testScope.runTest { 265 val pin = List(16) { 9 } 266 utils.authenticationRepository.apply { 267 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 268 overrideCredential(pin) 269 } 270 271 assertThat(underTest.authenticate(pin)).isTrue() 272 } 273 274 @Test 275 fun authenticate_withCorrectTooLongPin_returnsFalse() = 276 testScope.runTest { 277 // Max pin length is 16 digits. To avoid issues with overflows, this test ensures 278 // that all pins > 16 decimal digits are rejected. 279 280 // If the policy changes, there is work to do in SysUI. 281 assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17) 282 283 utils.authenticationRepository.setAuthenticationMethod( 284 DataLayerAuthenticationMethodModel.Pin 285 ) 286 assertThat(underTest.authenticate(List(17) { 9 })).isFalse() 287 } 288 289 @Test 290 fun authenticate_withCorrectPassword_returnsTrue() = 291 testScope.runTest { 292 val isThrottled by collectLastValue(underTest.isThrottled) 293 utils.authenticationRepository.setAuthenticationMethod( 294 DataLayerAuthenticationMethodModel.Password 295 ) 296 297 assertThat(underTest.authenticate("password".toList())).isTrue() 298 assertThat(isThrottled).isFalse() 299 } 300 301 @Test 302 fun authenticate_withIncorrectPassword_returnsFalse() = 303 testScope.runTest { 304 utils.authenticationRepository.setAuthenticationMethod( 305 DataLayerAuthenticationMethodModel.Password 306 ) 307 308 assertThat(underTest.authenticate("alohomora".toList())).isFalse() 309 } 310 311 @Test 312 fun authenticate_withCorrectPattern_returnsTrue() = 313 testScope.runTest { 314 utils.authenticationRepository.setAuthenticationMethod( 315 DataLayerAuthenticationMethodModel.Pattern 316 ) 317 318 assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)).isTrue() 319 } 320 321 @Test 322 fun authenticate_withIncorrectPattern_returnsFalse() = 323 testScope.runTest { 324 utils.authenticationRepository.setAuthenticationMethod( 325 DataLayerAuthenticationMethodModel.Pattern 326 ) 327 328 assertThat( 329 underTest.authenticate( 330 listOf( 331 AuthenticationPatternCoordinate( 332 x = 2, 333 y = 0, 334 ), 335 AuthenticationPatternCoordinate( 336 x = 2, 337 y = 1, 338 ), 339 AuthenticationPatternCoordinate( 340 x = 2, 341 y = 2, 342 ), 343 ) 344 ) 345 ) 346 .isFalse() 347 } 348 349 @Test 350 fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() = 351 testScope.runTest { 352 val isThrottled by collectLastValue(underTest.isThrottled) 353 utils.authenticationRepository.apply { 354 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 355 setAutoConfirmEnabled(true) 356 } 357 assertThat( 358 underTest.authenticate( 359 FakeAuthenticationRepository.DEFAULT_PIN.toMutableList().apply { 360 removeLast() 361 }, 362 tryAutoConfirm = true 363 ) 364 ) 365 .isNull() 366 assertThat(isThrottled).isFalse() 367 } 368 369 @Test 370 fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() = 371 testScope.runTest { 372 val isUnlocked by collectLastValue(underTest.isUnlocked) 373 utils.authenticationRepository.apply { 374 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 375 setAutoConfirmEnabled(true) 376 } 377 assertThat( 378 underTest.authenticate( 379 FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }, 380 tryAutoConfirm = true 381 ) 382 ) 383 .isFalse() 384 assertThat(isUnlocked).isFalse() 385 } 386 387 @Test 388 fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() = 389 testScope.runTest { 390 val isUnlocked by collectLastValue(underTest.isUnlocked) 391 utils.authenticationRepository.apply { 392 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 393 setAutoConfirmEnabled(true) 394 } 395 assertThat( 396 underTest.authenticate( 397 FakeAuthenticationRepository.DEFAULT_PIN + listOf(7), 398 tryAutoConfirm = true 399 ) 400 ) 401 .isFalse() 402 assertThat(isUnlocked).isFalse() 403 } 404 405 @Test 406 fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() = 407 testScope.runTest { 408 val isUnlocked by collectLastValue(underTest.isUnlocked) 409 utils.authenticationRepository.apply { 410 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 411 setAutoConfirmEnabled(true) 412 } 413 assertThat( 414 underTest.authenticate( 415 FakeAuthenticationRepository.DEFAULT_PIN, 416 tryAutoConfirm = true 417 ) 418 ) 419 .isTrue() 420 assertThat(isUnlocked).isTrue() 421 } 422 423 @Test 424 fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() = 425 testScope.runTest { 426 val isUnlocked by collectLastValue(underTest.isUnlocked) 427 utils.authenticationRepository.apply { 428 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 429 setAutoConfirmEnabled(false) 430 } 431 assertThat( 432 underTest.authenticate( 433 FakeAuthenticationRepository.DEFAULT_PIN, 434 tryAutoConfirm = true 435 ) 436 ) 437 .isNull() 438 assertThat(isUnlocked).isFalse() 439 } 440 441 @Test 442 fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() = 443 testScope.runTest { 444 val isUnlocked by collectLastValue(underTest.isUnlocked) 445 utils.authenticationRepository.setAuthenticationMethod( 446 DataLayerAuthenticationMethodModel.Password 447 ) 448 449 assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull() 450 assertThat(isUnlocked).isFalse() 451 } 452 453 @Test 454 fun throttling() = 455 testScope.runTest { 456 val isUnlocked by collectLastValue(underTest.isUnlocked) 457 val throttling by collectLastValue(underTest.throttling) 458 val isThrottled by collectLastValue(underTest.isThrottled) 459 utils.authenticationRepository.setAuthenticationMethod( 460 DataLayerAuthenticationMethodModel.Pin 461 ) 462 underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) 463 assertThat(isUnlocked).isTrue() 464 assertThat(isThrottled).isFalse() 465 assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) 466 467 utils.authenticationRepository.setUnlocked(false) 468 assertThat(isUnlocked).isFalse() 469 assertThat(isThrottled).isFalse() 470 assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) 471 472 // Make many wrong attempts, but just shy of what's needed to get throttled: 473 repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1) { 474 underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN 475 assertThat(isUnlocked).isFalse() 476 assertThat(isThrottled).isFalse() 477 assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) 478 } 479 480 // Make one more wrong attempt, leading to throttling: 481 underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN 482 assertThat(isUnlocked).isFalse() 483 assertThat(isThrottled).isTrue() 484 assertThat(throttling) 485 .isEqualTo( 486 AuthenticationThrottlingModel( 487 failedAttemptCount = 488 FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, 489 remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS, 490 ) 491 ) 492 493 // Correct PIN, but throttled, so doesn't attempt it: 494 assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isNull() 495 assertThat(isUnlocked).isFalse() 496 assertThat(isThrottled).isTrue() 497 assertThat(throttling) 498 .isEqualTo( 499 AuthenticationThrottlingModel( 500 failedAttemptCount = 501 FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, 502 remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS, 503 ) 504 ) 505 506 // Move the clock forward to ALMOST skip the throttling, leaving one second to go: 507 val throttleTimeoutSec = 508 FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds 509 .toInt() 510 repeat(throttleTimeoutSec - 1) { time -> 511 advanceTimeBy(1000) 512 assertThat(isThrottled).isTrue() 513 assertThat(throttling) 514 .isEqualTo( 515 AuthenticationThrottlingModel( 516 failedAttemptCount = 517 FakeAuthenticationRepository 518 .MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, 519 remainingMs = 520 ((throttleTimeoutSec - (time + 1)).seconds.inWholeMilliseconds) 521 .toInt(), 522 ) 523 ) 524 } 525 526 // Move the clock forward one more second, to completely finish the throttling period: 527 advanceTimeBy(1000) 528 assertThat(isUnlocked).isFalse() 529 assertThat(isThrottled).isFalse() 530 assertThat(throttling) 531 .isEqualTo( 532 AuthenticationThrottlingModel( 533 failedAttemptCount = 534 FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, 535 remainingMs = 0, 536 ) 537 ) 538 539 // Correct PIN and no longer throttled so unlocks successfully: 540 assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() 541 assertThat(isUnlocked).isTrue() 542 assertThat(isThrottled).isFalse() 543 assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) 544 } 545 546 @Test 547 fun hintedPinLength_withoutAutoConfirm_isNull() = 548 testScope.runTest { 549 val hintedPinLength by collectLastValue(underTest.hintedPinLength) 550 utils.authenticationRepository.apply { 551 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 552 setAutoConfirmEnabled(false) 553 } 554 555 assertThat(hintedPinLength).isNull() 556 } 557 558 @Test 559 fun hintedPinLength_withAutoConfirmPinTooShort_isNull() = 560 testScope.runTest { 561 val hintedPinLength by collectLastValue(underTest.hintedPinLength) 562 utils.authenticationRepository.apply { 563 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 564 overrideCredential( 565 buildList { 566 repeat(utils.authenticationRepository.hintedPinLength - 1) { add(it + 1) } 567 } 568 ) 569 setAutoConfirmEnabled(true) 570 } 571 572 assertThat(hintedPinLength).isNull() 573 } 574 575 @Test 576 fun hintedPinLength_withAutoConfirmPinAtRightLength_isSameLength() = 577 testScope.runTest { 578 val hintedPinLength by collectLastValue(underTest.hintedPinLength) 579 utils.authenticationRepository.apply { 580 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 581 setAutoConfirmEnabled(true) 582 overrideCredential( 583 buildList { 584 repeat(utils.authenticationRepository.hintedPinLength) { add(it + 1) } 585 } 586 ) 587 } 588 589 assertThat(hintedPinLength).isEqualTo(utils.authenticationRepository.hintedPinLength) 590 } 591 592 @Test 593 fun hintedPinLength_withAutoConfirmPinTooLong_isNull() = 594 testScope.runTest { 595 val hintedPinLength by collectLastValue(underTest.hintedPinLength) 596 utils.authenticationRepository.apply { 597 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) 598 overrideCredential( 599 buildList { 600 repeat(utils.authenticationRepository.hintedPinLength + 1) { add(it + 1) } 601 } 602 ) 603 setAutoConfirmEnabled(true) 604 } 605 606 assertThat(hintedPinLength).isNull() 607 } 608 609 @Test 610 fun isLockscreenDismissed() = 611 testScope.runTest { 612 val isLockscreenDismissed by collectLastValue(underTest.isLockscreenDismissed) 613 // Start on lockscreen. 614 switchToScene(SceneKey.Lockscreen) 615 assertThat(isLockscreenDismissed).isFalse() 616 617 // The user swipes down to reveal shade. 618 switchToScene(SceneKey.Shade) 619 assertThat(isLockscreenDismissed).isFalse() 620 621 // The user swipes down to reveal quick settings. 622 switchToScene(SceneKey.QuickSettings) 623 assertThat(isLockscreenDismissed).isFalse() 624 625 // The user swipes up to go back to shade. 626 switchToScene(SceneKey.Shade) 627 assertThat(isLockscreenDismissed).isFalse() 628 629 // The user swipes up to reveal bouncer. 630 switchToScene(SceneKey.Bouncer) 631 assertThat(isLockscreenDismissed).isFalse() 632 633 // The user hits back to return to lockscreen. 634 switchToScene(SceneKey.Lockscreen) 635 assertThat(isLockscreenDismissed).isFalse() 636 637 // The user swipes up to reveal bouncer. 638 switchToScene(SceneKey.Bouncer) 639 assertThat(isLockscreenDismissed).isFalse() 640 641 // The user enters correct credentials and goes to gone. 642 switchToScene(SceneKey.Gone) 643 assertThat(isLockscreenDismissed).isTrue() 644 645 // The user swipes down to reveal shade. 646 switchToScene(SceneKey.Shade) 647 assertThat(isLockscreenDismissed).isTrue() 648 649 // The user swipes down to reveal quick settings. 650 switchToScene(SceneKey.QuickSettings) 651 assertThat(isLockscreenDismissed).isTrue() 652 653 // The user swipes up to go back to shade. 654 switchToScene(SceneKey.Shade) 655 assertThat(isLockscreenDismissed).isTrue() 656 657 // The user swipes up to go back to gone. 658 switchToScene(SceneKey.Gone) 659 assertThat(isLockscreenDismissed).isTrue() 660 661 // The device goes to sleep, returning to the lockscreen. 662 switchToScene(SceneKey.Lockscreen) 663 assertThat(isLockscreenDismissed).isFalse() 664 } 665 666 private fun TestScope.switchToScene(sceneKey: SceneKey) { 667 val model = SceneModel(sceneKey) 668 val loggingReason = "reason" 669 sceneInteractor.changeScene(model, loggingReason) 670 sceneInteractor.onSceneChanged(model, loggingReason) 671 runCurrent() 672 } 673 } 674