1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.surefire.junitplatform;
20
21 import java.io.IOException;
22 import java.io.StringReader;
23 import java.io.UncheckedIOException;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.LinkedHashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Optional;
30 import java.util.Properties;
31 import java.util.logging.Logger;
32
33 import org.apache.maven.surefire.api.provider.AbstractProvider;
34 import org.apache.maven.surefire.api.provider.ProviderParameters;
35 import org.apache.maven.surefire.api.report.ReporterException;
36 import org.apache.maven.surefire.api.report.ReporterFactory;
37 import org.apache.maven.surefire.api.suite.RunResult;
38 import org.apache.maven.surefire.api.testset.TestSetFailedException;
39 import org.apache.maven.surefire.api.util.ScanResult;
40 import org.apache.maven.surefire.api.util.SurefireReflectionException;
41 import org.apache.maven.surefire.api.util.TestsToRun;
42 import org.apache.maven.surefire.shared.utils.StringUtils;
43 import org.junit.platform.engine.DiscoverySelector;
44 import org.junit.platform.engine.Filter;
45 import org.junit.platform.launcher.EngineFilter;
46 import org.junit.platform.launcher.Launcher;
47 import org.junit.platform.launcher.LauncherDiscoveryRequest;
48 import org.junit.platform.launcher.TagFilter;
49 import org.junit.platform.launcher.TestExecutionListener;
50 import org.junit.platform.launcher.TestIdentifier;
51 import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
52
53 import static java.util.Arrays.stream;
54 import static java.util.Collections.emptyMap;
55 import static java.util.Optional.empty;
56 import static java.util.Optional.of;
57 import static java.util.logging.Level.WARNING;
58 import static java.util.stream.Collectors.toList;
59 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.EXCLUDE_JUNIT5_ENGINES_PROP;
60 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.INCLUDE_JUNIT5_ENGINES_PROP;
61 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP;
62 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.TESTNG_GROUPS_PROP;
63 import static org.apache.maven.surefire.api.report.ConsoleOutputCapture.startCapture;
64 import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
65 import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
66 import static org.apache.maven.surefire.api.testset.TestListResolver.optionallyWildcardFilter;
67 import static org.apache.maven.surefire.api.util.TestsToRun.fromClass;
68 import static org.apache.maven.surefire.shared.utils.StringUtils.isBlank;
69 import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
70 import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;
71 import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
72
73
74
75
76
77
78 public class JUnitPlatformProvider extends AbstractProvider {
79 static final String CONFIGURATION_PARAMETERS = "configurationParameters";
80
81 private final ProviderParameters parameters;
82
83 private final Launcher launcher;
84
85 private final Filter<?>[] filters;
86
87 private final Map<String, String> configurationParameters;
88
89 public JUnitPlatformProvider(ProviderParameters parameters) {
90 this(parameters, new LazyLauncher());
91 }
92
93 JUnitPlatformProvider(ProviderParameters parameters, Launcher launcher) {
94 this.parameters = parameters;
95 this.launcher = launcher;
96 filters = newFilters();
97 configurationParameters = newConfigurationParameters();
98 }
99
100 @Override
101 public Iterable<Class<?>> getSuites() {
102 try {
103 return scanClasspath();
104 } finally {
105 closeLauncher();
106 }
107 }
108
109 @Override
110 public RunResult invoke(Object forkTestSet) throws TestSetFailedException, ReporterException {
111 ReporterFactory reporterFactory = parameters.getReporterFactory();
112 final RunResult runResult;
113 try {
114 RunListenerAdapter adapter = new RunListenerAdapter(reporterFactory.createTestReportListener());
115 adapter.setRunMode(NORMAL_RUN);
116
117 startCapture(adapter);
118 setupJunitLogger();
119 if (forkTestSet instanceof TestsToRun) {
120 invokeAllTests((TestsToRun) forkTestSet, adapter);
121 } else if (forkTestSet instanceof Class) {
122 invokeAllTests(fromClass((Class<?>) forkTestSet), adapter);
123 } else if (forkTestSet == null) {
124 invokeAllTests(scanClasspath(), adapter);
125 } else {
126 throw new IllegalArgumentException("Unexpected value of forkTestSet: " + forkTestSet);
127 }
128 } finally {
129 runResult = reporterFactory.close();
130 }
131 return runResult;
132 }
133
134 private static void setupJunitLogger() {
135 Logger logger = Logger.getLogger("org.junit");
136 if (logger.getLevel() == null) {
137 logger.setLevel(WARNING);
138 }
139 }
140
141 private TestsToRun scanClasspath() {
142 TestPlanScannerFilter filter = new TestPlanScannerFilter(launcher, filters);
143 ScanResult scanResult = parameters.getScanResult();
144 TestsToRun scannedClasses = scanResult.applyFilter(filter, parameters.getTestClassLoader());
145 return parameters.getRunOrderCalculator().orderTestClasses(scannedClasses);
146 }
147
148 private void invokeAllTests(TestsToRun testsToRun, RunListenerAdapter adapter) {
149 try {
150 execute(testsToRun, adapter);
151 } finally {
152 closeLauncher();
153 }
154
155 int count = parameters.getTestRequest().getRerunFailingTestsCount();
156 if (count > 0 && adapter.hasFailingTests()) {
157 adapter.setRunMode(RERUN_TEST_AFTER_FAILURE);
158 for (int i = 0; i < count; i++) {
159 try {
160
161 LauncherDiscoveryRequest discoveryRequest = buildLauncherDiscoveryRequestForRerunFailures(adapter);
162
163 adapter.reset();
164 launcher.execute(discoveryRequest, adapter);
165
166 if (!adapter.hasFailingTests()) {
167 break;
168 }
169 } finally {
170 closeLauncher();
171 }
172 }
173 }
174 }
175
176 private void execute(TestsToRun testsToRun, RunListenerAdapter adapter) {
177
178
179
180
181
182
183
184 TestExecutionListener[] testExecutionListeners = new TestExecutionListener[] {adapter};
185
186 if (testsToRun.allowEagerReading()) {
187 List<DiscoverySelector> selectors = new ArrayList<>();
188 testsToRun.iterator().forEachRemaining(c -> selectors.add(selectClass(c.getName())));
189
190 LauncherDiscoveryRequestBuilder builder = request()
191 .filters(filters)
192 .configurationParameters(configurationParameters)
193 .selectors(selectors);
194 launcher.execute(builder.build(), testExecutionListeners);
195 } else {
196 testsToRun.iterator().forEachRemaining(c -> {
197 LauncherDiscoveryRequestBuilder builder = request()
198 .filters(filters)
199 .configurationParameters(configurationParameters)
200 .selectors(selectClass(c.getName()));
201 launcher.execute(builder.build(), testExecutionListeners);
202 });
203 }
204 }
205
206 private void closeLauncher() {
207 if (launcher instanceof AutoCloseable) {
208 try {
209 ((AutoCloseable) launcher).close();
210 } catch (Exception e) {
211 throw new SurefireReflectionException(e);
212 }
213 }
214 }
215
216 private LauncherDiscoveryRequest buildLauncherDiscoveryRequestForRerunFailures(RunListenerAdapter adapter) {
217 LauncherDiscoveryRequestBuilder builder =
218 request().filters(filters).configurationParameters(configurationParameters);
219
220 for (TestIdentifier identifier :
221 new LinkedHashSet<>(adapter.getFailures().keySet())) {
222 builder.selectors(selectUniqueId(identifier.getUniqueId()));
223 }
224 return builder.build();
225 }
226
227 private Filter<?>[] newFilters() {
228 List<Filter<?>> filters = new ArrayList<>();
229
230 getPropertiesList(TESTNG_GROUPS_PROP).map(TagFilter::includeTags).ifPresent(filters::add);
231
232 getPropertiesList(TESTNG_EXCLUDEDGROUPS_PROP)
233 .map(TagFilter::excludeTags)
234 .ifPresent(filters::add);
235
236 of(optionallyWildcardFilter(parameters.getTestRequest().getTestListResolver()))
237 .filter(f -> !f.isEmpty())
238 .filter(f -> !f.isWildcard())
239 .map(TestMethodFilter::new)
240 .ifPresent(filters::add);
241
242 getPropertiesList(INCLUDE_JUNIT5_ENGINES_PROP)
243 .map(EngineFilter::includeEngines)
244 .ifPresent(filters::add);
245
246 getPropertiesList(EXCLUDE_JUNIT5_ENGINES_PROP)
247 .map(EngineFilter::excludeEngines)
248 .ifPresent(filters::add);
249
250 return filters.toArray(new Filter<?>[0]);
251 }
252
253 Filter<?>[] getFilters() {
254 return filters;
255 }
256
257 private Map<String, String> newConfigurationParameters() {
258 String content = parameters.getProviderProperties().get(CONFIGURATION_PARAMETERS);
259 if (content == null) {
260 return emptyMap();
261 }
262 try (StringReader reader = new StringReader(content)) {
263 Map<String, String> result = new HashMap<>();
264 Properties props = new Properties();
265 props.load(reader);
266 props.stringPropertyNames().forEach(key -> result.put(key, props.getProperty(key)));
267 return result;
268 } catch (IOException e) {
269 throw new UncheckedIOException("Error reading " + CONFIGURATION_PARAMETERS, e);
270 }
271 }
272
273 Map<String, String> getConfigurationParameters() {
274 return configurationParameters;
275 }
276
277 private Optional<List<String>> getPropertiesList(String key) {
278 String property = parameters.getProviderProperties().get(key);
279 return isBlank(property)
280 ? empty()
281 : of(stream(property.split("[,]+"))
282 .filter(StringUtils::isNotBlank)
283 .map(String::trim)
284 .collect(toList()));
285 }
286 }