diff options
author | 2025-03-12 17:10:03 -0700 | |
---|---|---|
committer | 2025-03-17 07:48:05 -0700 | |
commit | 88ef8a5a32c7dd2d1c3d7310e9005c0aa7cee432 (patch) | |
tree | e3b5f4cfea1649efa8a05389541991801c5d6eaf | |
parent | 2e0a5487614e6019e1ef5521fe1403c1a5ad5aa7 (diff) |
Don't look for ANR dialog if the device doesn't support it
On some devices, error dialogs aren't supposed. Skip the 'wait for the
app' test on those devices. Also skip looking for ANR dialog on the
'close app' test.
Also, increase the timeout for waiting for the activity to launch and
bind. When run on auto_md cf remote target, this is taking > 5 seconds
to launch and connect. So it used to fail with the following message
when the timeout was equal to 5 seconds:
STACKTRACE:
junit.framework.AssertionFailedError: UnresponsiveGestureMonitorActivity failed to call 'provideActivityInfo'
at junit.framework.Assert.fail(Assert.java:50)
at android.testing.PollingCheck.check(PollingCheck.java:73)
at com.android.test.input.AnrTest.startUnresponsiveActivity(AnrTest.kt:235)
at com.android.test.input.AnrTest.setUp(AnrTest.kt:132)
And even with the multiplied timeout, the test used to fail, so increase
the timeout to 10 seconds at the same time.
When the ANR dialog is skipped, we need to ensure that we wait for "exit
reasons" for a long time, because we are no longer waiting for the ANR
dialog to appear.
To run the test on auto cf md:
$ lunch cf_x86_64_only_auto_md-trunk_staging-userdebug
$ acloud create --local-image --boot-timeout 1000
$ atest AnrTest
Flag: TEST_ONLY
Bug: 339924248
Test: atest AnrTest
Change-Id: I031361e48c8bcdeac7489555d1c12df1a956a982
-rw-r--r-- | tests/Input/src/com/android/test/input/AnrTest.kt | 86 |
1 files changed, 50 insertions, 36 deletions
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt index f8cb86b7b1fe..3ad3763a5d20 100644 --- a/tests/Input/src/com/android/test/input/AnrTest.kt +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -16,6 +16,7 @@ package com.android.test.input import android.app.ActivityManager +import android.app.ActivityTaskManager import android.app.ApplicationExitInfo import android.app.Instrumentation import android.content.Intent @@ -28,6 +29,7 @@ import android.os.SystemClock import android.server.wm.CtsWindowInfoUtils.getWindowCenter import android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop import android.testing.PollingCheck +import android.util.Log import android.view.InputEvent import android.view.MotionEvent import android.view.MotionEvent.ACTION_DOWN @@ -46,21 +48,19 @@ import com.android.cts.input.inputeventmatchers.withMotionAction import java.time.Duration import java.util.concurrent.LinkedBlockingQueue import java.util.function.Supplier -import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Assert.fail -import org.junit.Before +import org.junit.Assume.assumeTrue import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith /** - * Click on the center of the window identified by the provided window token. - * The click is performed using "UinputTouchScreen" device. - * If the touchscreen device is closed too soon, it may cause the click to be dropped. Therefore, - * the provided runnable can ensure that the click is delivered before the device is closed, thus - * avoiding this race. + * Click on the center of the window identified by the provided window token. The click is performed + * using "UinputTouchScreen" device. If the touchscreen device is closed too soon, it may cause the + * click to be dropped. Therefore, the provided runnable can ensure that the click is delivered + * before the device is closed, thus avoiding this race. */ private fun clickOnWindow( token: IBinder, @@ -104,6 +104,10 @@ class AnrTest { private val remoteInputEvents = LinkedBlockingQueue<InputEvent>() private val verifier = BlockingQueueEventVerifier(remoteInputEvents) + // Some devices don't support ANR error dialogs, such as cars, TVs, etc. + private val anrDialogsAreSupported = + ActivityTaskManager.currentUiModeSupportsErrorDialogs(instrumentation.targetContext) + val binder = object : IAnrTestService.Stub() { override fun provideActivityInfo(token: IBinder, displayId: Int, pid: Int) { @@ -121,34 +125,37 @@ class AnrTest { @get:Rule val debugInputRule = DebugInputRule() - @Before - fun setUp() { - startUnresponsiveActivity() - PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.getName() - } - - @After fun tearDown() {} - @Test @DebugInputRule.DebugInput(bug = 339924248) fun testGestureMonitorAnr_Close() { + startUnresponsiveActivity() + val timestamp = System.currentTimeMillis() triggerAnr() - clickCloseAppOnAnrDialog() + if (anrDialogsAreSupported) { + clickCloseAppOnAnrDialog() + } else { + Log.i(TAG, "The device does not support ANR dialogs, skipping check for ANR window") + // We still want to wait for the app to get killed by the ActivityManager + } + waitForNewExitReasonAfter(timestamp) } @Test @DebugInputRule.DebugInput(bug = 339924248) fun testGestureMonitorAnr_Wait() { + assumeTrue(anrDialogsAreSupported) + startUnresponsiveActivity() triggerAnr() clickWaitOnAnrDialog() SystemClock.sleep(500) // Wait at least 500ms after tapping on wait // ANR dialog should reappear after a delay - find the close button on it to verify + val timestamp = System.currentTimeMillis() clickCloseAppOnAnrDialog() + waitForNewExitReasonAfter(timestamp) } private fun clickCloseAppOnAnrDialog() { // Find anr dialog and kill app - val timestamp = System.currentTimeMillis() val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) val closeAppButton: UiObject2? = uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000) @@ -157,14 +164,6 @@ class AnrTest { return } closeAppButton.click() - /** - * We must wait for the app to be fully closed before exiting this test. This is because - * another test may again invoke 'am start' for the same activity. If the 1st process that - * got ANRd isn't killed by the time second 'am start' runs, the killing logic will apply to - * the newly launched 'am start' instance, and the second test will fail because the - * unresponsive activity will never be launched. - */ - waitForNewExitReasonAfter(timestamp) } private fun clickWaitOnAnrDialog() { @@ -180,16 +179,27 @@ class AnrTest { } private fun getExitReasons(): List<ApplicationExitInfo> { + val packageName = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.name lateinit var infos: List<ApplicationExitInfo> instrumentation.runOnMainSync { val am = instrumentation.getContext().getSystemService(ActivityManager::class.java)!! - infos = am.getHistoricalProcessExitReasons(PACKAGE_NAME, remotePid!!, NO_MAX) + infos = am.getHistoricalProcessExitReasons(packageName, remotePid!!, NO_MAX) } return infos } + /** + * We must wait for the app to be fully closed before exiting this test. This is because another + * test may again invoke 'am start' for the same activity. If the 1st process that got ANRd + * isn't killed by the time second 'am start' runs, the killing logic will apply to the newly + * launched 'am start' instance, and the second test will fail because the unresponsive activity + * will never be launched. + * + * Also, we must ensure that we wait until it's killed, so that the next test can launch this + * activity again. + */ private fun waitForNewExitReasonAfter(timestamp: Long) { - PollingCheck.waitFor { + PollingCheck.waitFor(Duration.ofSeconds(20).toMillis() * Build.HW_TIMEOUT_MULTIPLIER) { val reasons = getExitReasons() !reasons.isEmpty() && reasons[0].timestamp >= timestamp } @@ -199,16 +209,15 @@ class AnrTest { } private fun triggerAnr() { - clickOnWindow( - remoteWindowToken!!, - remoteDisplayId!!, - instrumentation, - ) { verifier.assertReceivedMotion(withMotionAction(ACTION_DOWN)) } + clickOnWindow(remoteWindowToken!!, remoteDisplayId!!, instrumentation) { + verifier.assertReceivedMotion(withMotionAction(ACTION_DOWN)) + } SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors } private fun startUnresponsiveActivity() { + remoteWindowToken = null val intent = Intent(instrumentation.targetContext, UnresponsiveGestureMonitorActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NEW_DOCUMENT @@ -218,12 +227,17 @@ class AnrTest { instrumentation.targetContext.startActivity(intent) // first, wait for the token to become valid PollingCheck.check( - "UnresponsiveGestureMonitorActivity failed to call 'provideActivityInfo'", - Duration.ofSeconds(5).toMillis()) { remoteWindowToken != null } + "UnresponsiveGestureMonitorActivity failed to call 'provideActivityInfo'", + Duration.ofSeconds(10).toMillis() * Build.HW_TIMEOUT_MULTIPLIER, + ) { + remoteWindowToken != null + } // next, wait for the window of the activity to get on top // we could combine the two checks above, but the current setup makes it easier to detect // errors - assertTrue("Remote activity window did not become visible", - waitForWindowOnTop(Duration.ofSeconds(5), Supplier { remoteWindowToken })) + assertTrue( + "Remote activity window did not become visible", + waitForWindowOnTop(Duration.ofSeconds(5), Supplier { remoteWindowToken }), + ) } } |