1package com.xtremelabs.robolectric;
2
3import com.xtremelabs.robolectric.RobolectricTestRunner.InstrumentDetector;
4import com.xtremelabs.robolectric.bytecode.ClassHandler;
5import com.xtremelabs.robolectric.bytecode.RobolectricClassLoader;
6import com.xtremelabs.robolectric.bytecode.ShadowWrangler;
7import org.junit.Test;
8import org.junit.runner.JUnitCore;
9import org.junit.runner.Result;
10import org.junit.runner.RunWith;
11import org.junit.runners.model.InitializationError;
12
13import java.io.PrintWriter;
14import java.io.StringWriter;
15
16import static com.xtremelabs.robolectric.util.TestUtil.resourceFile;
17import static org.hamcrest.CoreMatchers.equalTo;
18import static org.junit.Assert.assertThat;
19import static org.junit.Assert.assertTrue;
20
21/**
22 * Tests related to custom instrument detection strategy
23 * that is used for introducing custom class loaders to test runners.
24 */
25public class InstrumentDetectorTest {
26
27    // ==================== CLASSLOADER ====================
28
29    /** Custom class loader. It must be singleton. */
30    public static class ClassLoaderForTesting extends RobolectricClassLoader {
31
32        /** Instance. */
33        private static ClassLoaderForTesting instance;
34
35        /** Usage flag. */
36        static boolean usageFlag = false;
37
38        public static ClassLoaderForTesting getInstance() {
39            if (instance == null) {
40                instance = new ClassLoaderForTesting(ShadowWrangler.getInstance());
41            }
42            return instance;
43        }
44
45        protected ClassLoaderForTesting(final ClassHandler classHandler) {
46            super(classHandler);
47        }
48
49        @Override
50        public Class<?> loadClass(final String name) throws ClassNotFoundException {
51            if (!usageFlag) {
52                System.setProperty("robolectric.cusomClassLoader", "yes");
53            }
54            usageFlag = true;
55            return super.loadClass(name);
56        }
57
58    }
59
60    // ==================== RUNNERS ====================
61
62    /** Custom runner that uses custom class loader but does not modify instrument detection.  */
63    public static class RunnerForTesting extends RobolectricTestRunner {
64
65        public RunnerForTesting(final Class<?> testClass) throws InitializationError {
66            super(
67                    testClass,
68                    isInstrumented() ? null : ShadowWrangler.getInstance(),
69                    isInstrumented() ? null : ClassLoaderForTesting.getInstance(),
70                    new RobolectricConfig(resourceFile("TestAndroidManifest.xml"), resourceFile("res"), resourceFile("assets"))
71                  );
72        }
73
74    }
75
76    /** Custom runner that uses custom class loader.  */
77    public static class RunnerForTestingThatWillPass extends RobolectricTestRunner {
78
79        static {
80            RunnerForTestingThatWillPass.setInstrumentDetector(CUSTOM_DETECTOR);
81        }
82
83        public RunnerForTestingThatWillPass(final Class<?> testClass) throws InitializationError {
84            super(
85                    testClass,
86                    isInstrumented() ? null : ShadowWrangler.getInstance(),
87                    isInstrumented() ? null : ClassLoaderForTesting.getInstance(),
88                    new RobolectricConfig(resourceFile("TestAndroidManifest.xml"), resourceFile("res"), resourceFile("assets"))
89                  );
90        }
91
92    }
93
94    /** Custom instrument detector. */
95    private static final InstrumentDetector CUSTOM_DETECTOR = new InstrumentDetector() {
96        @Override
97        public boolean isInstrumented() {
98            final String currentLoader = RunnerForTestingThatWillPass.class.getClassLoader().getClass().getName();
99            return currentLoader.contains(ClassLoaderForTesting.class.getName());
100        }
101    };
102
103
104    // ==================== TEST CLASSES ====================
105
106    /**
107     * Default behavior test.
108     */
109    public static class DefaultBehaviorTest {
110        @Test
111        public void fakeTest() {
112            assertThat(true, equalTo(true));
113        }
114    }
115
116    /**
117     * Test that is launched with a custom test runner and will fail.
118     */
119    @RunWith(RunnerForTesting.class)
120    public static class TestWithCustomRunner {
121        @Test
122        public void loadedWithCustomClassLoader_usageFlagShoudBeTrue() {
123            assertThat(System.getProperty("robolectric.cusomClassLoader"), equalTo("yes"));
124        }
125    }
126
127    /**
128     * Test that is launched with a custom test runner and will pass.
129     */
130    @RunWith(RunnerForTestingThatWillPass.class)
131    public static class TestWithCustomRunnerThatWillPass extends TestWithCustomRunner {
132        // nothing here, we just need another annotation
133    }
134
135
136    // ==================== RUN METHODS ====================
137
138    /**
139     * This is default behavior.
140     */
141    @Test
142    public void withDefaultDetectorAndDefaultRunner_shouldPass() {
143        final Result result =  JUnitCore.runClasses(DefaultBehaviorTest.class);
144        assertThat(result.getRunCount(), equalTo(1));
145        assertThat(result.getFailureCount(), equalTo(0));
146    }
147
148    /**
149     * This test simulates wrong instrument detection when custom class loader is used.
150     */
151    @Test
152    public void wrongInstrumentDetection_shouldRaiseLinkageError() {
153        final Result result =  JUnitCore.runClasses(TestWithCustomRunner.class);
154        assertThat(result.getRunCount(), equalTo(1));
155        assertThat(result.getFailureCount(), equalTo(1));
156
157        // check whether it's a linkage error
158        final StringWriter buffer = new StringWriter();
159        result.getFailures().get(0).getException().printStackTrace(new PrintWriter(buffer));
160        final int linkageErrorNameIndex = buffer.toString().indexOf(LinkageError.class.getName());
161        assertTrue(linkageErrorNameIndex >= 0);
162    }
163
164    /**
165     * Propper behavior test.
166     */
167    @Test
168    public void customizeInstumentDetection_shouldPass() {
169        final Result result =  JUnitCore.runClasses(TestWithCustomRunnerThatWillPass.class);
170        assertThat(result.getRunCount(), equalTo(1));
171        assertThat(result.getFailureCount(), equalTo(0));
172    }
173
174}
175