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