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