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