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