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.testng;
20  
21  import java.io.File;
22  import java.lang.reflect.Constructor;
23  import java.lang.reflect.InvocationTargetException;
24  import java.util.Collection;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.maven.surefire.api.booter.Command;
29  import org.apache.maven.surefire.api.cli.CommandLineOption;
30  import org.apache.maven.surefire.api.provider.AbstractProvider;
31  import org.apache.maven.surefire.api.provider.CommandChainReader;
32  import org.apache.maven.surefire.api.provider.CommandListener;
33  import org.apache.maven.surefire.api.provider.ProviderParameters;
34  import org.apache.maven.surefire.api.report.ReporterConfiguration;
35  import org.apache.maven.surefire.api.report.ReporterFactory;
36  import org.apache.maven.surefire.api.report.TestOutputReportEntry;
37  import org.apache.maven.surefire.api.report.TestReportListener;
38  import org.apache.maven.surefire.api.suite.RunResult;
39  import org.apache.maven.surefire.api.testset.TestListResolver;
40  import org.apache.maven.surefire.api.testset.TestRequest;
41  import org.apache.maven.surefire.api.testset.TestSetFailedException;
42  import org.apache.maven.surefire.api.util.RunOrderCalculator;
43  import org.apache.maven.surefire.api.util.ScanResult;
44  import org.apache.maven.surefire.api.util.TestsToRun;
45  import org.apache.maven.surefire.testng.utils.FailFastEventsSingleton;
46  
47  import static org.apache.maven.surefire.api.report.ConsoleOutputCapture.startCapture;
48  import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
49  import static org.apache.maven.surefire.api.testset.TestListResolver.getEmptyTestListResolver;
50  import static org.apache.maven.surefire.api.testset.TestListResolver.optionallyWildcardFilter;
51  import static org.apache.maven.surefire.api.util.TestsToRun.fromClass;
52  
53  /**
54   * @author Kristian Rosenvold
55   */
56  public class TestNGProvider extends AbstractProvider {
57      private final Map<String, String> providerProperties;
58  
59      private final ReporterConfiguration reporterConfiguration;
60  
61      private final ClassLoader testClassLoader;
62  
63      private final ScanResult scanResult;
64  
65      private final TestRequest testRequest;
66  
67      private final ProviderParameters providerParameters;
68  
69      private final RunOrderCalculator runOrderCalculator;
70  
71      private final List<CommandLineOption> mainCliOptions;
72  
73      private final CommandChainReader commandsReader;
74  
75      public TestNGProvider(ProviderParameters bootParams) {
76          // don't start a thread in CommandReader while we are in in-plugin process
77          commandsReader = bootParams.isInsideFork() ? bootParams.getCommandReader() : null;
78          providerParameters = bootParams;
79          testClassLoader = bootParams.getTestClassLoader();
80          runOrderCalculator = bootParams.getRunOrderCalculator();
81          providerProperties = bootParams.getProviderProperties();
82          testRequest = bootParams.getTestRequest();
83          reporterConfiguration = bootParams.getReporterConfiguration();
84          scanResult = bootParams.getScanResult();
85          mainCliOptions = bootParams.getMainCliOptions();
86      }
87  
88      @Override
89      public RunResult invoke(Object forkTestSet) throws TestSetFailedException {
90          if (isFailFast() && commandsReader != null) {
91              registerPleaseStopListener();
92          }
93  
94          ReporterFactory reporterFactory = providerParameters.getReporterFactory();
95          TestReportListener<TestOutputReportEntry> reporter = reporterFactory.createTestReportListener();
96  
97          RunResult runResult;
98          try {
99              if (isTestNGXmlTestSuite(testRequest)) {
100                 TestNGReporter testNGReporter = createTestNGReporter(reporter);
101                 testNGReporter.setRunMode(NORMAL_RUN);
102                 /*
103                  * {@link org.apache.maven.surefire.api.report.ConsoleOutputCapture#startCapture(ConsoleOutputReceiver)}
104                  * called in prior to initializing variable {@link #testsToRun}
105                  */
106                 startCapture(testNGReporter);
107 
108                 if (commandsReader != null) {
109                     commandsReader.awaitStarted();
110                 }
111                 TestNGXmlTestSuite testNGXmlTestSuite = newXmlSuite();
112                 testNGXmlTestSuite.locateTestSets();
113                 testNGXmlTestSuite.execute(testNGReporter);
114             } else {
115                 TestNGReporter testNGReporter = createTestNGReporter(reporter);
116                 testNGReporter.setRunMode(NORMAL_RUN);
117                 /*
118                  * {@link org.apache.maven.surefire.api.report.ConsoleOutputCapture#startCapture(ConsoleOutputReceiver)}
119                  * called in prior to initializing variable {@link #testsToRun}
120                  */
121                 startCapture(testNGReporter);
122 
123                 final TestsToRun testsToRun;
124                 if (forkTestSet instanceof TestsToRun) {
125                     testsToRun = (TestsToRun) forkTestSet;
126                 } else if (forkTestSet instanceof Class) {
127                     testsToRun = fromClass((Class<?>) forkTestSet);
128                 } else {
129                     testsToRun = scanClassPath();
130                 }
131 
132                 if (commandsReader != null) {
133                     registerShutdownListener(testsToRun);
134                     commandsReader.awaitStarted();
135                 }
136                 TestNGDirectoryTestSuite suite = newDirectorySuite();
137                 suite.execute(testsToRun, testNGReporter);
138             }
139         } finally {
140             runResult = reporterFactory.close();
141         }
142         return runResult;
143     }
144 
145     boolean isTestNGXmlTestSuite(TestRequest testSuiteDefinition) {
146         Collection<File> suiteXmlFiles = testSuiteDefinition.getSuiteXmlFiles();
147         return !suiteXmlFiles.isEmpty() && !hasSpecificTests();
148     }
149 
150     private boolean isFailFast() {
151         return providerParameters.getSkipAfterFailureCount() > 0;
152     }
153 
154     private int getSkipAfterFailureCount() {
155         return isFailFast() ? providerParameters.getSkipAfterFailureCount() : 0;
156     }
157 
158     private void registerShutdownListener(final TestsToRun testsToRun) {
159         commandsReader.addShutdownListener(new CommandListener() {
160             @Override
161             public void update(Command command) {
162                 testsToRun.markTestSetFinished();
163             }
164         });
165     }
166 
167     private void registerPleaseStopListener() {
168         commandsReader.addSkipNextTestsListener(new CommandListener() {
169             @Override
170             public void update(Command command) {
171                 FailFastEventsSingleton.getInstance().setSkipOnNextTest();
172             }
173         });
174     }
175 
176     private TestNGDirectoryTestSuite newDirectorySuite() {
177         return new TestNGDirectoryTestSuite(
178                 testRequest.getTestSourceDirectory().toString(),
179                 providerProperties,
180                 reporterConfiguration.getReportsDirectory(),
181                 getTestFilter(),
182                 mainCliOptions,
183                 getSkipAfterFailureCount());
184     }
185 
186     private TestNGXmlTestSuite newXmlSuite() {
187         return new TestNGXmlTestSuite(
188                 testRequest.getSuiteXmlFiles(),
189                 testRequest.getTestSourceDirectory().toString(),
190                 providerProperties,
191                 reporterConfiguration.getReportsDirectory(),
192                 getSkipAfterFailureCount());
193     }
194 
195     @Override
196     @SuppressWarnings("unchecked")
197     public Iterable<Class<?>> getSuites() {
198         if (isTestNGXmlTestSuite(testRequest)) {
199             try {
200                 return newXmlSuite().locateTestSets();
201             } catch (TestSetFailedException e) {
202                 throw new RuntimeException(e);
203             }
204         } else {
205             return scanClassPath();
206         }
207     }
208 
209     private TestsToRun scanClassPath() {
210         final TestsToRun scanned = scanResult.applyFilter(null, testClassLoader);
211         return runOrderCalculator.orderTestClasses(scanned);
212     }
213 
214     private boolean hasSpecificTests() {
215         TestListResolver specificTestPatterns = testRequest.getTestListResolver();
216         return !specificTestPatterns.isEmpty() && !specificTestPatterns.isWildcard();
217     }
218 
219     private TestListResolver getTestFilter() {
220         TestListResolver filter = optionallyWildcardFilter(testRequest.getTestListResolver());
221         return filter.isWildcard() ? getEmptyTestListResolver() : filter;
222     }
223 
224     // If we have access to IResultListener, return a ConfigurationAwareTestNGReporter.
225     // But don't cause NoClassDefFoundErrors if it isn't available; just return a regular TestNGReporter instead.
226     private static TestNGReporter createTestNGReporter(TestReportListener<TestOutputReportEntry> reportManager) {
227         try {
228             Class.forName("org.testng.internal.IResultListener");
229             Class<?> c = Class.forName("org.apache.maven.surefire.testng.ConfigurationAwareTestNGReporter");
230             Constructor<?> ctor = c.getConstructor(TestReportListener.class);
231             return (TestNGReporter) ctor.newInstance(reportManager);
232         } catch (InvocationTargetException e) {
233             throw new RuntimeException("Bug in ConfigurationAwareTestNGReporter", e.getCause());
234         } catch (ClassNotFoundException e) {
235             return new TestNGReporter(reportManager);
236         } catch (Exception e) {
237             throw new RuntimeException("Bug in ConfigurationAwareTestNGReporter", e);
238         }
239     }
240 }