robolectric / robolectric

Android Unit Testing Framework
http://robolectric.org
Other
5.81k stars 1.36k forks source link

Fix `ShadowPausedLooper` to not use cached `looperMode` in `unPause` method #9069

Open warnyul opened 3 weeks ago

warnyul commented 3 weeks ago

Overview

The unPause is called from resetLooperToInitialState, which means the cached looper mode might already have been reset. I think in unPause the looper mode should get from the registry.

Proposed Changes

Use ConfigurationRegistry.get(LooperMode.Mode.class) instead of looperMode() in the unPause method.

utzcoz commented 3 weeks ago

@warnyul Do you have evidence that it doesn't work as your expectation?

warnyul commented 3 weeks ago

@utzcoz With jUnit 4 I do not have evidence, because in that case line 63 solves this issue in SandboxManager.java. Because the key contains the LooperMode, 2 different sandboxes will be created with 2 different ClassLoaders. One for PAUSED and one for INSTRUMENTATION_TEST mode.

I am only experiencing this issue with jUnit5, because I reuse the Sandbox. in this case it is important that the shadows are reset. Because I cannot create new classloader per test method, because it is limited in jUnit 5. If I could create a new ClassLoader that would solve the issue, like with jUnit 4. Because the ShadowPausedLooper class is loaded with two different ClassLoaders when I have two test methods annotated with different looper modes.

hoisie commented 3 weeks ago

cc @brettchabot

Are you trying to reuse the same sandbox for some tests that use PAUSED looper mode (the default) and some tests that use INSTRUMENTATION_TEST looper mode? I am not sure if that is supported right now. @brettchabot would know more.

There are certain key parameters in Robolectric that will result in a new sandbox will be created. Looper mode is one of these. There is an LRU caching mechanism for sandboxes that tries to reuse sandboxes if possible.

https://github.com/robolectric/robolectric/blob/81831fc059a6f098466063cb95b8df670929ee8d/robolectric/src/main/java/org/robolectric/internal/SandboxManager.java#L87-L104

 public SandboxKey(
        InstrumentationConfiguration instrumentationConfiguration,
        Sdk sdk,
        ResourcesMode resourcesMode,
        LooperMode.Mode looperMode,
        GraphicsMode.Mode graphicsMode) {
      this.sdk = sdk;
      this.instrumentationConfiguration = instrumentationConfiguration;
      this.resourcesMode = resourcesMode;
      this.looperMode = looperMode;
      this.graphicsMode = graphicsMode;
    }

So in the JUnit4 version of Robolectric, if a test runs with PAUSED looper mode, and then a subsequent test runs with INSTRUMENTATION_TEST looper mode, a new sandbox will be created.

warnyul commented 3 weeks ago

@hoisie Yes, I am trying to reuse the same sandbox. I have some test and it seems working: RobolectricExtensionLooperModeSelfTest

In this case I just cleared the cached looper mode with reflection: https://github.com/apter-tech/junit5-robolectric-extension/pull/59/files#diff-f70ad8af7998e9e21db351e4d56ed03a59eef332f56919716375605a09e1a889R73

utzcoz commented 3 days ago

@warnyul Please rebase to the latest master branch to leverage bugfix for CI's Emulator testing.