View Javadoc
1   package org.apache.maven.surefire.junitcore;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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.JUnitTestFailureListener;
27  import org.apache.maven.surefire.common.junit4.Notifier;
28  import org.apache.maven.surefire.common.junit48.FilterFactory;
29  import org.apache.maven.surefire.common.junit48.JUnit48Reflector;
30  import org.apache.maven.surefire.common.junit48.JUnit48TestChecker;
31  import org.apache.maven.surefire.api.provider.AbstractProvider;
32  import org.apache.maven.surefire.api.provider.ProviderParameters;
33  import org.apache.maven.surefire.api.report.ConsoleStream;
34  import org.apache.maven.surefire.api.report.ReporterFactory;
35  import org.apache.maven.surefire.api.suite.RunResult;
36  import org.apache.maven.surefire.api.testset.TestListResolver;
37  import org.apache.maven.surefire.api.testset.TestSetFailedException;
38  import org.apache.maven.surefire.api.util.RunOrderCalculator;
39  import org.apache.maven.surefire.api.util.ScanResult;
40  import org.apache.maven.surefire.api.util.ScannerFilter;
41  import org.apache.maven.surefire.api.util.TestsToRun;
42  import org.junit.runner.Description;
43  import org.junit.runner.manipulation.Filter;
44  
45  import java.util.Map;
46  import java.util.Set;
47  import java.util.concurrent.ConcurrentHashMap;
48  
49  import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTestDescriptions;
50  import static org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory.createCustomListeners;
51  import static org.apache.maven.surefire.common.junit4.Notifier.pureNotifier;
52  import static org.apache.maven.surefire.junitcore.ConcurrentRunListener.createInstance;
53  import static org.apache.maven.surefire.api.report.ConsoleOutputCapture.startCapture;
54  import static org.apache.maven.surefire.api.testset.TestListResolver.optionallyWildcardFilter;
55  import static org.apache.maven.surefire.api.util.TestsToRun.fromClass;
56  
57  /**
58   * @author Kristian Rosenvold
59   */
60  @SuppressWarnings( { "UnusedDeclaration" } )
61  public class JUnitCoreProvider
62      extends AbstractProvider
63  {
64      private final ClassLoader testClassLoader;
65  
66      private final JUnitCoreParameters jUnitCoreParameters;
67  
68      private final ScannerFilter scannerFilter;
69  
70      private final String customRunListeners;
71  
72      private final ProviderParameters providerParameters;
73  
74      private final ScanResult scanResult;
75  
76      private final int rerunFailingTestsCount;
77  
78      private final JUnit48Reflector jUnit48Reflector;
79  
80      private final RunOrderCalculator runOrderCalculator;
81  
82      private final TestListResolver testResolver;
83  
84      private final CommandChainReader commandsReader;
85  
86      private TestsToRun testsToRun;
87  
88      public JUnitCoreProvider( ProviderParameters bootParams )
89      {
90          // don't start a thread in CommandReader while we are in in-plugin process
91          commandsReader = bootParams.isInsideFork() ? bootParams.getCommandReader() : null;
92          providerParameters = bootParams;
93          testClassLoader = bootParams.getTestClassLoader();
94          scanResult = bootParams.getScanResult();
95          runOrderCalculator = bootParams.getRunOrderCalculator();
96          jUnitCoreParameters = new JUnitCoreParameters( bootParams.getProviderProperties() );
97          scannerFilter = new JUnit48TestChecker( testClassLoader );
98          testResolver = bootParams.getTestRequest().getTestListResolver();
99          rerunFailingTestsCount = bootParams.getTestRequest().getRerunFailingTestsCount();
100         customRunListeners = bootParams.getProviderProperties().get( "listener" );
101         jUnit48Reflector = new JUnit48Reflector( testClassLoader );
102     }
103 
104     @Override
105     public Iterable<Class<?>> getSuites()
106     {
107         testsToRun = scanClassPath();
108         return testsToRun;
109     }
110 
111     private boolean isSingleThreaded()
112     {
113         return jUnitCoreParameters.isNoThreading();
114     }
115 
116     @Override
117     public RunResult invoke( Object forkTestSet )
118         throws TestSetFailedException
119     {
120         final ReporterFactory reporterFactory = providerParameters.getReporterFactory();
121 
122         final ConsoleStream consoleStream = providerParameters.getConsoleLogger();
123 
124         Notifier notifier =
125             new Notifier( createRunListener( reporterFactory, consoleStream ), getSkipAfterFailureCount() );
126         // startCapture() called in createRunListener() in prior to setTestsToRun()
127 
128         Filter filter = jUnit48Reflector.isJUnit48Available() ? createJUnit48Filter() : null;
129 
130         if ( testsToRun == null )
131         {
132             setTestsToRun( forkTestSet );
133         }
134 
135         // Add test failure listener
136         JUnitTestFailureListener testFailureListener = new JUnitTestFailureListener();
137         notifier.addListener( testFailureListener );
138 
139         if ( isFailFast() && commandsReader != null )
140         {
141             registerPleaseStopJUnitListener( notifier );
142         }
143 
144         final RunResult runResult;
145 
146         try
147         {
148             JUnitCoreWrapper core = new JUnitCoreWrapper( notifier, jUnitCoreParameters, consoleStream );
149 
150             if ( commandsReader != null )
151             {
152                 registerShutdownListener( testsToRun );
153                 commandsReader.awaitStarted();
154             }
155 
156             notifier.asFailFast( isFailFast() );
157             core.execute( testsToRun, createCustomListeners( customRunListeners ), filter );
158             notifier.asFailFast( false );
159 
160             // Rerun failing tests if rerunFailingTestsCount is larger than 0
161             if ( isRerunFailingTests() )
162             {
163                 Notifier rerunNotifier = pureNotifier();
164                 notifier.copyListenersTo( rerunNotifier );
165                 JUnitCoreWrapper rerunCore = new JUnitCoreWrapper( rerunNotifier, jUnitCoreParameters, consoleStream );
166                 for ( int i = 0; i < rerunFailingTestsCount && !testFailureListener.getAllFailures().isEmpty(); i++ )
167                 {
168                     Set<Description> failures = generateFailingTestDescriptions( testFailureListener.getAllFailures() );
169                     testFailureListener.reset();
170                     FilterFactory filterFactory = new FilterFactory( testClassLoader );
171                     Filter failureDescriptionFilter = filterFactory.createMatchAnyDescriptionFilter( failures );
172                     rerunCore.execute( testsToRun, failureDescriptionFilter );
173                 }
174             }
175         }
176         finally
177         {
178             runResult = reporterFactory.close();
179             notifier.removeListeners();
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             Class<?> theClass = (Class<?>) forkTestSet;
194             testsToRun = fromClass( theClass );
195         }
196         else
197         {
198             testsToRun = scanClassPath();
199         }
200     }
201 
202     private boolean isRerunFailingTests()
203     {
204         return rerunFailingTestsCount > 0;
205     }
206 
207     private boolean isFailFast()
208     {
209         return providerParameters.getSkipAfterFailureCount() > 0;
210     }
211 
212     private int getSkipAfterFailureCount()
213     {
214         return isFailFast() ? providerParameters.getSkipAfterFailureCount() : 0;
215     }
216 
217     private void registerShutdownListener( final TestsToRun testsToRun )
218     {
219         commandsReader.addShutdownListener( new CommandListener()
220         {
221             @Override
222             public void update( Command command )
223             {
224                 testsToRun.markTestSetFinished();
225             }
226         } );
227     }
228 
229     private void registerPleaseStopJUnitListener( final Notifier stoppable )
230     {
231         commandsReader.addSkipNextTestsListener( new CommandListener()
232         {
233             @Override
234             public void update( Command command )
235             {
236                 stoppable.pleaseStop();
237             }
238         } );
239     }
240 
241     private JUnit4RunListener createRunListener( ReporterFactory reporterFactory, ConsoleStream consoleStream )
242         throws TestSetFailedException
243     {
244         if ( isSingleThreaded() )
245         {
246             NonConcurrentRunListener rm = new NonConcurrentRunListener( reporterFactory.createReporter() );
247             startCapture( rm );
248             return rm;
249         }
250         else
251         {
252             final Map<String, TestSet> testSetMap = new ConcurrentHashMap<>();
253 
254             ConcurrentRunListener listener = createInstance( testSetMap, reporterFactory, isParallelTypes(),
255                                                              isParallelMethodsAndTypes(), consoleStream );
256             startCapture( listener );
257 
258             return new JUnitCoreRunListener( listener, testSetMap );
259         }
260     }
261 
262     private boolean isParallelMethodsAndTypes()
263     {
264         return jUnitCoreParameters.isParallelMethods() && isParallelTypes();
265     }
266 
267     private boolean isParallelTypes()
268     {
269         return jUnitCoreParameters.isParallelClasses() || jUnitCoreParameters.isParallelSuites();
270     }
271 
272     private Filter createJUnit48Filter()
273     {
274         final FilterFactory factory = new FilterFactory( testClassLoader );
275         Map<String, String> props = providerParameters.getProviderProperties();
276         Filter groupFilter = factory.canCreateGroupFilter( props ) ? factory.createGroupFilter( props ) : null;
277         TestListResolver methodFilter = optionallyWildcardFilter( testResolver );
278         boolean onlyGroups = methodFilter.isEmpty() || methodFilter.isWildcard();
279         if ( onlyGroups )
280         {
281             return groupFilter;
282         }
283         else
284         {
285             Filter jUnitMethodFilter = factory.createMethodFilter( methodFilter );
286             return groupFilter == null ? jUnitMethodFilter : factory.and( groupFilter, jUnitMethodFilter );
287         }
288     }
289 
290     private TestsToRun scanClassPath()
291     {
292         TestsToRun scanned = scanResult.applyFilter( scannerFilter, testClassLoader );
293         return runOrderCalculator.orderTestClasses( scanned );
294     }
295 }