package org.robolectric; import android.app.Application; import javax.annotation.Nonnull; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.internal.TextListener; import org.junit.runner.Result; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.robolectric.annotation.Config; import org.robolectric.annotation.internal.DoNotInstrument; import org.robolectric.internal.SdkConfig; import org.robolectric.internal.bytecode.InstrumentationConfiguration; import org.robolectric.internal.bytecode.Sandbox; import org.robolectric.manifest.AndroidManifest; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Properties; import static android.os.Build.VERSION_CODES.JELLY_BEAN; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.junit.Assert.assertTrue; import static org.robolectric.util.TestUtil.resourceFile; public class TestRunnerSequenceTest { public static class StateHolder { public static List<String> transcript; } @Before public void setUp() throws Exception { StateHolder.transcript = new ArrayList<>(); } @Test public void shouldRunThingsInTheRightOrder() throws Exception { assertNoFailures(run(new Runner(SimpleTest.class))); assertThat(StateHolder.transcript).containsExactly( "configureShadows", // "resetStaticState", // no longer an overridable hook // "setupApplicationState", // no longer an overridable hook "createApplication", "application.onCreate", "beforeTest", "application.beforeTest", "prepareTest", "application.prepareTest", "TEST!", "application.onTerminate", "afterTest", "application.afterTest" ); StateHolder.transcript.clear(); } @Test public void whenNoAppManifest_shouldRunThingsInTheRightOrder() throws Exception { assertNoFailures(run(new Runner(SimpleTest.class) { @Override protected AndroidManifest getAppManifest(Config config) { return new AndroidManifest(null, null, null, "package") { @Override public int getTargetSdkVersion() { return SdkConfig.FALLBACK_SDK_VERSION; } }; } })); assertThat(StateHolder.transcript).containsExactly( "configureShadows", "createApplication", "application.onCreate", "beforeTest", "application.beforeTest", "prepareTest", "application.prepareTest", "TEST!", "application.onTerminate", "afterTest", "application.afterTest" ); StateHolder.transcript.clear(); } @Test public void shouldReleaseAllStateAfterClassSoWeDontLeakMemory() throws Exception { final List<RobolectricTestRunner.RobolectricFrameworkMethod> methods = new ArrayList<>(); RobolectricTestRunner robolectricTestRunner = new Runner(SimpleTest.class) { @Override protected void finallyAfterTest(FrameworkMethod method) { super.finallyAfterTest(method); RobolectricFrameworkMethod roboMethod = (RobolectricFrameworkMethod) method; assertThat(roboMethod.parallelUniverseInterface).isNull(); assertThat(roboMethod.testLifecycle).isNull(); methods.add(roboMethod); } }; robolectricTestRunner.run(new RunNotifier()); assertThat(methods).isNotEmpty(); } public static class SimpleTest { @Test public void shouldDoNothingMuch() throws Exception { StateHolder.transcript.add("TEST!"); } } private Result run(Runner runner) throws InitializationError { RunNotifier notifier = new RunNotifier(); Result result = new Result(); notifier.addListener(result.createListener()); runner.run(notifier); return result; } private void assertNoFailures(Result result) { if (!result.wasSuccessful()) { for (Failure failure : result.getFailures()) { fail(failure.getMessage(), failure.getException()); } } } public static class Runner extends RobolectricTestRunner { public Runner(Class<?> testClass) throws InitializationError { super(testClass); } @Nonnull @Override protected SdkPicker createSdkPicker() { return new SdkPicker(singletonList(new SdkConfig(JELLY_BEAN)), new Properties()); } @Nonnull @Override protected InstrumentationConfiguration createClassLoaderConfig(FrameworkMethod method) { InstrumentationConfiguration.Builder builder = new InstrumentationConfiguration.Builder(super.createClassLoaderConfig(method)); builder.doNotAcquireClass(StateHolder.class); return builder.build(); } @Override protected AndroidManifest getAppManifest(Config config) { return new AndroidManifest(resourceFile("TestAndroidManifest.xml"), resourceFile("res"), resourceFile("assets")); } @Nonnull @Override protected Class<? extends TestLifecycle> getTestLifecycleClass() { return MyTestLifecycle.class; } @Override protected void configureShadows(FrameworkMethod frameworkMethod, Sandbox sandbox) { StateHolder.transcript.add("configureShadows"); super.configureShadows(frameworkMethod, sandbox); } } @DoNotInstrument public static class MyTestLifecycle extends DefaultTestLifecycle { @Override public Application createApplication(Method method, AndroidManifest appManifest, Config config) { StateHolder.transcript.add("createApplication"); return new MyApplication(); } @Override public void beforeTest(Method method) { StateHolder.transcript.add("beforeTest"); super.beforeTest(method); } @Override public void prepareTest(Object test) { StateHolder.transcript.add("prepareTest"); super.prepareTest(test); } @Override public void afterTest(Method method) { StateHolder.transcript.add("afterTest"); super.afterTest(method); } private static class MyApplication extends Application implements TestLifecycleApplication { @Override public void onCreate() { StateHolder.transcript.add("application.onCreate"); } @Override public void beforeTest(Method method) { StateHolder.transcript.add("application.beforeTest"); } @Override public void prepareTest(Object test) { StateHolder.transcript.add("application.prepareTest"); } @Override public void afterTest(Method method) { StateHolder.transcript.add("application.afterTest"); } @Override public void onTerminate() { StateHolder.transcript.add("application.onTerminate"); } } } }