Converted Biometric tests to Robolectric tests
Added TestWithLooperRule to replace AndroidTestingRunner.
Converted some tests to run in parallel as Robolectric tests.
Bug: 267340313
Fix: 269478124
Test: atest SystemUiRoboTests
atest SystemUITests
Change-Id: Ib2e1edfee4ecb45501f5cc42f1b63c9326d2faa4
This commit is contained in:
@@ -258,6 +258,24 @@ filegroup {
|
||||
"tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
|
||||
"tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
|
||||
"tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
|
||||
|
||||
// Biometric
|
||||
"tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt",
|
||||
"tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt",
|
||||
"tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt",
|
||||
"tests/src/com/android/systemui/biometrics/AuthControllerTest.java",
|
||||
"tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java",
|
||||
"tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt",
|
||||
"tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt",
|
||||
"tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt",
|
||||
"tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java",
|
||||
"tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java",
|
||||
"tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java",
|
||||
"tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java",
|
||||
"tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java",
|
||||
"tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt",
|
||||
"tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt",
|
||||
"tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt",
|
||||
],
|
||||
path: "tests/src",
|
||||
}
|
||||
@@ -402,6 +420,10 @@ android_app {
|
||||
privileged: true,
|
||||
resource_dirs: [],
|
||||
kotlincflags: ["-Xjvm-default=all"],
|
||||
optimize: {
|
||||
shrink_resources: false,
|
||||
proguard_flags_files: ["proguard.flags"],
|
||||
},
|
||||
|
||||
plugins: ["dagger2-compiler"],
|
||||
}
|
||||
|
||||
@@ -144,8 +144,7 @@ constructor(
|
||||
orientationListener.enable()
|
||||
}
|
||||
}
|
||||
@VisibleForTesting
|
||||
internal var overlayOffsets: SensorLocationInternal = SensorLocationInternal.DEFAULT
|
||||
@VisibleForTesting var overlayOffsets: SensorLocationInternal = SensorLocationInternal.DEFAULT
|
||||
|
||||
private val overlayViewParams =
|
||||
WindowManager.LayoutParams(
|
||||
@@ -297,7 +296,7 @@ constructor(
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun updateOverlayParams(display: Display, bounds: Rect) {
|
||||
fun updateOverlayParams(display: Display, bounds: Rect) {
|
||||
val isNaturalOrientation = display.isNaturalOrientation()
|
||||
val isDefaultOrientation =
|
||||
if (isReverseDefaultRotation) !isNaturalOrientation else isNaturalOrientation
|
||||
|
||||
@@ -254,7 +254,7 @@ constructor(
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
|
||||
suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
|
||||
return scope.launch {
|
||||
primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
|
||||
inputBouncerExpansion = bouncerExpansion
|
||||
@@ -265,7 +265,7 @@ constructor(
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal suspend fun listenForAlternateBouncerVisibility(scope: CoroutineScope): Job {
|
||||
suspend fun listenForAlternateBouncerVisibility(scope: CoroutineScope): Job {
|
||||
return scope.launch {
|
||||
alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
|
||||
showUdfpsBouncer(isVisible)
|
||||
|
||||
@@ -13,4 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
sdk=NEWEST_SDK
|
||||
sdk=NEWEST_SDK
|
||||
shadows=\
|
||||
com.android.systemui.testutils.shadow.ShadowLockPatternUtils \
|
||||
com.android.systemui.testutils.shadow.ShadowTestableLooper
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.testutils.shadow;
|
||||
|
||||
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
|
||||
@Implements(LockPatternUtils.class)
|
||||
public class ShadowLockPatternUtils {
|
||||
|
||||
@Implementation
|
||||
protected int getCredentialTypeForUser(int userHandle) {
|
||||
return CREDENTIAL_TYPE_NONE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.testutils.shadow;
|
||||
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.annotation.RealObject;
|
||||
|
||||
@Implements(TestableLooper.class)
|
||||
public class ShadowTestableLooper {
|
||||
@RealObject private TestableLooper mRealTestableLooper;
|
||||
/**
|
||||
* Process messages in the queue until no more are found.
|
||||
*/
|
||||
@Implementation
|
||||
protected void processAllMessages() {
|
||||
shadowOf(mRealTestableLooper.getLooper()).idle();
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,10 @@ package com.android.systemui.biometrics
|
||||
|
||||
import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE
|
||||
import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.View
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -36,7 +36,8 @@ import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
|
||||
|
||||
@@ -17,7 +17,7 @@ package com.android.systemui.biometrics
|
||||
|
||||
import android.hardware.biometrics.BiometricAuthenticator
|
||||
import android.os.Bundle
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import android.testing.TestableLooper
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.View
|
||||
@@ -37,7 +37,7 @@ import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
class AuthBiometricFingerprintViewTest : SysuiTestCase() {
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
||||
import android.os.Handler
|
||||
import android.os.IBinder
|
||||
import android.os.UserManager
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.testing.ViewUtils
|
||||
@@ -34,6 +33,7 @@ import android.view.View
|
||||
import android.view.WindowInsets
|
||||
import android.view.WindowManager
|
||||
import android.widget.ScrollView
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.jank.InteractionJankMonitor
|
||||
import com.android.internal.widget.LockPatternUtils
|
||||
@@ -62,7 +62,7 @@ import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
class AuthContainerViewTest : SysuiTestCase() {
|
||||
@@ -489,7 +489,7 @@ class AuthContainerViewTest : SysuiTestCase() {
|
||||
private fun AuthContainerView.addToView() {
|
||||
ViewUtils.attachView(this)
|
||||
waitForIdleSync()
|
||||
assertThat(isAttachedToWindow).isTrue()
|
||||
assertThat(isAttachedToWindow()).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserManager;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableContext;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
@@ -83,6 +82,7 @@ import android.view.DisplayInfo;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.R;
|
||||
@@ -117,7 +117,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RunWithLooper
|
||||
@SmallTest
|
||||
public class AuthControllerTest extends SysuiTestCase {
|
||||
@@ -265,7 +265,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps);
|
||||
|
||||
// Ensures that the operations posted on the handler get executed.
|
||||
mTestableLooper.processAllMessages();
|
||||
waitForIdleSync();
|
||||
}
|
||||
|
||||
// Callback tests
|
||||
@@ -285,14 +285,14 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
mFpAuthenticatorsRegisteredCaptor.capture());
|
||||
verify(mFaceManager).addAuthenticatorsRegisteredCallback(
|
||||
mFaceAuthenticatorsRegisteredCaptor.capture());
|
||||
mTestableLooper.processAllMessages();
|
||||
waitForIdleSync();
|
||||
|
||||
verify(mFingerprintManager, never()).registerBiometricStateListener(any());
|
||||
verify(mFaceManager, never()).registerBiometricStateListener(any());
|
||||
|
||||
mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
|
||||
mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
|
||||
mTestableLooper.processAllMessages();
|
||||
waitForIdleSync();
|
||||
|
||||
verify(mFingerprintManager).registerBiometricStateListener(any());
|
||||
verify(mFaceManager).registerBiometricStateListener(any());
|
||||
@@ -316,7 +316,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
// Emulates a device with no authenticators (empty list).
|
||||
mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
|
||||
mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
|
||||
mTestableLooper.processAllMessages();
|
||||
waitForIdleSync();
|
||||
|
||||
verify(mFingerprintManager).registerBiometricStateListener(
|
||||
mBiometricStateCaptor.capture());
|
||||
@@ -328,7 +328,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
listener.onEnrollmentsChanged(0 /* userId */,
|
||||
0xbeef /* sensorId */, true /* hasEnrollments */);
|
||||
}
|
||||
mTestableLooper.processAllMessages();
|
||||
waitForIdleSync();
|
||||
|
||||
// Nothing should crash.
|
||||
}
|
||||
@@ -692,7 +692,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
switchTask("other_package");
|
||||
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
|
||||
|
||||
mTestableLooper.processAllMessages();
|
||||
waitForIdleSync();
|
||||
|
||||
assertNull(mAuthController.mCurrentDialog);
|
||||
assertNull(mAuthController.mReceiver);
|
||||
@@ -709,7 +709,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
switchTask("other_package");
|
||||
|
||||
mAuthController.mTaskStackListener.onTaskStackChanged();
|
||||
mTestableLooper.processAllMessages();
|
||||
waitForIdleSync();
|
||||
|
||||
assertNull(mAuthController.mCurrentDialog);
|
||||
assertNull(mAuthController.mReceiver);
|
||||
@@ -742,7 +742,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
|
||||
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
||||
mAuthController.mBroadcastReceiver.onReceive(mContext, intent);
|
||||
mTestableLooper.processAllMessages();
|
||||
waitForIdleSync();
|
||||
|
||||
assertNull(mAuthController.mCurrentDialog);
|
||||
assertNull(mAuthController.mReceiver);
|
||||
@@ -1021,4 +1021,9 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void waitForIdleSync() {
|
||||
mTestableLooper.processAllMessages();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,17 +32,20 @@ import static org.mockito.Mockito.when;
|
||||
import android.content.Context;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Handler;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.Surface.Rotation;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -51,11 +54,8 @@ import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
public class BiometricDisplayListenerTest extends SysuiTestCase {
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.systemui.biometrics
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.keyguard.logging.BiometricMessageDeferralLogger
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -33,7 +33,7 @@ import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FaceHelpMessageDeferralTest : SysuiTestCase() {
|
||||
val threshold = .75f
|
||||
@Mock lateinit var logger: BiometricMessageDeferralLogger
|
||||
|
||||
@@ -34,7 +34,6 @@ import android.hardware.fingerprint.FingerprintSensorProperties
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
||||
import android.hardware.fingerprint.ISidefpsController
|
||||
import android.os.Handler
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.Display
|
||||
import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
|
||||
@@ -49,6 +48,7 @@ import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
|
||||
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
|
||||
import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
|
||||
import android.view.WindowMetrics
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.airbnb.lottie.LottieAnimationView
|
||||
import com.android.systemui.R
|
||||
@@ -90,7 +90,7 @@ private const val DISPLAY_ID = 2
|
||||
private const val SENSOR_ID = 1
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class SideFpsControllerTest : SysuiTestCase() {
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTING
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
@@ -77,7 +77,7 @@ private const val SENSOR_WIDTH = 30
|
||||
private const val SENSOR_HEIGHT = 60
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@ import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.VibrationAttributes;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
@@ -68,6 +67,7 @@ import android.view.ViewRootImpl;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.logging.InstanceIdSequence;
|
||||
@@ -124,7 +124,7 @@ import java.util.Optional;
|
||||
import javax.inject.Provider;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
public class UdfpsControllerTest extends SysuiTestCase {
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ import android.hardware.biometrics.SensorLocationInternal;
|
||||
import android.hardware.biometrics.SensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
@@ -35,7 +35,7 @@ import org.junit.runner.RunWith;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase {
|
||||
@Test
|
||||
|
||||
@@ -25,9 +25,9 @@ import static org.mockito.Mockito.when;
|
||||
import android.content.Context;
|
||||
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
@@ -40,7 +40,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
public class UdfpsDisplayModeTest extends SysuiTestCase {
|
||||
private static final int DISPLAY_ID = 0;
|
||||
|
||||
@@ -26,10 +26,10 @@ import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.shade.ShadeExpansionListener;
|
||||
@@ -39,7 +39,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewControllerBaseTest {
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
package com.android.systemui.biometrics
|
||||
|
||||
import android.os.Handler
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.keyguard.KeyguardSecurityModel
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
@@ -39,10 +39,9 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.android.systemui.util.time.SystemClock
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.TestCoroutineScope
|
||||
import kotlinx.coroutines.yield
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
@@ -54,22 +53,27 @@ import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@SmallTest
|
||||
@TestableLooper.RunWithLooper
|
||||
@kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControllerBaseTest() {
|
||||
lateinit var keyguardBouncerRepository: KeyguardBouncerRepository
|
||||
@Mock private lateinit var bouncerLogger: TableLogBuffer
|
||||
|
||||
private lateinit var testScope: TestScope
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
testScope = TestScope()
|
||||
|
||||
allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
|
||||
MockitoAnnotations.initMocks(this)
|
||||
keyguardBouncerRepository =
|
||||
KeyguardBouncerRepositoryImpl(
|
||||
mock(com.android.keyguard.ViewMediatorCallback::class.java),
|
||||
FakeSystemClock(),
|
||||
TestCoroutineScope(),
|
||||
testScope.backgroundScope,
|
||||
bouncerLogger,
|
||||
)
|
||||
super.setUp()
|
||||
@@ -107,7 +111,7 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle
|
||||
|
||||
@Test
|
||||
fun shadeLocked_showAlternateBouncer_unpauseAuth() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
// GIVEN view is attached + on the SHADE_LOCKED (udfps view not showing)
|
||||
mController.onViewAttached()
|
||||
captureStatusBarStateListeners()
|
||||
@@ -116,7 +120,7 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle
|
||||
// WHEN alternate bouncer is requested
|
||||
val job = mController.listenForAlternateBouncerVisibility(this)
|
||||
keyguardBouncerRepository.setAlternateVisible(true)
|
||||
yield()
|
||||
runCurrent()
|
||||
|
||||
// THEN udfps view will animate in & pause auth is updated to NOT pause
|
||||
verify(mView).animateInUdfpsBouncer(any())
|
||||
@@ -128,7 +132,7 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle
|
||||
/** After migration to MODERN_BOUNCER, replaces UdfpsKeyguardViewControllerTest version */
|
||||
@Test
|
||||
fun shouldPauseAuthBouncerShowing() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
// GIVEN view attached and we're on the keyguard
|
||||
mController.onViewAttached()
|
||||
captureStatusBarStateListeners()
|
||||
@@ -138,15 +142,11 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle
|
||||
val job = mController.listenForBouncerExpansion(this)
|
||||
keyguardBouncerRepository.setPrimaryShow(true)
|
||||
keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
|
||||
yield()
|
||||
runCurrent()
|
||||
|
||||
// THEN UDFPS shouldPauseAuth == true
|
||||
assertTrue(mController.shouldPauseAuth())
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
package com.android.systemui.biometrics
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.MotionEvent
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.biometrics.UdfpsController.UdfpsOverlayController
|
||||
@@ -39,7 +39,7 @@ import org.mockito.Mockito.`when` as whenEver
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class UdfpsShellTest : SysuiTestCase() {
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.android.systemui.biometrics
|
||||
import android.graphics.PointF
|
||||
import android.graphics.RectF
|
||||
import android.hardware.biometrics.SensorLocationInternal
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import android.testing.TestableLooper
|
||||
import android.testing.ViewUtils
|
||||
import android.view.LayoutInflater
|
||||
@@ -49,7 +49,7 @@ private const val SENSOR_Y = 250
|
||||
private const val SENSOR_RADIUS = 10
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class UdfpsViewTest : SysuiTestCase() {
|
||||
|
||||
|
||||
@@ -22,12 +22,14 @@ import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.testing.DexmakerShareClassLoaderRule;
|
||||
import android.testing.LeakCheck;
|
||||
import android.testing.TestWithLooperRule;
|
||||
import android.testing.TestableLooper;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -73,12 +75,21 @@ public abstract class SysuiTestCase {
|
||||
@Rule
|
||||
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
|
||||
new DexmakerShareClassLoaderRule();
|
||||
|
||||
// set the highest order so it's the innermost rule
|
||||
@Rule(order = Integer.MAX_VALUE)
|
||||
public TestWithLooperRule mlooperRule = new TestWithLooperRule();
|
||||
|
||||
public TestableDependency mDependency;
|
||||
private Instrumentation mRealInstrumentation;
|
||||
private FakeBroadcastDispatcher mFakeBroadcastDispatcher;
|
||||
|
||||
@Before
|
||||
public void SysuiSetup() throws Exception {
|
||||
// Manually associate a Display to context for Robolectric test. Similar to b/214297409
|
||||
if (isRobolectricTest()) {
|
||||
mContext = mContext.createDefaultDisplayContext();
|
||||
}
|
||||
SystemUIInitializer initializer =
|
||||
SystemUIInitializerFactory.createFromConfigNoAssert(mContext);
|
||||
initializer.init(true);
|
||||
@@ -215,6 +226,10 @@ public abstract class SysuiTestCase {
|
||||
idler.waitForIdle();
|
||||
}
|
||||
|
||||
public static boolean isRobolectricTest() {
|
||||
return Build.FINGERPRINT.contains("robolectric");
|
||||
}
|
||||
|
||||
private static final void validateThread(Looper l) {
|
||||
if (Looper.myLooper() == l) {
|
||||
throw new RuntimeException(
|
||||
|
||||
@@ -18,6 +18,7 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.testing.LeakCheck;
|
||||
@@ -56,6 +57,11 @@ public class SysuiTestableContext extends TestableContext {
|
||||
return context;
|
||||
}
|
||||
|
||||
public SysuiTestableContext createDefaultDisplayContext() {
|
||||
Display display = getBaseContext().getSystemService(DisplayManager.class).getDisplays()[0];
|
||||
return (SysuiTestableContext) createDisplayContext(display);
|
||||
}
|
||||
|
||||
public void cleanUpReceivers(String testName) {
|
||||
Set<BroadcastReceiver> copy;
|
||||
synchronized (mRegisteredReceivers) {
|
||||
|
||||
161
tests/testables/src/android/testing/TestWithLooperRule.java
Normal file
161
tests/testables/src/android/testing/TestWithLooperRule.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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 android.testing;
|
||||
|
||||
import android.testing.TestableLooper.LooperFrameworkMethod;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import org.junit.internal.runners.statements.InvokeMethod;
|
||||
import org.junit.rules.MethodRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* This rule is meant to be an alternative of using AndroidTestingRunner.
|
||||
* It let tests to start from background thread, and assigns mainLooper or new
|
||||
* Looper for the Statement.
|
||||
*/
|
||||
public class TestWithLooperRule implements MethodRule {
|
||||
|
||||
/*
|
||||
* This rule requires to be the inner most Rule, so the next statement is RunAfters
|
||||
* instead of another rule. You can set it by '@Rule(order = Integer.MAX_VALUE)'
|
||||
*/
|
||||
@Override
|
||||
public Statement apply(Statement base, FrameworkMethod method, Object target) {
|
||||
// getting testRunner check, if AndroidTestingRunning then we skip this rule
|
||||
RunWith runWithAnnotation = target.getClass().getAnnotation(RunWith.class);
|
||||
if (runWithAnnotation != null) {
|
||||
// if AndroidTestingRunner or it's subclass is in use, do nothing
|
||||
if (AndroidTestingRunner.class.isAssignableFrom(runWithAnnotation.value())) {
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
// check if RunWithLooper annotation is used. If not skip this rule
|
||||
RunWithLooper looperAnnotation = method.getAnnotation(RunWithLooper.class);
|
||||
if (looperAnnotation == null) {
|
||||
looperAnnotation = target.getClass().getAnnotation(RunWithLooper.class);
|
||||
}
|
||||
if (looperAnnotation == null) {
|
||||
return base;
|
||||
}
|
||||
|
||||
try {
|
||||
wrapMethodInStatement(base, method, target);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
// This method is based on JUnit4 test runner flow. It might need to be revisited when JUnit is
|
||||
// upgraded
|
||||
// TODO(b/277743626): use a cleaner way to wrap each statements; may require some JUnit
|
||||
// patching to facilitate this.
|
||||
private void wrapMethodInStatement(Statement base, FrameworkMethod method, Object target)
|
||||
throws Exception {
|
||||
Statement next = base;
|
||||
try {
|
||||
while (next != null) {
|
||||
switch (next.getClass().getSimpleName()) {
|
||||
case "RunAfters":
|
||||
this.<List<FrameworkMethod>>wrapFieldMethodFor(next,
|
||||
next.getClass(), "afters", method, target);
|
||||
next = getNextStatement(next, "next");
|
||||
break;
|
||||
case "RunBefores":
|
||||
this.<List<FrameworkMethod>>wrapFieldMethodFor(next,
|
||||
next.getClass(), "befores", method, target);
|
||||
next = getNextStatement(next, "next");
|
||||
break;
|
||||
case "FailOnTimeout":
|
||||
// Note: withPotentialTimeout() from BlockJUnit4ClassRunner might use
|
||||
// FailOnTimeout which always wraps a new thread during InvokeMethod
|
||||
// method evaluation.
|
||||
next = getNextStatement(next, "originalStatement");
|
||||
break;
|
||||
case "InvokeMethod":
|
||||
this.<FrameworkMethod>wrapFieldMethodFor(next,
|
||||
InvokeMethod.class, "testMethod", method, target);
|
||||
return;
|
||||
default:
|
||||
throw new Exception(
|
||||
String.format("Unexpected Statement received: [%s]",
|
||||
next.getClass().getName())
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapping the befores, afters, and InvokeMethods with LooperFrameworkMethod
|
||||
// within the statement.
|
||||
private <T> void wrapFieldMethodFor(Statement base, Class<?> targetClass, String fieldStr,
|
||||
FrameworkMethod method, Object target)
|
||||
throws NoSuchFieldException, IllegalAccessException {
|
||||
Field field = targetClass.getDeclaredField(fieldStr);
|
||||
field.setAccessible(true);
|
||||
T fieldInstance = (T) field.get(base);
|
||||
if (fieldInstance instanceof FrameworkMethod) {
|
||||
field.set(base, looperWrap(method, target, (FrameworkMethod) fieldInstance));
|
||||
} else {
|
||||
// Befores and afters methods lists
|
||||
field.set(base, looperWrap(method, target, (List<FrameworkMethod>) fieldInstance));
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the next wrapped statement based on the selected field string
|
||||
private Statement getNextStatement(Statement base, String fieldStr)
|
||||
throws NoSuchFieldException, IllegalAccessException {
|
||||
Field nextField = base.getClass().getDeclaredField(fieldStr);
|
||||
nextField.setAccessible(true);
|
||||
Object value = nextField.get(base);
|
||||
return value instanceof Statement ? (Statement) value : null;
|
||||
}
|
||||
|
||||
protected FrameworkMethod looperWrap(FrameworkMethod method, Object test,
|
||||
FrameworkMethod base) {
|
||||
RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
|
||||
if (annotation == null) annotation = test.getClass().getAnnotation(RunWithLooper.class);
|
||||
if (annotation != null) {
|
||||
return LooperFrameworkMethod.get(base, annotation.setAsMainLooper(), test);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
protected List<FrameworkMethod> looperWrap(FrameworkMethod method, Object test,
|
||||
List<FrameworkMethod> methods) {
|
||||
RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
|
||||
if (annotation == null) annotation = test.getClass().getAnnotation(RunWithLooper.class);
|
||||
if (annotation != null) {
|
||||
methods = new ArrayList<>(methods);
|
||||
for (int i = 0; i < methods.size(); i++) {
|
||||
methods.set(i, LooperFrameworkMethod.get(methods.get(i),
|
||||
annotation.setAsMainLooper(), test));
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user