Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActivityScenario.getResult() occasionally delays 45 secs #676

Open
mikepenz opened this issue Jun 24, 2020 · 18 comments
Open

ActivityScenario.getResult() occasionally delays 45 secs #676

mikepenz opened this issue Jun 24, 2020 · 18 comments

Comments

@mikepenz
Copy link

mikepenz commented Jun 24, 2020

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:

@jameswald
Copy link

This also happens in ActivityScenario.close() after a test manually calls finish() on the activity.

@mikepenz
Copy link
Author

mikepenz commented Jul 1, 2020

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

@free5ty1e
Copy link

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 :/

@mikepenz mikepenz changed the title ActivityScenario.getResult() occasionally delays 30 secs ActivityScenario.getResult() occasionally delays 45 secs Jul 14, 2020
@mikepenz
Copy link
Author

mikepenz commented Jul 14, 2020

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.

@mikepenz
Copy link
Author

Something like this may work: https://github.com/mikepenz/android-test/tree/fix/676 ?

@plastiv
Copy link

plastiv commented Sep 8, 2020

Issue is present at final 1.3.0 release still and workaround works. Kudos to Mike for documenting and issue and providing a fix👍

@theHilikus
Copy link

theHilikus commented Sep 17, 2020

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

@lostcart
Copy link

lostcart commented Jan 21, 2021

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

@LifeAlgorithm
Copy link

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

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();
    }
}

@crysxd
Copy link

crysxd commented Aug 10, 2021

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.

@yoonseopshin
Copy link

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 resolved same issue this way

@maxkernchen
Copy link

maxkernchen commented Sep 24, 2021

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

@kmo9709
Copy link

kmo9709 commented Apr 1, 2022

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.

@paolopaa
Copy link

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.

@chrisjenx
Copy link

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.

@ViliusSutkus89
Copy link

Could reproduce this reliably on androidx.test:core:1.4.0.
androidx.test:core:1.5.0-alpha02 seems to solve it.

@mikepenz
Copy link
Author

mikepenz commented Jun 1, 2023

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.

@donnfelker
Copy link

donnfelker commented Sep 21, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests