1package org.robolectric; 2 3import static android.os.Build.VERSION_CODES.JELLY_BEAN; 4import static java.util.Arrays.asList; 5import static java.util.Collections.singletonList; 6import static java.util.stream.Collectors.toSet; 7import static org.assertj.core.api.Assertions.assertThat; 8import static org.junit.Assert.fail; 9import static org.mockito.Mockito.mock; 10import static org.robolectric.util.ReflectionHelpers.callConstructor; 11 12import android.os.Build; 13import java.lang.reflect.Method; 14import java.util.ArrayList; 15import java.util.List; 16import java.util.Properties; 17import java.util.Set; 18import javax.annotation.Nonnull; 19import org.junit.Before; 20import org.junit.FixMethodOrder; 21import org.junit.Ignore; 22import org.junit.Test; 23import org.junit.runner.Description; 24import org.junit.runner.RunWith; 25import org.junit.runner.notification.Failure; 26import org.junit.runner.notification.RunListener; 27import org.junit.runner.notification.RunNotifier; 28import org.junit.runners.JUnit4; 29import org.junit.runners.MethodSorters; 30import org.junit.runners.model.InitializationError; 31import org.robolectric.RobolectricTestRunner.RobolectricFrameworkMethod; 32import org.robolectric.android.internal.ParallelUniverse; 33import org.robolectric.annotation.Config; 34import org.robolectric.internal.ParallelUniverseInterface; 35import org.robolectric.internal.SdkConfig; 36import org.robolectric.internal.SdkEnvironment; 37import org.robolectric.manifest.AndroidManifest; 38import org.robolectric.res.ResourceTable; 39import org.robolectric.util.PerfStatsCollector.Metric; 40import org.robolectric.util.PerfStatsReporter; 41 42@RunWith(JUnit4.class) 43public class RobolectricTestRunnerTest { 44 45 private RunNotifier notifier; 46 private List<String> events; 47 48 @Before 49 public void setUp() throws Exception { 50 notifier = new RunNotifier(); 51 events = new ArrayList<>(); 52 notifier.addListener(new RunListener() { 53 @Override 54 public void testIgnored(Description description) throws Exception { 55 events.add("ignored: " + description.getDisplayName()); 56 } 57 58 @Override 59 public void testFailure(Failure failure) throws Exception { 60 events.add("failure: " + failure.getMessage()); 61 } 62 }); 63 } 64 65 @Test 66 public void ignoredTestCanSpecifyUnsupportedSdkWithoutExploding() throws Exception { 67 RobolectricTestRunner runner = new MyRobolectricTestRunner(TestWithOldSdk.class); 68 runner.run(notifier); 69 assertThat(events).containsOnly( 70 "failure: Robolectric does not support API level 11.", 71 "ignored: ignoredOldSdkMethod(org.robolectric.RobolectricTestRunnerTest$TestWithOldSdk)" 72 ); 73 } 74 75 @Test 76 public void failureInResetterDoesntBreakAllTests() throws Exception { 77 RobolectricTestRunner runner = new MyRobolectricTestRunner(TestWithTwoMethods.class) { 78 @Override 79 ParallelUniverseInterface getHooksInterface(SdkEnvironment sdkEnvironment) { 80 Class<? extends ParallelUniverseInterface> clazz = sdkEnvironment 81 .bootstrappedClass(MyParallelUniverse.class); 82 return callConstructor(clazz); 83 } 84 }; 85 runner.run(notifier); 86 assertThat(events).containsExactly( 87 "failure: fake error in setUpApplicationState", 88 "failure: fake error in setUpApplicationState" 89 ); 90 } 91 92 @Test 93 public void equalityOfRobolectricFrameworkMethod() throws Exception { 94 Method method = TestWithTwoMethods.class.getMethod("first"); 95 RobolectricFrameworkMethod rfm16 = new RobolectricFrameworkMethod(method, 96 mock(AndroidManifest.class), new SdkConfig(16), mock(Config.class)); 97 RobolectricFrameworkMethod rfm17 = new RobolectricFrameworkMethod(method, 98 mock(AndroidManifest.class), new SdkConfig(17), mock(Config.class)); 99 RobolectricFrameworkMethod rfm16b = new RobolectricFrameworkMethod(method, 100 mock(AndroidManifest.class), new SdkConfig(16), mock(Config.class)); 101 102 assertThat(rfm16).isEqualTo(rfm16); 103 assertThat(rfm16).isNotEqualTo(rfm17); 104 assertThat(rfm16).isEqualTo(rfm16b); 105 106 assertThat(rfm16.hashCode()).isEqualTo((rfm16b.hashCode())); 107 } 108 109 @Test 110 public void shouldReportPerfStats() throws Exception { 111 List<Metric> metrics = new ArrayList<>(); 112 PerfStatsReporter reporter = (metadata, metrics1) -> metrics.addAll(metrics1); 113 114 RobolectricTestRunner runner = new MyRobolectricTestRunner(TestWithTwoMethods.class) { 115 @Nonnull 116 @Override 117 protected Iterable<PerfStatsReporter> getPerfStatsReporters() { 118 return singletonList(reporter); 119 } 120 }; 121 122 runner.run(notifier); 123 124 Set<String> metricNames = metrics.stream().map(Metric::getName).collect(toSet()); 125 assertThat(metricNames).contains("initialization"); 126 } 127 128 ///////////////////////////// 129 130 public static class MyParallelUniverse extends ParallelUniverse { 131 132 @Override 133 public void setUpApplicationState(Method method, AndroidManifest appManifest, Config config, 134 ResourceTable compileTimeResourceTable, ResourceTable appResourceTable, 135 ResourceTable systemResourceTable) { 136 throw new RuntimeException("fake error in setUpApplicationState"); 137 } 138 } 139 140 @Ignore 141 public static class TestWithOldSdk { 142 @Config(sdk = Build.VERSION_CODES.HONEYCOMB) 143 @Test 144 public void oldSdkMethod() throws Exception { 145 fail("I should not be run!"); 146 } 147 148 @Ignore("This test shouldn't run, and shouldn't cause the test runner to fail") 149 @Config(sdk = Build.VERSION_CODES.HONEYCOMB) 150 @Test 151 public void ignoredOldSdkMethod() throws Exception { 152 fail("I should not be run!"); 153 } 154 } 155 156 @Ignore 157 @FixMethodOrder(MethodSorters.NAME_ASCENDING) 158 public static class TestWithTwoMethods { 159 @Test 160 public void first() throws Exception { 161 } 162 163 @Test 164 public void second() throws Exception { 165 } 166 } 167 168 private static class MyRobolectricTestRunner extends RobolectricTestRunner { 169 public MyRobolectricTestRunner(Class<?> testClass) throws InitializationError { 170 super(testClass); 171 } 172 173 @Nonnull 174 @Override 175 protected SdkPicker createSdkPicker() { 176 return new SdkPicker(asList(new SdkConfig(JELLY_BEAN)), new Properties()); 177 } 178 } 179}