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