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