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