• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric;
2 
3 import static android.os.Build.VERSION_CODES.LOLLIPOP;
4 import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
5 import static android.os.Build.VERSION_CODES.M;
6 import static android.os.Build.VERSION_CODES.N;
7 import static android.os.Build.VERSION_CODES.N_MR1;
8 import static android.os.Build.VERSION_CODES.O;
9 import static com.google.common.truth.Truth.assertThat;
10 import static org.junit.Assert.fail;
11 import static org.robolectric.RobolectricTestRunner.defaultInjector;
12 
13 import android.os.Build;
14 import com.google.common.collect.Range;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.List;
18 import java.util.stream.Collectors;
19 import javax.annotation.Nonnull;
20 import org.junit.After;
21 import org.junit.Before;
22 import org.junit.Test;
23 import org.junit.runner.Description;
24 import org.junit.runner.RunWith;
25 import org.junit.runner.notification.RunListener;
26 import org.junit.runner.notification.RunNotifier;
27 import org.junit.runners.JUnit4;
28 import org.junit.runners.model.FrameworkMethod;
29 import org.junit.runners.model.InitializationError;
30 import org.robolectric.annotation.Config;
31 import org.robolectric.pluginapi.Sdk;
32 import org.robolectric.pluginapi.SdkPicker;
33 import org.robolectric.plugins.DefaultSdkPicker;
34 import org.robolectric.plugins.SdkCollection;
35 import org.robolectric.util.TestUtil;
36 import org.robolectric.util.inject.Injector;
37 
38 @RunWith(JUnit4.class)
39 public class RobolectricTestRunnerMultiApiTest {
40 
41   private static final int[] APIS_FOR_TEST = {LOLLIPOP, LOLLIPOP_MR1, M, N, N_MR1, O};
42 
43   private static SdkPicker delegateSdkPicker;
44   private static final Injector INJECTOR = defaultInjector()
45       .bind(SdkPicker.class, (config, usesSdk) -> delegateSdkPicker.selectSdks(config, usesSdk))
46       .build();
47 
48   private RobolectricTestRunner runner;
49   private RunNotifier runNotifier;
50   private MyRunListener runListener;
51 
52   private int numSupportedApis;
53   private String priorResourcesMode;
54   private String priorAlwaysInclude;
55 
56   private SdkCollection sdkCollection;
57 
58   @Before
setUp()59   public void setUp() {
60     numSupportedApis = APIS_FOR_TEST.length;
61 
62     runListener = new MyRunListener();
63     runNotifier = new RunNotifier();
64     runNotifier.addListener(runListener);
65     sdkCollection = new SdkCollection(() -> map(APIS_FOR_TEST));
66     delegateSdkPicker = new DefaultSdkPicker(sdkCollection, null);
67 
68     priorResourcesMode = System.getProperty("robolectric.resourcesMode");
69 
70     priorAlwaysInclude = System.getProperty("robolectric.alwaysIncludeVariantMarkersInTestName");
71     System.clearProperty("robolectric.alwaysIncludeVariantMarkersInTestName");
72   }
73 
74   @After
tearDown()75   public void tearDown() throws Exception {
76     TestUtil.resetSystemProperty(
77         "robolectric.alwaysIncludeVariantMarkersInTestName", priorAlwaysInclude);
78   }
79 
80   @Test
createChildrenForEachSupportedApi()81   public void createChildrenForEachSupportedApi() throws Throwable {
82     runner = runnerOf(TestWithNoConfig.class);
83     assertThat(apisFor(runner.getChildren()))
84         .containsExactly(LOLLIPOP, LOLLIPOP_MR1, M, N, N_MR1, O);
85   }
86 
87   @Test
withConfigSdkLatest_shouldUseLatestSupported()88   public void withConfigSdkLatest_shouldUseLatestSupported() throws Throwable {
89     runner = runnerOf(TestMethodWithNewestSdk.class);
90     assertThat(apisFor(runner.getChildren())).containsExactly(O);
91   }
92 
93   @Test
withConfigSdkAndMinMax_shouldUseMinMax()94   public void withConfigSdkAndMinMax_shouldUseMinMax() throws Throwable {
95     runner = runnerOf(TestMethodWithSdkAndMinMax.class);
96     try {
97       runner.getChildren();
98       fail();
99     } catch (IllegalArgumentException e) {
100       assertThat(e.getMessage())
101           .contains(
102               "sdk and minSdk/maxSdk may not be specified together"
103                   + " (sdk=[23], minSdk=23, maxSdk=24)");
104     }
105   }
106 
107   @Test
withEnabledSdks_createChildrenForEachSupportedSdk()108   public void withEnabledSdks_createChildrenForEachSupportedSdk() throws Throwable {
109     delegateSdkPicker = new DefaultSdkPicker(new SdkCollection(() -> map(21, 23)), null);
110 
111     runner = runnerOf(TestWithNoConfig.class);
112     assertThat(runner.getChildren()).hasSize(2);
113   }
114 
115   @Test
shouldAddApiLevelToNameOfAllButHighestNumberedMethodName()116   public void shouldAddApiLevelToNameOfAllButHighestNumberedMethodName() throws Throwable {
117     runner = runnerOf(TestMethodUpToAndIncludingN.class);
118     assertThat(runner.getChildren().get(0).getName()).isEqualTo("testSomeApiLevel[21]");
119     assertThat(runner.getChildren().get(1).getName()).isEqualTo("testSomeApiLevel[22]");
120     assertThat(runner.getChildren().get(2).getName()).isEqualTo("testSomeApiLevel[23]");
121     assertThat(runner.getChildren().get(3).getName()).isEqualTo("testSomeApiLevel");
122   }
123 
124   @Test
noConfig()125   public void noConfig() throws Throwable {
126     runner = runnerOf(TestWithNoConfig.class);
127     assertThat(apisFor(runner.getChildren()))
128         .containsExactly(LOLLIPOP, LOLLIPOP_MR1, M, N, N_MR1, O);
129     runner.run(runNotifier);
130 
131     assertThat(runListener.ignored).isEmpty();
132     assertThat(runListener.finished).hasSize(numSupportedApis);
133   }
134 
135   @Test
classConfigWithSdkGroup()136   public void classConfigWithSdkGroup() throws Throwable {
137     runner = runnerOf(TestClassConfigWithSdkGroup.class);
138     assertThat(apisFor(runner.getChildren())).containsExactly(M, N);
139 
140     runner.run(runNotifier);
141 
142     assertThat(runListener.ignored).isEmpty();
143     // Test method should be run for M and N
144     assertThat(runListener.finished).hasSize(2);
145   }
146 
147   @Test
methodConfigWithSdkGroup()148   public void methodConfigWithSdkGroup() throws Throwable {
149     runner = runnerOf(TestMethodConfigWithSdkGroup.class);
150     assertThat(apisFor(runner.getChildren())).containsExactly(M, N);
151 
152     runner.run(runNotifier);
153 
154     assertThat(runListener.ignored).isEmpty();
155     // Test method should be run for M, N
156     assertThat(runListener.finished).hasSize(2);
157   }
158 
159   @Test
classConfigMinSdk()160   public void classConfigMinSdk() throws Throwable {
161     runner = runnerOf(TestClassNAndUp.class);
162     assertThat(apisFor(runner.getChildren())).containsExactly(N, N_MR1, O);
163 
164     runner.run(runNotifier);
165 
166     assertThat(runListener.ignored).isEmpty();
167     int sdksAfterAndIncludingLollipop = 3;
168     assertThat(runListener.finished).hasSize(sdksAfterAndIncludingLollipop);
169   }
170 
171   @Test
classConfigMaxSdk()172   public void classConfigMaxSdk() throws Throwable {
173     runner = runnerOf(TestClassUpToAndIncludingN.class);
174     assertThat(apisFor(runner.getChildren())).containsExactly(LOLLIPOP, LOLLIPOP_MR1, M, N);
175 
176     runner.run(runNotifier);
177 
178     assertThat(runListener.ignored).isEmpty();
179     int sdksUpToAndIncludingLollipop = 4;
180     assertThat(runListener.finished).hasSize(sdksUpToAndIncludingLollipop);
181   }
182 
183   @Test
classConfigWithMinSdkAndMaxSdk()184   public void classConfigWithMinSdkAndMaxSdk() throws Throwable {
185     runner = runnerOf(TestClassBetweenLollipopMr1AndN.class);
186     assertThat(apisFor(runner.getChildren())).containsExactly(LOLLIPOP_MR1, M, N);
187 
188     runner.run(runNotifier);
189 
190     assertThat(runListener.ignored).isEmpty();
191     // Since test method should only be run once
192     int sdksInclusivelyInRange = 3;
193     assertThat(runListener.finished).hasSize(sdksInclusivelyInRange);
194   }
195 
196   @Test
methodConfigMinSdk()197   public void methodConfigMinSdk() throws Throwable {
198     runner = runnerOf(TestMethodNAndUp.class);
199     assertThat(apisFor(runner.getChildren())).containsExactly(N, N_MR1, O);
200 
201     runner.run(runNotifier);
202 
203     assertThat(runListener.ignored).isEmpty();
204     int sdksAfterAndIncludingLollipop = 3;
205     assertThat(runListener.finished).hasSize(sdksAfterAndIncludingLollipop);
206   }
207 
208   @Test
methodConfigMaxSdk()209   public void methodConfigMaxSdk() throws Throwable {
210     runner = runnerOf(TestMethodUpToAndIncludingN.class);
211     assertThat(apisFor(runner.getChildren())).containsExactly(LOLLIPOP, LOLLIPOP_MR1, M, N);
212 
213     runner.run(runNotifier);
214 
215     assertThat(runListener.ignored).isEmpty();
216     int sdksUpToAndIncludingLollipop = 4;
217     assertThat(runListener.finished).hasSize(sdksUpToAndIncludingLollipop);
218   }
219 
220   @Test
methodConfigWithMinSdkAndMaxSdk()221   public void methodConfigWithMinSdkAndMaxSdk() throws Throwable {
222     runner = runnerOf(TestMethodBetweenLollipopMr1AndN.class);
223     assertThat(apisFor(runner.getChildren())).containsExactly(LOLLIPOP_MR1, M, N);
224 
225     runner.run(runNotifier);
226 
227     assertThat(runListener.ignored).isEmpty();
228     int sdksInclusivelyInRange = 3;
229     assertThat(runListener.finished).hasSize(sdksInclusivelyInRange);
230   }
231 
232   ///////////////////////////
233 
234   @Nonnull
runnerOf(Class<?> testClass)235   private RobolectricTestRunner runnerOf(Class<?> testClass) throws InitializationError {
236     return new RobolectricTestRunner(testClass, INJECTOR);
237   }
238 
239   @Config(sdk = Config.ALL_SDKS)
240   public static class TestWithNoConfig {
test()241     @Test public void test() {}
242   }
243 
244   @Config(sdk = {M, N})
245   public static class TestClassConfigWithSdkGroup {
testShouldRunApi18()246     @Test public void testShouldRunApi18() {
247       assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(M, N));
248     }
249   }
250 
251   @Config(sdk = Config.ALL_SDKS)
252   public static class TestMethodConfigWithSdkGroup {
253     @Config(sdk = {M, N})
254     @Test
testShouldRunApi16()255     public void testShouldRunApi16() {
256       assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(M, N));
257     }
258   }
259 
260   @Config(minSdk = N)
261   public static class TestClassNAndUp {
testSomeApiLevel()262     @Test public void testSomeApiLevel() {
263       assertThat(Build.VERSION.SDK_INT).isAtLeast(N);
264     }
265   }
266 
267   @Config(maxSdk = N)
268   public static class TestClassUpToAndIncludingN {
testSomeApiLevel()269     @Test public void testSomeApiLevel() {
270       assertThat(Build.VERSION.SDK_INT).isAtMost(N);
271     }
272   }
273 
274   @Config(minSdk = LOLLIPOP_MR1, maxSdk = N)
275   public static class TestClassBetweenLollipopMr1AndN {
testSomeApiLevel()276     @Test public void testSomeApiLevel() {
277       assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(LOLLIPOP_MR1, N));
278     }
279   }
280 
281   @Config(sdk = Config.ALL_SDKS)
282   public static class TestMethodNAndUp {
283     @Config(minSdk = N)
284     @Test
testSomeApiLevel()285     public void testSomeApiLevel() {
286       assertThat(Build.VERSION.SDK_INT).isAtLeast(N);
287     }
288   }
289 
290   @Config(sdk = Config.ALL_SDKS)
291   public static class TestMethodUpToAndIncludingN {
292     @Config(maxSdk = N)
293     @Test
testSomeApiLevel()294     public void testSomeApiLevel() {
295       assertThat(Build.VERSION.SDK_INT).isAtMost(N);
296     }
297   }
298 
299   @Config(sdk = Config.ALL_SDKS)
300   public static class TestMethodBetweenLollipopMr1AndN {
301     @Config(minSdk = LOLLIPOP_MR1, maxSdk = N)
302     @Test
testSomeApiLevel()303     public void testSomeApiLevel() {
304       assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(LOLLIPOP_MR1, N));
305     }
306   }
307 
308   public static class TestMethodWithNewestSdk {
309     @Config(sdk = Config.NEWEST_SDK)
310     @Test
testWithLatest()311     public void testWithLatest() {
312       assertThat(Build.VERSION.SDK_INT).isEqualTo(O);
313     }
314   }
315 
316   @Config(sdk = Config.ALL_SDKS)
317   public static class TestMethodWithSdkAndMinMax {
318     @Config(sdk = M, minSdk = M, maxSdk = N)
319     @Test
testWithSdkRange()320     public void testWithSdkRange() {
321       assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(M, N));
322     }
323   }
324 
apisFor(List<FrameworkMethod> children)325   private static List<Integer> apisFor(List<FrameworkMethod> children) {
326     List<Integer> apis = new ArrayList<>();
327     for (FrameworkMethod child : children) {
328       apis.add(
329           ((RobolectricTestRunner.RobolectricFrameworkMethod) child).getSdk().getApiLevel());
330     }
331     return apis;
332   }
333 
334   private static class MyRunListener extends RunListener {
335     private List<String> started = new ArrayList<>();
336     private List<String> finished = new ArrayList<>();
337     private List<String> ignored = new ArrayList<>();
338 
339     @Override
testStarted(Description description)340     public void testStarted(Description description) throws Exception {
341       started.add(description.getDisplayName());
342     }
343 
344     @Override
testFinished(Description description)345     public void testFinished(Description description) throws Exception {
346       finished.add(description.getDisplayName());
347     }
348 
349     @Override
testIgnored(Description description)350     public void testIgnored(Description description) throws Exception {
351       ignored.add(description.getDisplayName());
352     }
353   }
354 
map(int... sdkInts)355   private List<Sdk> map(int... sdkInts) {
356     SdkCollection allSdks = TestUtil.getSdkCollection();
357     return Arrays.stream(sdkInts).mapToObj(allSdks::getSdk).collect(Collectors.toList());
358   }
359 }
360