1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.surefire.junit4;
20
21 import java.util.Collection;
22 import java.util.Set;
23
24 import org.apache.maven.surefire.api.booter.Command;
25 import org.apache.maven.surefire.api.provider.AbstractProvider;
26 import org.apache.maven.surefire.api.provider.CommandChainReader;
27 import org.apache.maven.surefire.api.provider.CommandListener;
28 import org.apache.maven.surefire.api.provider.ProviderParameters;
29 import org.apache.maven.surefire.api.report.ReporterFactory;
30 import org.apache.maven.surefire.api.report.RunListener;
31 import org.apache.maven.surefire.api.report.SimpleReportEntry;
32 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
33 import org.apache.maven.surefire.api.report.TestReportListener;
34 import org.apache.maven.surefire.api.suite.RunResult;
35 import org.apache.maven.surefire.api.testset.TestListResolver;
36 import org.apache.maven.surefire.api.testset.TestRequest;
37 import org.apache.maven.surefire.api.testset.TestSetFailedException;
38 import org.apache.maven.surefire.api.util.RunOrderCalculator;
39 import org.apache.maven.surefire.api.util.ScanResult;
40 import org.apache.maven.surefire.api.util.TestsToRun;
41 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
42 import org.apache.maven.surefire.common.junit4.JUnit4TestChecker;
43 import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
44 import org.apache.maven.surefire.common.junit4.Notifier;
45 import org.apache.maven.surefire.report.ClassMethodIndexer;
46 import org.apache.maven.surefire.report.PojoStackTraceWriter;
47 import org.apache.maven.surefire.report.RunModeSetter;
48 import org.junit.runner.Description;
49 import org.junit.runner.Request;
50 import org.junit.runner.Result;
51 import org.junit.runner.Runner;
52 import org.junit.runner.manipulation.Filter;
53 import org.junit.runner.notification.StoppedByUserException;
54
55 import static java.lang.reflect.Modifier.isAbstract;
56 import static java.lang.reflect.Modifier.isInterface;
57 import static org.apache.maven.surefire.api.report.ConsoleOutputCapture.startCapture;
58 import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
59 import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
60 import static org.apache.maven.surefire.api.report.SimpleReportEntry.withException;
61 import static org.apache.maven.surefire.api.testset.TestListResolver.optionallyWildcardFilter;
62 import static org.apache.maven.surefire.api.util.TestsToRun.fromClass;
63 import static org.apache.maven.surefire.api.util.internal.ObjectUtils.systemProps;
64 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.createMatchAnyDescriptionFilter;
65 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTestDescriptions;
66 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.isFailureInsideJUnitItself;
67 import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createDescription;
68 import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createIgnored;
69 import static org.apache.maven.surefire.common.junit4.JUnit4RunListener.rethrowAnyTestMechanismFailures;
70 import static org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory.createCustomListeners;
71 import static org.apache.maven.surefire.common.junit4.Notifier.pureNotifier;
72 import static org.junit.runner.Request.aClass;
73
74
75
76
77 public class JUnit4Provider extends AbstractProvider {
78 private static final String UNDETERMINED_TESTS_DESCRIPTION = "cannot determine test in forked JVM with surefire";
79
80 private final ClassMethodIndexer classMethodIndexer = new ClassMethodIndexer();
81
82 private final ClassLoader testClassLoader;
83
84 private final String customRunListeners;
85
86 private final JUnit4TestChecker jUnit4TestChecker;
87
88 private final TestListResolver testResolver;
89
90 private final ProviderParameters providerParameters;
91
92 private final RunOrderCalculator runOrderCalculator;
93
94 private final ScanResult scanResult;
95
96 private final int rerunFailingTestsCount;
97
98 private final CommandChainReader commandsReader;
99
100 private TestsToRun testsToRun;
101
102 public JUnit4Provider(ProviderParameters bootParams) {
103
104 commandsReader = bootParams.isInsideFork() ? bootParams.getCommandReader() : null;
105 providerParameters = bootParams;
106 testClassLoader = bootParams.getTestClassLoader();
107 scanResult = bootParams.getScanResult();
108 runOrderCalculator = bootParams.getRunOrderCalculator();
109 customRunListeners = bootParams.getProviderProperties().get("listener");
110 jUnit4TestChecker = new JUnit4TestChecker(testClassLoader);
111 TestRequest testRequest = bootParams.getTestRequest();
112 testResolver = testRequest.getTestListResolver();
113 rerunFailingTestsCount = testRequest.getRerunFailingTestsCount();
114 }
115
116 @Override
117 public RunResult invoke(Object forkTestSet) throws TestSetFailedException {
118 upgradeCheck();
119
120 ReporterFactory reporterFactory = providerParameters.getReporterFactory();
121
122 RunResult runResult;
123 try {
124 TestReportListener<TestOutputReportEntry> reporter = reporterFactory.createTestReportListener();
125 JUnit4RunListener listener = new JUnit4RunListener(reporter);
126 listener.setRunMode(NORMAL_RUN);
127
128 startCapture(listener);
129
130
131 if (testsToRun == null) {
132 setTestsToRun(forkTestSet);
133 }
134
135 Notifier notifier = new Notifier(listener, getSkipAfterFailureCount());
136 Result result = new Result();
137 notifier.addListeners(createCustomListeners(customRunListeners)).addListener(result.createListener());
138
139 if (isFailFast() && commandsReader != null) {
140 registerPleaseStopJUnitListener(notifier);
141 }
142
143 try {
144 notifier.fireTestRunStarted(
145 testsToRun.allowEagerReading()
146 ? createTestsDescription(testsToRun)
147 : createDescription(UNDETERMINED_TESTS_DESCRIPTION));
148
149 if (commandsReader != null) {
150 registerShutdownListener(testsToRun);
151 commandsReader.awaitStarted();
152 }
153
154 for (Class<?> testToRun : testsToRun) {
155 executeTestSet(testToRun, reporter, notifier, listener);
156 }
157 } finally {
158 notifier.fireTestRunFinished(result);
159 notifier.removeListeners();
160 }
161 rethrowAnyTestMechanismFailures(result);
162 } finally {
163 runResult = reporterFactory.close();
164 }
165 return runResult;
166 }
167
168 private void setTestsToRun(Object forkTestSet) throws TestSetFailedException {
169 if (forkTestSet instanceof TestsToRun) {
170 testsToRun = (TestsToRun) forkTestSet;
171 } else if (forkTestSet instanceof Class) {
172 testsToRun = fromClass((Class<?>) forkTestSet);
173 } else {
174 testsToRun = scanClassPath();
175 }
176 }
177
178 private boolean isRerunFailingTests() {
179 return rerunFailingTestsCount > 0;
180 }
181
182 private boolean isFailFast() {
183 return providerParameters.getSkipAfterFailureCount() > 0;
184 }
185
186 private int getSkipAfterFailureCount() {
187 return isFailFast() ? providerParameters.getSkipAfterFailureCount() : 0;
188 }
189
190 private void registerShutdownListener(final TestsToRun testsToRun) {
191 commandsReader.addShutdownListener(new CommandListener() {
192 @Override
193 public void update(Command command) {
194 testsToRun.markTestSetFinished();
195 }
196 });
197 }
198
199 private void registerPleaseStopJUnitListener(final Notifier notifier) {
200 commandsReader.addSkipNextTestsListener(new CommandListener() {
201 @Override
202 public void update(Command command) {
203 notifier.pleaseStop();
204 }
205 });
206 }
207
208 private void executeTestSet(Class<?> clazz, RunListener reporter, Notifier notifier, RunModeSetter runMode) {
209 long testRunId = classMethodIndexer.indexClass(clazz.getName());
210 SimpleReportEntry report =
211 new SimpleReportEntry(NORMAL_RUN, testRunId, clazz.getName(), null, null, null, systemProps());
212 reporter.testSetStarting(report);
213 try {
214 executeWithRerun(clazz, notifier, runMode);
215 } catch (Throwable e) {
216 if (isFailFast() && e instanceof StoppedByUserException) {
217 String reason = e.getClass().getName();
218 Description skippedTest = createDescription(clazz.getName(), createIgnored(reason));
219 notifier.fireTestIgnored(skippedTest);
220 } else {
221 String reportName = report.getName();
222 String reportSourceName = report.getSourceName();
223 PojoStackTraceWriter stackWriter = new PojoStackTraceWriter(reportSourceName, reportName, e);
224 reporter.testError(
225 withException(NORMAL_RUN, testRunId, reportSourceName, null, reportName, null, stackWriter));
226 }
227 } finally {
228 reporter.testSetCompleted(report);
229 }
230 }
231
232 private void executeWithRerun(Class<?> clazz, Notifier notifier, RunModeSetter runMode) {
233 JUnitTestFailureListener failureListener = new JUnitTestFailureListener();
234 notifier.addListener(failureListener);
235 boolean hasMethodFilter = testResolver != null && testResolver.hasMethodPatterns();
236
237 try {
238 try {
239 notifier.asFailFast(isFailFast());
240 execute(clazz, notifier, hasMethodFilter ? createMethodFilter() : null);
241 } finally {
242 notifier.asFailFast(false);
243 }
244
245
246 if (isRerunFailingTests()) {
247 runMode.setRunMode(RERUN_TEST_AFTER_FAILURE);
248 Notifier rerunNotifier = pureNotifier();
249 notifier.copyListenersTo(rerunNotifier);
250 for (int i = 0;
251 i < rerunFailingTestsCount
252 && !failureListener.getAllFailures().isEmpty();
253 i++) {
254 Set<Description> failures = generateFailingTestDescriptions(failureListener.getAllFailures());
255 failureListener.reset();
256 Filter failureDescriptionFilter = createMatchAnyDescriptionFilter(failures);
257 execute(clazz, rerunNotifier, failureDescriptionFilter);
258 }
259 }
260 } finally {
261 notifier.removeListener(failureListener);
262 }
263 }
264
265 @Override
266 public Iterable<Class<?>> getSuites() {
267 testsToRun = scanClassPath();
268 return testsToRun;
269 }
270
271 private TestsToRun scanClassPath() {
272 final TestsToRun scannedClasses = scanResult.applyFilter(jUnit4TestChecker, testClassLoader);
273 return runOrderCalculator.orderTestClasses(scannedClasses);
274 }
275
276 private void upgradeCheck() throws TestSetFailedException {
277 if (isJUnit4UpgradeCheck()) {
278 Collection<Class<?>> classesSkippedByValidation =
279 scanResult.getClassesSkippedByValidation(jUnit4TestChecker, testClassLoader);
280 if (!classesSkippedByValidation.isEmpty()) {
281 StringBuilder reason = new StringBuilder();
282 reason.append("Updated check failed\n");
283 reason.append("There are tests that would be run with junit4 / surefire 2.6 but not with [2.7,):\n");
284 for (Class testClass : classesSkippedByValidation) {
285 reason.append(" ");
286 reason.append(testClass.getName());
287 reason.append("\n");
288 }
289 throw new TestSetFailedException(reason.toString());
290 }
291 }
292 }
293
294 static Description createTestsDescription(Iterable<Class<?>> classes) {
295
296 Description description = createDescription("null");
297 for (Class<?> clazz : classes) {
298 description.addChild(createDescription(clazz.getName()));
299 }
300 return description;
301 }
302
303 private static boolean isJUnit4UpgradeCheck() {
304 return System.getProperty("surefire.junit4.upgradecheck") != null;
305 }
306
307 private static void execute(Class<?> testClass, Notifier notifier, Filter filter) {
308 final int classModifiers = testClass.getModifiers();
309 if (!isAbstract(classModifiers) && !isInterface(classModifiers)) {
310 Request request = aClass(testClass);
311 if (filter != null) {
312 request = request.filterWith(filter);
313 }
314 Runner runner = request.getRunner();
315 if (countTestsInRunner(runner.getDescription()) != 0) {
316 runner.run(notifier);
317 }
318 }
319 }
320
321
322
323
324
325
326
327
328 private static int countTestsInRunner(Description description) {
329 if (description.isSuite()) {
330 int count = 0;
331 for (Description child : description.getChildren()) {
332 if (!hasFilteredOutAllChildren(child)) {
333 count += countTestsInRunner(child);
334 }
335 }
336 return count;
337 } else if (description.isTest()) {
338 return hasFilteredOutAllChildren(description) ? 0 : 1;
339 } else {
340 return 0;
341 }
342 }
343
344 private static boolean hasFilteredOutAllChildren(Description description) {
345 if (isFailureInsideJUnitItself(description)) {
346 return true;
347 }
348 String name = description.getDisplayName();
349
350 if (name == null) {
351 return true;
352 } else {
353 name = name.trim();
354 return name.startsWith("initializationError0(org.junit.runner.manipulation.Filter)")
355 || name.startsWith("initializationError(org.junit.runner.manipulation.Filter)");
356 }
357 }
358
359 private Filter createMethodFilter() {
360 TestListResolver methodFilter = optionallyWildcardFilter(testResolver);
361 return methodFilter.isEmpty() || methodFilter.isWildcard() ? null : new TestResolverFilter(methodFilter);
362 }
363 }