From ddfaf1251deec88c8c614239729eb8e2d730ef8c Mon Sep 17 00:00:00 2001 From: Matthew Reynolds Date: Wed, 12 Feb 2025 17:56:37 +0000 Subject: Moved failing Robolectric tests from multivalentTests to tests Bug: 383114684 Test: tested manually with atest Flag: TEST_ONLY Change-Id: Ia5744f5582d9ec255308ae297afa5492bc5a96f2 --- .../src/com/android/systemui/OnTeardownRuleTest.kt | 124 -- .../IMagnificationConnectionTest.java | 327 ---- .../biometrics/ui/viewmodel/PromptViewModelTest.kt | 2054 -------------------- .../qsdialog/AudioSharingDialogDelegateTest.kt | 116 -- .../dreams/DreamOverlayAnimationsControllerTest.kt | 156 -- .../com/android/systemui/dump/LogEulogizerTest.kt | 185 -- .../ui/view/ContextualEduUiCoordinatorTest.kt | 242 --- .../KeyguardIndicationAreaViewModelTest.kt | 139 -- .../com/android/systemui/lifecycle/HydratorTest.kt | 85 - .../systemui/qs/panels/ui/QSFragmentComposeTest.kt | 182 -- .../sensorprivacy/SensorUseStartedActivityTest.kt | 38 - .../shade/QuickSettingsControllerImplTest.java | 608 ------ .../systemui/shadow/DoubleShadowTextClockTest.kt | 90 - .../shared/clocks/AnimatableClockViewTest.kt | 108 - .../shared/condition/CombinedConditionTest.kt | 450 ----- .../system/UncaughtExceptionPreHandlerTest.kt | 77 - .../viewmodel/StatusBarPopupChipsViewModelTest.kt | 66 - .../layout/StatusBarBoundsProviderTest.kt | 172 -- .../coordinator/PreparationCoordinatorTest.java | 756 ------- .../notification/row/BigPictureIconManagerTest.kt | 488 ----- .../notification/row/RowImageInflaterTest.kt | 120 -- .../phone/KeyguardStatusBarViewControllerTest.kt | 871 --------- .../phone/StatusBarKeyguardViewManagerTest.java | 992 ---------- .../TouchableRegionViewControllerTest.kt | 84 - .../unfold/FoldAodAnimationControllerTest.kt | 226 --- .../systemui/unfold/UnfoldLatencyTrackerTest.kt | 209 -- ...icsBasedUnfoldTransitionProgressProviderTest.kt | 192 -- .../unfold/progress/UnfoldRemoteFilterTest.kt | 91 - .../systemui/usb/UsbPermissionActivityTest.kt | 106 - .../systemui/user/CreateUserActivityTest.kt | 55 - .../android/systemui/util/kotlin/FlowUtilTests.kt | 432 ---- .../PackageManagerExtComponentEnabledTest.kt | 154 -- .../util/sensors/AsyncSensorManagerTest.java | 105 - .../wallpapers/GradientColorWallpaperTest.kt | 117 -- .../src/com/android/systemui/OnTeardownRuleTest.kt | 124 ++ .../IMagnificationConnectionTest.java | 327 ++++ .../biometrics/ui/viewmodel/PromptViewModelTest.kt | 2054 ++++++++++++++++++++ .../qsdialog/AudioSharingDialogDelegateTest.kt | 116 ++ .../dreams/DreamOverlayAnimationsControllerTest.kt | 156 ++ .../com/android/systemui/dump/LogEulogizerTest.kt | 185 ++ .../ui/view/ContextualEduUiCoordinatorTest.kt | 242 +++ .../KeyguardIndicationAreaViewModelTest.kt | 139 ++ .../com/android/systemui/lifecycle/HydratorTest.kt | 85 + .../systemui/qs/panels/ui/QSFragmentComposeTest.kt | 182 ++ .../sensorprivacy/SensorUseStartedActivityTest.kt | 38 + .../shade/QuickSettingsControllerImplTest.java | 608 ++++++ .../systemui/shadow/DoubleShadowTextClockTest.kt | 90 + .../shared/clocks/AnimatableClockViewTest.kt | 108 + .../shared/condition/CombinedConditionTest.kt | 450 +++++ .../system/UncaughtExceptionPreHandlerTest.kt | 77 + .../viewmodel/StatusBarPopupChipsViewModelTest.kt | 66 + .../layout/StatusBarBoundsProviderTest.kt | 172 ++ .../coordinator/PreparationCoordinatorTest.java | 756 +++++++ .../notification/row/BigPictureIconManagerTest.kt | 488 +++++ .../notification/row/RowImageInflaterTest.kt | 120 ++ .../phone/KeyguardStatusBarViewControllerTest.kt | 871 +++++++++ .../phone/StatusBarKeyguardViewManagerTest.java | 992 ++++++++++ .../TouchableRegionViewControllerTest.kt | 84 + .../unfold/FoldAodAnimationControllerTest.kt | 226 +++ .../systemui/unfold/UnfoldLatencyTrackerTest.kt | 209 ++ ...icsBasedUnfoldTransitionProgressProviderTest.kt | 192 ++ .../unfold/progress/UnfoldRemoteFilterTest.kt | 91 + .../systemui/usb/UsbPermissionActivityTest.kt | 106 + .../systemui/user/CreateUserActivityTest.kt | 55 + .../android/systemui/util/kotlin/FlowUtilTests.kt | 432 ++++ .../PackageManagerExtComponentEnabledTest.kt | 154 ++ .../util/sensors/AsyncSensorManagerTest.java | 105 + .../wallpapers/GradientColorWallpaperTest.kt | 117 ++ 68 files changed, 10217 insertions(+), 10217 deletions(-) delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogEulogizerTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/StatusBarBoundsProviderTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/RowImageInflaterTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/user/CreateUserActivityTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java delete mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/OnTeardownRuleTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/lifecycle/HydratorTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/layout/StatusBarBoundsProviderTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowImageInflaterTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt deleted file mode 100644 index 8635bb0e8ab2..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui - -import androidx.test.filters.SmallTest -import com.google.common.truth.Truth.assertThat -import org.junit.Assert.fail -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.JUnitCore - -@Suppress("JUnitMalformedDeclaration") -@SmallTest -class OnTeardownRuleTest : SysuiTestCase() { - // None of these inner classes should be run except as part of this utilities-testing test - class HasTeardown { - @get:Rule val teardownRule = OnTeardownRule() - - @Before - fun setUp() { - teardownWasRun = false - teardownRule.onTeardown { teardownWasRun = true } - } - - @Test fun doTest() {} - - companion object { - var teardownWasRun = false - } - } - - @Test - fun teardownRuns() { - val result = JUnitCore().run(HasTeardown::class.java) - assertThat(result.failures).isEmpty() - assertThat(HasTeardown.teardownWasRun).isTrue() - } - - class FirstTeardownFails { - @get:Rule val teardownRule = OnTeardownRule() - - @Before - fun setUp() { - teardownWasRun = false - teardownRule.onTeardown { fail("One fails") } - teardownRule.onTeardown { teardownWasRun = true } - } - - @Test fun doTest() {} - - companion object { - var teardownWasRun = false - } - } - - @Test - fun allTeardownsRun() { - val result = JUnitCore().run(FirstTeardownFails::class.java) - assertThat(result.failures.map { it.message }).isEqualTo(listOf("One fails")) - assertThat(FirstTeardownFails.teardownWasRun).isTrue() - } - - class ThreeTeardowns { - @get:Rule val teardownRule = OnTeardownRule() - - @Before - fun setUp() { - messages.clear() - } - - @Test - fun doTest() { - teardownRule.onTeardown { messages.add("A") } - teardownRule.onTeardown { messages.add("B") } - teardownRule.onTeardown { messages.add("C") } - } - - companion object { - val messages = mutableListOf() - } - } - - @Test - fun reverseOrder() { - val result = JUnitCore().run(ThreeTeardowns::class.java) - assertThat(result.failures).isEmpty() - assertThat(ThreeTeardowns.messages).isEqualTo(listOf("C", "B", "A")) - } - - class TryToDoABadThing { - @get:Rule val teardownRule = OnTeardownRule() - - @Test - fun doTest() { - teardownRule.onTeardown { - teardownRule.onTeardown { - // do nothing - } - } - } - } - - @Test - fun prohibitTeardownDuringTeardown() { - val result = JUnitCore().run(TryToDoABadThing::class.java) - assertThat(result.failures.map { it.message }) - .isEqualTo(listOf("Cannot add new teardown routines after test complete.")) - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java deleted file mode 100644 index 6d75c4ca3a38..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.accessibility; - -import static com.android.systemui.accessibility.MagnificationImpl.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.hardware.display.DisplayManager; -import android.os.RemoteException; -import android.provider.Settings; -import android.testing.TestableLooper; -import android.view.Display; -import android.view.IWindowManager; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.IMagnificationConnection; -import android.view.accessibility.IMagnificationConnectionCallback; -import android.view.accessibility.IRemoteMagnificationAnimationCallback; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.model.SysUiState; -import com.android.systemui.recents.LauncherProxyService; -import com.android.systemui.settings.FakeDisplayTracker; -import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.util.settings.SecureSettings; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Tests for {@link android.view.accessibility.IMagnificationConnection} retrieved from - * {@link MagnificationImpl} - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -public class IMagnificationConnectionTest extends SysuiTestCase { - - private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; - @Mock - private AccessibilityManager mAccessibilityManager; - @Mock - private CommandQueue mCommandQueue; - @Mock - private IMagnificationConnectionCallback mConnectionCallback; - @Mock - private WindowMagnificationController mWindowMagnificationController; - @Mock - private FullscreenMagnificationController mFullscreenMagnificationController; - @Mock - private MagnificationSettingsController mMagnificationSettingsController; - @Mock - private ModeSwitchesController mModeSwitchesController; - @Mock - private SysUiState mSysUiState; - @Mock - private IRemoteMagnificationAnimationCallback mAnimationCallback; - @Mock - private LauncherProxyService mLauncherProxyService; - @Mock - private SecureSettings mSecureSettings; - @Mock - private AccessibilityLogger mA11yLogger; - @Mock - private IWindowManager mIWindowManager; - @Mock - private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; - - private IMagnificationConnection mIMagnificationConnection; - private MagnificationImpl mMagnification; - private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); - private TestableLooper mTestableLooper; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - getContext().addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager); - doAnswer(invocation -> { - mIMagnificationConnection = invocation.getArgument(0); - return null; - }).when(mAccessibilityManager).setMagnificationConnection( - any(IMagnificationConnection.class)); - mTestableLooper = TestableLooper.get(this); - assertNotNull(mTestableLooper); - mMagnification = new MagnificationImpl(getContext(), - mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue, - mModeSwitchesController, mSysUiState, mLauncherProxyService, mSecureSettings, - mDisplayTracker, getContext().getSystemService(DisplayManager.class), - mA11yLogger, mIWindowManager, mAccessibilityManager, - mViewCaptureAwareWindowManager); - mMagnification.mWindowMagnificationControllerSupplier = - new FakeWindowMagnificationControllerSupplier( - mContext.getSystemService(DisplayManager.class)); - mMagnification.mFullscreenMagnificationControllerSupplier = - new FakeFullscreenMagnificationControllerSupplier( - mContext.getSystemService(DisplayManager.class)); - mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier( - mContext.getSystemService(DisplayManager.class)); - - mMagnification.requestMagnificationConnection(true); - assertNotNull(mIMagnificationConnection); - mIMagnificationConnection.setConnectionCallback(mConnectionCallback); - } - - @Test - public void enableWindowMagnification_passThrough() throws RemoteException { - mIMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN, - Float.NaN, 0f, 0f, mAnimationCallback); - processAllPendingMessages(); - - verify(mWindowMagnificationController).enableWindowMagnification(eq(3.0f), - eq(Float.NaN), eq(Float.NaN), eq(0f), eq(0f), eq(mAnimationCallback)); - } - - @Test - public void onFullscreenMagnificationActivationChanged_passThrough() throws RemoteException { - mIMagnificationConnection.onFullscreenMagnificationActivationChanged(TEST_DISPLAY, true); - processAllPendingMessages(); - - verify(mFullscreenMagnificationController) - .onFullscreenMagnificationActivationChanged(eq(true)); - } - - @Test - public void disableWindowMagnification_deleteWindowMagnification() throws RemoteException { - mIMagnificationConnection.disableWindowMagnification(TEST_DISPLAY, - mAnimationCallback); - processAllPendingMessages(); - - verify(mWindowMagnificationController).deleteWindowMagnification( - mAnimationCallback); - } - - @Test - public void setScaleForWindowMagnification() throws RemoteException { - mIMagnificationConnection.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f); - processAllPendingMessages(); - - verify(mWindowMagnificationController).setScale(3.0f); - } - - @Test - public void moveWindowMagnifier() throws RemoteException { - mIMagnificationConnection.moveWindowMagnifier(TEST_DISPLAY, 100f, 200f); - processAllPendingMessages(); - - verify(mWindowMagnificationController).moveWindowMagnifier(100f, 200f); - } - - @Test - public void moveWindowMagnifierToPosition() throws RemoteException { - mIMagnificationConnection.moveWindowMagnifierToPosition(TEST_DISPLAY, - 100f, 200f, mAnimationCallback); - processAllPendingMessages(); - - verify(mWindowMagnificationController).moveWindowMagnifierToPosition( - eq(100f), eq(200f), any(IRemoteMagnificationAnimationCallback.class)); - } - - @Test - public void showMagnificationButton_delayedShowButton() throws RemoteException { - when(mMagnificationSettingsController.isMagnificationSettingsShowing()).thenReturn(false); - - mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY, - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - // This processAllPendingMessages lets the IMagnificationConnection to delegate the - // showMagnificationButton request to Magnification. - processAllPendingMessages(); - - // The delayed message would be processed after DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS. - // So call this processAllPendingMessages with a timeout to verify the showButton - // will be called. - int timeout = DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100; - processAllPendingMessages(timeout); - verify(mModeSwitchesController).showButton(TEST_DISPLAY, - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - } - - @Test - public void showMagnificationButton_settingsPanelShowing_doNotShowButton() - throws RemoteException { - when(mMagnificationSettingsController.isMagnificationSettingsShowing()).thenReturn(true); - - mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY, - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - // This processAllPendingMessages lets the IMagnificationConnection to delegate the - // showMagnificationButton request to Magnification. - processAllPendingMessages(); - - // The isMagnificationSettingsShowing will be checked after timeout, so - // process all message after a timeout here to verify the showButton will not be called. - processAllPendingMessages(DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100); - verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY, - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - } - - @Test - public void removeMagnificationButton() throws RemoteException { - mIMagnificationConnection.removeMagnificationButton(TEST_DISPLAY); - processAllPendingMessages(); - - verify(mModeSwitchesController).removeButton(TEST_DISPLAY); - } - - @Test - public void removeMagnificationButton_delayingShowButton_doNotShowButtonAfterTimeout() - throws RemoteException { - when(mMagnificationSettingsController.isMagnificationSettingsShowing()).thenReturn(false); - - mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY, - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - mIMagnificationConnection.removeMagnificationButton(TEST_DISPLAY); - // This processAllPendingMessages lets the IMagnificationConnection to delegate the - // requests to Magnification. - processAllPendingMessages(); - - // Call this processAllPendingMessages with a timeout to ensure the delayed show button - // message should be removed and thus the showButton will not be called after timeout. - int timeout = DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100; - processAllPendingMessages(/* timeForwardMs= */ timeout); - verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY, - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - } - - @Test - public void removeMagnificationSettingsPanel() throws RemoteException { - mIMagnificationConnection.removeMagnificationSettingsPanel(TEST_DISPLAY); - processAllPendingMessages(); - - verify(mMagnificationSettingsController).closeMagnificationSettings(); - } - - @Test - public void onUserMagnificationScaleChanged() throws RemoteException { - final int testUserId = 1; - final float testScale = 3.0f; - mIMagnificationConnection.onUserMagnificationScaleChanged( - testUserId, TEST_DISPLAY, testScale); - processAllPendingMessages(); - - assertTrue(mMagnification.mUsersScales.contains(testUserId)); - assertEquals(mMagnification.mUsersScales.get(testUserId).get(TEST_DISPLAY), - (Float) testScale); - verify(mMagnificationSettingsController).setMagnificationScale(eq(testScale)); - } - - private void processAllPendingMessages() { - processAllPendingMessages(/* timeForwardMs=*/ 0); - } - - private void processAllPendingMessages(int timeForwardMs) { - if (timeForwardMs > 0) { - mTestableLooper.moveTimeForward(timeForwardMs); - } - mTestableLooper.processAllMessages(); - } - - private class FakeWindowMagnificationControllerSupplier extends - DisplayIdIndexSupplier { - - FakeWindowMagnificationControllerSupplier(DisplayManager displayManager) { - super(displayManager); - } - - @Override - protected WindowMagnificationController createInstance(Display display) { - return mWindowMagnificationController; - } - } - - private class FakeFullscreenMagnificationControllerSupplier extends - DisplayIdIndexSupplier { - - FakeFullscreenMagnificationControllerSupplier(DisplayManager displayManager) { - super(displayManager); - } - - @Override - protected FullscreenMagnificationController createInstance(Display display) { - return mFullscreenMagnificationController; - } - } - - private class FakeSettingsSupplier extends - DisplayIdIndexSupplier { - - FakeSettingsSupplier(DisplayManager displayManager) { - super(displayManager); - } - - @Override - protected MagnificationSettingsController createInstance(Display display) { - return mMagnificationSettingsController; - } - } -} - diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt deleted file mode 100644 index b6c63479990e..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ /dev/null @@ -1,2054 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics.ui.viewmodel - -import android.app.ActivityManager.RunningTaskInfo -import android.content.ComponentName -import android.content.applicationContext -import android.content.packageManager -import android.content.pm.ActivityInfo -import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager.NameNotFoundException -import android.graphics.Bitmap -import android.graphics.Point -import android.graphics.Rect -import android.graphics.drawable.BitmapDrawable -import android.hardware.biometrics.BiometricFingerprintConstants -import android.hardware.biometrics.PromptContentItemBulletedText -import android.hardware.biometrics.PromptContentView -import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton -import android.hardware.biometrics.PromptInfo -import android.hardware.biometrics.PromptVerticalListContentView -import android.hardware.face.FaceSensorPropertiesInternal -import android.hardware.fingerprint.FingerprintSensorProperties -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal -import android.os.UserHandle -import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags -import android.view.HapticFeedbackConstants -import android.view.MotionEvent -import android.view.Surface -import androidx.test.filters.SmallTest -import com.android.app.activityTaskManager -import com.android.keyguard.AuthInteractionProperties -import com.android.systemui.Flags -import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.AuthController -import com.android.systemui.biometrics.Utils.toBitmap -import com.android.systemui.biometrics.authController -import com.android.systemui.biometrics.data.repository.biometricStatusRepository -import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository -import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor -import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor -import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor -import com.android.systemui.biometrics.extractAuthenticatorTypes -import com.android.systemui.biometrics.faceSensorPropertiesInternal -import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal -import com.android.systemui.biometrics.shared.model.AuthenticationReason -import com.android.systemui.biometrics.shared.model.BiometricModalities -import com.android.systemui.biometrics.shared.model.BiometricModality -import com.android.systemui.biometrics.shared.model.DisplayRotation -import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams -import com.android.systemui.biometrics.shared.model.toSensorStrength -import com.android.systemui.biometrics.shared.model.toSensorType -import com.android.systemui.biometrics.udfpsUtils -import com.android.systemui.concurrency.fakeExecutor -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.coroutines.collectValues -import com.android.systemui.display.data.repository.displayStateRepository -import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus -import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.testScope -import com.android.systemui.res.R -import com.android.systemui.util.mockito.withArgCaptor -import com.google.android.msdl.data.model.MSDLToken -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.junit.MockitoJUnit -import org.mockito.kotlin.any -import org.mockito.kotlin.whenever -import platform.test.runner.parameterized.ParameterizedAndroidJunit4 -import platform.test.runner.parameterized.Parameters - -private const val USER_ID = 4 -private const val WORK_USER_ID = 100 -private const val REQUEST_ID = 4L -private const val CHALLENGE = 2L -private const val DELAY = 1000L -private const val OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO = "should.use.activiy.logo" -private const val OP_PACKAGE_NAME_WITH_APP_LOGO = "biometric.testapp" -private const val OP_PACKAGE_NAME_NO_LOGO_INFO = "biometric.testapp.nologoinfo" -private const val OP_PACKAGE_NAME_CAN_NOT_BE_FOUND = "can.not.be.found" - -@SmallTest -@RunWith(ParameterizedAndroidJunit4::class) -internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCase() { - - @JvmField @Rule var mockitoRule = MockitoJUnit.rule() - - @Mock private lateinit var authController: AuthController - @Mock private lateinit var applicationInfoWithIconAndDescription: ApplicationInfo - @Mock private lateinit var applicationInfoNoIconOrDescription: ApplicationInfo - @Mock private lateinit var activityInfo: ActivityInfo - @Mock private lateinit var runningTaskInfo: RunningTaskInfo - - private val defaultLogoIconFromAppInfo = context.getDrawable(R.drawable.ic_android) - private val defaultLogoIconFromActivityInfo = context.getDrawable(R.drawable.ic_add) - private val defaultLogoIconWithBadge = context.getDrawable(R.drawable.ic_alarm) - private val logoResFromApp = R.drawable.ic_cake - private val logoDrawableFromAppRes = context.getDrawable(logoResFromApp) - private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565) - private val defaultLogoDescriptionFromAppInfo = "Test Android App" - private val defaultLogoDescriptionFromActivityInfo = "Test Coke App" - private val defaultLogoDescriptionWithBadge = "Work app" - private val logoDescriptionFromApp = "Test Cake App" - - private val authInteractionProperties = AuthInteractionProperties() - - /** Prompt panel size padding */ - private val smallHorizontalGuidelinePadding = - context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_land_small_horizontal_guideline_padding - ) - private val udfpsHorizontalGuidelinePadding = - context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding - ) - private val udfpsHorizontalShorterGuidelinePadding = - context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding - ) - private val mediumTopGuidelinePadding = - context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding - ) - private val mediumHorizontalGuidelinePadding = - context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding - ) - private val mockFaceIconSize = 200 - private val mockFingerprintIconWidth = 300 - private val mockFingerprintIconHeight = 300 - - private val faceIconAuthingDescription = - R.string.biometric_dialog_face_icon_description_authenticating - private val faceIconAuthedDescription = - R.string.biometric_dialog_face_icon_description_authenticated - private val faceIconConfirmedDescription = - R.string.biometric_dialog_face_icon_description_confirmed - private val faceIconIdleDescription = R.string.biometric_dialog_face_icon_description_idle - private val sfpsFindSensorDescription = - R.string.security_settings_sfps_enroll_find_sensor_message - private val udfpsIconDescription = R.string.accessibility_fingerprint_label - private val faceFailedDescription = R.string.keyguard_face_failed - private val bpTryAgainDescription = R.string.biometric_dialog_try_again - private val bpConfirmDescription = R.string.biometric_dialog_confirm - private val fingerprintIconAuthenticatedDescription = - R.string.fingerprint_dialog_authenticated_confirmation - - /** Mock [UdfpsOverlayParams] for a test. */ - private fun mockUdfpsOverlayParams(isLandscape: Boolean = false): UdfpsOverlayParams = - UdfpsOverlayParams( - sensorBounds = Rect(400, 1600, 600, 1800), - overlayBounds = Rect(0, 1200, 1000, 2400), - naturalDisplayWidth = 1000, - naturalDisplayHeight = 3000, - scaleFactor = 1f, - rotation = if (isLandscape) Surface.ROTATION_90 else Surface.ROTATION_0, - ) - - private lateinit var promptContentView: PromptContentView - private lateinit var promptContentViewWithMoreOptionsButton: - PromptContentViewWithMoreOptionsButton - - private val kosmos = Kosmos() - - @Before - fun setup() { - setupLogo() - overrideResource(R.dimen.biometric_dialog_fingerprint_icon_width, mockFingerprintIconWidth) - overrideResource( - R.dimen.biometric_dialog_fingerprint_icon_height, - mockFingerprintIconHeight, - ) - overrideResource(R.dimen.biometric_dialog_face_icon_size, mockFaceIconSize) - - kosmos.applicationContext = context - - if (testCase.fingerprint?.isAnyUdfpsType == true) { - kosmos.authController = authController - } - - testCase.fingerprint?.let { - kosmos.fakeFingerprintPropertyRepository.setProperties( - it.sensorId, - it.sensorStrength.toSensorStrength(), - it.sensorType.toSensorType(), - it.allLocations.associateBy { sensorLocationInternal -> - sensorLocationInternal.displayId - }, - ) - } - - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) - testCase.fingerprint?.isAnySidefpsType.let { - kosmos.displayStateRepository.setIsInRearDisplayMode(testCase.isInRearDisplayMode) - } - - promptContentView = - PromptVerticalListContentView.Builder() - .addListItem(PromptContentItemBulletedText("content item 1")) - .addListItem(PromptContentItemBulletedText("content item 2"), 1) - .build() - - promptContentViewWithMoreOptionsButton = - PromptContentViewWithMoreOptionsButton.Builder() - .setDescription("test") - .setMoreOptionsButtonListener(kosmos.fakeExecutor) { _, _ -> } - .build() - } - - private fun setupLogo() { - // Set up app customized logo - overrideResource(logoResFromApp, logoDrawableFromAppRes) - - // Set up when activity info should be used - overrideResource( - R.array.config_useActivityLogoForBiometricPrompt, - arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO), - ) - whenever(kosmos.packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo) - whenever(kosmos.iconProvider.getIcon(activityInfo)) - .thenReturn(defaultLogoIconFromActivityInfo) - whenever(activityInfo.loadLabel(kosmos.packageManager)) - .thenReturn(defaultLogoDescriptionFromActivityInfo) - - // Set up when application info should be used for default logo - whenever( - kosmos.packageManager.getApplicationInfo( - eq(OP_PACKAGE_NAME_WITH_APP_LOGO), - anyInt(), - ) - ) - .thenReturn(applicationInfoWithIconAndDescription) - whenever( - kosmos.packageManager.getApplicationInfo( - eq(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO), - anyInt(), - ) - ) - .thenReturn(applicationInfoWithIconAndDescription) - whenever(kosmos.packageManager.getApplicationIcon(applicationInfoWithIconAndDescription)) - .thenReturn(defaultLogoIconFromAppInfo) - whenever(kosmos.packageManager.getApplicationLabel(applicationInfoWithIconAndDescription)) - .thenReturn(defaultLogoDescriptionFromAppInfo) - - // Set up when package name cannot but found - whenever( - kosmos.packageManager.getApplicationInfo( - eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND), - anyInt(), - ) - ) - .thenThrow(NameNotFoundException()) - - // Set up when no default logo from application info - whenever( - kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_LOGO_INFO), anyInt()) - ) - .thenReturn(applicationInfoNoIconOrDescription) - whenever(kosmos.packageManager.getApplicationIcon(applicationInfoNoIconOrDescription)) - .thenReturn(null) - whenever(kosmos.packageManager.getApplicationLabel(applicationInfoNoIconOrDescription)) - .thenReturn("") - - // Set up work badge - whenever(kosmos.packageManager.getUserBadgedIcon(any(), eq(UserHandle.of(USER_ID)))).then { - it.getArgument(0) - } - whenever(kosmos.packageManager.getUserBadgedLabel(any(), eq(UserHandle.of(USER_ID)))).then { - it.getArgument(0) - } - whenever(kosmos.packageManager.getUserBadgedIcon(any(), eq(UserHandle.of(WORK_USER_ID)))) - .then { defaultLogoIconWithBadge } - whenever(kosmos.packageManager.getUserBadgedLabel(any(), eq(UserHandle.of(WORK_USER_ID)))) - .then { defaultLogoDescriptionWithBadge } - context.setMockPackageManager(kosmos.packageManager) - } - - @Test - fun start_idle_and_show_authenticating() = - runGenericTest(doNotStart = true) { - var expectedPromptSize = - if (testCase.shouldStartAsImplicitFlow) PromptSize.SMALL else PromptSize.MEDIUM - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val modalities by collectLastValue(kosmos.promptViewModel.modalities) - val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset) - val shouldAnimateIconView by - collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView) - val iconContentDescriptionId by - collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId) - val message by collectLastValue(kosmos.promptViewModel.message) - val size by collectLastValue(kosmos.promptViewModel.size) - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isNotAuthenticated).isTrue() - with(modalities ?: throw Exception("missing modalities")) { - assertThat(hasFace).isEqualTo(testCase.face != null) - assertThat(hasFingerprint).isEqualTo(testCase.fingerprint != null) - } - - assertThat(message).isEqualTo(PromptMessage.Empty) - assertThat(size).isEqualTo(expectedPromptSize) - - val forceExplicitFlow = - testCase.isCoex && testCase.confirmationRequested || - testCase.authenticatedByFingerprint - - if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) { - // Face-only or implicit co-ex auth - assertThat(iconAsset).isEqualTo(R.raw.face_dialog_idle_static) - assertThat(shouldAnimateIconView).isEqualTo(false) - } - - if (forceExplicitFlow) { - expectedPromptSize = PromptSize.MEDIUM - kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true) - } - - val startMessage = "here we go" - kosmos.promptViewModel.showAuthenticating(startMessage, isRetry = false) - verifyIconSize(forceExplicitFlow) - - // Icon asset assertions - if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) { - // Face-only or implicit co-ex auth - assertThat(iconAsset).isEqualTo(R.raw.face_dialog_authenticating) - assertThat(iconContentDescriptionId).isEqualTo(faceIconAuthingDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) { - // Fingerprint-only or explicit co-ex auth - if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) { - assertThat(iconAsset).isEqualTo(getSfpsAsset_fingerprintAuthenticating()) - assertThat(iconContentDescriptionId).isEqualTo(sfpsFindSensorDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else { - assertThat(iconAsset) - .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie) - assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription) - assertThat(shouldAnimateIconView).isEqualTo(false) - } - } - - assertThat(message).isEqualTo(PromptMessage.Help(startMessage)) - assertThat(authenticating).isTrue() - assertThat(authenticated?.isNotAuthenticated).isTrue() - assertThat(size).isEqualTo(expectedPromptSize) - assertButtonsVisible(negative = expectedPromptSize != PromptSize.SMALL) - } - - @Test - fun start_authenticating_show_and_clear_error() = runGenericTest { - val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset) - val iconContentDescriptionId by - collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId) - val shouldAnimateIconView by - collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView) - val message by collectLastValue(kosmos.promptViewModel.message) - - var forceExplicitFlow = - testCase.isCoex && testCase.confirmationRequested || testCase.authenticatedByFingerprint - if (forceExplicitFlow) { - kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true) - } - verifyIconSize(forceExplicitFlow) - - val errorJob = launch { - kosmos.promptViewModel.showTemporaryError( - "so sad", - messageAfterError = "", - authenticateAfterError = testCase.isFingerprintOnly || testCase.isCoex, - ) - forceExplicitFlow = true - // Usually done by binder - kosmos.promptViewModel.iconViewModel.setPreviousIconWasError(true) - } - - assertThat(message?.isError).isEqualTo(true) - assertThat(message?.message).isEqualTo("so sad") - - // Icon asset assertions - if (testCase.isFaceOnly) { - // Face-only auth - assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_error) - assertThat(iconContentDescriptionId).isEqualTo(faceFailedDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - - // Clear error, go to idle - errorJob.join() - - assertThat(iconAsset).isEqualTo(R.raw.face_dialog_error_to_idle) - assertThat(iconContentDescriptionId).isEqualTo(faceIconIdleDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) { - // Fingerprint-only or explicit co-ex auth - if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) { - assertThat(iconAsset).isEqualTo(getSfpsAsset_fingerprintToError()) - assertThat(iconContentDescriptionId).isEqualTo(bpTryAgainDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else { - assertThat(iconAsset) - .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie) - assertThat(iconContentDescriptionId).isEqualTo(bpTryAgainDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } - - // Clear error, restart authenticating - errorJob.join() - - if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) { - assertThat(iconAsset).isEqualTo(getSfpsAsset_errorToFingerprint()) - assertThat(iconContentDescriptionId).isEqualTo(sfpsFindSensorDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else { - assertThat(iconAsset) - .isEqualTo(R.raw.fingerprint_dialogue_error_to_fingerprint_lottie) - assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } - } - } - - @Test - fun shows_error_to_unlock_or_success() { - // Face-only auth does not use error -> unlock or error -> success assets - if (testCase.isFingerprintOnly || testCase.isCoex) { - runGenericTest { - // Distinct asset for error -> success only applicable for fingerprint-only / - // explicit co-ex auth - val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset) - val iconContentDescriptionId by - collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId) - val shouldAnimateIconView by - collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView) - - var forceExplicitFlow = - testCase.isCoex && testCase.confirmationRequested || - testCase.authenticatedByFingerprint - if (forceExplicitFlow) { - kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true) - } - verifyIconSize(forceExplicitFlow) - - kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true) - kosmos.promptViewModel.iconViewModel.setPreviousIconWasError(true) - - kosmos.promptViewModel.showAuthenticated( - modality = testCase.authenticatedModality, - dismissAfterDelay = DELAY, - ) - - // SFPS test cases - if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) { - // Covers (1) fingerprint-only (2) co-ex, authenticated by fingerprint - if (testCase.authenticatedByFingerprint) { - assertThat(iconAsset).isEqualTo(R.raw.biometricprompt_sfps_error_to_success) - assertThat(iconContentDescriptionId).isEqualTo(sfpsFindSensorDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else { // Covers co-ex, authenticated by face - assertThat(iconAsset).isEqualTo(R.raw.biometricprompt_sfps_error_to_unlock) - assertThat(iconContentDescriptionId) - .isEqualTo(fingerprintIconAuthenticatedDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - - // Confirm authentication - kosmos.promptViewModel.confirmAuthenticated() - - assertThat(iconAsset) - .isEqualTo(R.raw.biometricprompt_sfps_unlock_to_success) - assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } - } else { // Non-SFPS (UDFPS / rear-FPS) test cases - // Covers (1) fingerprint-only (2) co-ex, authenticated by fingerprint - if (testCase.authenticatedByFingerprint) { - assertThat(iconAsset) - .isEqualTo(R.raw.fingerprint_dialogue_error_to_success_lottie) - assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else { // co-ex, authenticated by face - assertThat(iconAsset) - .isEqualTo(R.raw.fingerprint_dialogue_error_to_unlock_lottie) - assertThat(iconContentDescriptionId).isEqualTo(bpConfirmDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - - // Confirm authentication - kosmos.promptViewModel.confirmAuthenticated() - - assertThat(iconAsset) - .isEqualTo( - R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie - ) - assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } - } - } - } - } - - @Test - fun shows_authenticated_no_errors_no_confirmation_required() { - if (!testCase.confirmationRequested) { - runGenericTest { - val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset) - val iconContentDescriptionId by - collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId) - val shouldAnimateIconView by - collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView) - val message by collectLastValue(kosmos.promptViewModel.message) - verifyIconSize() - - kosmos.promptViewModel.showAuthenticated( - modality = testCase.authenticatedModality, - dismissAfterDelay = DELAY, - "TEST", - ) - - if (testCase.isFingerprintOnly) { - // Fingerprint icon asset assertions - if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) { - assertThat(iconAsset).isEqualTo(getSfpsAsset_fingerprintToSuccess()) - assertThat(iconContentDescriptionId).isEqualTo(sfpsFindSensorDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else { - assertThat(iconAsset) - .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_success_lottie) - assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } - } else if (testCase.isFaceOnly || testCase.isCoex) { - // Face icon asset assertions - // If co-ex, use implicit flow (explicit flow always requires confirmation) - assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_checkmark) - assertThat(iconContentDescriptionId).isEqualTo(faceIconAuthedDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - assertThat(message).isEqualTo(PromptMessage.Empty) - } - } - } - } - - @Test - fun shows_pending_confirmation() { - if (testCase.authenticatedByFace && testCase.confirmationRequested) { - runGenericTest { - val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset) - val iconContentDescriptionId by - collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId) - val shouldAnimateIconView by - collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView) - - val forceExplicitFlow = testCase.isCoex && testCase.confirmationRequested - verifyIconSize(forceExplicitFlow = forceExplicitFlow) - - kosmos.promptViewModel.showAuthenticated( - modality = testCase.authenticatedModality, - dismissAfterDelay = DELAY, - ) - - if (testCase.isFaceOnly) { - assertThat(iconAsset).isEqualTo(R.raw.face_dialog_wink_from_dark) - assertThat(iconContentDescriptionId).isEqualTo(faceIconAuthedDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else if (testCase.isCoex) { // explicit flow, confirmation requested - if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) { - assertThat(iconAsset).isEqualTo(getSfpsAsset_fingerprintToUnlock()) - assertThat(iconContentDescriptionId) - .isEqualTo(fingerprintIconAuthenticatedDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else { - assertThat(iconAsset) - .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie) - assertThat(iconContentDescriptionId).isEqualTo(bpConfirmDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } - } - } - } - } - - @Test - fun shows_authenticated_explicitly_confirmed() { - if (testCase.authenticatedByFace && testCase.confirmationRequested) { - runGenericTest { - val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset) - val iconContentDescriptionId by - collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId) - val shouldAnimateIconView by - collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView) - val forceExplicitFlow = testCase.isCoex && testCase.confirmationRequested - verifyIconSize(forceExplicitFlow = forceExplicitFlow) - - kosmos.promptViewModel.showAuthenticated( - modality = testCase.authenticatedModality, - dismissAfterDelay = DELAY, - ) - - kosmos.promptViewModel.confirmAuthenticated() - - if (testCase.isFaceOnly) { - assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_checkmark) - assertThat(iconContentDescriptionId).isEqualTo(faceIconConfirmedDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } - - // explicit flow because confirmation requested - if (testCase.isCoex) { - if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) { - assertThat(iconAsset) - .isEqualTo(R.raw.biometricprompt_sfps_unlock_to_success) - assertThat(shouldAnimateIconView).isEqualTo(true) - } else { - assertThat(iconAsset) - .isEqualTo( - R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie - ) - assertThat(iconContentDescriptionId).isEqualTo(udfpsIconDescription) - assertThat(shouldAnimateIconView).isEqualTo(true) - } - } - } - } - } - - private fun getSfpsAsset_fingerprintAuthenticating(): Int = - if (testCase.isInRearDisplayMode) { - R.raw.biometricprompt_sfps_rear_display_fingerprint_authenticating - } else { - R.raw.biometricprompt_sfps_fingerprint_authenticating - } - - private fun getSfpsAsset_fingerprintToError(): Int = - if (testCase.isInRearDisplayMode) { - R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error - } else { - R.raw.biometricprompt_sfps_fingerprint_to_error - } - - private fun getSfpsAsset_fingerprintToUnlock(): Int = - if (testCase.isInRearDisplayMode) { - R.raw.biometricprompt_sfps_rear_display_fingerprint_to_unlock - } else { - R.raw.biometricprompt_sfps_fingerprint_to_unlock - } - - private fun getSfpsAsset_errorToFingerprint(): Int = - if (testCase.isInRearDisplayMode) { - R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint - } else { - R.raw.biometricprompt_sfps_error_to_fingerprint - } - - private fun getSfpsAsset_fingerprintToSuccess(): Int = - if (testCase.isInRearDisplayMode) { - R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success - } else { - R.raw.biometricprompt_sfps_fingerprint_to_success - } - - @Test - fun shows_authenticated_with_no_errors() = runGenericTest { - // this case can't happen until fingerprint is started - // trigger it now since no error has occurred in this test - val forceError = testCase.isCoex && testCase.authenticatedByFingerprint - - if (forceError) { - assertThat(kosmos.promptViewModel.fingerprintStartMode.first()) - .isEqualTo(FingerprintStartMode.Pending) - kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true) - } - - showAuthenticated( - testCase.authenticatedModality, - testCase.expectConfirmation(atLeastOneFailure = forceError), - ) - } - - // Verifies expected icon sizes for all modalities - private fun TestScope.verifyIconSize(forceExplicitFlow: Boolean = false) { - val iconSize by collectLastValue(kosmos.promptViewModel.iconSize) - if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) { - // Face-only or implicit co-ex auth - assertThat(iconSize).isEqualTo(Pair(mockFaceIconSize, mockFaceIconSize)) - } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) { - // Fingerprint-only or explicit co-ex auth - if (testCase.fingerprint?.isAnyUdfpsType == true) { - val udfpsOverlayParams by - collectLastValue(kosmos.promptViewModel.udfpsOverlayParams) - val expectedUdfpsOverlayParams = mockUdfpsOverlayParams() - assertThat(udfpsOverlayParams).isEqualTo(expectedUdfpsOverlayParams) - - assertThat(iconSize) - .isEqualTo( - Pair( - expectedUdfpsOverlayParams.sensorBounds.width(), - expectedUdfpsOverlayParams.sensorBounds.height(), - ) - ) - } else { - assertThat(iconSize) - .isEqualTo(Pair(mockFingerprintIconWidth, mockFingerprintIconHeight)) - } - } - } - - @Test - @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() = - runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L) - - val hapticsPreConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - if (expectConfirmation) { - assertThat(hapticsPreConfirm).isEqualTo(PromptViewModel.HapticsToPlay.None) - } else { - val confirmHaptics = - hapticsPreConfirm as PromptViewModel.HapticsToPlay.HapticConstant - assertThat(confirmHaptics.constant) - .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM) - assertThat(confirmHaptics.flag).isNull() - } - - if (expectConfirmation) { - kosmos.promptViewModel.confirmAuthenticated() - } - - val hapticsPostConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val confirmedHaptics = - hapticsPostConfirm as PromptViewModel.HapticsToPlay.HapticConstant - assertThat(confirmedHaptics.constant) - .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM) - assertThat(confirmedHaptics.flag).isNull() - } - - @Test - @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun set_msdl_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() = - runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L) - - val hapticsPreConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - - if (expectConfirmation) { - assertThat(hapticsPreConfirm).isEqualTo(PromptViewModel.HapticsToPlay.None) - } else { - val confirmHaptics = hapticsPreConfirm as PromptViewModel.HapticsToPlay.MSDL - assertThat(confirmHaptics.token).isEqualTo(MSDLToken.UNLOCK) - assertThat(confirmHaptics.properties).isEqualTo(authInteractionProperties) - } - - if (expectConfirmation) { - kosmos.promptViewModel.confirmAuthenticated() - } - - val hapticsPostConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val confirmedHaptics = hapticsPostConfirm as PromptViewModel.HapticsToPlay.MSDL - assertThat(confirmedHaptics.token).isEqualTo(MSDLToken.UNLOCK) - assertThat(confirmedHaptics.properties).isEqualTo(authInteractionProperties) - } - - @Test - @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L) - - if (expectConfirmation) { - kosmos.promptViewModel.confirmAuthenticated() - } - - val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val currentHaptics = haptics as PromptViewModel.HapticsToPlay.HapticConstant - assertThat(currentHaptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM) - assertThat(currentHaptics.flag).isNull() - } - - @Test - @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun playSuccessHaptic_SetsUnlockMSDLFeedback() = runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L) - - if (expectConfirmation) { - kosmos.promptViewModel.confirmAuthenticated() - } - - val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val currentHaptics = haptics as PromptViewModel.HapticsToPlay.MSDL - assertThat(currentHaptics.token).isEqualTo(MSDLToken.UNLOCK) - assertThat(currentHaptics.properties).isEqualTo(authInteractionProperties) - } - - @Test - @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun playErrorHaptic_SetsRejectConstant() = runGenericTest { - kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false) - - val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val currentHaptics = haptics as PromptViewModel.HapticsToPlay.HapticConstant - assertThat(currentHaptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT) - assertThat(currentHaptics.flag).isNull() - } - - @Test - @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun playErrorHaptic_SetsFailureMSDLFeedback() = runGenericTest { - kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false) - - val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val currentHaptics = haptics as PromptViewModel.HapticsToPlay.MSDL - assertThat(currentHaptics.token).isEqualTo(MSDLToken.FAILURE) - assertThat(currentHaptics.properties).isEqualTo(authInteractionProperties) - } - - // biometricprompt_sfps_fingerprint_authenticating reused across rotations - // Other SFPS assets change across rotations, testing authenticated asset - @Test - fun sfpsAuthenticatedIconUpdates_onRotation() { - if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) { - runGenericTest { - val currentIcon by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset) - - kosmos.promptViewModel.showAuthenticated( - modality = testCase.authenticatedModality, - dismissAfterDelay = DELAY, - ) - - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) - val iconRotation0 = currentIcon - - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) - val iconRotation90 = currentIcon - - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) - val iconRotation180 = currentIcon - - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) - val iconRotation270 = currentIcon - - assertThat(iconRotation0).isNotEqualTo(iconRotation90) - assertThat(iconRotation0).isNotEqualTo(iconRotation180) - assertThat(iconRotation0).isNotEqualTo(iconRotation270) - assertThat(iconRotation90).isNotEqualTo(iconRotation180) - assertThat(iconRotation90).isNotEqualTo(iconRotation270) - assertThat(iconRotation180).isNotEqualTo(iconRotation270) - } - } - } - - @Test - fun sfpsIconUpdates_onRearDisplayMode() { - if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) { - runGenericTest { - val currentIcon by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset) - - kosmos.displayStateRepository.setIsInRearDisplayMode(false) - val iconNotRearDisplayMode = currentIcon - - kosmos.displayStateRepository.setIsInRearDisplayMode(true) - val iconRearDisplayMode = currentIcon - - assertThat(iconNotRearDisplayMode).isNotEqualTo(iconRearDisplayMode) - } - } - } - - private suspend fun TestScope.showAuthenticated( - authenticatedModality: BiometricModality, - expectConfirmation: Boolean, - ) { - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val fpStartMode by collectLastValue(kosmos.promptViewModel.fingerprintStartMode) - val size by collectLastValue(kosmos.promptViewModel.size) - - val authWithSmallPrompt = - testCase.shouldStartAsImplicitFlow && - (fpStartMode == FingerprintStartMode.Pending || testCase.isFaceOnly) - assertThat(authenticating).isTrue() - assertThat(authenticated?.isNotAuthenticated).isTrue() - assertThat(size).isEqualTo(if (authWithSmallPrompt) PromptSize.SMALL else PromptSize.MEDIUM) - assertButtonsVisible(negative = !authWithSmallPrompt) - - kosmos.promptViewModel.showAuthenticated(authenticatedModality, DELAY) - - assertThat(authenticated?.isAuthenticated).isTrue() - assertThat(authenticated?.delay).isEqualTo(DELAY) - assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation) - assertThat(size) - .isEqualTo( - if (authenticatedModality == BiometricModality.Fingerprint || expectConfirmation) { - PromptSize.MEDIUM - } else { - PromptSize.SMALL - } - ) - - assertButtonsVisible(cancel = expectConfirmation, confirm = expectConfirmation) - } - - @Test - fun shows_temporary_errors() = runGenericTest { - val checkAtEnd = suspend { assertButtonsVisible(negative = true) } - - showTemporaryErrors(restart = false) { checkAtEnd() } - showTemporaryErrors(restart = false, helpAfterError = "foo") { checkAtEnd() } - showTemporaryErrors(restart = true) { checkAtEnd() } - } - - @Test - @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun set_haptic_on_errors() = runGenericTest { - kosmos.promptViewModel.showTemporaryError( - "so sad", - messageAfterError = "", - authenticateAfterError = false, - hapticFeedback = true, - ) - - val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.HapticConstant - assertThat(haptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT) - assertThat(haptics.flag).isNull() - } - - @Test - @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun set_msdl_haptic_on_errors() = runGenericTest { - kosmos.promptViewModel.showTemporaryError( - "so sad", - messageAfterError = "", - authenticateAfterError = false, - hapticFeedback = true, - ) - - val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.MSDL - assertThat(haptics.token).isEqualTo(MSDLToken.FAILURE) - assertThat(haptics.properties).isEqualTo(authInteractionProperties) - } - - @Test - @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun plays_haptic_on_errors_unless_skipped() = runGenericTest { - kosmos.promptViewModel.showTemporaryError( - "still sad", - messageAfterError = "", - authenticateAfterError = false, - hapticFeedback = false, - ) - - val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - assertThat(hapticsToPlay).isEqualTo(PromptViewModel.HapticsToPlay.None) - } - - @Test - @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun plays_msdl_haptic_on_errors_unless_skipped() = runGenericTest { - kosmos.promptViewModel.showTemporaryError( - "still sad", - messageAfterError = "", - authenticateAfterError = false, - hapticFeedback = false, - ) - - val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - assertThat(hapticsToPlay).isEqualTo(PromptViewModel.HapticsToPlay.None) - } - - @Test - @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun plays_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - kosmos.promptViewModel.showTemporaryError( - "still sad", - messageAfterError = "", - authenticateAfterError = false, - hapticFeedback = true, - ) - - val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.HapticConstant - if (expectConfirmation) { - assertThat(haptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT) - assertThat(haptics.flag).isNull() - } else { - assertThat(haptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM) - } - } - - @Test - @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) - fun plays_msdl_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - kosmos.promptViewModel.showTemporaryError( - "still sad", - messageAfterError = "", - authenticateAfterError = false, - hapticFeedback = true, - ) - - val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay) - val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.MSDL - if (expectConfirmation) { - assertThat(haptics.token).isEqualTo(MSDLToken.FAILURE) - } else { - assertThat(haptics.token).isEqualTo(MSDLToken.UNLOCK) - } - assertThat(haptics.properties).isEqualTo(authInteractionProperties) - } - - private suspend fun TestScope.showTemporaryErrors( - restart: Boolean, - helpAfterError: String = "", - block: suspend TestScope.() -> Unit = {}, - ) { - val errorMessage = "oh no!" - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val message by collectLastValue(kosmos.promptViewModel.message) - val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible) - val size by collectLastValue(kosmos.promptViewModel.size) - val canTryAgainNow by collectLastValue(kosmos.promptViewModel.canTryAgainNow) - - val errorJob = launch { - kosmos.promptViewModel.showTemporaryError( - errorMessage, - authenticateAfterError = restart, - messageAfterError = helpAfterError, - ) - } - - assertThat(size).isEqualTo(PromptSize.MEDIUM) - assertThat(message).isEqualTo(PromptMessage.Error(errorMessage)) - assertThat(messageVisible).isTrue() - - // temporary error should disappear after a delay - errorJob.join() - if (helpAfterError.isNotBlank()) { - assertThat(message).isEqualTo(PromptMessage.Help(helpAfterError)) - assertThat(messageVisible).isTrue() - } else { - assertThat(message).isEqualTo(PromptMessage.Empty) - assertThat(messageVisible).isFalse() - } - - assertThat(authenticating).isEqualTo(restart) - assertThat(authenticated?.isNotAuthenticated).isTrue() - assertThat(canTryAgainNow).isFalse() - - block() - } - - @Test - fun no_errors_or_temporary_help_after_authenticated() = runGenericTest { - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val message by collectLastValue(kosmos.promptViewModel.message) - val messageIsShowing by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible) - val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow) - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - val verifyNoError = { - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - assertThat(message).isEqualTo(PromptMessage.Empty) - assertThat(canTryAgain).isFalse() - } - - val errorJob = launch { - kosmos.promptViewModel.showTemporaryError( - "error", - messageAfterError = "", - authenticateAfterError = false, - ) - } - verifyNoError() - errorJob.join() - verifyNoError() - - val helpJob = launch { kosmos.promptViewModel.showTemporaryHelp("hi") } - verifyNoError() - helpJob.join() - verifyNoError() - - // persistent help is allowed - val stickyHelpMessage = "blah" - kosmos.promptViewModel.showHelp(stickyHelpMessage) - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - assertThat(message).isEqualTo(PromptMessage.Help(stickyHelpMessage)) - assertThat(messageIsShowing).isTrue() - } - - @Test - fun suppress_temporary_error() = runGenericTest { - val messages by collectValues(kosmos.promptViewModel.message) - - for (error in listOf("never", "see", "me")) { - launch { - kosmos.promptViewModel.showTemporaryError( - error, - messageAfterError = "or me", - authenticateAfterError = false, - suppressIf = { _, _ -> true }, - ) - } - } - - testScheduler.advanceUntilIdle() - assertThat(messages).containsExactly(PromptMessage.Empty) - } - - @Test - fun suppress_temporary_error_when_already_showing_when_requested() = - suppress_temporary_error_when_already_showing(suppress = true) - - @Test - fun do_not_suppress_temporary_error_when_already_showing_when_not_requested() = - suppress_temporary_error_when_already_showing(suppress = false) - - private fun suppress_temporary_error_when_already_showing(suppress: Boolean) = runGenericTest { - val errors = listOf("woot", "oh yeah", "nope") - val afterSuffix = "(after)" - val expectedErrorMessage = if (suppress) errors.first() else errors.last() - val messages by collectValues(kosmos.promptViewModel.message) - - for (error in errors) { - launch { - kosmos.promptViewModel.showTemporaryError( - error, - messageAfterError = "$error $afterSuffix", - authenticateAfterError = false, - suppressIf = { currentMessage, _ -> suppress && currentMessage.isError }, - ) - } - } - - testScheduler.runCurrent() - assertThat(messages) - .containsExactly(PromptMessage.Empty, PromptMessage.Error(expectedErrorMessage)) - .inOrder() - - testScheduler.advanceUntilIdle() - assertThat(messages) - .containsExactly( - PromptMessage.Empty, - PromptMessage.Error(expectedErrorMessage), - PromptMessage.Help("$expectedErrorMessage $afterSuffix"), - ) - .inOrder() - } - - @Test - fun authenticated_at_most_once_same_modality() = runGenericTest { - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - } - - @Test - fun authenticating_cannot_restart_after_authenticated() = runGenericTest { - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - - kosmos.promptViewModel.showAuthenticating("again!") - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - } - - @Test - fun confirm_authentication() = runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val message by collectLastValue(kosmos.promptViewModel.message) - val size by collectLastValue(kosmos.promptViewModel.size) - val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow) - - assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation) - if (expectConfirmation) { - assertThat(size).isEqualTo(PromptSize.MEDIUM) - assertButtonsVisible(cancel = true, confirm = true) - - kosmos.promptViewModel.confirmAuthenticated() - assertThat(message).isEqualTo(PromptMessage.Empty) - assertButtonsVisible() - } - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - assertThat(canTryAgain).isFalse() - } - - @Test - fun second_authentication_acts_as_confirmation() = runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val message by collectLastValue(kosmos.promptViewModel.message) - val size by collectLastValue(kosmos.promptViewModel.size) - val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow) - - assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation) - if (expectConfirmation) { - assertThat(size).isEqualTo(PromptSize.MEDIUM) - assertButtonsVisible(cancel = true, confirm = true) - - if (testCase.modalities.hasSfps) { - kosmos.promptViewModel.showAuthenticated(BiometricModality.Fingerprint, 0) - assertThat(message).isEqualTo(PromptMessage.Empty) - assertButtonsVisible() - } - } - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - assertThat(canTryAgain).isFalse() - } - - @Test - fun auto_confirm_authentication_when_finger_down() = runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - - if (testCase.isCoex) { - kosmos.promptViewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN)) - } - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val message by collectLastValue(kosmos.promptViewModel.message) - val size by collectLastValue(kosmos.promptViewModel.size) - val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow) - - assertThat(authenticating).isFalse() - assertThat(canTryAgain).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - - if (expectConfirmation) { - if (testCase.isFaceOnly) { - assertThat(size).isEqualTo(PromptSize.MEDIUM) - assertButtonsVisible(cancel = true, confirm = true) - - kosmos.promptViewModel.confirmAuthenticated() - } else if (testCase.isCoex) { - assertThat(authenticated?.isAuthenticatedAndConfirmed).isTrue() - } - assertThat(message).isEqualTo(PromptMessage.Empty) - assertButtonsVisible() - } - } - - @Test - fun cannot_auto_confirm_authentication_when_finger_up() = runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - - if (testCase.isCoex) { - kosmos.promptViewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN)) - kosmos.promptViewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_UP)) - } - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val message by collectLastValue(kosmos.promptViewModel.message) - val size by collectLastValue(kosmos.promptViewModel.size) - val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow) - - assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation) - if (expectConfirmation) { - assertThat(size).isEqualTo(PromptSize.MEDIUM) - assertButtonsVisible(cancel = true, confirm = true) - - kosmos.promptViewModel.confirmAuthenticated() - assertThat(message).isEqualTo(PromptMessage.Empty) - assertButtonsVisible() - } - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - assertThat(canTryAgain).isFalse() - } - - @Test - fun cannot_confirm_unless_authenticated() = runGenericTest { - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - - kosmos.promptViewModel.confirmAuthenticated() - assertThat(authenticating).isTrue() - assertThat(authenticated?.isNotAuthenticated).isTrue() - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - - // reconfirm should be a no-op - kosmos.promptViewModel.confirmAuthenticated() - kosmos.promptViewModel.confirmAuthenticated() - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isNotAuthenticated).isFalse() - } - - @Test - fun shows_help_before_authenticated() = runGenericTest { - val helpMessage = "please help yourself to some cookies" - val message by collectLastValue(kosmos.promptViewModel.message) - val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible) - val size by collectLastValue(kosmos.promptViewModel.size) - - kosmos.promptViewModel.showHelp(helpMessage) - - assertThat(size).isEqualTo(PromptSize.MEDIUM) - assertThat(message).isEqualTo(PromptMessage.Help(helpMessage)) - assertThat(messageVisible).isTrue() - - assertThat(kosmos.promptViewModel.isAuthenticating.first()).isFalse() - assertThat(kosmos.promptViewModel.isAuthenticated.first().isNotAuthenticated).isTrue() - } - - @Test - fun shows_help_after_authenticated() = runGenericTest { - val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - val helpMessage = "more cookies please" - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val message by collectLastValue(kosmos.promptViewModel.message) - val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible) - val size by collectLastValue(kosmos.promptViewModel.size) - val confirmationRequired by collectLastValue(kosmos.promptViewModel.isConfirmationRequired) - - if (testCase.isCoex && testCase.authenticatedByFingerprint) { - kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true) - } - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - kosmos.promptViewModel.showHelp(helpMessage) - - assertThat(size).isEqualTo(PromptSize.MEDIUM) - - assertThat(message).isEqualTo(PromptMessage.Help(helpMessage)) - assertThat(messageVisible).isTrue() - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isTrue() - assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation) - assertButtonsVisible(cancel = expectConfirmation, confirm = expectConfirmation) - } - - @Test - fun retries_after_failure() = runGenericTest { - val errorMessage = "bad" - val helpMessage = "again?" - val expectTryAgainButton = testCase.isFaceOnly - val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating) - val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated) - val message by collectLastValue(kosmos.promptViewModel.message) - val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible) - val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow) - - kosmos.promptViewModel.showAuthenticating("go") - val errorJob = launch { - kosmos.promptViewModel.showTemporaryError( - errorMessage, - messageAfterError = helpMessage, - authenticateAfterError = false, - failedModality = testCase.authenticatedModality, - ) - } - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isFalse() - assertThat(message).isEqualTo(PromptMessage.Error(errorMessage)) - assertThat(messageVisible).isTrue() - assertThat(canTryAgain).isEqualTo(testCase.authenticatedByFace) - assertButtonsVisible(negative = true, tryAgain = expectTryAgainButton) - - errorJob.join() - - assertThat(authenticating).isFalse() - assertThat(authenticated?.isAuthenticated).isFalse() - assertThat(message).isEqualTo(PromptMessage.Help(helpMessage)) - assertThat(messageVisible).isTrue() - assertThat(canTryAgain).isEqualTo(testCase.authenticatedByFace) - assertButtonsVisible(negative = true, tryAgain = expectTryAgainButton) - - val helpMessage2 = "foo" - kosmos.promptViewModel.showAuthenticating(helpMessage2, isRetry = true) - assertThat(authenticating).isTrue() - assertThat(authenticated?.isAuthenticated).isFalse() - assertThat(message).isEqualTo(PromptMessage.Help(helpMessage2)) - assertThat(messageVisible).isTrue() - assertButtonsVisible(negative = true) - } - - @Test - fun switch_to_credential_fallback() = runGenericTest { - val size by collectLastValue(kosmos.promptViewModel.size) - - // TODO(b/251476085): remove Spaghetti, migrate logic, and update this test - kosmos.promptViewModel.onSwitchToCredential() - - assertThat(size).isEqualTo(PromptSize.LARGE) - } - - @Test - fun hint_for_talkback_guidance() = runGenericTest { - val hint by collectLastValue(kosmos.promptViewModel.accessibilityHint) - - // Touches should fall outside of sensor area - whenever(kosmos.udfpsUtils.getTouchInNativeCoordinates(any(), any(), any())) - .thenReturn(Point(0, 0)) - whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any())) - .thenReturn("Direction") - - kosmos.promptViewModel.onAnnounceAccessibilityHint( - obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER), - true, - ) - - if (testCase.modalities.hasUdfps) { - assertThat(hint?.isNotBlank()).isTrue() - } else { - assertThat(hint.isNullOrBlank()).isTrue() - } - } - - @Test - fun no_hint_for_talkback_guidance_after_auth() = runGenericTest { - val hint by collectLastValue(kosmos.promptViewModel.accessibilityHint) - - kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0) - kosmos.promptViewModel.confirmAuthenticated() - - // Touches should fall outside of sensor area - whenever(kosmos.udfpsUtils.getTouchInNativeCoordinates(any(), any(), any())) - .thenReturn(Point(0, 0)) - whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any())) - .thenReturn("Direction") - - kosmos.promptViewModel.onAnnounceAccessibilityHint( - obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER), - true, - ) - - assertThat(hint.isNullOrBlank()).isTrue() - } - - @Test - fun descriptionOverriddenByVerticalListContentView() = - runGenericTest(description = "test description", contentView = promptContentView) { - val contentView by collectLastValue(kosmos.promptViewModel.contentView) - val description by collectLastValue(kosmos.promptViewModel.description) - - assertThat(description).isEqualTo("") - assertThat(contentView).isEqualTo(promptContentView) - } - - @Test - fun descriptionOverriddenByContentViewWithMoreOptionsButton() = - runGenericTest( - description = "test description", - contentView = promptContentViewWithMoreOptionsButton, - ) { - val contentView by collectLastValue(kosmos.promptViewModel.contentView) - val description by collectLastValue(kosmos.promptViewModel.description) - - assertThat(description).isEqualTo("") - assertThat(contentView).isEqualTo(promptContentViewWithMoreOptionsButton) - } - - @Test - fun descriptionWithoutContentView() = - runGenericTest(description = "test description") { - val contentView by collectLastValue(kosmos.promptViewModel.contentView) - val description by collectLastValue(kosmos.promptViewModel.description) - - assertThat(description).isEqualTo("test description") - assertThat(contentView).isNull() - } - - @Test - fun logo_nullIfPkgNameNotFound() = - runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo).isNotNull() - assertThat(logoInfo!!.first).isNull() - assertThat(logoInfo!!.second).isEqualTo("") - } - - @Test - fun logo_defaultIsNull() = - runGenericTest(packageName = OP_PACKAGE_NAME_NO_LOGO_INFO) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo).isNotNull() - assertThat(logoInfo!!.first).isNull() - assertThat(logoInfo!!.second).isEqualTo("") - } - - @Test - fun logo_defaultFromActivityInfo() = - runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - - assertThat(logoInfo).isNotNull() - // 1. PM.getApplicationInfo(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) is set to return - // applicationInfoWithIconAndDescription with "defaultLogoIconFromAppInfo", - // 2. iconProvider.getIcon(activityInfo) is set to return - // "defaultLogoIconFromActivityInfo" - // For the apps with OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO, 2 should be called instead of 1 - assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconFromActivityInfo) - // 1. PM.getApplicationInfo(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) is set to return - // applicationInfoWithIconAndDescription with "defaultLogoDescriptionFromAppInfo", - // 2. activityInfo.loadLabel() is set to return defaultLogoDescriptionFromActivityInfo - // For the apps with OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO, 2 should be called instead of 1 - assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromActivityInfo) - } - - @Test - fun logo_defaultFromApplicationInfo() = runGenericTest { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo).isNotNull() - assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconFromAppInfo) - assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo) - } - - @Test - fun logo_defaultWithWorkBadge() = - runGenericTest(userId = WORK_USER_ID) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo).isNotNull() - assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconWithBadge) - // Logo label does not use badge info. - assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo) - } - - @Test - fun logoRes_setByApp() = - runGenericTest(logoRes = logoResFromApp) { - val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap() - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo).isNotNull() - assertThat((logoInfo!!.first as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue() - } - - @Test - fun logoBitmap_setByApp() = - runGenericTest(logoBitmap = logoBitmapFromApp) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat((logoInfo!!.first as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp) - } - - @Test - fun logoDescription_setByApp() = - runGenericTest(logoDescription = logoDescriptionFromApp) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo!!.second).isEqualTo(logoDescriptionFromApp) - } - - @Test - fun position_bottom_rotation0() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) - val position by collectLastValue(kosmos.promptViewModel.position) - assertThat(position).isEqualTo(PromptPosition.Bottom) - } // TODO(b/335278136): Add test for no sensor landscape - - @Test - fun position_bottom_forceLarge() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) - kosmos.promptViewModel.onSwitchToCredential() - val position by collectLastValue(kosmos.promptViewModel.position) - assertThat(position).isEqualTo(PromptPosition.Bottom) - } - - @Test - fun position_bottom_largeScreen() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) - kosmos.displayStateRepository.setIsLargeScreen(true) - val position by collectLastValue(kosmos.promptViewModel.position) - assertThat(position).isEqualTo(PromptPosition.Bottom) - } - - @Test - fun position_right_rotation90() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) - val position by collectLastValue(kosmos.promptViewModel.position) - assertThat(position).isEqualTo(PromptPosition.Right) - } - - @Test - fun position_left_rotation270() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) - val position by collectLastValue(kosmos.promptViewModel.position) - assertThat(position).isEqualTo(PromptPosition.Left) - } - - @Test - fun position_top_rotation180() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) - val position by collectLastValue(kosmos.promptViewModel.position) - if (testCase.modalities.hasUdfps) { - assertThat(position).isEqualTo(PromptPosition.Top) - } else { - assertThat(position).isEqualTo(PromptPosition.Bottom) - } - } - - @Test - fun guideline_bottom() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) - val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds) - assertThat(guidelineBounds).isEqualTo(Rect(0, mediumTopGuidelinePadding, 0, 0)) - } // TODO(b/335278136): Add test for no sensor landscape - - @Test - fun guideline_right() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) - - val isSmall = testCase.shouldStartAsImplicitFlow - val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds) - - if (isSmall) { - assertThat(guidelineBounds).isEqualTo(Rect(-smallHorizontalGuidelinePadding, 0, 0, 0)) - } else if (testCase.modalities.hasUdfps) { - assertThat(guidelineBounds).isEqualTo(Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0)) - } else { - assertThat(guidelineBounds).isEqualTo(Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0)) - } - } - - @Test - fun guideline_right_onlyShortTitle() = - runGenericTest(subtitle = "") { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) - - val isSmall = testCase.shouldStartAsImplicitFlow - val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds) - - if (!isSmall && testCase.modalities.hasUdfps) { - assertThat(guidelineBounds) - .isEqualTo(Rect(-udfpsHorizontalShorterGuidelinePadding, 0, 0, 0)) - } - } - - @Test - fun guideline_left() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) - - val isSmall = testCase.shouldStartAsImplicitFlow - val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds) - - if (isSmall) { - assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -smallHorizontalGuidelinePadding, 0)) - } else if (testCase.modalities.hasUdfps) { - assertThat(guidelineBounds).isEqualTo(Rect(0, 0, udfpsHorizontalGuidelinePadding, 0)) - } else { - assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -mediumHorizontalGuidelinePadding, 0)) - } - } - - @Test - fun guideline_left_onlyShortTitle() = - runGenericTest(subtitle = "") { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) - - val isSmall = testCase.shouldStartAsImplicitFlow - val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds) - - if (!isSmall && testCase.modalities.hasUdfps) { - assertThat(guidelineBounds) - .isEqualTo(Rect(0, 0, -udfpsHorizontalShorterGuidelinePadding, 0)) - } - } - - @Test - fun guideline_top() = runGenericTest { - kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) - val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds) - if (testCase.modalities.hasUdfps) { - assertThat(guidelineBounds).isEqualTo(Rect(0, 0, 0, 0)) - } - } - - @Test - fun iconViewLoaded() = runGenericTest { - val isIconViewLoaded by collectLastValue(kosmos.promptViewModel.isIconViewLoaded) - // TODO(b/328677869): Add test for noIcon logic. - assertThat(isIconViewLoaded).isFalse() - - kosmos.promptViewModel.setIsIconViewLoaded(true) - - assertThat(isIconViewLoaded).isTrue() - } - - /** Asserts that the selected buttons are visible now. */ - private suspend fun TestScope.assertButtonsVisible( - tryAgain: Boolean = false, - confirm: Boolean = false, - cancel: Boolean = false, - negative: Boolean = false, - credential: Boolean = false, - ) { - runCurrent() - assertThat(kosmos.promptViewModel.isTryAgainButtonVisible.first()).isEqualTo(tryAgain) - assertThat(kosmos.promptViewModel.isConfirmButtonVisible.first()).isEqualTo(confirm) - assertThat(kosmos.promptViewModel.isCancelButtonVisible.first()).isEqualTo(cancel) - assertThat(kosmos.promptViewModel.isNegativeButtonVisible.first()).isEqualTo(negative) - assertThat(kosmos.promptViewModel.isCredentialButtonVisible.first()).isEqualTo(credential) - } - - private fun runGenericTest( - doNotStart: Boolean = false, - allowCredentialFallback: Boolean = false, - subtitle: String? = "s", - description: String? = null, - contentView: PromptContentView? = null, - logoRes: Int = 0, - logoBitmap: Bitmap? = null, - logoDescription: String? = null, - packageName: String = OP_PACKAGE_NAME_WITH_APP_LOGO, - userId: Int = USER_ID, - block: suspend TestScope.() -> Unit, - ) { - val topActivity = ComponentName(packageName, "test app") - runningTaskInfo.topActivity = topActivity - whenever(kosmos.activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo)) - kosmos.promptSelectorInteractor.resetPrompt(REQUEST_ID) - - kosmos.promptSelectorInteractor.initializePrompt( - requireConfirmation = testCase.confirmationRequested, - allowCredentialFallback = allowCredentialFallback, - fingerprint = testCase.fingerprint, - face = testCase.face, - subtitleFromApp = subtitle, - descriptionFromApp = description, - contentViewFromApp = contentView, - logoResFromApp = logoRes, - logoBitmapFromApp = if (logoRes != 0) logoDrawableFromAppRes.toBitmap() else logoBitmap, - logoDescriptionFromApp = logoDescription, - packageName = packageName, - userId = userId, - ) - - kosmos.biometricStatusRepository.setFingerprintAcquiredStatus( - AcquiredFingerprintAuthenticationStatus( - AuthenticationReason.BiometricPromptAuthentication, - BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN, - ) - ) - - // put the view model in the initial authenticating state, unless explicitly skipped - val startMode = - when { - doNotStart -> null - testCase.isCoex -> FingerprintStartMode.Delayed - else -> FingerprintStartMode.Normal - } - when (startMode) { - FingerprintStartMode.Normal -> { - kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = false) - kosmos.promptViewModel.showAuthenticating() - } - FingerprintStartMode.Delayed -> { - kosmos.promptViewModel.showAuthenticating() - } - else -> { - /* skip */ - } - } - - if (testCase.fingerprint?.isAnyUdfpsType == true) { - kosmos.testScope.collectLastValue(kosmos.udfpsOverlayInteractor.udfpsOverlayParams) - kosmos.testScope.runCurrent() - overrideUdfpsOverlayParams() - } - - kosmos.testScope.runTest { block() } - } - - private fun overrideUdfpsOverlayParams(isLandscape: Boolean = false) { - val authControllerCallback = authController.captureCallback() - authControllerCallback.onUdfpsLocationChanged( - mockUdfpsOverlayParams(isLandscape = isLandscape) - ) - } - - /** Obtain a MotionEvent with the specified MotionEvent action constant */ - private fun obtainMotionEvent(action: Int): MotionEvent = - MotionEvent.obtain(0, 0, action, 0f, 0f, 0) - - companion object { - @JvmStatic - @Parameters(name = "{0}") - fun data(): Collection = singleModalityTestCases + coexTestCases - - private val singleModalityTestCases = - listOf( - TestCase( - face = faceSensorPropertiesInternal(strong = true).first(), - authenticatedModality = BiometricModality.Face, - ), - TestCase( - fingerprint = - fingerprintSensorPropertiesInternal( - sensorType = FingerprintSensorProperties.TYPE_REAR - ) - .first(), - authenticatedModality = BiometricModality.Fingerprint, - ), - TestCase( - fingerprint = - fingerprintSensorPropertiesInternal( - strong = true, - sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON, - ) - .first(), - authenticatedModality = BiometricModality.Fingerprint, - isInRearDisplayMode = false, - ), - TestCase( - fingerprint = - fingerprintSensorPropertiesInternal( - strong = true, - sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON, - ) - .first(), - authenticatedModality = BiometricModality.Fingerprint, - isInRearDisplayMode = true, - ), - TestCase( - fingerprint = - fingerprintSensorPropertiesInternal( - strong = true, - sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - ) - .first(), - authenticatedModality = BiometricModality.Fingerprint, - ), - TestCase( - face = faceSensorPropertiesInternal(strong = true).first(), - authenticatedModality = BiometricModality.Face, - confirmationRequested = true, - ), - TestCase( - fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), - authenticatedModality = BiometricModality.Fingerprint, - confirmationRequested = true, - ), - TestCase( - fingerprint = - fingerprintSensorPropertiesInternal( - strong = true, - sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON, - ) - .first(), - authenticatedModality = BiometricModality.Fingerprint, - confirmationRequested = true, - ), - ) - - private val coexTestCases = - listOf( - TestCase( - face = faceSensorPropertiesInternal(strong = true).first(), - fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), - authenticatedModality = BiometricModality.Face, - ), - TestCase( - face = faceSensorPropertiesInternal(strong = true).first(), - fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), - authenticatedModality = BiometricModality.Face, - confirmationRequested = true, - ), - TestCase( - face = faceSensorPropertiesInternal(strong = true).first(), - fingerprint = - fingerprintSensorPropertiesInternal( - strong = true, - sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON, - ) - .first(), - authenticatedModality = BiometricModality.Fingerprint, - confirmationRequested = true, - ), - TestCase( - face = faceSensorPropertiesInternal(strong = true).first(), - fingerprint = - fingerprintSensorPropertiesInternal( - strong = true, - sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - ) - .first(), - authenticatedModality = BiometricModality.Fingerprint, - ), - ) - } -} - -internal data class TestCase( - val fingerprint: FingerprintSensorPropertiesInternal? = null, - val face: FaceSensorPropertiesInternal? = null, - val isInRearDisplayMode: Boolean = false, - val authenticatedModality: BiometricModality, - val confirmationRequested: Boolean = false, -) { - override fun toString(): String { - val modality = - when { - fingerprint != null && face != null -> "coex" - fingerprint != null && fingerprint.isAnySidefpsType -> "fingerprint only, sideFps" - fingerprint != null && fingerprint.isAnyUdfpsType -> "fingerprint only, udfps" - fingerprint != null && - fingerprint.sensorType == FingerprintSensorProperties.TYPE_REAR -> - "fingerprint only, rearFps" - face != null -> "face only" - else -> "?" - } - return "[$modality, isInRearDisplayMode: $isInRearDisplayMode, by: " + - "$authenticatedModality, confirm: $confirmationRequested]" - } - - fun expectConfirmation(atLeastOneFailure: Boolean): Boolean = - when { - isCoex && authenticatedModality == BiometricModality.Face -> - atLeastOneFailure || confirmationRequested - isFaceOnly -> confirmationRequested - else -> false - } - - val modalities: BiometricModalities - get() = BiometricModalities(fingerprint, face) - - val authenticatedByFingerprint: Boolean - get() = authenticatedModality == BiometricModality.Fingerprint - - val authenticatedByFace: Boolean - get() = authenticatedModality == BiometricModality.Face - - val isFaceOnly: Boolean - get() = face != null && fingerprint == null - - val isFingerprintOnly: Boolean - get() = face == null && fingerprint != null - - val isCoex: Boolean - get() = face != null && fingerprint != null - - @FingerprintSensorProperties.SensorType val sensorType: Int? = fingerprint?.sensorType - - val shouldStartAsImplicitFlow: Boolean - get() = (isFaceOnly || isCoex) && !confirmationRequested -} - -/** Initialize the test by selecting the give [fingerprint] or [face] configuration(s). */ -private fun PromptSelectorInteractor.initializePrompt( - fingerprint: FingerprintSensorPropertiesInternal? = null, - face: FaceSensorPropertiesInternal? = null, - requireConfirmation: Boolean = false, - allowCredentialFallback: Boolean = false, - subtitleFromApp: String? = "s", - descriptionFromApp: String? = null, - contentViewFromApp: PromptContentView? = null, - logoResFromApp: Int = 0, - logoBitmapFromApp: Bitmap? = null, - logoDescriptionFromApp: String? = null, - packageName: String = OP_PACKAGE_NAME_WITH_APP_LOGO, - userId: Int = USER_ID, -) { - val info = - PromptInfo().apply { - logoDescription = logoDescriptionFromApp - title = "t" - subtitle = subtitleFromApp - description = descriptionFromApp - contentView = contentViewFromApp - authenticators = listOf(face, fingerprint).extractAuthenticatorTypes() - isDeviceCredentialAllowed = allowCredentialFallback - isConfirmationRequested = requireConfirmation - } - if (logoBitmapFromApp != null) { - info.setLogo(logoResFromApp, logoBitmapFromApp) - } - - setPrompt( - info, - userId, - REQUEST_ID, - BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face), - CHALLENGE, - packageName, - onSwitchToCredential = false, - isLandscape = false, - ) -} - -private fun AuthController.captureCallback() = - withArgCaptor { - Mockito.verify(this@captureCallback).addCallback(capture()) - } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt deleted file mode 100644 index b4200b6850c8..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.bluetooth.qsdialog - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import android.testing.TestableLooper -import android.widget.Button -import android.widget.TextView -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.kosmos.testScope -import com.android.systemui.res.R -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.testKosmos -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule -import org.mockito.kotlin.spy -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever - -@SmallTest -@RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -class AudioSharingDialogDelegateTest : SysuiTestCase() { - @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() - private val kosmos = testKosmos() - private val updateFlow = MutableSharedFlow() - private lateinit var underTest: AudioSharingDialogDelegate - - @Before - fun setUp() { - with(kosmos) { - // TODO(b/364515243): use real object instead of mock - whenever(deviceItemInteractor.deviceItemUpdateRequest).thenReturn(updateFlow) - whenever(deviceItemInteractor.deviceItemUpdate) - .thenReturn(MutableStateFlow(emptyList())) - underTest = audioSharingDialogDelegate - } - } - - @Test - fun testCreateDialog() = - kosmos.testScope.runTest { - val dialog = underTest.createDialog() - assertThat(dialog).isInstanceOf(SystemUIDialog::class.java) - } - - @Test - fun testCreateDialog_showState() = - with(kosmos) { - testScope.runTest { - val availableDeviceName = "name" - whenever(cachedBluetoothDevice.name).thenReturn(availableDeviceName) - val dialog = spy(underTest.createDialog()) - dialog.show() - runCurrent() - val subtitleTextView = dialog.findViewById(R.id.subtitle) - val switchActiveButton = dialog.findViewById