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

IdlingRegistry doesn't set registerIdleTransitionCallback until a IdlingResourceRegistry sync #20

Closed
MerryMaryMaguire opened this issue Jun 14, 2019 · 4 comments · Fixed by #28
Assignees

Comments

@MerryMaryMaguire
Copy link

MerryMaryMaguire commented Jun 14, 2019

io.reactivex.rxjava2:rxandroid:2.1.0
io.reactivex.rxjava2:rxjava:2.2.2
com.squareup.rx.idler:rx2-idler:0.9.1
androidx.test.espresso:espresso-core:3.2.0
androidx.test.espresso:espresso-idling-resource:3.2.0

I have an application that injects our ScheduleProviders, so in our test app component I was having to use the RxIdler.wrap() functionality and I am registering them as IdlingResources in a custom ActivityTestRule before the base?.evaluate().

Since Espresso.registerIdlingResources() is now a deprecated function, I was using the IdlingRegistry.getInstance().register(). However I have a couple of tests that have no UI interactions that were crashing for null pointer exceptions on the DelegatingIdlingResourceSchedulers.stopWork() for a null ResourceCallback. Even though I could debug through and see the Schedulers in the list of registered resources in the IdlingRegistry. This didn't happen if I changed the call to use the deprecated Espresso.registerIdlingResources().
After much debugging I discovered that this is because:
Espresso.registerIdlingResources() immediately causes a sync on the IdlingResourceRegistry which is where the registerIdleTransitionCallback() happens. Whereas the IdlingRegistry does not sync with the IdlingResourceRegistry until an IdleNotifier is necessary.
I am able to force this by calling an Espresso interaction immediately after registering.

All of my tests now work the same by exchanging

@Suppress("DEPRECATION")
Espresso.registerIdlingResources(scheduler)

with

IdlingRegistry.getInstance().register(scheduler)
Espresso.onIdle()

I thought this information might help a few other people, or could be an addition to the README for how to fully register these IdlingResources with the newer IdlingRegistry.

@kenyee
Copy link

kenyee commented Dec 5, 2019

Related to this, we get a similar crash by trying to do the init "(RxJavaPlugins.setInitIoSchedulerHandler(Rx2Idler.create(RXJAVA_IO_SCHEDULER_NAME))" in TestRunner's onCreate (otherwise, the app init creates schedulers before onStart so it will never get overridden w/ the idling one). If we do the init in onStart, the IOScheduler is already started so InitIoSchedulerHandler never gets called (verified w/ breakpoints)

@kenyee
Copy link

kenyee commented Dec 5, 2019

DelegatingIdlingResourceScheduler.stopWork() probably needs a null check for callback but it also looks like it's not initialized properly...

@technoir42
Copy link
Contributor

I'm having a crash in DelegatingIdlingResourceScheduler.stopWork as well due to callback being null.

@danieldello
Copy link

danieldello commented Apr 15, 2020

I've written a workaround for this error while the issue is not merged, it's far from perfect but it does the trick for me.

class TestRunner : AndroidJUnitRunner() {

    override fun onStart() {
        RxJavaPlugins.setInitComputationSchedulerHandler {
            Rx2Idler.create("RxJava Computation Scheduler").apply(it).also {
                forceIdlingResourceRegistrySync()
            }
        }
        RxJavaPlugins.setInitIoSchedulerHandler {
            Rx2Idler.create("RxJava IO Scheduler").apply(it).also {
                forceIdlingResourceRegistrySync()
            }
        }
        super.onStart()
    }

    @Suppress("DEPRECATION")
    private fun forceIdlingResourceRegistrySync() {
        // Workaround for https://github.com/square/RxIdler/issues/20
        // This forces espresso to sync idling resources, cause the callback to be set for all available
        // resources and preventing RxIdler from crashing
        // By registering and unregistering a new resource we will force the registry to be updated,
        // we don't really care for the resource we are registering itself
        // TODO PLEASE REMOVE THIS ONCE THE BUG ON RXIDLER HAS BEEN FIXED
        val mockIdlingResource = object : IdlingResource {
            override fun getName(): String = "Mock Idling Resource Name"

            override fun isIdleNow(): Boolean = false

            override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
            }
        }
        Espresso.registerIdlingResources(mockIdlingResource)
        Espresso.unregisterIdlingResources(mockIdlingResource)
    }
}

I'm not sure if a simple call to Espresso.onIdle would suffice, and since I can't seem to reproduce it consistently I went with a flow that I could easily track. If there's a cleaner way to do this please let me know

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

Successfully merging a pull request may close this issue.

5 participants