Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Got null root node from accessibility - UiAutomator

I'm getting the Got null root node from accessibility - Retrying... message from the UiAutomator and I have no idea why.

Shortly:

We're executing the E2E tests on our app and each test starts with the launch activity. The first test runs successfully. When the second test starts, it's all fine until we start finding a UiObject. On that call, we're getting the error.

Detailed:

The first test starts StartActivity and navigates through the onboarding to the MainScreen.

Start screen starting:

val intent = Intent(appContext, StartActivity::class.java)
appContext.startActivity(intent)

I've tried like this as well:

val intent = appContext.packageManager.getLaunchIntentForPackage(appContext.originalPackageName).apply {
    addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
    addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
appContext.startActivity(intent)

but there is no difference, as test runner checks automatically if there is a running activity after the test run, and clears it.

When the second test is run, it still starts from the StartActivity but the onboarding process is skipped now, and user (test runner) is navigated to the screen which contains the Continue button. Therefore, we're finding an UiObject with the text Continue and wait for it to appear on the screen. Once it appears, we'll click it. However, as soon as we initiate "finding the Continue button" the message from the title starts appearing and after a couple of seconds everything crashes with: Test running failed: Instrumentation run failed due to 'Process crashed.'

Snippet:

val continueButton = viewByText { appContext stringOf R.string.all_continue }
continueButton.waitToBecomeVisible(1.minute)
continueButton.click()

I guess the final question would be, what causes the UiAutomator to lose all root nodes? Debugging the methods in UiDevice like getUiAutomation, getWindowRoots and actual getRootNode() from QueryContoller, which is logging the message above, didn't help.

Syntax sugar:

appContext

val appContext: Context get() = InstrumentationRegistry.getTargetContext() 

viewByText:

fun viewByText(text: () -> String): UiObject = device.objectByText { text() }

infix fun UiDevice.objectByText(text: () -> String): UiObject = 
    findObject(UiSelector().text(text()))

waitToBecomeVisible:

infix fun UiObject.waitToBecomeVisible(timeOutMillis: Long) {
    if(!waitForExists(timeOutMillis)){
        throw UiObjectNotFoundException(
            "Timeout: ${timeOutMillis.toDouble()/1000}s. ${this.selector}"
        )
    }
}
like image 747
bajicdusko Avatar asked Aug 24 '18 07:08

bajicdusko


1 Answers

I'm feeling obliged to put an answer to this question, as this was not a problem with the UIAutomator at all.

The message Got null root node from accessibility - Retrying... is something you'll find in your logs all the time. However, UIAutomator is able to continue after few retries.

In our case, the application we were testing had a SignalR integration with buggy "lifecycle" implementation. In combination with the state described above, when the retry is in progress, SignalR failed with NPE, crashing the app and as a consequence, the test instrumentation process is crashed as well.

It took as a long time to figure out the cause and effects this situation had on the app and the tests, but the good thing, we solved the nasty bug from the SignalR.

Therefore, if it happens that the test instrumentation runner crash with the bunch of Got null... messages logged, try searching for some other cause, as main thread is blocked during retries and lifecycle aware component might get into the trouble.

like image 148
bajicdusko Avatar answered Nov 06 '22 19:11

bajicdusko