Loading packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt +45 −17 Original line number Diff line number Diff line Loading @@ -35,8 +35,10 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobile import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.function.Function import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runCurrent Loading @@ -58,6 +60,7 @@ class AuthenticationRepositoryTest : SysuiTestCase() { private val testUtils = SceneTestUtils(this) private val testScope = testUtils.testScope private val clock = FakeSystemClock() private val userRepository = FakeUserRepository() private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository Loading @@ -78,8 +81,10 @@ class AuthenticationRepositoryTest : SysuiTestCase() { underTest = AuthenticationRepositoryImpl( applicationScope = testScope.backgroundScope, getSecurityMode = getSecurityMode, backgroundDispatcher = testUtils.testDispatcher, flags = testUtils.sceneContainerFlags, clock = clock, getSecurityMode = getSecurityMode, userRepository = userRepository, lockPatternUtils = lockPatternUtils, broadcastDispatcher = fakeBroadcastDispatcher, Loading Loading @@ -140,22 +145,6 @@ class AuthenticationRepositoryTest : SysuiTestCase() { assertThat(values.last()).isTrue() } @Test fun reportAuthenticationAttempt_emitsAuthenticationChallengeResult() = testScope.runTest { val authenticationChallengeResults by collectValues(underTest.authenticationChallengeResult) runCurrent() underTest.reportAuthenticationAttempt(true) runCurrent() underTest.reportAuthenticationAttempt(false) runCurrent() underTest.reportAuthenticationAttempt(true) assertThat(authenticationChallengeResults).isEqualTo(listOf(true, false, true)) } @Test fun isPinEnhancedPrivacyEnabled() = testScope.runTest { Loading @@ -172,6 +161,45 @@ class AuthenticationRepositoryTest : SysuiTestCase() { assertThat(values.last()).isTrue() } @Test fun lockoutEndTimestamp() = testScope.runTest { val lockoutEndMs = clock.elapsedRealtime() + 30.seconds.inWholeMilliseconds whenever(lockPatternUtils.getLockoutAttemptDeadline(USER_INFOS[0].id)) .thenReturn(lockoutEndMs) whenever(lockPatternUtils.getLockoutAttemptDeadline(USER_INFOS[1].id)).thenReturn(0) // Switch to a user who is not locked-out. userRepository.setSelectedUserInfo(USER_INFOS[1]) assertThat(underTest.lockoutEndTimestamp).isNull() // Switch back to the locked-out user, verify the timestamp is up-to-date. userRepository.setSelectedUserInfo(USER_INFOS[0]) assertThat(underTest.lockoutEndTimestamp).isEqualTo(lockoutEndMs) // After the lockout expires, null is returned. clock.setElapsedRealtime(lockoutEndMs) assertThat(underTest.lockoutEndTimestamp).isNull() } @Test fun hasLockoutOccurred() = testScope.runTest { val hasLockoutOccurred by collectLastValue(underTest.hasLockoutOccurred) assertThat(hasLockoutOccurred).isFalse() underTest.reportLockoutStarted(1000) assertThat(hasLockoutOccurred).isTrue() clock.setElapsedRealtime(clock.elapsedRealtime() + 60.seconds.inWholeMilliseconds) underTest.reportAuthenticationAttempt(isSuccessful = false) assertThat(hasLockoutOccurred).isTrue() underTest.reportAuthenticationAttempt(isSuccessful = true) assertThat(hasLockoutOccurred).isFalse() } private fun setSecurityModeAndDispatchBroadcast( securityMode: KeyguardSecurityModel.SecurityMode, ) { Loading packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +184 −187 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +28 −52 Original line number Diff line number Diff line Loading @@ -21,15 +21,15 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.AuthenticationResult import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent Loading Loading @@ -79,7 +79,7 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) runCurrent() underTest.clearMessage() assertThat(message).isEmpty() assertThat(message).isNull() underTest.resetMessage() assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) Loading Loading @@ -149,7 +149,7 @@ class BouncerInteractorTest : SysuiTestCase() { // Incomplete input. assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)) .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEmpty() assertThat(message).isNull() // Correct input. assertThat( Loading @@ -159,7 +159,7 @@ class BouncerInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEmpty() assertThat(message).isNull() } @Test Loading Loading @@ -246,57 +246,40 @@ class BouncerInteractorTest : SysuiTestCase() { } @Test fun lockout() = fun lockoutStarted() = testScope.runTest { val lockout by collectLastValue(underTest.lockout) val lockoutStartedEvents by collectValues(underTest.onLockoutStarted) val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(lockout).isNull() assertThat(lockoutStartedEvents).isEmpty() // Try the wrong PIN repeatedly, until lockout is triggered: repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { times -> // Wrong PIN. assertThat(underTest.authenticate(listOf(6, 7, 8, 9))) .isEqualTo(AuthenticationResult.FAILED) if (times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) { assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) assertThat(lockoutStartedEvents).isEmpty() assertThat(message).isNotEmpty() } } assertThat(lockout) .isEqualTo( AuthenticationLockoutModel( failedAttemptCount = FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT, remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS, ) ) assertTryAgainMessage( message, FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt() ) // Correct PIN, but locked out, so doesn't change away from the bouncer scene: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SKIPPED) assertTryAgainMessage( message, FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt() ) lockout?.remainingSeconds?.let { seconds -> repeat(seconds) { time -> advanceTimeBy(1000) val remainingTimeSec = seconds - time - 1 if (remainingTimeSec > 0) { assertTryAgainMessage(message, remainingTimeSec) } assertThat(authenticationInteractor.lockoutEndTimestamp).isNotNull() assertThat(lockoutStartedEvents.size).isEqualTo(1) assertThat(message).isNull() // Advance the time to finish the lockout: advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS.seconds) assertThat(authenticationInteractor.lockoutEndTimestamp).isNull() assertThat(message).isNull() assertThat(lockoutStartedEvents.size).isEqualTo(1) // Trigger lockout again: repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. underTest.authenticate(listOf(6, 7, 8, 9)) } } assertThat(message).isEqualTo("") assertThat(lockout).isNull() // Correct PIN and no longer locked out so changes to the Gone scene: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(lockout).isNull() assertThat(lockoutStartedEvents.size).isEqualTo(2) } @Test Loading Loading @@ -326,13 +309,6 @@ class BouncerInteractorTest : SysuiTestCase() { verify(keyguardFaceAuthInteractor).onPrimaryBouncerUserInput() } private fun assertTryAgainMessage( message: String?, time: Int, ) { assertThat(message).isEqualTo("Try again in $time seconds.") } companion object { private const val MESSAGE_ENTER_YOUR_PIN = "Enter your PIN" private const val MESSAGE_ENTER_YOUR_PASSWORD = "Enter your password" Loading packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +51 −13 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.currentTime import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test Loading Loading @@ -135,19 +136,47 @@ class BouncerViewModelTest : SysuiTestCase() { fun message() = testScope.runTest { val message by collectLastValue(underTest.message) val lockout by collectLastValue(bouncerInteractor.lockout) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(message?.isUpdateAnimated).isTrue() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) bouncerInteractor.authenticate(WRONG_PIN) } assertThat(message?.isUpdateAnimated).isFalse() lockout?.remainingSeconds?.let { remainingSeconds -> advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds) val lockoutEndMs = authenticationInteractor.lockoutEndTimestamp ?: 0 advanceTimeBy(lockoutEndMs - testScope.currentTime) assertThat(message?.isUpdateAnimated).isTrue() } @Test fun lockoutMessage() = testScope.runTest { val authMethodViewModel by collectLastValue(underTest.authMethodViewModel) val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(utils.authenticationRepository.lockoutEndTimestamp).isNull() assertThat(authMethodViewModel?.lockoutMessageId).isNotNull() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { times -> bouncerInteractor.authenticate(WRONG_PIN) if (times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) { assertThat(message?.text).isEqualTo(bouncerInteractor.message.value) assertThat(message?.isUpdateAnimated).isTrue() } } val lockoutSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS assertTryAgainMessage(message?.text, lockoutSeconds) assertThat(message?.isUpdateAnimated).isFalse() repeat(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS) { time -> advanceTimeBy(1.seconds) val remainingSeconds = lockoutSeconds - time - 1 if (remainingSeconds > 0) { assertTryAgainMessage(message?.text, remainingSeconds) } } assertThat(message?.text).isEmpty() assertThat(message?.isUpdateAnimated).isTrue() } Loading @@ -160,32 +189,30 @@ class BouncerViewModelTest : SysuiTestCase() { authViewModel?.isInputEnabled ?: emptyFlow() } ) val lockout by collectLastValue(bouncerInteractor.lockout) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(isInputEnabled).isTrue() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) bouncerInteractor.authenticate(WRONG_PIN) } assertThat(isInputEnabled).isFalse() lockout?.remainingSeconds?.let { remainingSeconds -> advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds) } val lockoutEndMs = authenticationInteractor.lockoutEndTimestamp ?: 0 advanceTimeBy(lockoutEndMs - testScope.currentTime) assertThat(isInputEnabled).isTrue() } @Test fun dialogMessage() = testScope.runTest { val authMethodViewModel by collectLastValue(underTest.authMethodViewModel) val dialogMessage by collectLastValue(underTest.dialogMessage) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(authMethodViewModel?.lockoutMessageId).isNotNull() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. assertThat(dialogMessage).isNull() bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) bouncerInteractor.authenticate(WRONG_PIN) } assertThat(dialogMessage).isNotEmpty() Loading Loading @@ -241,4 +268,15 @@ class BouncerViewModelTest : SysuiTestCase() { AuthenticationMethodModel.Sim, ) } private fun assertTryAgainMessage( message: String?, time: Int, ) { assertThat(message).isEqualTo("Try again in $time seconds.") } companion object { private val WRONG_PIN = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 } } } packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +12 −21 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues Loading @@ -28,6 +27,7 @@ import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow Loading @@ -45,11 +45,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val authenticationRepository = utils.authenticationRepository private val authenticationInteractor = utils.authenticationInteractor( repository = authenticationRepository, ) private val authenticationInteractor = utils.authenticationInteractor() private val sceneInteractor = utils.sceneInteractor() private val bouncerInteractor = utils.bouncerInteractor( Loading @@ -61,12 +57,13 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { authenticationInteractor = authenticationInteractor, actionButtonInteractor = utils.bouncerActionButtonInteractor(), ) private val isInputEnabled = MutableStateFlow(true) private val underTest = PasswordBouncerViewModel( viewModelScope = testScope.backgroundScope, interactor = bouncerInteractor, isInputEnabled = MutableStateFlow(true).asStateFlow(), isInputEnabled.asStateFlow(), ) @Before Loading Loading @@ -123,8 +120,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateKeyPressed_whenCorrect() = testScope.runTest { val authResult by collectLastValue(authenticationInteractor.authenticationChallengeResult) val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult) lockDeviceAndOpenPasswordBouncer() underTest.onPasswordInputChanged("password") Loading Loading @@ -169,8 +165,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateKeyPressed_correctAfterWrong() = testScope.runTest { val authResult by collectLastValue(authenticationInteractor.authenticationChallengeResult) val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) lockDeviceAndOpenPasswordBouncer() Loading Loading @@ -333,19 +328,15 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { ) { if (isLockedOut) { repeat(failedAttemptCount) { authenticationRepository.reportAuthenticationAttempt(false) utils.authenticationRepository.reportAuthenticationAttempt(false) } val remainingTimeSeconds = 30 authenticationRepository.setLockoutDuration(remainingTimeSeconds * 1000) authenticationRepository.lockout.value = AuthenticationLockoutModel( failedAttemptCount = failedAttemptCount, remainingSeconds = remainingTimeSeconds, utils.authenticationRepository.reportLockoutStarted( 30.seconds.inWholeMilliseconds.toInt() ) } else { authenticationRepository.reportAuthenticationAttempt(true) authenticationRepository.lockout.value = null utils.authenticationRepository.reportAuthenticationAttempt(true) } isInputEnabled.value = !isLockedOut runCurrent() } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt +45 −17 Original line number Diff line number Diff line Loading @@ -35,8 +35,10 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobile import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.function.Function import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runCurrent Loading @@ -58,6 +60,7 @@ class AuthenticationRepositoryTest : SysuiTestCase() { private val testUtils = SceneTestUtils(this) private val testScope = testUtils.testScope private val clock = FakeSystemClock() private val userRepository = FakeUserRepository() private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository Loading @@ -78,8 +81,10 @@ class AuthenticationRepositoryTest : SysuiTestCase() { underTest = AuthenticationRepositoryImpl( applicationScope = testScope.backgroundScope, getSecurityMode = getSecurityMode, backgroundDispatcher = testUtils.testDispatcher, flags = testUtils.sceneContainerFlags, clock = clock, getSecurityMode = getSecurityMode, userRepository = userRepository, lockPatternUtils = lockPatternUtils, broadcastDispatcher = fakeBroadcastDispatcher, Loading Loading @@ -140,22 +145,6 @@ class AuthenticationRepositoryTest : SysuiTestCase() { assertThat(values.last()).isTrue() } @Test fun reportAuthenticationAttempt_emitsAuthenticationChallengeResult() = testScope.runTest { val authenticationChallengeResults by collectValues(underTest.authenticationChallengeResult) runCurrent() underTest.reportAuthenticationAttempt(true) runCurrent() underTest.reportAuthenticationAttempt(false) runCurrent() underTest.reportAuthenticationAttempt(true) assertThat(authenticationChallengeResults).isEqualTo(listOf(true, false, true)) } @Test fun isPinEnhancedPrivacyEnabled() = testScope.runTest { Loading @@ -172,6 +161,45 @@ class AuthenticationRepositoryTest : SysuiTestCase() { assertThat(values.last()).isTrue() } @Test fun lockoutEndTimestamp() = testScope.runTest { val lockoutEndMs = clock.elapsedRealtime() + 30.seconds.inWholeMilliseconds whenever(lockPatternUtils.getLockoutAttemptDeadline(USER_INFOS[0].id)) .thenReturn(lockoutEndMs) whenever(lockPatternUtils.getLockoutAttemptDeadline(USER_INFOS[1].id)).thenReturn(0) // Switch to a user who is not locked-out. userRepository.setSelectedUserInfo(USER_INFOS[1]) assertThat(underTest.lockoutEndTimestamp).isNull() // Switch back to the locked-out user, verify the timestamp is up-to-date. userRepository.setSelectedUserInfo(USER_INFOS[0]) assertThat(underTest.lockoutEndTimestamp).isEqualTo(lockoutEndMs) // After the lockout expires, null is returned. clock.setElapsedRealtime(lockoutEndMs) assertThat(underTest.lockoutEndTimestamp).isNull() } @Test fun hasLockoutOccurred() = testScope.runTest { val hasLockoutOccurred by collectLastValue(underTest.hasLockoutOccurred) assertThat(hasLockoutOccurred).isFalse() underTest.reportLockoutStarted(1000) assertThat(hasLockoutOccurred).isTrue() clock.setElapsedRealtime(clock.elapsedRealtime() + 60.seconds.inWholeMilliseconds) underTest.reportAuthenticationAttempt(isSuccessful = false) assertThat(hasLockoutOccurred).isTrue() underTest.reportAuthenticationAttempt(isSuccessful = true) assertThat(hasLockoutOccurred).isFalse() } private fun setSecurityModeAndDispatchBroadcast( securityMode: KeyguardSecurityModel.SecurityMode, ) { Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +184 −187 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +28 −52 Original line number Diff line number Diff line Loading @@ -21,15 +21,15 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.AuthenticationResult import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent Loading Loading @@ -79,7 +79,7 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) runCurrent() underTest.clearMessage() assertThat(message).isEmpty() assertThat(message).isNull() underTest.resetMessage() assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) Loading Loading @@ -149,7 +149,7 @@ class BouncerInteractorTest : SysuiTestCase() { // Incomplete input. assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)) .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEmpty() assertThat(message).isNull() // Correct input. assertThat( Loading @@ -159,7 +159,7 @@ class BouncerInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEmpty() assertThat(message).isNull() } @Test Loading Loading @@ -246,57 +246,40 @@ class BouncerInteractorTest : SysuiTestCase() { } @Test fun lockout() = fun lockoutStarted() = testScope.runTest { val lockout by collectLastValue(underTest.lockout) val lockoutStartedEvents by collectValues(underTest.onLockoutStarted) val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(lockout).isNull() assertThat(lockoutStartedEvents).isEmpty() // Try the wrong PIN repeatedly, until lockout is triggered: repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { times -> // Wrong PIN. assertThat(underTest.authenticate(listOf(6, 7, 8, 9))) .isEqualTo(AuthenticationResult.FAILED) if (times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) { assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) assertThat(lockoutStartedEvents).isEmpty() assertThat(message).isNotEmpty() } } assertThat(lockout) .isEqualTo( AuthenticationLockoutModel( failedAttemptCount = FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT, remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS, ) ) assertTryAgainMessage( message, FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt() ) // Correct PIN, but locked out, so doesn't change away from the bouncer scene: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SKIPPED) assertTryAgainMessage( message, FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt() ) lockout?.remainingSeconds?.let { seconds -> repeat(seconds) { time -> advanceTimeBy(1000) val remainingTimeSec = seconds - time - 1 if (remainingTimeSec > 0) { assertTryAgainMessage(message, remainingTimeSec) } assertThat(authenticationInteractor.lockoutEndTimestamp).isNotNull() assertThat(lockoutStartedEvents.size).isEqualTo(1) assertThat(message).isNull() // Advance the time to finish the lockout: advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS.seconds) assertThat(authenticationInteractor.lockoutEndTimestamp).isNull() assertThat(message).isNull() assertThat(lockoutStartedEvents.size).isEqualTo(1) // Trigger lockout again: repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. underTest.authenticate(listOf(6, 7, 8, 9)) } } assertThat(message).isEqualTo("") assertThat(lockout).isNull() // Correct PIN and no longer locked out so changes to the Gone scene: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(lockout).isNull() assertThat(lockoutStartedEvents.size).isEqualTo(2) } @Test Loading Loading @@ -326,13 +309,6 @@ class BouncerInteractorTest : SysuiTestCase() { verify(keyguardFaceAuthInteractor).onPrimaryBouncerUserInput() } private fun assertTryAgainMessage( message: String?, time: Int, ) { assertThat(message).isEqualTo("Try again in $time seconds.") } companion object { private const val MESSAGE_ENTER_YOUR_PIN = "Enter your PIN" private const val MESSAGE_ENTER_YOUR_PASSWORD = "Enter your password" Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +51 −13 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.currentTime import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test Loading Loading @@ -135,19 +136,47 @@ class BouncerViewModelTest : SysuiTestCase() { fun message() = testScope.runTest { val message by collectLastValue(underTest.message) val lockout by collectLastValue(bouncerInteractor.lockout) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(message?.isUpdateAnimated).isTrue() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) bouncerInteractor.authenticate(WRONG_PIN) } assertThat(message?.isUpdateAnimated).isFalse() lockout?.remainingSeconds?.let { remainingSeconds -> advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds) val lockoutEndMs = authenticationInteractor.lockoutEndTimestamp ?: 0 advanceTimeBy(lockoutEndMs - testScope.currentTime) assertThat(message?.isUpdateAnimated).isTrue() } @Test fun lockoutMessage() = testScope.runTest { val authMethodViewModel by collectLastValue(underTest.authMethodViewModel) val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(utils.authenticationRepository.lockoutEndTimestamp).isNull() assertThat(authMethodViewModel?.lockoutMessageId).isNotNull() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { times -> bouncerInteractor.authenticate(WRONG_PIN) if (times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) { assertThat(message?.text).isEqualTo(bouncerInteractor.message.value) assertThat(message?.isUpdateAnimated).isTrue() } } val lockoutSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS assertTryAgainMessage(message?.text, lockoutSeconds) assertThat(message?.isUpdateAnimated).isFalse() repeat(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS) { time -> advanceTimeBy(1.seconds) val remainingSeconds = lockoutSeconds - time - 1 if (remainingSeconds > 0) { assertTryAgainMessage(message?.text, remainingSeconds) } } assertThat(message?.text).isEmpty() assertThat(message?.isUpdateAnimated).isTrue() } Loading @@ -160,32 +189,30 @@ class BouncerViewModelTest : SysuiTestCase() { authViewModel?.isInputEnabled ?: emptyFlow() } ) val lockout by collectLastValue(bouncerInteractor.lockout) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(isInputEnabled).isTrue() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) bouncerInteractor.authenticate(WRONG_PIN) } assertThat(isInputEnabled).isFalse() lockout?.remainingSeconds?.let { remainingSeconds -> advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds) } val lockoutEndMs = authenticationInteractor.lockoutEndTimestamp ?: 0 advanceTimeBy(lockoutEndMs - testScope.currentTime) assertThat(isInputEnabled).isTrue() } @Test fun dialogMessage() = testScope.runTest { val authMethodViewModel by collectLastValue(underTest.authMethodViewModel) val dialogMessage by collectLastValue(underTest.dialogMessage) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(authMethodViewModel?.lockoutMessageId).isNotNull() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. assertThat(dialogMessage).isNull() bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) bouncerInteractor.authenticate(WRONG_PIN) } assertThat(dialogMessage).isNotEmpty() Loading Loading @@ -241,4 +268,15 @@ class BouncerViewModelTest : SysuiTestCase() { AuthenticationMethodModel.Sim, ) } private fun assertTryAgainMessage( message: String?, time: Int, ) { assertThat(message).isEqualTo("Try again in $time seconds.") } companion object { private val WRONG_PIN = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 } } }
packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +12 −21 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues Loading @@ -28,6 +27,7 @@ import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow Loading @@ -45,11 +45,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val authenticationRepository = utils.authenticationRepository private val authenticationInteractor = utils.authenticationInteractor( repository = authenticationRepository, ) private val authenticationInteractor = utils.authenticationInteractor() private val sceneInteractor = utils.sceneInteractor() private val bouncerInteractor = utils.bouncerInteractor( Loading @@ -61,12 +57,13 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { authenticationInteractor = authenticationInteractor, actionButtonInteractor = utils.bouncerActionButtonInteractor(), ) private val isInputEnabled = MutableStateFlow(true) private val underTest = PasswordBouncerViewModel( viewModelScope = testScope.backgroundScope, interactor = bouncerInteractor, isInputEnabled = MutableStateFlow(true).asStateFlow(), isInputEnabled.asStateFlow(), ) @Before Loading Loading @@ -123,8 +120,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateKeyPressed_whenCorrect() = testScope.runTest { val authResult by collectLastValue(authenticationInteractor.authenticationChallengeResult) val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult) lockDeviceAndOpenPasswordBouncer() underTest.onPasswordInputChanged("password") Loading Loading @@ -169,8 +165,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateKeyPressed_correctAfterWrong() = testScope.runTest { val authResult by collectLastValue(authenticationInteractor.authenticationChallengeResult) val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) lockDeviceAndOpenPasswordBouncer() Loading Loading @@ -333,19 +328,15 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { ) { if (isLockedOut) { repeat(failedAttemptCount) { authenticationRepository.reportAuthenticationAttempt(false) utils.authenticationRepository.reportAuthenticationAttempt(false) } val remainingTimeSeconds = 30 authenticationRepository.setLockoutDuration(remainingTimeSeconds * 1000) authenticationRepository.lockout.value = AuthenticationLockoutModel( failedAttemptCount = failedAttemptCount, remainingSeconds = remainingTimeSeconds, utils.authenticationRepository.reportLockoutStarted( 30.seconds.inWholeMilliseconds.toInt() ) } else { authenticationRepository.reportAuthenticationAttempt(true) authenticationRepository.lockout.value = null utils.authenticationRepository.reportAuthenticationAttempt(true) } isInputEnabled.value = !isLockedOut runCurrent() } Loading