package com.xtremelabs.robolectric; import com.xtremelabs.robolectric.RobolectricTestRunner.InstrumentDetector; import com.xtremelabs.robolectric.bytecode.ClassHandler; import com.xtremelabs.robolectric.bytecode.RobolectricClassLoader; import com.xtremelabs.robolectric.bytecode.ShadowWrangler; import org.junit.Test; import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.RunWith; import org.junit.runners.model.InitializationError; import java.io.PrintWriter; import java.io.StringWriter; import static com.xtremelabs.robolectric.util.TestUtil.resourceFile; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** * Tests related to custom instrument detection strategy * that is used for introducing custom class loaders to test runners. */ public class InstrumentDetectorTest { // ==================== CLASSLOADER ==================== /** Custom class loader. It must be singleton. */ public static class ClassLoaderForTesting extends RobolectricClassLoader { /** Instance. */ private static ClassLoaderForTesting instance; /** Usage flag. */ static boolean usageFlag = false; public static ClassLoaderForTesting getInstance() { if (instance == null) { instance = new ClassLoaderForTesting(ShadowWrangler.getInstance()); } return instance; } protected ClassLoaderForTesting(final ClassHandler classHandler) { super(classHandler); } @Override public Class loadClass(final String name) throws ClassNotFoundException { if (!usageFlag) { System.setProperty("robolectric.cusomClassLoader", "yes"); } usageFlag = true; return super.loadClass(name); } } // ==================== RUNNERS ==================== /** Custom runner that uses custom class loader but does not modify instrument detection. */ public static class RunnerForTesting extends RobolectricTestRunner { public RunnerForTesting(final Class testClass) throws InitializationError { super( testClass, isInstrumented() ? null : ShadowWrangler.getInstance(), isInstrumented() ? null : ClassLoaderForTesting.getInstance(), new RobolectricConfig(resourceFile("TestAndroidManifest.xml"), resourceFile("res"), resourceFile("assets")) ); } } /** Custom runner that uses custom class loader. */ public static class RunnerForTestingThatWillPass extends RobolectricTestRunner { static { RunnerForTestingThatWillPass.setInstrumentDetector(CUSTOM_DETECTOR); } public RunnerForTestingThatWillPass(final Class testClass) throws InitializationError { super( testClass, isInstrumented() ? null : ShadowWrangler.getInstance(), isInstrumented() ? null : ClassLoaderForTesting.getInstance(), new RobolectricConfig(resourceFile("TestAndroidManifest.xml"), resourceFile("res"), resourceFile("assets")) ); } } /** Custom instrument detector. */ private static final InstrumentDetector CUSTOM_DETECTOR = new InstrumentDetector() { @Override public boolean isInstrumented() { final String currentLoader = RunnerForTestingThatWillPass.class.getClassLoader().getClass().getName(); return currentLoader.contains(ClassLoaderForTesting.class.getName()); } }; // ==================== TEST CLASSES ==================== /** * Default behavior test. */ public static class DefaultBehaviorTest { @Test public void fakeTest() { assertThat(true, equalTo(true)); } } /** * Test that is launched with a custom test runner and will fail. */ @RunWith(RunnerForTesting.class) public static class TestWithCustomRunner { @Test public void loadedWithCustomClassLoader_usageFlagShoudBeTrue() { assertThat(System.getProperty("robolectric.cusomClassLoader"), equalTo("yes")); } } /** * Test that is launched with a custom test runner and will pass. */ @RunWith(RunnerForTestingThatWillPass.class) public static class TestWithCustomRunnerThatWillPass extends TestWithCustomRunner { // nothing here, we just need another annotation } // ==================== RUN METHODS ==================== /** * This is default behavior. */ @Test public void withDefaultDetectorAndDefaultRunner_shouldPass() { final Result result = JUnitCore.runClasses(DefaultBehaviorTest.class); assertThat(result.getRunCount(), equalTo(1)); assertThat(result.getFailureCount(), equalTo(0)); } /** * This test simulates wrong instrument detection when custom class loader is used. */ @Test public void wrongInstrumentDetection_shouldRaiseLinkageError() { final Result result = JUnitCore.runClasses(TestWithCustomRunner.class); assertThat(result.getRunCount(), equalTo(1)); assertThat(result.getFailureCount(), equalTo(1)); // check whether it's a linkage error final StringWriter buffer = new StringWriter(); result.getFailures().get(0).getException().printStackTrace(new PrintWriter(buffer)); final int linkageErrorNameIndex = buffer.toString().indexOf(LinkageError.class.getName()); assertTrue(linkageErrorNameIndex >= 0); } /** * Propper behavior test. */ @Test public void customizeInstumentDetection_shouldPass() { final Result result = JUnitCore.runClasses(TestWithCustomRunnerThatWillPass.class); assertThat(result.getRunCount(), equalTo(1)); assertThat(result.getFailureCount(), equalTo(0)); } }