android-test
android-test copied to clipboard
ActivityScenario.getResult() occasionally delays 45 secs
Description
Using the new ActivityScenario
for our tests. Sadly in various occasions the getResult()
will wait 30 seconds until it returns successfully. This behavior is not deterministic as sometimes it works again.
In both cases the result will be delivered successfully.
In cases the getResult()
waits for 45 seconds the tested activity is already finished and the empty activity with black background is shown.
Steps to Reproduce
The steps in our project to reproduce:
launchActivity
ActivityScenario.use{
// do the testing
// ui code finishing the activity
// retrieve `result`
result.checkResultData()
}
Expected Results
It would not wait for 45 seconds but less time before retrieving the result data
Actual Results
It freezes randomly for 45 seconds before successful result data is retrieved
AndroidX Test and Android OS Versions
- 3.3.0-rc01
- Android 11, but also happening on Android 10
Link to a public git repo demonstrating the problem:
This also happens in ActivityScenario.close()
after a test manually calls finish()
on the activity.
if there would be something in the log which I can provide to help resolving this problem or anything else that would be great :/
it's adding significant of additional time onto our ui tests
I've been trying to migrate to ActivityScenario / ActivityScenarioRule from the deprecated ActivityTestRule and this black screen / 30 second freeze happens every test for me at the scenario.close() line. I'm going to have to go back to the ActivityTestRule for now :/
I started to look further into the issue and I identify the problem. It's actually a race condition + lock on the main thread between the latch which waits for the activity result and the broadcast receiver.
Basically the thought of the InstrumentationActivityInvoker
would be to place a latch and await for it to be counted down, the issue surfaces that this latch blocks the main thread, but the broadcast receiver would also be returned on the main thread. So if the latch awaits it will ultimately prevent the broadcast receiver to countdown on the latch, never returning.
This is the problematic latch: https://github.com/android/android-test/blob/master/core/java/androidx/test/core/app/InstrumentationActivityInvoker.java#L268
And here's the problematic broadcast receiver: https://github.com/android/android-test/blob/master/core/java/androidx/test/core/app/InstrumentationActivityInvoker.java#L443-L449
To solve this issue I'd actually recommend that the testing library implements a polling mechanism for the time of of the timeout, and loops to check for the result until the timeout is reached.
So the great news is that I actually also have 1 working workaround and 1 alternative (which doesn't work it seems).
Workaround
// define our custom result property
val ActivityScenario<*>.safeResult: Instrumentation.ActivityResult
get() {
awaitBlock { state == Lifecycle.State.DESTROYED } // await for the activity to be destroyed
return this.result // this will return quick as the result is already retrieved
}
// util function to retry and await until the block is true or the timeout is reached
fun awaitBlock(timeOut: Int = 7500, block: () -> Boolean) {
val start = System.currentTimeMillis()
var value = block.invoke()
while (!value && System.currentTimeMillis() < start + timeOut) {
wait(50)
value = block.invoke()
}
Assert.assertTrue("Couldn't await the condition", value)
}
Alternative Workaround
Based on the code, the latch timeout is retrieved from the arguments from the registry:
https://github.com/android/android-test/blob/master/runner/monitor/java/androidx/test/internal/platform/app/ActivityLifecycleTimeout.java#L16 https://github.com/android/android-test/blob/master/runner/monitor/java/androidx/test/internal/platform/util/InstrumentationParameterUtil.java#L22
So in theory lowering the timeout (which should be absolutely fine, no clue in which case it would be 45 seconds?!) should lower the lost time:
InstrumentationRegistry.getArguments().putString("activityLifecycleChangeTimeoutMillis", "5000")
But when debugged it would not have this value, so I am a bit puzzled.
Something like this may work: https://github.com/mikepenz/android-test/tree/fix/676 ?
Issue is present at final 1.3.0 release still and workaround works. Kudos to Mike for documenting and issue and providing a fix👍
I've spent hours with this bug thinking I was using the library wrong. Doing specifically what @jameswald mentioned: an activity under test that calls finish()
and then calling close()
on the ActivityScenario
blocking for 45 seconds only to succeed the test.
I found another, uglier but simpler workaround: just giving time to the main thread to update the state of the activity. So putting a Thread.sleep()
before calling scenario.close()
is also fixing it for me. Sleeps are ugly, unreliable, etc etc. But at least one more workaround
I'm seeing a very similar issue, but the workarounds mentioned here don't seem to be alleviating it for me. If I do what's mentioned here https://stackoverflow.com/questions/65112750/android-test-with-activityscenariorule-hanging-forever and remove singleInstance
from my manifest I'm all good, but obviously changing the functionality of my app to make the UI tests run correctly isn't ideal
I've spent hours with this bug thinking I was using the library wrong. Doing specifically what @jameswald mentioned: an activity under test that calls
finish()
and then callingclose()
on theActivityScenario
blocking for 45 seconds only to succeed the test.I found another, uglier but simpler workaround: just giving time to the main thread to update the state of the activity. So putting a
Thread.sleep()
before callingscenario.close()
is also fixing it for me. Sleeps are ugly, unreliable, etc etc. But at least one more workaround
This worked for me. To save someone else future trouble, here's how this workaround might look in a sample test:
@Test
public void when_cancelButtonClicked_then_activityIsFinished() throws InterruptedException {
onView(withId(R.id.cancelButton))
.perform(click());
final ActivityScenario scenario = activityRule.getScenario();
assertEquals(Activity.RESULT_OK, scenario.getResult().getResultCode());
Thread.sleep(1000);
scenario.close();
}
}
I'm not quite sure if this is related, but I have the same issue where the test will keep running 45s after the Activity is closed. I was able to link the behaviour to launchMode="singleInstance"
. Changing the launch mode to singleTask
resolves the issue and the test finishes instantly when the Activity is closed and the test code is all run.
I'm not quite sure if this is related, but I have the same issue where the test will keep running 45s after the Activity is closed. I was able to link the behaviour to
launchMode="singleInstance"
. Changing the launch mode tosingleTask
resolves the issue and the test finishes instantly when the Activity is closed and the test code is all run.
I resolved same issue this way
I believe I have a similar issue my tests are very simple but after one runs I receive an empty activity like this post: https://stackoverflow.com/questions/65112750/android-test-with-activityscenariorule-hanging-forever I know my both my test cases are finishing as I debugged them to the last line. But once this blank activity is reached it never moves on the next test.
Link to Test class, should be using all the latest dependencies:
https://github.com/maxkernchen/WalkingAlarm/blob/master/app/src/androidTest/java/com/example/walkingalarm/WalkingAlarmUITests.java
In the developer docs it is stated that you should not directly call close
https://developer.android.com/reference/androidx/test/core/app/ActivityScenario#close()
The recommendation is to try with resources for Java, or "use" with Kotlin
Instead try the following:
Java
@Test
public void when_cancelButtonClicked_then_activityIsFinished() throws InterruptedException
try(activityRule.getScenario()){
onView(withId(R.id.cancelButton)).perform(click());
assertEquals(Activity.RESULT_OK, scenario.getResult().getResultCode());
}
}
Kotlin
@Test
fun when_cancelButtonClicked_then_activityIsFinished() {
activityRule.scenario.use{
onView(withId(R.id.cancelButton)).perform(click());
assertEquals(Activity.RESULT_OK, scenario.getResult().getResultCode());
}
}
More examples here: https://developer.android.com/guide/components/activities/testing#determine-current-state
I was running into this issue and using separate scenarios for each test in my class, removed the race condition issue and deadlock problems I was having by manually closing before or after the activity had been destroyed.
Hey guys, I spent several hours on this same problem and then I stumble upon this thread.
Pls give this version a try androidx.test:core-ktx:1.5.0-alpha01" (https://developer.android.com/jetpack/androidx/releases/test#core_150_2).
I'm using launchActivityForResult(startIntent)
and it is working reliably.
We tried a bunch of suggestions the only consistant working one was @mikepenz first workaround, not ideal, but way better than 45 waits. Seems like they need to put the getReport
on a different thread.
Could reproduce this reliably on androidx.test:core:1.4.0
.
androidx.test:core:1.5.0-alpha02
seems to solve it.
At this time, we are still able to see this problem occurring with the most recent 1.5.0 release of the androidx.test
release.
I'm also experiencing this issue. It would be great if this could get fixed soon. Testing on Android is already difficult enough without issues like this. Also, can confirm @mikepenz solution workaround is what I'm using now too.