1 package org.robolectric; 2 3 import static com.google.common.truth.Truth.assertThat; 4 import static java.util.Arrays.asList; 5 import static java.util.Collections.singletonList; 6 import static java.util.stream.Collectors.toSet; 7 import static org.junit.Assert.fail; 8 import static org.mockito.Mockito.mock; 9 import static org.robolectric.util.ReflectionHelpers.callConstructor; 10 11 import android.app.Application; 12 import android.os.Build; 13 import java.lang.reflect.Method; 14 import java.util.ArrayList; 15 import java.util.List; 16 import java.util.Set; 17 import javax.annotation.Nonnull; 18 import org.junit.After; 19 import org.junit.Before; 20 import org.junit.FixMethodOrder; 21 import org.junit.Ignore; 22 import org.junit.Test; 23 import org.junit.runner.Description; 24 import org.junit.runner.RunWith; 25 import org.junit.runner.notification.Failure; 26 import org.junit.runner.notification.RunListener; 27 import org.junit.runner.notification.RunNotifier; 28 import org.junit.runners.JUnit4; 29 import org.junit.runners.MethodSorters; 30 import org.junit.runners.model.InitializationError; 31 import org.robolectric.RobolectricTestRunner.ResourcesMode; 32 import org.robolectric.RobolectricTestRunner.RobolectricFrameworkMethod; 33 import org.robolectric.RobolectricTestRunnerTest.TestWithBrokenAppCreate.MyTestApplication; 34 import org.robolectric.android.internal.ParallelUniverse; 35 import org.robolectric.annotation.Config; 36 import org.robolectric.internal.ParallelUniverseInterface; 37 import org.robolectric.internal.SdkConfig; 38 import org.robolectric.internal.SdkEnvironment; 39 import org.robolectric.manifest.AndroidManifest; 40 import org.robolectric.util.PerfStatsCollector.Metric; 41 import org.robolectric.util.PerfStatsReporter; 42 import org.robolectric.util.TestUtil; 43 44 @RunWith(JUnit4.class) 45 public class RobolectricTestRunnerTest { 46 47 private RunNotifier notifier; 48 private List<String> events; 49 private String priorEnabledSdks; 50 private String priorAlwaysInclude; 51 52 @Before setUp()53 public void setUp() throws Exception { 54 notifier = new RunNotifier(); 55 events = new ArrayList<>(); 56 notifier.addListener(new RunListener() { 57 @Override 58 public void testIgnored(Description description) throws Exception { 59 events.add("ignored: " + description.getDisplayName()); 60 } 61 62 @Override 63 public void testFailure(Failure failure) throws Exception { 64 events.add("failure: " + failure.getMessage()); 65 } 66 }); 67 68 priorEnabledSdks = System.getProperty("robolectric.enabledSdks"); 69 System.clearProperty("robolectric.enabledSdks"); 70 71 priorAlwaysInclude = System.getProperty("robolectric.alwaysIncludeVariantMarkersInTestName"); 72 System.clearProperty("robolectric.alwaysIncludeVariantMarkersInTestName"); 73 } 74 75 @After tearDown()76 public void tearDown() throws Exception { 77 TestUtil.resetSystemProperty( 78 "robolectric.alwaysIncludeVariantMarkersInTestName", priorAlwaysInclude); 79 TestUtil.resetSystemProperty("robolectric.enabledSdks", priorEnabledSdks); 80 } 81 82 @Test ignoredTestCanSpecifyUnsupportedSdkWithoutExploding()83 public void ignoredTestCanSpecifyUnsupportedSdkWithoutExploding() throws Exception { 84 RobolectricTestRunner runner = new MyRobolectricTestRunner(TestWithOldSdk.class); 85 runner.run(notifier); 86 assertThat(events).containsExactly( 87 "failure: Robolectric does not support API level 11.", 88 "ignored: ignoredOldSdkMethod(org.robolectric.RobolectricTestRunnerTest$TestWithOldSdk)" 89 ); 90 } 91 92 @Test failureInResetterDoesntBreakAllTests()93 public void failureInResetterDoesntBreakAllTests() throws Exception { 94 RobolectricTestRunner runner = 95 new MyRobolectricTestRunner(TestWithTwoMethods.class) { 96 @Override 97 ParallelUniverseInterface getHooksInterface(SdkEnvironment sdkEnvironment) { 98 Class<? extends ParallelUniverseInterface> clazz = 99 sdkEnvironment.bootstrappedClass(MyParallelUniverseWithFailingSetUp.class); 100 return callConstructor(clazz); 101 } 102 }; 103 runner.run(notifier); 104 assertThat(events).containsExactly( 105 "failure: fake error in setUpApplicationState", 106 "failure: fake error in setUpApplicationState" 107 ); 108 } 109 110 @Test failureInAppOnCreateDoesntBreakAllTests()111 public void failureInAppOnCreateDoesntBreakAllTests() throws Exception { 112 RobolectricTestRunner runner = new MyRobolectricTestRunner(TestWithBrokenAppCreate.class); 113 runner.run(notifier); 114 System.out.println("events = " + events); 115 assertThat(events) 116 .containsExactly( 117 "failure: fake error in application.onCreate", 118 "failure: fake error in application.onCreate"); 119 } 120 121 @Test equalityOfRobolectricFrameworkMethod()122 public void equalityOfRobolectricFrameworkMethod() throws Exception { 123 Method method = TestWithTwoMethods.class.getMethod("first"); 124 RobolectricFrameworkMethod rfm16 = 125 new RobolectricFrameworkMethod( 126 method, 127 mock(AndroidManifest.class), 128 new SdkConfig(16), 129 mock(Config.class), 130 ResourcesMode.legacy, 131 ResourcesMode.legacy, 132 false); 133 RobolectricFrameworkMethod rfm17 = 134 new RobolectricFrameworkMethod( 135 method, 136 mock(AndroidManifest.class), 137 new SdkConfig(17), 138 mock(Config.class), 139 ResourcesMode.legacy, 140 ResourcesMode.legacy, 141 false); 142 RobolectricFrameworkMethod rfm16b = 143 new RobolectricFrameworkMethod( 144 method, 145 mock(AndroidManifest.class), 146 new SdkConfig(16), 147 mock(Config.class), 148 ResourcesMode.legacy, 149 ResourcesMode.legacy, 150 false); 151 RobolectricFrameworkMethod rfm16c = 152 new RobolectricFrameworkMethod( 153 method, 154 mock(AndroidManifest.class), 155 new SdkConfig(16), 156 mock(Config.class), 157 ResourcesMode.binary, 158 ResourcesMode.legacy, 159 false); 160 161 assertThat(rfm16).isNotEqualTo(rfm17); 162 assertThat(rfm16).isEqualTo(rfm16b); 163 assertThat(rfm16).isNotEqualTo(rfm16c); 164 165 assertThat(rfm16.hashCode()).isEqualTo((rfm16b.hashCode())); 166 } 167 168 @Test shouldReportPerfStats()169 public void shouldReportPerfStats() throws Exception { 170 List<Metric> metrics = new ArrayList<>(); 171 PerfStatsReporter reporter = (metadata, metrics1) -> metrics.addAll(metrics1); 172 173 RobolectricTestRunner runner = new MyRobolectricTestRunner(TestWithTwoMethods.class) { 174 @Nonnull 175 @Override 176 protected Iterable<PerfStatsReporter> getPerfStatsReporters() { 177 return singletonList(reporter); 178 } 179 }; 180 181 runner.run(notifier); 182 183 Set<String> metricNames = metrics.stream().map(Metric::getName).collect(toSet()); 184 assertThat(metricNames).contains("initialization"); 185 } 186 187 ///////////////////////////// 188 189 public static class MyParallelUniverseWithFailingSetUp extends ParallelUniverse { 190 191 @Override setUpApplicationState(ApkLoader apkLoader, Method method, Config config, AndroidManifest appManifest, SdkEnvironment environment)192 public void setUpApplicationState(ApkLoader apkLoader, Method method, 193 Config config, AndroidManifest appManifest, SdkEnvironment environment) { 194 throw new RuntimeException("fake error in setUpApplicationState"); 195 } 196 } 197 198 @Ignore 199 public static class TestWithOldSdk { 200 @Config(sdk = Build.VERSION_CODES.HONEYCOMB) 201 @Test oldSdkMethod()202 public void oldSdkMethod() throws Exception { 203 fail("I should not be run!"); 204 } 205 206 @Ignore("This test shouldn't run, and shouldn't cause the test runner to fail") 207 @Config(sdk = Build.VERSION_CODES.HONEYCOMB) 208 @Test ignoredOldSdkMethod()209 public void ignoredOldSdkMethod() throws Exception { 210 fail("I should not be run!"); 211 } 212 } 213 214 @Ignore 215 @FixMethodOrder(MethodSorters.NAME_ASCENDING) 216 public static class TestWithTwoMethods { 217 @Test first()218 public void first() throws Exception { 219 } 220 221 @Test second()222 public void second() throws Exception { 223 } 224 } 225 226 @Ignore 227 @FixMethodOrder(MethodSorters.NAME_ASCENDING) 228 @Config(application = MyTestApplication.class) 229 public static class TestWithBrokenAppCreate { 230 @Test first()231 public void first() throws Exception {} 232 233 @Test second()234 public void second() throws Exception {} 235 236 public static class MyTestApplication extends Application { 237 @Override onCreate()238 public void onCreate() { 239 throw new RuntimeException("fake error in application.onCreate"); 240 } 241 } 242 } 243 244 @Ignore 245 @FixMethodOrder(MethodSorters.NAME_ASCENDING) 246 @Config(application = MyTestApplication.class) 247 public static class TestWithBrokenAppTerminate { 248 @Test first()249 public void first() throws Exception {} 250 251 @Test second()252 public void second() throws Exception {} 253 254 public static class MyTestApplication extends Application { 255 @Override onTerminate()256 public void onTerminate() { 257 throw new RuntimeException("fake error in application.onTerminate"); 258 } 259 } 260 } 261 262 private static class MyRobolectricTestRunner extends RobolectricTestRunner { MyRobolectricTestRunner(Class<?> testClass)263 public MyRobolectricTestRunner(Class<?> testClass) throws InitializationError { 264 super(testClass); 265 } 266 267 @Nonnull 268 @Override createSdkPicker()269 protected SdkPicker createSdkPicker() { 270 return new SdkPicker(asList(new SdkConfig(SdkConfig.MAX_SDK_VERSION)), null); 271 } 272 273 @Override getResourcesMode()274 ResourcesMode getResourcesMode() { 275 return ResourcesMode.legacy; 276 } 277 } 278 } 279