Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 211cae6a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Wait for ANRd process to be killed before starting next test"

parents 2a0ef1ed 461d4bad
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ android_test {
        "androidx.test.ext.junit",
        "androidx.test.rules",
        "services.core.unboosted",
        "testables",
        "truth-prebuilt",
        "ub-uiautomator",
    ],
+54 −15
Original line number Diff line number Diff line
@@ -19,7 +19,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.filters.MediumTest

import android.app.ActivityManager
import android.app.ApplicationExitInfo
import android.graphics.Rect
import android.os.Build
import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS
import android.os.SystemClock
import android.provider.Settings
import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
@@ -27,10 +31,13 @@ import android.support.test.uiautomator.By
import android.support.test.uiautomator.UiDevice
import android.support.test.uiautomator.UiObject2
import android.support.test.uiautomator.Until
import android.testing.PollingCheck
import android.view.InputDevice
import android.view.MotionEvent

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.Test
@@ -51,22 +58,28 @@ import org.junit.runner.RunWith
class AnrTest {
    companion object {
        private const val TAG = "AnrTest"
        private const val ALL_PIDS = 0
        private const val NO_MAX = 0
    }

    val mInstrumentation = InstrumentationRegistry.getInstrumentation()
    var mHideErrorDialogs = 0
    private val instrumentation = InstrumentationRegistry.getInstrumentation()
    private var hideErrorDialogs = 0
    private lateinit var PACKAGE_NAME: String
    private val DISPATCHING_TIMEOUT = (UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
            Build.HW_TIMEOUT_MULTIPLIER)

    @Before
    fun setUp() {
        val contentResolver = mInstrumentation.targetContext.contentResolver
        mHideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
        val contentResolver = instrumentation.targetContext.contentResolver
        hideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
        Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
        PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage().getName()
    }

    @After
    fun tearDown() {
        val contentResolver = mInstrumentation.targetContext.contentResolver
        Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, mHideErrorDialogs)
        val contentResolver = instrumentation.targetContext.contentResolver
        Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, hideErrorDialogs)
    }

    @Test
@@ -86,19 +99,28 @@ class AnrTest {

    private fun clickCloseAppOnAnrDialog() {
        // Find anr dialog and kill app
        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
        val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
        val closeAppButton: UiObject2? =
                uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
        if (closeAppButton == null) {
            fail("Could not find anr dialog")
            return
        }
        val initialReasons = getExitReasons()
        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.
         */
        waitForNewExitReason(initialReasons[0].timestamp)
    }

    private fun clickWaitOnAnrDialog() {
        // Find anr dialog and tap on wait
        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
        val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
        val waitButton: UiObject2? =
                uiDevice.wait(Until.findObject(By.res("android:id/aerr_wait")), 20000)
        if (waitButton == null) {
@@ -108,9 +130,27 @@ class AnrTest {
        waitButton.click()
    }

    private fun getExitReasons(): List<ApplicationExitInfo> {
        lateinit var infos: List<ApplicationExitInfo>
        instrumentation.runOnMainSync {
            val am = instrumentation.getContext().getSystemService(ActivityManager::class.java)
            infos = am.getHistoricalProcessExitReasons(PACKAGE_NAME, ALL_PIDS, NO_MAX)
        }
        return infos
    }

    private fun waitForNewExitReason(previousExitTimestamp: Long) {
        PollingCheck.waitFor {
            getExitReasons()[0].timestamp > previousExitTimestamp
        }
        val reasons = getExitReasons()
        assertTrue(reasons[0].timestamp > previousExitTimestamp)
        assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason)
    }

    private fun triggerAnr() {
        startUnresponsiveActivity()
        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
        val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
        val obj: UiObject2? = uiDevice.wait(Until.findObject(
                By.text("Unresponsive gesture monitor")), 10000)

@@ -125,15 +165,14 @@ class AnrTest {
                MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */)
        downEvent.source = InputDevice.SOURCE_TOUCHSCREEN

        mInstrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
        instrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)

        // Todo: replace using timeout from android.hardware.input.IInputManager
        SystemClock.sleep(5000) // default ANR timeout for gesture monitors
        SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors
    }

    private fun startUnresponsiveActivity() {
        val flags = " -W -n "
        val startCmd = "am start $flags com.android.test.input/.UnresponsiveGestureMonitorActivity"
        mInstrumentation.uiAutomation.executeShellCommand(startCmd)
        val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity"
        instrumentation.uiAutomation.executeShellCommand(startCmd)
    }
}