diff --git a/tests/robotests/src/com/android/settings/biometrics2/OWNERS b/tests/robotests/src/com/android/settings/biometrics2/OWNERS new file mode 100644 index 00000000000..a257ed861ad --- /dev/null +++ b/tests/robotests/src/com/android/settings/biometrics2/OWNERS @@ -0,0 +1 @@ +include /src/com/android/settings/biometrics/OWNERS diff --git a/tests/robotests/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java b/tests/robotests/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java new file mode 100644 index 00000000000..89626def4c3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java @@ -0,0 +1,179 @@ +/* + * 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.settings.biometrics2.ui.viewmodel; + +import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL; +import static android.hardware.fingerprint.FingerprintManager.ENROLL_FIND_SENSOR; +import static android.hardware.fingerprint.FingerprintManager.EnrollReason; +import static android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; + +import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_STEPS; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Application; +import android.content.res.Resources; +import android.os.CancellationSignal; + +import com.android.settings.R; +import com.android.settings.biometrics.fingerprint.FingerprintUpdater; +import com.android.settings.biometrics2.ui.model.EnrollmentProgress; +import com.android.settings.testutils.InstantTaskExecutorRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class FingerprintEnrollProgressViewModelTest { + + @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule(); + + @Mock private Application mApplication; + @Mock private Resources mResources; + @Mock private FingerprintUpdater mFingerprintUpdater; + + private FingerprintEnrollProgressViewModel mViewModel; + + @Before + public void setUp() { + when(mApplication.getResources()).thenReturn(mResources); + when(mResources.getBoolean(R.bool.enrollment_message_display_controller_flag)) + .thenReturn(false); + mViewModel = new FingerprintEnrollProgressViewModel(mApplication, mFingerprintUpdater); + } + + @Test + public void testStartEnrollment() { + @EnrollReason final int enrollReason = ENROLL_FIND_SENSOR; + final int userId = 334; + final byte[] token = new byte[] { 1, 2, 3 }; + mViewModel.setToken(token); + mViewModel.setUserId(userId); + + // Start enrollment + final boolean ret = mViewModel.startEnrollment(enrollReason); + + assertThat(ret).isTrue(); + verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), + eq(userId), any(EnrollmentCallback.class), eq(enrollReason)); + } + + @Test + public void testStartEnrollmentFailBecauseOfNoToken() { + // Start enrollment + final boolean ret = mViewModel.startEnrollment(ENROLL_FIND_SENSOR); + + assertThat(ret).isFalse(); + verify(mFingerprintUpdater, never()).enroll(any(byte[].class), + any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt()); + } + + @Test + public void testCancelEnrollment() { + @EnrollReason final int enrollReason = ENROLL_ENROLL; + final int userId = 334; + final byte[] token = new byte[] { 1, 2, 3 }; + mViewModel.setToken(token); + mViewModel.setUserId(userId); + + final TestWrapper signalWrapper = new TestWrapper<>(); + doAnswer(invocation -> { + signalWrapper.mValue = invocation.getArgument(1); + return null; + }).when(mFingerprintUpdater).enroll(any(byte[].class), any(CancellationSignal.class), + eq(userId), any(EnrollmentCallback.class), anyInt()); + + // Start enrollment + final boolean ret = mViewModel.startEnrollment(enrollReason); + assertThat(ret).isTrue(); + assertThat(signalWrapper.mValue).isNotNull(); + + // Cancel enrollment + mViewModel.cancelEnrollment(); + + assertThat(signalWrapper.mValue.isCanceled()).isTrue(); + } + + @Test + public void testProgressUpdate() { + @EnrollReason final int enrollReason = ENROLL_ENROLL; + final int userId = 334; + final byte[] token = new byte[] { 1, 2, 3 }; + mViewModel.setToken(token); + mViewModel.setUserId(userId); + + final TestWrapper callbackWrapper = new TestWrapper<>(); + doAnswer(invocation -> { + callbackWrapper.mValue = invocation.getArgument(3); + return null; + }).when(mFingerprintUpdater).enroll(any(byte[].class), any(CancellationSignal.class), + eq(userId), any(EnrollmentCallback.class), anyInt()); + + // Start enrollment + final boolean ret = mViewModel.startEnrollment(enrollReason); + assertThat(ret).isTrue(); + assertThat(callbackWrapper.mValue).isNotNull(); + + // Update first progress + callbackWrapper.mValue.onEnrollmentProgress(25); + EnrollmentProgress progress = mViewModel.getProgressLiveData().getValue(); + assertThat(progress).isNotNull(); + assertThat(progress.getSteps()).isEqualTo(25); + // TODO(b/260957933) verify getRemaining() when it is really used + //assertThat(progress.getRemaining()).isEqualTo(25); + + // Update second progress + callbackWrapper.mValue.onEnrollmentProgress(20); + progress = mViewModel.getProgressLiveData().getValue(); + assertThat(progress).isNotNull(); + assertThat(progress.getSteps()).isEqualTo(25); + // TODO(b/260957933) verify getRemaining() when it is really used + //assertThat(progress.getRemaining()).isEqualTo(20); + + // Clear progress + mViewModel.clearProgressLiveData(); + progress = mViewModel.getProgressLiveData().getValue(); + assertThat(progress).isNotNull(); + assertThat(progress.getSteps()).isEqualTo(INITIAL_STEPS); + // TODO(b/260957933) verify getRemaining() when it is really used + //assertThat(progress.getRemaining()).isEqualTo(INITIAL_REMAINING); + } + + // TODO(b/260957933): FingerprintEnrollProgressViewModel::getErrorLiveData() and + // FingerprintEnrollProgressViewModel::getHelpLiveData() doesn't built into apk because no one + // uses it. We shall test it when new FingerprintEnrollEnrolling has used these 2 methods. + + private static class TestWrapper { + T mValue; + } +} diff --git a/tests/robotests/src/com/android/settings/testutils/InstantTaskExecutorRule.java b/tests/robotests/src/com/android/settings/testutils/InstantTaskExecutorRule.java new file mode 100644 index 00000000000..e906cb89dc6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/InstantTaskExecutorRule.java @@ -0,0 +1,59 @@ +/* + * 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.settings.testutils; + +import androidx.arch.core.executor.ArchTaskExecutor; +import androidx.arch.core.executor.TaskExecutor; + +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +/** + * A JUnit Test Rule that swaps the background executor used by the Architecture Components with a + * different one which executes each task synchronously. + * + * We can't refer it in prebuilt androidX library. + * Copied it from androidx/arch/core/executor/testing/InstantTaskExecutorRule.java + */ +public class InstantTaskExecutorRule extends TestWatcher { + @Override + protected void starting(Description description) { + super.starting(description); + ArchTaskExecutor.getInstance().setDelegate(new TaskExecutor() { + @Override + public void executeOnDiskIO(Runnable runnable) { + runnable.run(); + } + + @Override + public void postToMainThread(Runnable runnable) { + runnable.run(); + } + + @Override + public boolean isMainThread() { + return true; + } + }); + } + + @Override + protected void finished(Description description) { + super.finished(description); + ArchTaskExecutor.getInstance().setDelegate(null); + } +}