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