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}