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.plugin.surefire;
20  
21  import javax.annotation.Nonnull;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.math.BigDecimal;
27  import java.nio.file.Files;
28  import java.text.ChoiceFormat;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.Collections;
32  import java.util.Enumeration;
33  import java.util.HashMap;
34  import java.util.HashSet;
35  import java.util.LinkedHashSet;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Map.Entry;
39  import java.util.Optional;
40  import java.util.Properties;
41  import java.util.Set;
42  import java.util.SortedSet;
43  import java.util.TreeSet;
44  import java.util.concurrent.ConcurrentHashMap;
45  import java.util.function.Function;
46  import java.util.stream.Collectors;
47  import java.util.zip.ZipFile;
48  
49  import org.apache.maven.artifact.Artifact;
50  import org.apache.maven.artifact.DefaultArtifact;
51  import org.apache.maven.artifact.handler.ArtifactHandler;
52  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
53  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
54  import org.apache.maven.artifact.versioning.ComparableVersion;
55  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
56  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
57  import org.apache.maven.artifact.versioning.VersionRange;
58  import org.apache.maven.execution.MavenSession;
59  import org.apache.maven.model.Dependency;
60  import org.apache.maven.model.Plugin;
61  import org.apache.maven.plugin.AbstractMojo;
62  import org.apache.maven.plugin.MojoExecutionException;
63  import org.apache.maven.plugin.MojoFailureException;
64  import org.apache.maven.plugin.descriptor.PluginDescriptor;
65  import org.apache.maven.plugin.surefire.booterclient.ChecksumCalculator;
66  import org.apache.maven.plugin.surefire.booterclient.ClasspathForkConfiguration;
67  import org.apache.maven.plugin.surefire.booterclient.ForkConfiguration;
68  import org.apache.maven.plugin.surefire.booterclient.ForkStarter;
69  import org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfiguration;
70  import org.apache.maven.plugin.surefire.booterclient.ModularClasspathForkConfiguration;
71  import org.apache.maven.plugin.surefire.booterclient.Platform;
72  import org.apache.maven.plugin.surefire.extensions.LegacyForkNodeFactory;
73  import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
74  import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
75  import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
76  import org.apache.maven.plugin.surefire.log.PluginConsoleLogger;
77  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
78  import org.apache.maven.plugin.surefire.util.DependencyScanner;
79  import org.apache.maven.plugin.surefire.util.DirectoryScanner;
80  import org.apache.maven.plugins.annotations.Component;
81  import org.apache.maven.plugins.annotations.Parameter;
82  import org.apache.maven.project.MavenProject;
83  import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
84  import org.apache.maven.surefire.api.booter.ProviderParameterNames;
85  import org.apache.maven.surefire.api.booter.Shutdown;
86  import org.apache.maven.surefire.api.cli.CommandLineOption;
87  import org.apache.maven.surefire.api.report.ReporterConfiguration;
88  import org.apache.maven.surefire.api.report.ReporterFactoryOptions;
89  import org.apache.maven.surefire.api.suite.RunResult;
90  import org.apache.maven.surefire.api.testset.DirectoryScannerParameters;
91  import org.apache.maven.surefire.api.testset.RunOrderParameters;
92  import org.apache.maven.surefire.api.testset.TestArtifactInfo;
93  import org.apache.maven.surefire.api.testset.TestListResolver;
94  import org.apache.maven.surefire.api.testset.TestRequest;
95  import org.apache.maven.surefire.api.testset.TestSetFailedException;
96  import org.apache.maven.surefire.api.util.DefaultScanResult;
97  import org.apache.maven.surefire.api.util.RunOrder;
98  import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
99  import org.apache.maven.surefire.booter.Classpath;
100 import org.apache.maven.surefire.booter.ClasspathConfiguration;
101 import org.apache.maven.surefire.booter.KeyValueSource;
102 import org.apache.maven.surefire.booter.ModularClasspath;
103 import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
104 import org.apache.maven.surefire.booter.ProcessCheckerType;
105 import org.apache.maven.surefire.booter.ProviderConfiguration;
106 import org.apache.maven.surefire.booter.StartupConfiguration;
107 import org.apache.maven.surefire.booter.SurefireBooterForkException;
108 import org.apache.maven.surefire.booter.SurefireExecutionException;
109 import org.apache.maven.surefire.extensions.ForkNodeFactory;
110 import org.apache.maven.surefire.providerapi.ConfigurableProviderInfo;
111 import org.apache.maven.surefire.providerapi.ProviderDetector;
112 import org.apache.maven.surefire.providerapi.ProviderInfo;
113 import org.apache.maven.surefire.providerapi.ProviderRequirements;
114 import org.apache.maven.surefire.shared.utils.StringUtils;
115 import org.apache.maven.surefire.shared.utils.io.FileUtils;
116 import org.apache.maven.toolchain.DefaultToolchain;
117 import org.apache.maven.toolchain.Toolchain;
118 import org.apache.maven.toolchain.ToolchainManager;
119 import org.apache.maven.toolchain.java.DefaultJavaToolChain;
120 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
121 import org.codehaus.plexus.languages.java.jpms.LocationManager;
122 import org.codehaus.plexus.languages.java.jpms.ResolvePathRequest;
123 import org.codehaus.plexus.languages.java.jpms.ResolvePathResult;
124 import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
125 import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
126 import org.slf4j.Logger;
127 import org.slf4j.LoggerFactory;
128 
129 import static java.lang.Integer.parseInt;
130 import static java.util.Arrays.asList;
131 import static java.util.Collections.addAll;
132 import static java.util.Collections.emptyList;
133 import static java.util.Collections.singletonList;
134 import static java.util.Collections.singletonMap;
135 import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.PluginFailureReason.COULD_NOT_RUN_DEFAULT_TESTS;
136 import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.PluginFailureReason.COULD_NOT_RUN_SPECIFIED_TESTS;
137 import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.PluginFailureReason.NONE;
138 import static org.apache.maven.plugin.surefire.SurefireDependencyResolver.isWithinVersionSpec;
139 import static org.apache.maven.plugin.surefire.SurefireHelper.replaceThreadNumberPlaceholders;
140 import static org.apache.maven.plugin.surefire.util.DependencyScanner.filter;
141 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.EXCLUDE_JUNIT5_ENGINES_PROP;
142 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.INCLUDE_JUNIT5_ENGINES_PROP;
143 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.JUNIT_VINTAGE_DETECTED;
144 import static org.apache.maven.surefire.api.suite.RunResult.failure;
145 import static org.apache.maven.surefire.api.suite.RunResult.noTestsRun;
146 import static org.apache.maven.surefire.booter.Classpath.emptyClasspath;
147 import static org.apache.maven.surefire.booter.SystemUtils.endsWithJavaPath;
148 import static org.apache.maven.surefire.booter.SystemUtils.isBuiltInJava9AtLeast;
149 import static org.apache.maven.surefire.booter.SystemUtils.isJava9AtLeast;
150 import static org.apache.maven.surefire.booter.SystemUtils.toJdkHomeFromJvmExec;
151 import static org.apache.maven.surefire.booter.SystemUtils.toJdkVersionFromReleaseFile;
152 import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT;
153 import static org.apache.maven.surefire.shared.lang3.StringUtils.substringBeforeLast;
154 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
155 import static org.apache.maven.surefire.shared.utils.StringUtils.capitalizeFirstLetter;
156 import static org.apache.maven.surefire.shared.utils.StringUtils.isEmpty;
157 import static org.apache.maven.surefire.shared.utils.StringUtils.isNotBlank;
158 import static org.apache.maven.surefire.shared.utils.StringUtils.isNotEmpty;
159 import static org.apache.maven.surefire.shared.utils.StringUtils.split;
160 import static org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils.addShutDownHook;
161 import static org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils.removeShutdownHook;
162 
163 /**
164  * Abstract base class for running tests using Surefire.
165  *
166  * @author Stephen Connolly
167  */
168 public abstract class AbstractSurefireMojo extends AbstractMojo implements SurefireExecutionParameters {
169     private static final Map<String, String> JAVA_9_MATCHER_OLD_NOTATION = singletonMap("version", "[1.9,)");
170     private static final Map<String, String> JAVA_9_MATCHER = singletonMap("version", "[9,)");
171     private static final Platform PLATFORM = new Platform();
172 
173     private final ClasspathCache classpathCache = new ClasspathCache();
174 
175     @Parameter
176     private SurefireStatelessReporter statelessTestsetReporter;
177 
178     @Parameter
179     private SurefireConsoleOutputReporter consoleOutputReporter;
180 
181     @Parameter
182     private SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter;
183 
184     /**
185      * Information about this plugin, mainly used to lookup this plugin's configuration from the currently executing
186      * project.
187      *
188      * @since 2.12
189      */
190     @Parameter(defaultValue = "${plugin}", readonly = true, required = true)
191     private PluginDescriptor pluginDescriptor;
192 
193     /**
194      * Set this to "true" to skip running tests, but still compile them. Its use is NOT RECOMMENDED, but quite
195      * convenient on occasion.<br>
196      * Failsafe plugin deprecated the parameter {@code skipTests} and the parameter will be removed in
197      * <i>Failsafe 3.0.0</i> as it is a source of conflicts between Failsafe and Surefire plugin.
198      *
199      * @since 2.4
200      */
201     @Parameter(property = "skipTests", defaultValue = "false")
202     protected boolean skipTests;
203 
204     /**
205      * This old parameter is just like {@code skipTests}, but bound to the old property "maven.test.skip.exec".
206      *
207      * @since 2.3
208      * @deprecated Use skipTests instead.
209      */
210     @Deprecated
211     @Parameter(property = "maven.test.skip.exec")
212     protected boolean skipExec;
213 
214     /**
215      * Set this to "true" to bypass unit tests entirely. Its use is NOT RECOMMENDED, especially if you enable it using
216      * the "maven.test.skip" property, because maven.test.skip disables both running the tests and compiling the tests.
217      * Consider using the {@code skipTests} parameter instead.
218      */
219     @Parameter(property = "maven.test.skip", defaultValue = "false")
220     protected boolean skip;
221 
222     /**
223      * The Maven Project Object.
224      */
225     @Parameter(defaultValue = "${project}", required = true, readonly = true)
226     private MavenProject project;
227 
228     /**
229      * The base directory of the project being tested. This can be obtained in your integration test via
230      * System.getProperty("basedir").
231      */
232     @Parameter(defaultValue = "${basedir}", readonly = true, required = true)
233     protected File basedir;
234 
235     /**
236      * The directory containing generated test classes of the project being tested. This will be included at the
237      * beginning of the test classpath.
238      */
239     @Parameter(defaultValue = "${project.build.testOutputDirectory}")
240     protected File testClassesDirectory;
241 
242     /**
243      * List of dependencies to exclude from the test classpath at runtime.
244      * Each item is passed as pattern to {@link PatternIncludesArtifactFilter}.
245      * The pattern is matched against the following artifact ids:
246      * <ul>
247      * <li>{@code groupId:artifactId} (Short ID)</li>
248      * <li>{@code groupId:artifactId:type:classifier} (Dependency Conflict ID)</li>
249      * <li>{@code groupId:artifactId:type:classifier:version} (Full ID)</li>
250      * </ul>
251      * The matching algorithm is described in detail in <a href="https://maven.apache.org/plugins/maven-assembly-plugin/advanced-descriptor-topics.html#advanced-artifact-matching-in-includes-and-excludes">Advanced Artifact-Matching</a> for the maven-assembly-plugin. This parameter behaves the same as the {@code excludes} pattern described there.
252      * The dependency matching is applied to the project dependency IDs (including transitive ones) <i>after resolving</i>, i.e. excluding
253      * one dependency will not exclude its transitive dependencies!
254      * @since 2.6
255      */
256     @Parameter(property = "maven.test.dependency.excludes")
257     private String[] classpathDependencyExcludes;
258 
259     /**
260      * A dependency scope to exclude from the test classpath at runtime. The scope should be one of the scopes defined by
261      * org.apache.maven.artifact.Artifact. This includes the following:
262      * <br>
263      * <ul>
264      * <li><i>compile</i> - system, provided, compile
265      * <li><i>runtime</i> - compile, runtime
266      * <li><i>compile+runtime</i> - system, provided, compile, runtime
267      * <li><i>runtime+system</i> - system, compile, runtime
268      * <li><i>test</i> - system, provided, compile, runtime, test
269      * </ul>
270      *
271      * @since 2.6
272      */
273     @Parameter(defaultValue = "")
274     private String classpathDependencyScopeExclude;
275 
276     /**
277      * Additional elements to be appended to the test classpath at runtime.
278      * Each element must be a file system path to a JAR file or a directory containing classes.
279      * No wildcards are allowed here.
280      *
281      * @since 2.4
282      */
283     @Parameter(property = "maven.test.additionalClasspath")
284     private String[] additionalClasspathElements;
285 
286     /**
287      * Additional Maven dependencies to be added to the test classpath at runtime.
288      * Each element supports the parametrization like documented in <a href="https://maven.apache.org/pom.html#dependencies">POM Reference: Dependencies</a>.
289      * <p>
290      * Those dependencies are automatically collected (i.e. have their full dependency tree calculated) and then all underlying artifacts are resolved from the repository (including their transitive dependencies).
291      * Afterwards the resolved artifacts are filtered to only contain {@code compile} and {@code runtime} scoped ones and appended to the test classpath at runtime
292      * (after the ones from {@link #additionalClasspathElements}).
293      * <p>
294      * The following differences to regular project dependency resolving apply:
295      * <ul>
296      * <li>The dependency management from the project is not taken into account.</li>
297      * <li>Conflicts between the different items and the project dependencies are not resolved.</li>
298      * <li>Only external dependencies (outside the current Maven reactor) are supported.</li>
299      * </ul>
300      *
301      * @since 3.2
302      */
303     @Parameter(property = "maven.test.additionalClasspathDependencies")
304     private List<Dependency> additionalClasspathDependencies;
305 
306     /**
307      * The test source directory containing test class sources.
308      * Important <b>only</b> for TestNG HTML reports.
309      *
310      * @since 2.2
311      */
312     @Parameter(defaultValue = "${project.build.testSourceDirectory}")
313     private File testSourceDirectory;
314 
315     /**
316      * List of System properties to pass to a provider.
317      *
318      * @deprecated Use {@link #systemPropertyVariables} instead.
319      * @see #systemPropertyVariables {@code systemPropertyVariables} for how the effective provider properties are calculated
320      */
321     @Deprecated
322     @Parameter
323     Properties systemProperties;
324 
325     /**
326      * List of System properties to pass to a provider.
327      * The effective system properties given to a provider are contributed from several sources:
328      * <ol>
329      * <li>properties set via {@link #argLine} with {@code -D} (only for forked executions)</li>
330      * <li>{@link #systemProperties}</li>
331      * <li>{@link AbstractSurefireMojo#getSystemPropertiesFile()} (set via parameter {@code systemPropertiesFile} on some goals)</li>
332      * <li>{@link #systemPropertyVariables}</li>
333      * <li>User properties from {@link MavenSession#getUserProperties()}, usually set via CLI options given with {@code -D} on the current Maven process (only used as source if {@link #promoteUserPropertiesToSystemProperties} is {@code true})</li>
334      * </ol>
335      * Later sources may overwrite same named properties from earlier sources, that means for example that one cannot overwrite user properties with either
336      * {@link #systemProperties}, {@link #getSystemPropertiesFile()} or {@link #systemPropertyVariables}.
337      * <p>
338      * Certain properties may only be overwritten via {@link #argLine} (applicable only for forked executions) because their values are cached and only evaluated at the start of the JRE.
339      * Those include:
340      * <ul>
341      * <li>{@code java.library.path}</li>
342      * <li>{@code file.encoding}</li>
343      * <li>{@code jdk.map.althashing.threshold}</li>
344      * <li>{@code line.separator}</li>
345      * </ul>
346      *
347      * @since 2.5
348      * @see #systemProperties
349      */
350     @Parameter
351     Map<String, String> systemPropertyVariables;
352 
353     /**
354      * If set to {@code true} will also pass all user properties exposed via {@link MavenSession#getUserProperties()} as system properties to a provider.
355      * Those always take precedence over same named system properties set via any other means.
356      *
357      * @since 3.4.0
358      * @see #systemPropertyVariables
359      */
360     @Parameter(defaultValue = "true")
361     boolean promoteUserPropertiesToSystemProperties;
362 
363     /**
364      * List of properties for configuring the testing provider. This is the preferred method of
365      * configuring TestNG and JUnit platform providers.<br><br>
366      * JUnit platform properties may be defined in a {@code configurationParameters} element:
367      * <pre><code>{@literal <properties>}
368      *     {@literal <configurationParameters>}
369      *         junit.jupiter.execution.parallel.enabled = true
370      *         junit.jupiter.execution.parallel.mode.default = concurrent
371      *     {@literal </configurationParameters>}
372      * {@literal </properties>}</code></pre>
373      * <br>
374      * TestNG properties may be defined as distinct element blocks:
375      * <pre><code>{@literal <properties>}
376      *     {@literal <property>}
377      *         {@literal <name>parallel</name>}
378      *         {@literal <value>methods</value>}
379      *     {@literal </property>}
380      * {@literal </properties>}</code></pre>
381      * @since 2.4
382      */
383     @Parameter
384     private Properties properties;
385 
386     /**
387      * Package prefixes to filter from stack traces when capturing test output.
388      * When specified, this REPLACES the default prefixes (java., javax., sun., jdk.).
389      * <br>
390      * <ul>
391      *   <li>Not specified: uses defaults (java., javax., sun., jdk.)</li>
392      *   <li>Custom list: only those prefixes are filtered (replaces defaults)</li>
393      * </ul>
394      * <br>
395      * Example - filter only specific packages:
396      * <pre><code>{@literal <stackTraceFilterPrefixes>}
397      *     {@literal <prefix>org.springframework.</prefix>}
398      *     {@literal <prefix>org.junit.</prefix>}
399      * {@literal </stackTraceFilterPrefixes>}</code></pre>
400      *
401      * @since 3.6.0
402      */
403     @Parameter(property = "surefire.stackTraceFilterPrefixes")
404     private List<String> stackTraceFilterPrefixes;
405 
406     /**
407      * Maximum number of stack trace frames to capture when associating console output with test classes.
408      * Default is 15. For performance reason this could be set to 0 or a negative value to disable stack trace capture entirely.
409      * In the case of low value, Surefire will capture only the top frames of the stack trace, which might miss the test class
410      * and so confuse stack trace in case of parallel test execution.
411      * In the case of high value, Surefire will capture more frames, which might have a negative impact on performance and memory usage.
412      * @since 3.6.0
413      */
414     @Parameter(property = "surefire.stackTraceMaxFrames", defaultValue = "15")
415     private int stackTraceMaxFrames;
416 
417     /**
418      * Map of plugin artifacts.
419      */
420     @Parameter(property = "plugin.artifactMap", required = true, readonly = true)
421     private Map<String, Artifact> pluginArtifactMap;
422 
423     /**
424      * Map of project artifacts.
425      */
426     @Parameter(property = "project.artifactMap", readonly = true, required = true)
427     private Map<String, Artifact> projectArtifactMap;
428 
429     /**
430      * Add custom text into report filename: TEST-testClassName-reportNameSuffix.xml,
431      * testClassName-reportNameSuffix.txt and testClassName-reportNameSuffix-output.txt.
432      * File TEST-testClassName-reportNameSuffix.xml has changed attributes 'testsuite'--'name'
433      * and 'testcase'--'classname' - reportNameSuffix is added to the attribute value.
434      */
435     @Parameter(property = "surefire.reportNameSuffix", defaultValue = "")
436     private String reportNameSuffix;
437 
438     /**
439      * Set this to "true" to redirect the unit test standard output to a file (found in
440      * reportsDirectory/testName-output.txt).
441      *
442      * @since 2.3
443      */
444     @Parameter(property = "maven.test.redirectTestOutputToFile", defaultValue = "false")
445     private boolean redirectTestOutputToFile;
446 
447     /**
448      * Set this to "true" to cause a failure if there are no tests to run. Defaults to "false".
449      *
450      * @since 2.4
451      */
452     @Parameter(property = "failIfNoTests", defaultValue = "false")
453     private boolean failIfNoTests;
454 
455     /**
456      * Relative path to <i>temporary-surefire-boot</i> directory containing internal Surefire temporary files.
457      * <br>
458      * The <i>temporary-surefire-boot</i> directory is <i>project.build.directory</i> on most platforms or
459      * <i>system default temporary-directory</i> specified by the system property {@code java.io.tmpdir}
460      * on Windows (see <a href="https://issues.apache.org/jira/browse/SUREFIRE-1400">SUREFIRE-1400</a>).
461      * <br>
462      * It is deleted after the test set has completed.
463      *
464      * @since 2.20
465      */
466     @Parameter(property = "tempDir", defaultValue = "surefire")
467     private String tempDir;
468 
469     /**
470      * Option to specify the jvm (or path to the java executable) to use with the forking options. For the default, the
471      * jvm will be a new instance of the same VM as the one used to run Maven. JVM settings are not inherited from
472      * MAVEN_OPTS.
473      *
474      * @since 2.1
475      */
476     @Parameter(property = "jvm")
477     private String jvm;
478 
479     /**
480      * Arbitrary JVM options to set on the command line. Only effective for forked executions.
481      * <br>
482      * <br>
483      * Using an alternate syntax for placeholders in {@code argLine}, namely <code>@{...}</code> allows late replacement
484      * of properties when the plugin is executed, so properties that have been <i>modified</i> by other plugins will be picked
485      * up correctly.
486      * In contrast to the standard Maven property replacement the <code>@{...}</code> placeholder is replaced by the empty string if the referenced
487      * propery cannot be found.
488      * <br>
489      * <i>This mechanism is only necessary for properties which are modified i.e. also available with a different value at initialization of the
490      * POM model. Otherwise standard Maven property replacement kicks in properly directly before this goal is being executed (instead of when the POM model is being resolved)</i>.
491      * <br>
492      * See the Frequently Asked Questions page with more details:<br>
493      * <a href="http://maven.apache.org/surefire/maven-surefire-plugin/faq.html">
494      *     http://maven.apache.org/surefire/maven-surefire-plugin/faq.html</a>
495      * <br>
496      * <a href="http://maven.apache.org/surefire/maven-failsafe-plugin/faq.html">
497      *     http://maven.apache.org/surefire/maven-failsafe-plugin/faq.html</a>
498      *
499      * @see #forkCount
500      * @since 2.1
501      */
502     @Parameter(property = "argLine")
503     private String argLine;
504 
505     /**
506      * Additional environment variables to set on the command line.
507      *
508      * @since 2.1.3
509      */
510     @Parameter
511     private Map<String, String> environmentVariables = new HashMap<>();
512 
513     /**
514      * Command line working directory.
515      *
516      * @since 2.1.3
517      */
518     @Parameter(property = "basedir")
519     private File workingDirectory;
520 
521     /**
522      * When false it makes tests run using the standard classloader delegation instead of the default Maven isolated
523      * classloader. Only used when forking ({@code forkCount} is greater than zero).<br>
524      * Setting it to false helps with some problems caused by conflicts between xml parsers in the classpath and the
525      * Java 5 provider parser.
526      *
527      * @since 2.1
528      */
529     @Parameter(property = "childDelegation", defaultValue = "false")
530     private boolean childDelegation;
531 
532     /**
533      * (TestNG/JUnit47 provider with JUnit4.8+ only and JUnit5+ provider since 2.22.0) Groups/categories/tags for this
534      * test. Only classes/methods/etc decorated with one of the groups/categories/tags specified here will be included
535      * in test run, if specified.<br>
536      * For JUnit4 tests, this parameter forces the use of the 4.7 provider. For JUnit5 tests, this parameter forces
537      * the use of the JUnit platform provider.<br>
538      * Since version 2.18.1 and JUnit 4.12, the {@code @Category} annotation type is automatically inherited from
539      * superclasses, see {@code @java.lang.annotation.Inherited}. Make sure that test class inheritance still makes
540      * sense together with {@code @Category} annotation of the JUnit 4.12 or higher appeared in superclass.
541      *
542      * @since 2.2
543      */
544     @Parameter(property = "groups")
545     private String groups;
546 
547     /**
548      * (TestNG/JUnit47 provider with JUnit4.8+ only and JUnit5+ provider since 2.22.0) Excluded groups/categories/tags.
549      * Any methods/classes/etc with one of the groups/categories/tags specified in this list will specifically not be
550      * run.<br>
551      * For JUnit4, this parameter forces the use of the 4.7 provider. For JUnit5, this parameter forces the use of the
552      * JUnit platform provider.<br>
553      * Since version 2.18.1 and JUnit 4.12, the {@code @Category} annotation type is automatically inherited from
554      * superclasses, see {@code @java.lang.annotation.Inherited}. Make sure that test class inheritance still makes
555      * sense together with {@code @Category} annotation of the JUnit 4.12 or higher appeared in superclass.
556      *
557      * @since 2.2
558      */
559     @Parameter(property = "excludedGroups")
560     private String excludedGroups;
561 
562     /**
563      * Allows you to specify the name of the JUnit artifact. If not set, {@code junit:junit} will be used.
564      *
565      * @since 2.3.1
566      */
567     @Parameter(property = "junitArtifactName", defaultValue = "junit:junit")
568     private String junitArtifactName;
569 
570     /**
571      * Allows you to specify the name of the TestNG artifact. If not set, {@code org.testng:testng} will be used.
572      *
573      * @since 2.3.1
574      */
575     @Parameter(property = "testNGArtifactName", defaultValue = "org.testng:testng")
576     private String testNGArtifactName;
577 
578     /**
579      * (TestNG/JUnit 4.7 provider) The attribute thread-count allows you to specify how many threads should be
580      * allocated for this execution. Only makes sense to use in conjunction with the {@code parallel} parameter.
581      *
582      * @since 2.2
583      */
584     @Parameter(property = "threadCount")
585     private int threadCount;
586 
587     /**
588      * Option to specify the number of VMs to fork in parallel in order to execute the tests. When terminated with "C",
589      * the number part is multiplied with the number of CPU cores. Floating point value are only accepted together with
590      * "C". If set to "0", no VM is forked and all tests are executed within the main process.<br>
591      * <br>
592      * Example values: "1.5C", "4"<br>
593      * <br>
594      * The system properties and the {@code argLine} of the forked processes may contain the place holder string
595      * <code>${surefire.forkNumber}</code>, which is replaced with a fixed number for each of the parallel forks,
596      * ranging from <b>1</b> to the effective value of {@code forkCount} times the maximum number of parallel
597      * Surefire executions in maven parallel builds, i.e. the effective value of the <b>-T</b> command line
598      * argument of maven core.
599      *
600      * @since 2.14
601      */
602     @Parameter(property = "forkCount", defaultValue = "1")
603     String forkCount;
604 
605     /**
606      * Indicates if forked VMs can be reused. If set to "false", a new VM is forked for each test class to be executed.
607      * If set to "true", up to {@code forkCount} VMs will be forked and then reused to execute all tests.
608      *
609      * @since 2.13
610      */
611     @Parameter(property = "reuseForks", defaultValue = "true")
612     private boolean reuseForks;
613 
614     /**
615      * (JUnit 4.7 provider) Indicates that threadCount, threadCountSuites, threadCountClasses, threadCountMethods
616      * are per cpu core.
617      *
618      * @since 2.5
619      */
620     @Parameter(property = "perCoreThreadCount", defaultValue = "true")
621     private boolean perCoreThreadCount;
622 
623     /**
624      * (JUnit 4.7 provider) Indicates that the thread pool will be unlimited. The {@code parallel} parameter and
625      * the actual number of classes/methods will decide. Setting this to "true" effectively disables
626      * {@code perCoreThreadCount} and {@code threadCount}. Defaults to "false".
627      *
628      * @since 2.5
629      */
630     @Parameter(property = "useUnlimitedThreads", defaultValue = "false")
631     private boolean useUnlimitedThreads;
632 
633     /**
634      * (TestNG provider) When you use the parameter {@code parallel}, TestNG will try to run all your test methods
635      * in separate threads, except for methods that depend on each other, which will be run in the same thread in order
636      * to respect their order of execution.  Supports two values: {@code classes} or {@code methods}.
637      * <br>
638      * (JUnit 4.7 provider) Supports values {@code classes}, {@code methods}, {@code both} to run
639      * in separate threads been controlled by {@code threadCount}.
640      * <br>
641      * <br>
642      * Since version 2.16 (JUnit 4.7 provider), the value {@code both} is <strong>DEPRECATED</strong>.
643      * Use {@code classesAndMethods} instead.
644      * <br>
645      * <br>
646      * By default, Surefire does not execute tests in parallel. You can set the parameter {@code parallel} to
647      * {@code none} to explicitly disable parallel execution (e.g. when disabling parallel execution in special Maven
648      * profiles when executing coverage analysis).
649      *
650      * @since 2.2
651      */
652     @Parameter(property = "parallel")
653     private String parallel;
654 
655     /**
656      * (JUnit 4.7 / provider only) The thread counts do not exceed the number of parallel suite, class runners and
657      * average number of methods per class if set to <strong>true</strong>.
658      * <br>
659      * True by default.
660      *
661      * @since 2.17
662      */
663     @Parameter(property = "parallelOptimized", defaultValue = "true")
664     private boolean parallelOptimized;
665 
666     /**
667      * (JUnit 4.7 provider) This attribute allows you to specify the concurrency in test suites, i.e.:
668      * <ul>
669      *  <li>number of concurrent suites if {@code threadCount} is 0 or unspecified</li>
670      *  <li>limited suites concurrency if {@code useUnlimitedThreads} is set to <strong>true</strong></li>
671      *  <li>if {@code threadCount} and certain thread-count parameters are &gt; 0 for {@code parallel}, the
672      *  concurrency is computed from ratio. For instance {@code parallel=all} and the ratio between
673      *      {@code threadCountSuites}:{@code threadCountClasses}:{@code threadCountMethods} is
674      *      <b>2</b>:3:5, there is 20% of {@code threadCount} which appeared in concurrent suites.</li>
675      * </ul>
676      *
677      * Only makes sense to use in conjunction with the {@code parallel} parameter.
678      * The default value <b>0</b> behaves same as unspecified one.
679      *
680      * @since 2.16
681      */
682     @Parameter(property = "threadCountSuites", defaultValue = "0")
683     private int threadCountSuites;
684 
685     /**
686      * (JUnit 4.7 provider) This attribute allows you to specify the concurrency in test classes, i.e.:
687      * <ul>
688      *  <li>number of concurrent classes if {@code threadCount} is 0 or unspecified</li>
689      *  <li>limited classes concurrency if {@code useUnlimitedThreads} is set to <strong>true</strong></li>
690      *  <li>if {@code threadCount} and certain thread-count parameters are &gt; 0 for {@code parallel}, the
691      *  concurrency is computed from ratio. For instance {@code parallel=all} and the ratio between
692      *      {@code threadCountSuites}:{@code threadCountClasses}:{@code threadCountMethods} is
693      *      2:<b>3</b>:5, there is 30% of {@code threadCount} in concurrent classes.</li>
694      *  <li>as in the previous case but without this leaf thread-count. Example: {@code parallel=suitesAndClasses},
695      *  {@code threadCount=16}, {@code threadCountSuites=5}, {@code threadCountClasses} is unspecified leaf, the number
696      *  of concurrent classes is varying from &gt;= 11 to 14 or 15. The {@code threadCountSuites} become
697      *  given number of threads.</li>
698      * </ul>
699      *
700      * Only makes sense to use in conjunction with the {@code parallel} parameter.
701      * The default value <b>0</b> behaves same as unspecified one.
702      *
703      * @since 2.16
704      */
705     @Parameter(property = "threadCountClasses", defaultValue = "0")
706     private int threadCountClasses;
707 
708     /**
709      * (JUnit 4.7 provider) This attribute allows you to specify the concurrency in test methods, i.e.:
710      * <ul>
711      * <li>number of concurrent methods if {@code threadCount} is 0 or unspecified</li>
712      * <li>limited concurrency of methods if {@code useUnlimitedThreads} is set to <strong>true</strong></li>
713      * <li>if {@code threadCount} and certain thread-count parameters are &gt; 0 for {@code parallel}, the
714      * concurrency is computed from ratio. For instance parallel=all and the ratio between
715      * {@code threadCountSuites}:{@code threadCountClasses}:{@code threadCountMethods} is 2:3:<b>5</b>,
716      * there is 50% of {@code threadCount} which appears in concurrent methods.</li>
717      * <li>as in the previous case but without this leaf thread-count. Example: {@code parallel=all},
718      * {@code threadCount=16}, {@code threadCountSuites=2}, {@code threadCountClasses=3}, but {@code threadCountMethods}
719      * is unspecified leaf, the number of concurrent methods is varying from &gt;= 11 to 14 or 15.
720      * The {@code threadCountSuites} and {@code threadCountClasses} become given number of threads.</li>
721      * </ul>
722      * Only makes sense to use in conjunction with the {@code parallel} parameter. The default value <b>0</b>
723      * behaves same as unspecified one.
724      *
725      * @since 2.16
726      */
727     @Parameter(property = "threadCountMethods", defaultValue = "0")
728     private int threadCountMethods;
729 
730     /**
731      * Whether to trim the stack trace in the reports to just the lines within the test, or show the full trace.
732      *
733      * @since 2.2
734      */
735     @Parameter(property = "trimStackTrace", defaultValue = "false")
736     private boolean trimStackTrace;
737 
738     /**
739      * Flag to disable the generation of report files in xml format.
740      * Deprecated since 3.0.0-M4.
741      * @deprecated Instead use <em>disable</em> within {@code statelessTestsetReporter} since of 3.0.0-M6.
742      * @since 2.2
743      */
744     @Deprecated // todo make readonly to handle system property
745     @Parameter(property = "disableXmlReport")
746     private Boolean disableXmlReport;
747 
748     /**
749      * By default, Surefire enables JVM assertions for the execution of your test cases. To disable the assertions, set
750      * this flag to "false".
751      *
752      * @since 2.3.1
753      */
754     @Parameter(property = "enableAssertions", defaultValue = "true")
755     private boolean enableAssertions;
756 
757     /**
758      * Flag for including/excluding {@code <system-out />} and {@code <system-err />} elements for
759      * successfully passed tests in XML reports.
760      * Note that the default value may change to {@code false} is a future version.
761      *
762      * @since 3.3.1
763      */
764     @Parameter(property = "enableOutErrElements", defaultValue = "true")
765     private boolean enableOutErrElements;
766 
767     /**
768      * Flag for including/excluding {@code <properties />} element for successfully passed tests in XML reports.
769      *
770      * @since 3.3.1
771      */
772     @Parameter(property = "enablePropertiesElement", defaultValue = "true")
773     private boolean enablePropertiesElement;
774 
775     /**
776      * The current build session instance.
777      */
778     @Parameter(defaultValue = "${session}", required = true, readonly = true)
779     private MavenSession session;
780 
781     private Logger logger = LoggerFactory.getLogger(getClass());
782 
783     private PluginConsoleLogger consoleLogger = new PluginConsoleLogger(logger);
784 
785     /**
786      * (TestNG only) Define the factory class used to create all test instances.
787      *
788      * @since 2.5
789      */
790     @Parameter(property = "objectFactory")
791     private String objectFactory;
792 
793     /**
794      * Parallel Maven Execution.
795      */
796     @Parameter(defaultValue = "${session.parallel}", readonly = true)
797     private Boolean parallelMavenExecution;
798 
799     /**
800      * Read-only parameter with value of Maven property <i>project.build.directory</i>.
801      * @since 2.20
802      */
803     @Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true)
804     private File projectBuildDirectory;
805 
806     /**
807      * List of dependencies to scan for test classes to include in the test run.
808      * The child elements of this element must be &lt;dependency&gt; elements, and the
809      * contents of each of these elements must be a string which follows the general form:
810      *
811      * <p>{@code groupId[:artifactId[:type[:classifier][:version]]]}</p>
812      *
813      * <p>The wildcard character <code>*</code> can be used within the sub parts of those composite identifiers to
814      * do glob-like pattern matching. The classifier may be omitted when matching dependencies without a classifier.</p>
815      *
816      * <p>Examples:</p>
817      *
818      * <ul>
819      *     <li>{@code group} or, equivalently, {@code group:*}</li>
820      *     <li>{@code g*p:*rtifac*}</li>
821      *     <li>{@code group:*:jar}</li>
822      *     <li>{@code group:artifact:*:1.0.0} (no classifier)</li>
823      *     <li>{@code group:*:test-jar:tests}</li>
824      *     <li>{@code *:artifact:*:*:1.0.0}</li>
825      * </ul>
826      *
827      * <p>Since version 2.22.0 you can scan for test classes from a project
828      * dependency of your multi-module project.</p>
829      *
830      * <p>In versions before 3.0.0-M4, only <code>groupId:artifactId</code> is supported.</p>
831      *
832      * @since 2.15
833      */
834     @Parameter(property = "dependenciesToScan")
835     private String[] dependenciesToScan;
836 
837     /**
838      * <p>
839      *     Allow for configuration of the test jvm via maven toolchains.
840      *     This permits a configuration where the project is built with one jvm and tested with another.
841      *     This is similar to {@link #jvm}, but avoids hardcoding paths.
842      *     The two parameters are mutually exclusive (jvm wins)
843      * </p>
844      *
845      * <p>Examples:</p>
846      * (see <a href="https://maven.apache.org/guides/mini/guide-using-toolchains.html">
847      *     Guide to Toolchains</a> for more info)
848      *
849      * <pre>
850      * {@code
851      *    <configuration>
852      *        ...
853      *        <jdkToolchain>
854      *            <version>1.11</version>
855      *        </jdkToolchain>
856      *    </configuration>
857      *
858      *    <configuration>
859      *        ...
860      *        <jdkToolchain>
861      *            <version>1.8</version>
862      *            <vendor>zulu</vendor>
863      *        </jdkToolchain>
864      *    </configuration>
865      *    }
866      * </pre>
867      *
868      * @since 3.0.0-M5 and Maven 3.3.x
869      */
870     @Parameter
871     private Map<String, String> jdkToolchain;
872 
873     /**
874      *
875      */
876     @Component
877     private ToolchainManager toolchainManager;
878 
879     @Component
880     private LocationManager locationManager;
881 
882     @Component
883     private ProviderDetector providerDetector;
884 
885     private Toolchain toolchain;
886 
887     private int effectiveForkCount = -1;
888 
889     protected abstract String getPluginName();
890 
891     protected abstract int getRerunFailingTestsCount();
892 
893     @Override
894     public abstract List<String> getIncludes();
895 
896     public abstract File getIncludesFile();
897 
898     @Override
899     public abstract void setIncludes(List<String> includes);
900 
901     public abstract File getExcludesFile();
902 
903     protected abstract String[] getExcludedEnvironmentVariables();
904 
905     public abstract String getRunOrder();
906 
907     public abstract void setRunOrder(String runOrder);
908 
909     public abstract Long getRunOrderRandomSeed();
910 
911     public abstract void setRunOrderRandomSeed(Long runOrderRandomSeed);
912 
913     public abstract String getRunOrderStatisticsFileChecksum();
914 
915     public abstract void setRunOrderStatisticsFileChecksum(String runOrderStatisticsFileChecksum);
916 
917     protected abstract void handleSummary(RunResult summary, Exception firstForkException)
918             throws MojoExecutionException, MojoFailureException;
919 
920     protected abstract boolean isSkipExecution();
921 
922     protected abstract String[] getDefaultIncludes();
923 
924     protected abstract String getReportSchemaLocation();
925 
926     protected abstract boolean useModulePath();
927 
928     protected abstract void setUseModulePath(boolean useModulePath);
929 
930     protected abstract String getEnableProcessChecker();
931 
932     protected abstract ForkNodeFactory getForkNode();
933 
934     /**
935      * This plugin MOJO artifact.
936      *
937      * @return non-null plugin artifact
938      */
939     protected Artifact getMojoArtifact() {
940         return getPluginDescriptor().getPluginArtifact();
941     }
942 
943     private String getDefaultExcludes() {
944         return "**/*$*";
945     }
946 
947     @Component(role = SurefireDependencyResolver.class)
948     private SurefireDependencyResolver surefireDependencyResolver;
949 
950     private TestListResolver specificTests;
951 
952     private TestListResolver includedExcludedTests;
953 
954     private List<CommandLineOption> cli;
955 
956     @Override
957     public void execute() throws MojoExecutionException, MojoFailureException {
958         if (isSkipExecution()) {
959             getConsoleLogger().info("Tests are skipped.");
960             return;
961         }
962 
963         cli = commandLineOptions();
964         // Stuff that should have been final
965         setupStuff();
966         Platform platform = PLATFORM.withJdkExecAttributesForTests(getEffectiveJvm());
967         Thread shutdownThread = new Thread(platform::setShutdownState, "Surefire Shutdown Hook");
968         addShutDownHook(shutdownThread);
969         try {
970             if (verifyParameters() && !hasExecutedBefore()) {
971                 DefaultScanResult scan = scanForTestClasses();
972                 if (scan.isEmpty()) {
973                     switch (getEffectiveFailIfNoTests()) {
974                         case COULD_NOT_RUN_DEFAULT_TESTS:
975                             throw new MojoFailureException(
976                                     "No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.)");
977                         case COULD_NOT_RUN_SPECIFIED_TESTS:
978                             throw new MojoFailureException("No tests matching pattern \""
979                                     + getSpecificTests().toString()
980                                     + "\" were executed! (Set "
981                                     + "-D" + getPluginName()
982                                     + ".failIfNoSpecifiedTests=false to ignore this error.)");
983                         default:
984                             handleSummary(noTestsRun(), null);
985                             return;
986                     }
987                 } else {
988                     if (getConsoleLogger().isDebugEnabled()) {
989                         getConsoleLogger().debug("Tests to run: " + scan.getClasses());
990                     }
991                 }
992                 logReportsDirectory();
993                 executeAfterPreconditionsChecked(scan, platform);
994             }
995         } finally {
996             platform.clearShutdownState();
997             removeShutdownHook(shutdownThread);
998         }
999     }
1000 
1001     void setLogger(Logger logger) {
1002         this.logger = logger;
1003         this.consoleLogger = new PluginConsoleLogger(logger);
1004     }
1005 
1006     void setSurefireDependencyResolver(SurefireDependencyResolver surefireDependencyResolver) {
1007         this.surefireDependencyResolver = surefireDependencyResolver;
1008     }
1009 
1010     SurefireDependencyResolver getSurefireDependencyResolver() {
1011         return surefireDependencyResolver;
1012     }
1013 
1014     @Nonnull
1015     protected final PluginConsoleLogger getConsoleLogger() {
1016         return consoleLogger;
1017     }
1018 
1019     private Toolchain getToolchain() throws MojoFailureException {
1020         Toolchain tc = null;
1021 
1022         if (getJdkToolchain() != null) {
1023             List<Toolchain> tcs = getToolchainManager().getToolchains(getSession(), "jdk", getJdkToolchain());
1024             if (tcs.isEmpty()) {
1025                 throw new MojoFailureException(
1026                         "Requested toolchain specification did not match any configured toolchain: "
1027                                 + getJdkToolchain());
1028             }
1029             tc = tcs.get(0);
1030         }
1031 
1032         if (tc == null) {
1033             tc = getToolchainManager().getToolchainFromBuildContext("jdk", getSession());
1034         }
1035 
1036         return tc;
1037     }
1038 
1039     private void setupStuff() throws MojoFailureException {
1040 
1041         if (getBooterArtifact() == null) {
1042             throw new RuntimeException("Unable to locate surefire-booter in the list of plugin artifacts");
1043         }
1044 
1045         if (getToolchainManager() != null) {
1046             toolchain = getToolchain();
1047         }
1048     }
1049 
1050     @Nonnull
1051     private DefaultScanResult scanForTestClasses() throws MojoFailureException {
1052         DefaultScanResult scan = scanDirectories();
1053         DefaultScanResult scanDeps = scanDependencies();
1054         return scan.append(scanDeps);
1055     }
1056 
1057     private DefaultScanResult scanDirectories() throws MojoFailureException {
1058         DirectoryScanner scanner = new DirectoryScanner(getTestClassesDirectory(), getIncludedAndExcludedTests());
1059         return scanner.scan();
1060     }
1061 
1062     List<Artifact> getProjectTestArtifacts() {
1063         return project.getTestArtifacts();
1064     }
1065 
1066     DefaultScanResult scanDependencies() throws MojoFailureException {
1067         if (getDependenciesToScan() == null) {
1068             return null;
1069         } else {
1070             try {
1071                 DefaultScanResult result = null;
1072 
1073                 List<Artifact> dependenciesToScan = filter(getProjectTestArtifacts(), asList(getDependenciesToScan()));
1074 
1075                 for (Artifact artifact : dependenciesToScan) {
1076                     String type = artifact.getType();
1077                     File out = artifact.getFile();
1078                     if (out == null
1079                             || !out.exists()
1080                             || !("jar".equals(type)
1081                                     || out.isDirectory()
1082                                     || out.getName().endsWith(".jar"))) {
1083                         continue;
1084                     }
1085 
1086                     if (out.isFile()) {
1087                         DependencyScanner scanner =
1088                                 new DependencyScanner(singletonList(out), getIncludedAndExcludedTests());
1089                         result = result == null ? scanner.scan() : result.append(scanner.scan());
1090                     } else if (out.isDirectory()) {
1091                         DirectoryScanner scanner = new DirectoryScanner(out, getIncludedAndExcludedTests());
1092                         result = result == null ? scanner.scan() : result.append(scanner.scan());
1093                     }
1094                 }
1095 
1096                 return result;
1097             } catch (Exception e) {
1098                 throw new MojoFailureException(e.getLocalizedMessage(), e);
1099             }
1100         }
1101     }
1102 
1103     boolean verifyParameters() throws MojoFailureException, MojoExecutionException {
1104         setProperties(new SurefireProperties(getProperties()));
1105 
1106         String jvmToUse = getJvm();
1107         if (toolchain != null) {
1108             getConsoleLogger().info("Toolchain in maven-" + getPluginName() + "-plugin: " + toolchain);
1109             if (jvmToUse != null) {
1110                 getConsoleLogger().warning("Toolchains are ignored, 'jvm' parameter is set to " + jvmToUse);
1111             }
1112         }
1113 
1114         if (!getTestClassesDirectory().exists()
1115                 && (getDependenciesToScan() == null || getDependenciesToScan().length == 0)) {
1116             if (getFailIfNoTests()) {
1117                 throw new MojoFailureException("No tests to run!");
1118             }
1119             getConsoleLogger().info("No tests to run.");
1120         } else {
1121             ensureEnableProcessChecker();
1122             ensureWorkingDirectoryExists();
1123             ensureParallelRunningCompatibility();
1124             warnIfUselessUseSystemClassLoaderParameter();
1125             warnIfDefunctGroupsCombinations();
1126             warnIfRerunClashes();
1127             warnIfWrongShutdownValue();
1128             warnIfIllegalTempDir();
1129             warnIfForkCountIsZero();
1130             warnIfIllegalFailOnFlakeCount();
1131             printDefaultSeedIfNecessary();
1132         }
1133         return true;
1134     }
1135 
1136     private void warnIfForkCountIsZero() {
1137         if ("0".equals(getForkCount())) {
1138             getConsoleLogger()
1139                     .warning("The parameter forkCount should likely not be 0. Forking a JVM for tests "
1140                             + "improves test accuracy. Ensure to have a <forkCount> >= 1.");
1141         }
1142     }
1143 
1144     private void executeAfterPreconditionsChecked(@Nonnull DefaultScanResult scanResult, @Nonnull Platform platform)
1145             throws MojoExecutionException, MojoFailureException {
1146 
1147         TestClassPath testClasspath = generateTestClasspath();
1148         List<ProviderInfo> providers = createProviders(testClasspath);
1149         ResolvePathResultWrapper wrapper =
1150                 findModuleDescriptor(platform.getJdkExecAttributesForTests().getJdkHome());
1151 
1152         RunResult current = noTestsRun();
1153 
1154         Exception firstForkException = null;
1155         for (ProviderInfo provider : providers) {
1156             try {
1157                 current = current.aggregate(executeProvider(provider, scanResult, testClasspath, platform, wrapper));
1158             } catch (SurefireBooterForkException | SurefireExecutionException | TestSetFailedException e) {
1159                 if (firstForkException == null) {
1160                     firstForkException = e;
1161                 }
1162             }
1163         }
1164 
1165         if (firstForkException != null) {
1166             current = failure(current, firstForkException);
1167         }
1168 
1169         handleSummary(current, firstForkException);
1170     }
1171 
1172     protected List<ProviderInfo> createProviders(TestClassPath testClasspath) throws MojoExecutionException {
1173         List<ProviderInfo> providerInfos = providerDetector.resolve(
1174                 new DynamicProviderInfo(null),
1175                 new JUnitPlatformProviderInfo(
1176                         getJUnitPlatformRunnerArtifact(),
1177                         getJUnit5Artifact(),
1178                         testClasspath,
1179                         getJunitArtifact(),
1180                         getBooterArtifact(),
1181                         surefireDependencyResolver,
1182                         session,
1183                         project,
1184                         pluginDescriptor,
1185                         pluginArtifactMap,
1186                         consoleLogger,
1187                         getTestNgArtifact()),
1188                 new JUnitPlatformProviderShadefireInfo(
1189                         getJUnitPlatformRunnerArtifact(),
1190                         getJUnit5Artifact(),
1191                         testClasspath,
1192                         getJunitArtifact(),
1193                         getBooterArtifact(),
1194                         surefireDependencyResolver,
1195                         session,
1196                         project,
1197                         pluginDescriptor,
1198                         pluginArtifactMap,
1199                         consoleLogger,
1200                         getTestNgArtifact()));
1201         if (providerInfos.isEmpty()
1202                 && getJunitArtifact() != null
1203                 && !isWithinVersionSpec(getJunitArtifact(), "[4.12,)")) {
1204             throw new MojoExecutionException(String.format(
1205                     "The used JUnit Version %s is not supported anymore. Please update to version 4.12+",
1206                     getJunitArtifact().getVersion()));
1207         }
1208         return providerInfos;
1209     }
1210 
1211     SurefireProperties setupProperties() {
1212         SurefireProperties sysPropsFromFile = null;
1213         try {
1214             sysPropsFromFile = SurefireProperties.loadProperties(getSystemPropertiesFile());
1215         } catch (IOException e) {
1216             String msg = "The file '" + getSystemPropertiesFile().getAbsolutePath() + "' can't be read.";
1217             if (getConsoleLogger().isDebugEnabled()) {
1218                 getConsoleLogger().debug(msg, e);
1219             } else {
1220                 getConsoleLogger().warning(msg);
1221             }
1222         }
1223 
1224         SurefireProperties result = calculateEffectiveProperties(
1225                 getSystemProperties(),
1226                 getSystemPropertyVariables(),
1227                 promoteUserPropertiesToSystemProperties ? getUserProperties() : new Properties(),
1228                 sysPropsFromFile);
1229 
1230         result.setProperty("basedir", getBasedir().getAbsolutePath());
1231         result.setProperty("localRepository", getLocalRepositoryPath());
1232         if (isForking()) {
1233             for (Object o : result.propertiesThatCannotBeSetASystemProperties()) {
1234                 if (getArgLine() == null || !getArgLine().contains("-D" + o + "=")) {
1235                     getConsoleLogger()
1236                             .warning(o + " cannot be set as system property, use <argLine>-D" + o
1237                                     + "=...</argLine> instead");
1238                 }
1239             }
1240             for (Object systemPropertyMatchingArgLine : systemPropertiesMatchingArgLine(result)) {
1241                 getConsoleLogger()
1242                         .warning("The system property "
1243                                 + systemPropertyMatchingArgLine
1244                                 + " is configured twice! "
1245                                 + "The property appears in <argLine/> and any of <systemPropertyVariables/>, "
1246                                 + "<systemProperties/> or user property.");
1247             }
1248         } else {
1249             result.setProperty("user.dir", getWorkingDirectory().getAbsolutePath());
1250         }
1251 
1252         if (getConsoleLogger().isDebugEnabled()) {
1253             showToLog(result, getConsoleLogger());
1254         }
1255 
1256         return result;
1257     }
1258 
1259     private SurefireProperties calculateEffectiveProperties(
1260             Properties systemProperties,
1261             Map<String, String> systemPropertyVariables,
1262             Properties userProperties,
1263             SurefireProperties sysPropsFromFile) {
1264         SurefireProperties result = new SurefireProperties();
1265         result.copyPropertiesFrom(systemProperties);
1266 
1267         Collection<String> overwrittenProperties = result.copyPropertiesFrom(sysPropsFromFile);
1268         if (!overwrittenProperties.isEmpty() && getConsoleLogger().isDebugEnabled()) {
1269             getConsoleLogger().debug(getOverwrittenPropertiesLogMessage(overwrittenProperties, "systemPropertiesFile"));
1270         }
1271         overwrittenProperties = result.copyPropertiesFrom(systemPropertyVariables);
1272         if (!overwrittenProperties.isEmpty() && getConsoleLogger().isDebugEnabled()) {
1273             getConsoleLogger()
1274                     .debug(getOverwrittenPropertiesLogMessage(overwrittenProperties, "systemPropertyVariables"));
1275         }
1276         // We used to take all of our system properties and dump them in with the
1277         // user specified properties for SUREFIRE-121, causing SUREFIRE-491.
1278         // Not gonna do THAT any more... instead, we only propagate those system properties
1279         // that have been explicitly specified by the user via -Dkey=value on the CLI
1280         if (!userProperties.isEmpty()) {
1281             overwrittenProperties = result.copyPropertiesFrom(userProperties);
1282             if (!overwrittenProperties.isEmpty()) {
1283                 getConsoleLogger()
1284                         .warning(getOverwrittenPropertiesLogMessage(
1285                                 overwrittenProperties, "user properties from Maven session"));
1286             }
1287         }
1288         return result;
1289     }
1290 
1291     private Set<Object> systemPropertiesMatchingArgLine(SurefireProperties result) {
1292         Set<Object> intersection = new HashSet<>();
1293         if (isNotBlank(getArgLine())) {
1294             for (Object systemProperty : result.getStringKeySet()) {
1295                 if (getArgLine().contains("-D" + systemProperty + "=")) {
1296                     intersection.add(systemProperty);
1297                 }
1298             }
1299 
1300             Set<Object> ignored = result.propertiesThatCannotBeSetASystemProperties();
1301             intersection.removeAll(ignored);
1302         }
1303         return intersection;
1304     }
1305 
1306     private String getOverwrittenPropertiesLogMessage(
1307             Collection<String> overwrittenProperties, String overwrittenBySource) {
1308         if (overwrittenProperties.isEmpty()) {
1309             throw new IllegalArgumentException("overwrittenProperties must not be empty");
1310         }
1311         // one or multiple?
1312         ChoiceFormat propertyChoice = new ChoiceFormat("1#property|1>properties");
1313         StringBuilder message = new StringBuilder("System ");
1314         message.append(propertyChoice.format(overwrittenProperties.size())).append(" ");
1315         message.append(overwrittenProperties.stream().collect(Collectors.joining("], [", "[", "]")));
1316         message.append(" overwritten by ").append(overwrittenBySource);
1317         return message.toString();
1318     }
1319 
1320     private void showToLog(SurefireProperties props, ConsoleLogger log) {
1321         for (Object key : props.getStringKeySet()) {
1322             String value = props.getProperty((String) key);
1323             log.debug("Setting system property [" + key + "]=[" + value + "]");
1324         }
1325     }
1326 
1327     @Nonnull
1328     private RunResult executeProvider(
1329             @Nonnull ProviderInfo provider,
1330             @Nonnull DefaultScanResult scanResult,
1331             @Nonnull TestClassPath testClasspathWrapper,
1332             @Nonnull Platform platform,
1333             @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult)
1334             throws MojoExecutionException, MojoFailureException, SurefireExecutionException,
1335                     SurefireBooterForkException, TestSetFailedException {
1336         getConsoleLogger().debug("Using the provider " + provider.getProviderName());
1337         SurefireProperties effectiveProperties = setupProperties();
1338         ClassLoaderConfiguration classLoaderConfiguration = getClassLoaderConfiguration();
1339         provider.addProviderProperties();
1340         RunOrderParameters runOrderParameters =
1341                 new RunOrderParameters(getRunOrder(), getStatisticsFile(), getRunOrderRandomSeed());
1342 
1343         if (isNotForking()) {
1344             Properties originalSystemProperties =
1345                     (Properties) System.getProperties().clone();
1346             try {
1347                 createCopyAndReplaceForkNumPlaceholder(effectiveProperties, 1).copyToSystemProperties();
1348                 getConsoleLogger().debug("Using in-process starter");
1349                 InPluginVMSurefireStarter surefireStarter = createInprocessStarter(
1350                         provider,
1351                         classLoaderConfiguration,
1352                         runOrderParameters,
1353                         scanResult,
1354                         platform,
1355                         testClasspathWrapper);
1356                 return surefireStarter.runSuitesInProcess(scanResult);
1357             } finally {
1358                 System.setProperties(originalSystemProperties);
1359             }
1360         } else {
1361             ForkConfiguration forkConfiguration = createForkConfiguration(platform, resolvedJavaModularityResult);
1362             if (getConsoleLogger().isDebugEnabled()) {
1363                 getConsoleLogger()
1364                         .debug("Using fork starter with configuration implementation "
1365                                 + forkConfiguration.getClass().getName());
1366                 showMap(getEnvironmentVariables(), "environment variable");
1367                 showArray(getExcludedEnvironmentVariables(), "excluded environment variable");
1368             }
1369 
1370             Properties originalSystemProperties =
1371                     (Properties) System.getProperties().clone();
1372             ForkStarter forkStarter = null;
1373             try {
1374                 forkStarter = createForkStarter(
1375                         provider,
1376                         forkConfiguration,
1377                         classLoaderConfiguration,
1378                         runOrderParameters,
1379                         getConsoleLogger(),
1380                         scanResult,
1381                         testClasspathWrapper,
1382                         platform,
1383                         resolvedJavaModularityResult);
1384 
1385                 return forkStarter.run(effectiveProperties, scanResult);
1386             } catch (SurefireBooterForkException e) {
1387                 forkStarter.killOrphanForks();
1388                 throw e;
1389             } finally {
1390                 System.setProperties(originalSystemProperties);
1391                 cleanupForkConfiguration(forkConfiguration);
1392             }
1393         }
1394     }
1395 
1396     public static SurefireProperties createCopyAndReplaceForkNumPlaceholder(
1397             SurefireProperties effectiveSystemProperties, int threadNumber) {
1398         SurefireProperties filteredProperties = new SurefireProperties((KeyValueSource) effectiveSystemProperties);
1399         for (Entry<Object, Object> entry : effectiveSystemProperties.entrySet()) {
1400             if (entry.getValue() instanceof String) {
1401                 String value = (String) entry.getValue();
1402                 filteredProperties.put(entry.getKey(), replaceThreadNumberPlaceholders(value, threadNumber));
1403             }
1404         }
1405         return filteredProperties;
1406     }
1407 
1408     protected void cleanupForkConfiguration(ForkConfiguration forkConfiguration) {
1409         if (!getConsoleLogger().isDebugEnabled() && forkConfiguration != null) {
1410             File tempDirectory = forkConfiguration.getTempDirectory();
1411             try {
1412                 FileUtils.deleteDirectory(tempDirectory);
1413             } catch (IOException e) {
1414                 getConsoleLogger()
1415                         .warning("Could not delete temp directory " + tempDirectory + " because " + e.getMessage());
1416             }
1417         }
1418     }
1419 
1420     protected void logReportsDirectory() {
1421         logDebugOrCliShowErrors(capitalizeFirstLetter(getPluginName()) + " report directory: " + getReportsDirectory());
1422     }
1423 
1424     private boolean existsModuleDescriptor(ResolvePathResultWrapper resolvedJavaModularityResult) {
1425         return resolvedJavaModularityResult.getResolvePathResult() != null;
1426     }
1427 
1428     private ResolvePathResultWrapper findModuleDescriptor(File jdkHome) {
1429         ResolvePathResultWrapper test = findModuleDescriptor(jdkHome, getTestClassesDirectory(), false);
1430         return test.getResolvePathResult() == null ? findModuleDescriptor(jdkHome, getMainBuildPath(), true) : test;
1431     }
1432 
1433     private ResolvePathResultWrapper findModuleDescriptor(File jdkHome, File buildPath, boolean isMainDescriptor) {
1434         boolean isJpmsModule =
1435                 buildPath.isDirectory() ? new File(buildPath, "module-info.class").exists() : isModule(buildPath);
1436 
1437         if (!isJpmsModule) {
1438             return new ResolvePathResultWrapper(null, isMainDescriptor);
1439         }
1440 
1441         try {
1442             ResolvePathRequest<?> request = ResolvePathRequest.ofFile(buildPath).setJdkHome(jdkHome);
1443             ResolvePathResult result = getLocationManager().resolvePath(request);
1444             boolean isEmpty = result.getModuleNameSource() == null;
1445             return new ResolvePathResultWrapper(isEmpty ? null : result, isMainDescriptor);
1446         } catch (Exception e) {
1447             return new ResolvePathResultWrapper(null, isMainDescriptor);
1448         }
1449     }
1450 
1451     private static boolean isModule(File jar) {
1452         try (ZipFile zip = new ZipFile(jar)) {
1453             return zip.getEntry("module-info.class") != null;
1454         } catch (IOException e) {
1455             return false;
1456         }
1457     }
1458 
1459     private boolean canExecuteProviderWithModularPath(
1460             @Nonnull Platform platform, @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult) {
1461         return useModulePath()
1462                 && platform.getJdkExecAttributesForTests().isJava9AtLeast()
1463                 && existsModuleDescriptor(resolvedJavaModularityResult);
1464     }
1465 
1466     /**
1467      * Converts old TestNG configuration parameters over to new properties based configuration
1468      * method. (if any are defined the old way)
1469      */
1470     private void convertTestNGParameters() throws MojoExecutionException {
1471         if (this.getParallel() != null) {
1472             getProperties().setProperty(ProviderParameterNames.PARALLEL_PROP, this.getParallel());
1473         }
1474 
1475         Optional.ofNullable(getProperties().get("surefire.testng.verbose"))
1476                 .ifPresent(s -> getProperties().setProperty("testng.verbose", (String) s));
1477 
1478         convertGroupParameters();
1479 
1480         if (this.getThreadCount() > 0) {
1481             getProperties()
1482                     .setProperty(ProviderParameterNames.THREADCOUNT_PROP, Integer.toString(this.getThreadCount()));
1483         }
1484         if (this.getObjectFactory() != null) {
1485             getProperties().setProperty("objectfactory", this.getObjectFactory());
1486         }
1487         if (this.getTestClassesDirectory() != null) {
1488             getProperties()
1489                     .setProperty(
1490                             "testng.test.classpath", getTestClassesDirectory().getAbsolutePath());
1491         }
1492 
1493         Artifact testNgArtifact = getTestNgArtifact();
1494         if (testNgArtifact != null) {
1495             getProperties().setProperty("testng.version", testNgArtifact.getVersion());
1496         }
1497     }
1498 
1499     private void convertGroupParameters() {
1500         if (this.getExcludedGroups() != null) {
1501             getProperties().setProperty(ProviderParameterNames.EXCLUDEDGROUPS_PROP, this.getExcludedGroups());
1502         }
1503         if (this.getGroups() != null) {
1504             getProperties().setProperty(ProviderParameterNames.GROUPS_PROP, this.getGroups());
1505         }
1506     }
1507 
1508     private void convertJunitEngineParameters() {
1509         if (getIncludeJUnit5Engines() != null && getIncludeJUnit5Engines().length != 0) {
1510             getProperties().setProperty(INCLUDE_JUNIT5_ENGINES_PROP, join(getIncludeJUnit5Engines()));
1511         }
1512 
1513         if (getExcludeJUnit5Engines() != null && getExcludeJUnit5Engines().length != 0) {
1514             getProperties().setProperty(EXCLUDE_JUNIT5_ENGINES_PROP, join(getExcludeJUnit5Engines()));
1515         }
1516     }
1517 
1518     private static String join(String[] array) {
1519         return String.join(",", array);
1520     }
1521 
1522     protected boolean isAnyConcurrencySelected() {
1523         return getParallel() != null && !getParallel().trim().isEmpty();
1524     }
1525 
1526     protected boolean isAnyGroupsSelected() {
1527         return this.getGroups() != null || this.getExcludedGroups() != null;
1528     }
1529 
1530     /**
1531      * Converts old JUnit configuration parameters over to new properties based configuration
1532      * method. (if any are defined the old way)
1533      */
1534     void convertJunitCoreParameters() throws MojoFailureException {
1535         checkThreadCountEntity(getThreadCountSuites(), "suites");
1536         checkThreadCountEntity(getThreadCountClasses(), "classes");
1537         checkThreadCountEntity(getThreadCountMethods(), "methods");
1538 
1539         String usedParallel = (getParallel() != null) ? getParallel() : "none";
1540 
1541         if (!"none".equals(usedParallel)) {
1542             checkNonForkedThreads(parallel);
1543         }
1544 
1545         getProperties()
1546                 .setProperty(
1547                         "junit.vintage.execution.parallel.enabled", Boolean.toString(!"none".equals(usedParallel)));
1548         if (this.getThreadCount() > 0) {
1549             getProperties()
1550                     .setProperty("junit.vintage.execution.parallel.pool-size", Integer.toString(getThreadCount()));
1551         } else if (getThreadCountClasses() > 0 || getThreadCountMethods() > 0) {
1552             getProperties()
1553                     .setProperty(
1554                             "junit.vintage.execution.parallel.pool-size",
1555                             Integer.toString(Math.max(getThreadCountClasses(), getThreadCountMethods())));
1556         }
1557 
1558         if ("classes".equals(parallel) || "classesAndMethods".equals(parallel) || "both".equals(parallel)) {
1559             getProperties().setProperty("junit.vintage.execution.parallel.classes", "true");
1560         }
1561         if ("methods".equals(parallel) || "classesAndMethods".equals(parallel) || "both".equals(parallel)) {
1562             getProperties().setProperty("junit.vintage.execution.parallel.methods", "true");
1563         }
1564 
1565         if ("methods".equals(parallel)
1566                 || "tests".equals(parallel)
1567                 || "classes".equals(parallel)
1568                 || "instances".equals(parallel)) {
1569             getProperties().setProperty("testng.parallel", parallel);
1570         }
1571 
1572         if (threadCount > 0) {
1573             getProperties().setProperty("testng.threadCount", Integer.toString(threadCount));
1574         }
1575 
1576         if (groups != null) {
1577             getProperties().setProperty("testng.groups", groups);
1578         }
1579 
1580         if (excludedGroups != null) {
1581             getProperties().setProperty("testng.excludedGroups", excludedGroups);
1582         }
1583 
1584         getProperties().setProperty("perCoreThreadCount", Boolean.toString(getPerCoreThreadCount()));
1585         getProperties().setProperty("useUnlimitedThreads", Boolean.toString(getUseUnlimitedThreads()));
1586         getProperties()
1587                 .setProperty(ProviderParameterNames.THREADCOUNTSUITES_PROP, Integer.toString(getThreadCountSuites()));
1588         getProperties()
1589                 .setProperty(ProviderParameterNames.THREADCOUNTCLASSES_PROP, Integer.toString(getThreadCountClasses()));
1590         getProperties()
1591                 .setProperty(ProviderParameterNames.THREADCOUNTMETHODS_PROP, Integer.toString(getThreadCountMethods()));
1592         getProperties()
1593                 .setProperty(
1594                         ProviderParameterNames.PARALLEL_TIMEOUT_PROP,
1595                         Double.toString(getParallelTestsTimeoutInSeconds()));
1596         getProperties()
1597                 .setProperty(
1598                         ProviderParameterNames.PARALLEL_TIMEOUTFORCED_PROP,
1599                         Double.toString(getParallelTestsTimeoutForcedInSeconds()));
1600         getProperties()
1601                 .setProperty(ProviderParameterNames.PARALLEL_OPTIMIZE_PROP, Boolean.toString(isParallelOptimized()));
1602         getProperties()
1603                 .setProperty(
1604                         ProviderParameterNames.ENABLE_OUT_ERR_ELEMENTS_PROP,
1605                         Boolean.toString(isEnableOutErrElements()));
1606         if (!getIncludedScanList().isEmpty()) { // && getJunitArtifact() != null) {
1607             getProperties()
1608                     .setProperty(ProviderParameterNames.INCLUDES_SCAN_LIST, String.join(",", getIncludedScanList()));
1609         }
1610 
1611         if (!getExcludedScanList().isEmpty()) { // && getJunitArtifact() != null) {
1612             getProperties()
1613                     .setProperty(ProviderParameterNames.EXCLUDES_SCAN_LIST, String.join(",", getExcludedScanList()));
1614         }
1615 
1616         String message = "parallel='" + usedParallel + '\''
1617                 + ", perCoreThreadCount=" + getPerCoreThreadCount()
1618                 + ", threadCount=" + getThreadCount()
1619                 + ", useUnlimitedThreads=" + getUseUnlimitedThreads()
1620                 + ", threadCountSuites=" + getThreadCountSuites()
1621                 + ", threadCountClasses=" + getThreadCountClasses()
1622                 + ", threadCountMethods=" + getThreadCountMethods()
1623                 + ", parallelOptimized=" + isParallelOptimized()
1624                 + ", enableOutErrElements=" + isEnableOutErrElements()
1625                 + ", enablePropertiesElement=" + isEnablePropertiesElement();
1626 
1627         logDebugOrCliShowErrors(message);
1628     }
1629 
1630     private void checkNonForkedThreads(String parallel) throws MojoFailureException {
1631         if ("suites".equals(parallel)) {
1632             if (!(getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountSuites() > 0)) {
1633                 throw new MojoFailureException(
1634                         "Use threadCount or threadCountSuites > 0 or useUnlimitedThreads=true for parallel='suites'");
1635             }
1636             setThreadCountClasses(0);
1637             setThreadCountMethods(0);
1638         } else if ("classes".equals(parallel)) {
1639             setThreadCountSuites(0);
1640             setThreadCountMethods(0);
1641         } else if ("methods".equals(parallel)) {
1642             setThreadCountSuites(0);
1643             setThreadCountClasses(0);
1644         } else if ("suitesAndClasses".equals(parallel)) {
1645             if (!(getUseUnlimitedThreads()
1646                     || onlyThreadCount()
1647                     || getThreadCountSuites() > 0
1648                             && getThreadCountClasses() > 0
1649                             && getThreadCount() == 0
1650                             && getThreadCountMethods() == 0
1651                     || getThreadCount() > 0
1652                             && getThreadCountSuites() > 0
1653                             && getThreadCountClasses() > 0
1654                             && getThreadCountMethods() == 0
1655                     || getThreadCount() > 0
1656                             && getThreadCountSuites() > 0
1657                             && getThreadCount() > getThreadCountSuites()
1658                             && getThreadCountClasses() == 0
1659                             && getThreadCountMethods() == 0)) {
1660                 throw new MojoFailureException("Use useUnlimitedThreads=true, "
1661                         + "or only threadCount > 0, "
1662                         + "or (threadCountSuites > 0 and threadCountClasses > 0), "
1663                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0) "
1664                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) "
1665                         + "for parallel='suitesAndClasses' or 'both'");
1666             }
1667             setThreadCountMethods(0);
1668         } else if ("suitesAndMethods".equals(parallel)) {
1669             if (!(getUseUnlimitedThreads()
1670                     || onlyThreadCount()
1671                     || getThreadCountSuites() > 0
1672                             && getThreadCountMethods() > 0
1673                             && getThreadCount() == 0
1674                             && getThreadCountClasses() == 0
1675                     || getThreadCount() > 0
1676                             && getThreadCountSuites() > 0
1677                             && getThreadCountMethods() > 0
1678                             && getThreadCountClasses() == 0
1679                     || getThreadCount() > 0
1680                             && getThreadCountSuites() > 0
1681                             && getThreadCount() > getThreadCountSuites()
1682                             && getThreadCountClasses() == 0
1683                             && getThreadCountMethods() == 0)) {
1684                 throw new MojoFailureException("Use useUnlimitedThreads=true, "
1685                         + "or only threadCount > 0, "
1686                         + "or (threadCountSuites > 0 and threadCountMethods > 0), "
1687                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCountMethods > 0), "
1688                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) "
1689                         + "for parallel='suitesAndMethods'");
1690             }
1691             setThreadCountClasses(0);
1692         } else if ("both".equals(parallel) || "classesAndMethods".equals(parallel)) {
1693             if (!(getUseUnlimitedThreads()
1694                     || onlyThreadCount()
1695                     || getThreadCountClasses() > 0
1696                             && getThreadCountMethods() > 0
1697                             && getThreadCount() == 0
1698                             && getThreadCountSuites() == 0
1699                     || getThreadCount() > 0
1700                             && getThreadCountClasses() > 0
1701                             && getThreadCountMethods() > 0
1702                             && getThreadCountSuites() == 0
1703                     || getThreadCount() > 0
1704                             && getThreadCountClasses() > 0
1705                             && getThreadCount() > getThreadCountClasses()
1706                             && getThreadCountSuites() == 0
1707                             && getThreadCountMethods() == 0)) {
1708                 throw new MojoFailureException("Use useUnlimitedThreads=true, "
1709                         + "or only threadCount > 0, "
1710                         + "or (threadCountClasses > 0 and threadCountMethods > 0), "
1711                         + "or (threadCount > 0 and threadCountClasses > 0 and threadCountMethods > 0), "
1712                         + "or (threadCount > 0 and threadCountClasses > 0 and threadCount > threadCountClasses) "
1713                         + "for parallel='both' or parallel='classesAndMethods'");
1714             }
1715             setThreadCountSuites(0);
1716         } else if ("all".equals(parallel)) {
1717             if (!(getUseUnlimitedThreads()
1718                     || onlyThreadCount()
1719                     || getThreadCountSuites() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() > 0
1720                     || getThreadCount() > 0
1721                             && getThreadCountSuites() > 0
1722                             && getThreadCountClasses() > 0
1723                             && getThreadCountMethods() == 0
1724                             && getThreadCount() > (getThreadCountSuites() + getThreadCountClasses()))) {
1725                 throw new MojoFailureException("Use useUnlimitedThreads=true, "
1726                         + "or only threadCount > 0, "
1727                         + "or (threadCountSuites > 0 and threadCountClasses > 0 and threadCountMethods > 0), "
1728                         + "or every thread-count is specified, "
1729                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0 "
1730                         + "and threadCount > threadCountSuites + threadCountClasses) "
1731                         + "for parallel='all'");
1732             }
1733         } else {
1734             throw new MojoFailureException("Illegal parallel='" + parallel + "'");
1735         }
1736     }
1737 
1738     private boolean onlyThreadCount() {
1739         return getThreadCount() > 0
1740                 && getThreadCountSuites() == 0
1741                 && getThreadCountClasses() == 0
1742                 && getThreadCountMethods() == 0;
1743     }
1744 
1745     private static void checkThreadCountEntity(int count, String entity) throws MojoFailureException {
1746         if (count < 0) {
1747             throw new MojoFailureException("parallel maven execution does not allow negative thread-count" + entity);
1748         }
1749     }
1750 
1751     private boolean isJunit47Compatible(Artifact artifact) {
1752         return isWithinVersionSpec(artifact, "[4.7,)");
1753     }
1754 
1755     private boolean isAnyJunit4(Artifact artifact) {
1756         return isWithinVersionSpec(artifact, "[4.0,)");
1757     }
1758 
1759     protected boolean isForking() {
1760         return 0 < getEffectiveForkCount();
1761     }
1762 
1763     private List<RunOrder> getRunOrders() {
1764         String runOrderString = getRunOrder();
1765         RunOrder[] runOrder = runOrderString == null ? RunOrder.DEFAULT : RunOrder.valueOfMulti(runOrderString);
1766         return asList(runOrder);
1767     }
1768 
1769     private boolean requiresRunHistory() {
1770         final List<RunOrder> runOrders = getRunOrders();
1771         return runOrders.contains(RunOrder.BALANCED) || runOrders.contains(RunOrder.FAILEDFIRST);
1772     }
1773 
1774     private PluginFailureReason getEffectiveFailIfNoTests() {
1775         if (isSpecificTestSpecified()) {
1776             return getFailIfNoSpecifiedTests() ? COULD_NOT_RUN_SPECIFIED_TESTS : NONE;
1777         } else {
1778             return getFailIfNoTests() ? COULD_NOT_RUN_DEFAULT_TESTS : NONE;
1779         }
1780     }
1781 
1782     private ProviderConfiguration createProviderConfiguration(RunOrderParameters runOrderParameters)
1783             throws MojoExecutionException, MojoFailureException {
1784         final ReporterConfiguration reporterConfiguration =
1785                 new ReporterConfiguration(getReportsDirectory(), isTrimStackTrace());
1786 
1787         final Artifact testNgArtifact = getTestNgArtifact();
1788         final boolean isTestNg = testNgArtifact != null;
1789         final TestArtifactInfo testNg =
1790                 isTestNg ? new TestArtifactInfo(testNgArtifact.getVersion(), testNgArtifact.getClassifier()) : null;
1791         final TestRequest testSuiteDefinition =
1792                 new TestRequest(getTestSourceDirectory(), getSpecificTests(), getRerunFailingTestsCount());
1793 
1794         // @todo remove these three params and use DirectoryScannerParameters to pass into DirectoryScanner only
1795         // @todo or remove it in next major version :: 3.0
1796         // @todo remove deprecated methods in ProviderParameters => included|excluded|specificTests not needed here
1797 
1798         List<String> actualIncludes = getIncludeList(); // Collections.emptyList(); behaves same
1799         List<String> actualExcludes = getExcludeList(); // Collections.emptyList(); behaves same
1800         // Collections.emptyList(); behaves same
1801         List<String> specificTests = Collections.emptyList();
1802 
1803         DirectoryScannerParameters directoryScannerParameters = new DirectoryScannerParameters(
1804                 getTestClassesDirectory(), actualIncludes, actualExcludes, specificTests, getRunOrder());
1805 
1806         // Configure stack trace filtering:
1807         // - null or empty list: don't set property, use defaults (java., javax., sun., jdk.)
1808         // - non-empty list: set to comma-separated values, replaces defaults
1809         // Note: Maven initializes unspecified list parameters as empty lists, not null
1810         if (stackTraceFilterPrefixes != null && !stackTraceFilterPrefixes.isEmpty()) {
1811             getProperties()
1812                     .setProperty(
1813                             ProviderParameterNames.STACK_TRACE_FILTER_PREFIXES,
1814                             String.join(",", stackTraceFilterPrefixes));
1815         }
1816 
1817         getProperties().setProperty(ProviderParameterNames.STACK_TRACE_MAX_FRAMES, String.valueOf(stackTraceMaxFrames));
1818 
1819         Map<String, String> providerProperties = toStringProperties(getProperties());
1820 
1821         return new ProviderConfiguration(
1822                 directoryScannerParameters,
1823                 runOrderParameters,
1824                 reporterConfiguration,
1825                 testNg, // Not really used in provider. Limited to de/serializer.
1826                 testSuiteDefinition,
1827                 providerProperties,
1828                 null,
1829                 false,
1830                 cli,
1831                 getSkipAfterFailureCount(),
1832                 Shutdown.parameterOf(getShutdown()),
1833                 getForkedProcessExitTimeoutInSeconds());
1834     }
1835 
1836     private static Map<String, String> toStringProperties(Properties properties) {
1837         Map<String, String> h = new ConcurrentHashMap<>(properties.size());
1838         for (Enumeration<?> e = properties.keys(); e.hasMoreElements(); ) {
1839             Object k = e.nextElement();
1840             Object v = properties.get(k);
1841             if (k.getClass() == String.class && v.getClass() == String.class) {
1842                 h.put((String) k, (String) v);
1843             }
1844         }
1845         return h;
1846     }
1847 
1848     private File getStatisticsFile() {
1849         String checksum = getRunOrderStatisticsFileChecksum();
1850         if (checksum == null || checksum.isEmpty()) {
1851             checksum = getConfigChecksum();
1852         }
1853         return new File(getBasedir(), ".surefire-" + checksum);
1854     }
1855 
1856     private StartupConfiguration createStartupConfiguration(
1857             @Nonnull ProviderInfo provider,
1858             boolean isForking,
1859             @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
1860             @Nonnull DefaultScanResult scanResult,
1861             @Nonnull TestClassPath testClasspathWrapper,
1862             @Nonnull Platform platform,
1863             @Nonnull ResolvePathResultWrapper resolvedJavaModularity)
1864             throws MojoExecutionException {
1865         try {
1866             if (isForking && canExecuteProviderWithModularPath(platform, resolvedJavaModularity)) {
1867                 File jdkHome = platform.getJdkExecAttributesForTests().getJdkHome();
1868                 return newStartupConfigWithModularPath(
1869                         classLoaderConfiguration,
1870                         provider,
1871                         resolvedJavaModularity,
1872                         scanResult,
1873                         jdkHome.getAbsolutePath(),
1874                         testClasspathWrapper);
1875             } else {
1876                 return newStartupConfigWithClasspath(classLoaderConfiguration, provider, testClasspathWrapper);
1877             }
1878         } catch (IOException e) {
1879             throw new MojoExecutionException(e.getMessage(), e);
1880         }
1881     }
1882 
1883     private StartupConfiguration newStartupConfigWithClasspath(
1884             @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
1885             @Nonnull ProviderInfo providerInfo,
1886             @Nonnull TestClassPath testClasspathWrapper)
1887             throws MojoExecutionException {
1888         Classpath testClasspath =
1889                 providerInfo.decorateTestClassPath(testClasspathWrapper).toClasspath();
1890         Set<Artifact> providerArtifacts = providerInfo.getProviderClasspath();
1891         String providerName = providerInfo.getProviderName();
1892         Classpath providerClasspath = classpathCache.getCachedClassPath(providerName);
1893         if (providerClasspath == null) {
1894             providerClasspath = classpathCache.setCachedClasspath(providerName, providerArtifacts);
1895         }
1896 
1897         getConsoleLogger().debug(testClasspath.getLogMessage("test classpath:"));
1898         getConsoleLogger().debug(providerClasspath.getLogMessage("provider classpath:"));
1899         getConsoleLogger().debug(testClasspath.getCompactLogMessage("test(compact) classpath:"));
1900         getConsoleLogger().debug(providerClasspath.getCompactLogMessage("provider(compact) classpath:"));
1901 
1902         Artifact[] additionalInProcArtifacts = {
1903             getCommonArtifact(),
1904             getBooterArtifact(),
1905             getExtensionsArtifact(),
1906             getApiArtifact(),
1907             getSpiArtifact(),
1908             getLoggerApiArtifact(),
1909             getSurefireSharedUtilsArtifact()
1910         };
1911         Set<Artifact> inProcArtifacts = retainInProcArtifactsUnique(providerArtifacts, additionalInProcArtifacts);
1912         Classpath inProcClasspath = createInProcClasspath(providerClasspath, inProcArtifacts);
1913         getConsoleLogger().debug(inProcClasspath.getLogMessage("in-process classpath:"));
1914         getConsoleLogger().debug(inProcClasspath.getCompactLogMessage("in-process(compact) classpath:"));
1915 
1916         ClasspathConfiguration classpathConfiguration = new ClasspathConfiguration(
1917                 testClasspath, providerClasspath, inProcClasspath, effectiveIsEnableAssertions(), isChildDelegation());
1918         ProviderRequirements forkRequirements = new ProviderRequirements(false, false, false);
1919         return new StartupConfiguration(
1920                 providerName,
1921                 classpathConfiguration,
1922                 classLoaderConfiguration,
1923                 ProcessCheckerType.toEnum(getEnableProcessChecker()),
1924                 providerInfo.getJpmsArguments(forkRequirements));
1925     }
1926 
1927     private static Set<Artifact> retainInProcArtifactsUnique(
1928             Set<Artifact> providerArtifacts, Artifact... inPluginArtifacts) {
1929         Set<Artifact> result = new LinkedHashSet<>();
1930         for (Artifact inPluginArtifact : inPluginArtifacts) {
1931             boolean contains = false;
1932             for (Artifact providerArtifact : providerArtifacts) {
1933                 if (hasGroupArtifactId(
1934                         providerArtifact.getGroupId(), providerArtifact.getArtifactId(), inPluginArtifact)) {
1935                     contains = true;
1936                     break;
1937                 }
1938             }
1939             if (!contains) {
1940                 result.add(inPluginArtifact);
1941             }
1942         }
1943         return result;
1944     }
1945 
1946     private static boolean hasGroupArtifactId(String groupId, String artifactId, Artifact artifact) {
1947         return groupId.equals(artifact.getGroupId()) && artifactId.equals(artifact.getArtifactId());
1948     }
1949 
1950     private static Classpath createInProcClasspath(Classpath providerClasspath, Set<Artifact> newArtifacts) {
1951         Classpath inprocClasspath = providerClasspath.clone();
1952         for (Artifact newArtifact : newArtifacts) {
1953             inprocClasspath =
1954                     inprocClasspath.addClassPathElementUrl(newArtifact.getFile().getAbsolutePath());
1955         }
1956         return inprocClasspath;
1957     }
1958 
1959     /**
1960      * For testing purposes - Mockito.
1961      * @return plexus component
1962      */
1963     private LocationManager getLocationManager() {
1964         return locationManager;
1965     }
1966 
1967     private StartupConfiguration newStartupConfigWithModularPath(
1968             @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
1969             @Nonnull ProviderInfo providerInfo,
1970             @Nonnull ResolvePathResultWrapper moduleDescriptor,
1971             @Nonnull DefaultScanResult scanResult,
1972             @Nonnull String javaHome,
1973             @Nonnull TestClassPath testClasspathWrapper)
1974             throws MojoExecutionException, IOException {
1975         boolean isMainDescriptor = moduleDescriptor.isMainModuleDescriptor();
1976         JavaModuleDescriptor javaModuleDescriptor =
1977                 moduleDescriptor.getResolvePathResult().getModuleDescriptor();
1978         SortedSet<String> packages = new TreeSet<>();
1979 
1980         Classpath testClasspath = testClasspathWrapper.toClasspath();
1981         Set<Artifact> providerArtifacts = providerInfo.getProviderClasspath();
1982         String providerName = providerInfo.getProviderName();
1983         Classpath providerClasspath = classpathCache.getCachedClassPath(providerName);
1984         if (providerClasspath == null) {
1985             providerClasspath = classpathCache.setCachedClasspath(providerName, providerArtifacts);
1986         }
1987 
1988         final ProviderRequirements providerRequirements;
1989         final Classpath testModulepath;
1990         if (isMainDescriptor) {
1991             providerRequirements = new ProviderRequirements(true, true, false);
1992             ResolvePathsRequest<String> req = ResolvePathsRequest.ofStrings(testClasspath.getClassPath())
1993                     .setIncludeAllProviders(true)
1994                     .setJdkHome(javaHome)
1995                     .setIncludeStatic(true)
1996                     .setModuleDescriptor(javaModuleDescriptor);
1997 
1998             ResolvePathsResult<String> result = getLocationManager().resolvePaths(req);
1999             for (Entry<String, Exception> entry : result.getPathExceptions().entrySet()) {
2000                 // Probably JDK version < 9. Other known causes: passing a non-jar or a corrupted jar.
2001                 getConsoleLogger().warning("Exception for '" + entry.getKey() + "'.", entry.getValue());
2002             }
2003 
2004             testClasspath = new Classpath(result.getClasspathElements());
2005             testModulepath = new Classpath(result.getModulepathElements().keySet());
2006 
2007             for (String className : scanResult.getClasses()) {
2008                 packages.add(substringBeforeLast(className, "."));
2009             }
2010         } else {
2011             providerRequirements = new ProviderRequirements(true, false, true);
2012             testModulepath = testClasspath;
2013             testClasspath = emptyClasspath();
2014         }
2015 
2016         getConsoleLogger().debug("main module descriptor name: " + javaModuleDescriptor.name());
2017 
2018         ModularClasspath modularClasspath = new ModularClasspath(
2019                 javaModuleDescriptor.name(),
2020                 testModulepath.getClassPath(),
2021                 packages,
2022                 isMainDescriptor ? getTestClassesDirectory() : null,
2023                 isMainDescriptor);
2024 
2025         Artifact[] additionalInProcArtifacts = {
2026             getCommonArtifact(),
2027             getBooterArtifact(),
2028             getExtensionsArtifact(),
2029             getApiArtifact(),
2030             getSpiArtifact(),
2031             getLoggerApiArtifact(),
2032             getSurefireSharedUtilsArtifact()
2033         };
2034         Set<Artifact> inProcArtifacts = retainInProcArtifactsUnique(providerArtifacts, additionalInProcArtifacts);
2035         Classpath inProcClasspath = createInProcClasspath(providerClasspath, inProcArtifacts);
2036 
2037         ModularClasspathConfiguration classpathConfiguration = new ModularClasspathConfiguration(
2038                 modularClasspath,
2039                 testClasspath,
2040                 providerClasspath,
2041                 inProcClasspath,
2042                 effectiveIsEnableAssertions(),
2043                 isChildDelegation());
2044 
2045         getConsoleLogger().debug(testClasspath.getLogMessage("test classpath:"));
2046         getConsoleLogger().debug(testModulepath.getLogMessage("test modulepath:"));
2047         getConsoleLogger().debug(providerClasspath.getLogMessage("provider classpath:"));
2048         getConsoleLogger().debug(testClasspath.getCompactLogMessage("test(compact) classpath:"));
2049         getConsoleLogger().debug(testModulepath.getCompactLogMessage("test(compact) modulepath:"));
2050         getConsoleLogger().debug(providerClasspath.getCompactLogMessage("provider(compact) classpath:"));
2051         getConsoleLogger().debug(inProcClasspath.getLogMessage("in-process classpath:"));
2052         getConsoleLogger().debug(inProcClasspath.getCompactLogMessage("in-process(compact) classpath:"));
2053 
2054         ProcessCheckerType processCheckerType = ProcessCheckerType.toEnum(getEnableProcessChecker());
2055         List<String[]> jpmsArgs = providerInfo.getJpmsArguments(providerRequirements);
2056         return new StartupConfiguration(
2057                 providerName, classpathConfiguration, classLoaderConfiguration, processCheckerType, jpmsArgs);
2058     }
2059 
2060     private Artifact getCommonArtifact() {
2061         return getPluginArtifactMap().get("org.apache.maven.surefire:maven-surefire-common");
2062     }
2063 
2064     private Artifact getExtensionsArtifact() {
2065         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-extensions-api");
2066     }
2067 
2068     private Artifact getSpiArtifact() {
2069         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-extensions-spi");
2070     }
2071 
2072     private Artifact getApiArtifact() {
2073         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-api");
2074     }
2075 
2076     private Artifact getSurefireSharedUtilsArtifact() {
2077         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-shared-utils");
2078     }
2079 
2080     private Artifact getLoggerApiArtifact() {
2081         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-logger-api");
2082     }
2083 
2084     Artifact getBooterArtifact() {
2085         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-booter");
2086     }
2087 
2088     private Artifact getShadefireArtifact() {
2089         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-shadefire");
2090     }
2091 
2092     private StartupReportConfiguration getStartupReportConfiguration(boolean isForking, ProviderInfo providerInfo) {
2093         SurefireStatelessReporter xmlReporter =
2094                 statelessTestsetReporter == null ? new SurefireStatelessReporter() : statelessTestsetReporter;
2095 
2096         if (disableXmlReport != null) {
2097             xmlReporter.setDisable(disableXmlReport);
2098         }
2099 
2100         SurefireConsoleOutputReporter outReporter =
2101                 consoleOutputReporter == null ? new SurefireConsoleOutputReporter() : consoleOutputReporter;
2102 
2103         SurefireStatelessTestsetInfoReporter testsetReporter = statelessTestsetInfoReporter == null
2104                 ? new SurefireStatelessTestsetInfoReporter()
2105                 : statelessTestsetInfoReporter;
2106 
2107         // SUREFIRE-1643 we can force this here as junit5 will need this for multi thread test
2108         ReporterFactoryOptions reporterFactoryOptions = new ReporterFactoryOptions();
2109         if ("org.apache.maven.surefire.junitplatform.JUnitPlatformProvider".equals(providerInfo.getProviderName())) {
2110             reporterFactoryOptions.setStatPerSourceName(true);
2111         }
2112         return new StartupReportConfiguration(
2113                 isUseFile(),
2114                 isPrintSummary(),
2115                 getReportFormat(),
2116                 isRedirectTestOutputToFile(),
2117                 getReportsDirectory(),
2118                 isTrimStackTrace(),
2119                 getReportNameSuffix(),
2120                 getStatisticsFile(),
2121                 requiresRunHistory(),
2122                 getRerunFailingTestsCount(),
2123                 getReportSchemaLocation(),
2124                 getEncoding(),
2125                 isForking,
2126                 isEnableOutErrElements(),
2127                 isEnablePropertiesElement(),
2128                 xmlReporter,
2129                 outReporter,
2130                 testsetReporter,
2131                 reporterFactoryOptions);
2132     }
2133 
2134     private boolean isSpecificTestSpecified() {
2135         return isNotBlank(getTest());
2136     }
2137 
2138     @Nonnull
2139     private List<String> readListFromFile(@Nonnull final File file) {
2140         getConsoleLogger().debug("Reading list from: " + file);
2141 
2142         if (!file.exists()) {
2143             throw new RuntimeException("Failed to load list from file: " + file);
2144         }
2145 
2146         try {
2147             List<String> list = FileUtils.loadFile(file);
2148 
2149             if (getConsoleLogger().isDebugEnabled()) {
2150                 getConsoleLogger().debug("List contents:");
2151                 for (String entry : list) {
2152                     getConsoleLogger().debug("  " + entry);
2153                 }
2154             }
2155             return list;
2156         } catch (IOException e) {
2157             throw new RuntimeException("Failed to load list from file: " + file, e);
2158         }
2159     }
2160 
2161     @Nonnull
2162     private List<String> getExcludedScanList() throws MojoFailureException {
2163         return getExcludeList(true);
2164     }
2165 
2166     @Nonnull
2167     private List<String> getExcludeList() throws MojoFailureException {
2168         return getExcludeList(false);
2169     }
2170 
2171     /**
2172      * Computes a merge list of test exclusions.
2173      * Used only in {@link #getExcludeList()} and {@link #getExcludedScanList()}.
2174      * @param asScanList true if dependency or directory scanner
2175      * @return list of patterns
2176      * @throws MojoFailureException if the excludes breaks a pattern format
2177      */
2178     @Nonnull
2179     private List<String> getExcludeList(boolean asScanList) throws MojoFailureException {
2180         List<String> excludes;
2181         if (isSpecificTestSpecified()) {
2182             excludes = Collections.emptyList();
2183         } else {
2184             excludes = new ArrayList<>();
2185             if (asScanList) {
2186                 if (getExcludes() != null) {
2187                     excludes.addAll(getExcludes());
2188                 }
2189                 checkMethodFilterInIncludesExcludes(excludes);
2190             }
2191 
2192             if (getExcludesFile() != null) {
2193                 excludes.addAll(readListFromFile(getExcludesFile()));
2194             }
2195 
2196             if (asScanList && excludes.isEmpty()) {
2197                 excludes = Collections.singletonList(getDefaultExcludes());
2198             }
2199         }
2200         return filterNulls(excludes);
2201     }
2202 
2203     @Nonnull
2204     private List<String> getIncludedScanList() throws MojoFailureException {
2205         return getIncludeList(true);
2206     }
2207 
2208     @Nonnull
2209     private List<String> getIncludeList() throws MojoFailureException {
2210         return getIncludeList(false);
2211     }
2212 
2213     /**
2214      * Computes a merge list of test inclusions.
2215      * Used only in {@link #getIncludeList()} and {@link #getIncludedScanList()}.
2216      * @param asScanList true if dependency or directory scanner
2217      * @return list of patterns
2218      * @throws MojoFailureException if the includes breaks a pattern format
2219      */
2220     @Nonnull
2221     private List<String> getIncludeList(boolean asScanList) throws MojoFailureException {
2222         final List<String> includes = new ArrayList<>();
2223         if (isSpecificTestSpecified()) {
2224             addAll(includes, split(getTest(), ","));
2225         } else {
2226             if (asScanList) {
2227                 if (getIncludes() != null) {
2228                     includes.addAll(getIncludes());
2229                 }
2230                 checkMethodFilterInIncludesExcludes(includes);
2231             }
2232 
2233             if (getIncludesFile() != null) {
2234                 includes.addAll(readListFromFile(getIncludesFile()));
2235             }
2236 
2237             if (asScanList && includes.isEmpty()) {
2238                 addAll(includes, getDefaultIncludes());
2239             }
2240         }
2241 
2242         return filterNulls(includes);
2243     }
2244 
2245     private void checkMethodFilterInIncludesExcludes(Iterable<String> patterns) throws MojoFailureException {
2246         for (String pattern : patterns) {
2247             if (pattern != null && pattern.contains("#")) {
2248                 throw new MojoFailureException("Method filter prohibited in includes|excludes parameter: " + pattern);
2249             }
2250         }
2251     }
2252 
2253     private TestListResolver getIncludedAndExcludedTests() throws MojoFailureException {
2254         if (includedExcludedTests == null) {
2255             includedExcludedTests = new TestListResolver(getIncludedScanList(), getExcludedScanList());
2256             getConsoleLogger().debug("Resolved included and excluded patterns: " + includedExcludedTests);
2257         }
2258         return includedExcludedTests;
2259     }
2260 
2261     public TestListResolver getSpecificTests() throws MojoFailureException {
2262         if (specificTests == null) {
2263             specificTests = new TestListResolver(getIncludeList(), getExcludeList());
2264         }
2265         return specificTests;
2266     }
2267 
2268     @Nonnull
2269     private List<String> filterNulls(@Nonnull List<String> toFilter) {
2270         List<String> result = new ArrayList<>(toFilter.size());
2271         for (String item : toFilter) {
2272             if (item != null) {
2273                 item = item.trim();
2274                 if (!item.isEmpty()) {
2275                     result.add(item);
2276                 }
2277             }
2278         }
2279 
2280         return result;
2281     }
2282 
2283     private Artifact getTestNgArtifact() throws MojoExecutionException {
2284         Artifact artifact = getProjectArtifactMap().get(getTestNGArtifactName());
2285         Artifact projectArtifact = project.getArtifact();
2286         String projectArtifactName = projectArtifact.getGroupId() + ":" + projectArtifact.getArtifactId();
2287 
2288         if (artifact != null) {
2289             try {
2290                 VersionRange range = VersionRange.createFromVersionSpec("[6.14.3,)");
2291                 if (!range.containsVersion(new DefaultArtifactVersion(artifact.getVersion()))) {
2292                     throw new MojoExecutionException(
2293                             "TestNG support requires version 6.14.3 or above. You have declared version "
2294                                     + artifact.getVersion());
2295                 }
2296             } catch (InvalidVersionSpecificationException e) {
2297                 throw new MojoExecutionException("Unable to parse version of TestNG: " + artifact.getVersion(), e);
2298             }
2299         } else if (projectArtifactName.equals(getTestNGArtifactName())) {
2300             artifact = projectArtifact;
2301         }
2302 
2303         return artifact;
2304     }
2305 
2306     private Artifact getJunitArtifact() {
2307         Artifact artifact = getProjectArtifactMap().get(getJunitArtifactName());
2308         Artifact projectArtifact = project.getArtifact();
2309         String projectArtifactName = projectArtifact.getGroupId() + ":" + projectArtifact.getArtifactId();
2310 
2311         if (artifact == null && projectArtifactName.equals(getJunitArtifactName())) {
2312             artifact = projectArtifact;
2313         }
2314 
2315         return artifact;
2316     }
2317 
2318     private Artifact getJUnitPlatformRunnerArtifact() {
2319         return getProjectArtifactMap().get("org.junit.platform:junit-platform-runner");
2320     }
2321 
2322     private Artifact getJUnit5Artifact() {
2323         Artifact artifact = getPluginArtifactMap().get("org.junit.platform:junit-platform-engine");
2324         if (artifact == null) {
2325             return getProjectArtifactMap().get("org.junit.platform:junit-platform-commons");
2326         }
2327 
2328         return artifact;
2329     }
2330 
2331     private ForkStarter createForkStarter(
2332             @Nonnull ProviderInfo provider,
2333             @Nonnull ForkConfiguration forkConfiguration,
2334             @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
2335             @Nonnull RunOrderParameters runOrderParameters,
2336             @Nonnull ConsoleLogger log,
2337             @Nonnull DefaultScanResult scanResult,
2338             @Nonnull TestClassPath testClasspathWrapper,
2339             @Nonnull Platform platform,
2340             @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult)
2341             throws MojoExecutionException, MojoFailureException {
2342         StartupConfiguration startupConfiguration = createStartupConfiguration(
2343                 provider,
2344                 true,
2345                 classLoaderConfiguration,
2346                 scanResult,
2347                 testClasspathWrapper,
2348                 platform,
2349                 resolvedJavaModularityResult);
2350         StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration(true, provider);
2351 
2352         ProviderConfiguration providerConfiguration = createProviderConfiguration(runOrderParameters);
2353         return new ForkStarter(
2354                 providerConfiguration,
2355                 startupConfiguration,
2356                 forkConfiguration,
2357                 getForkedProcessTimeoutInSeconds(),
2358                 startupReportConfiguration,
2359                 log);
2360     }
2361 
2362     private InPluginVMSurefireStarter createInprocessStarter(
2363             @Nonnull ProviderInfo provider,
2364             @Nonnull ClassLoaderConfiguration classLoaderConfig,
2365             @Nonnull RunOrderParameters runOrderParameters,
2366             @Nonnull DefaultScanResult scanResult,
2367             @Nonnull Platform platform,
2368             @Nonnull TestClassPath testClasspathWrapper)
2369             throws MojoExecutionException, MojoFailureException {
2370         StartupConfiguration startupConfiguration = createStartupConfiguration(
2371                 provider,
2372                 false,
2373                 classLoaderConfig,
2374                 scanResult,
2375                 testClasspathWrapper,
2376                 platform,
2377                 new ResolvePathResultWrapper(null, true));
2378         StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration(false, provider);
2379         ProviderConfiguration providerConfiguration = createProviderConfiguration(runOrderParameters);
2380         return new InPluginVMSurefireStarter(
2381                 startupConfiguration, providerConfiguration, startupReportConfiguration, getConsoleLogger(), platform);
2382     }
2383 
2384     // todo this is in separate method and can be better tested than whole method createForkConfiguration()
2385     @Nonnull
2386     private ForkNodeFactory getForkNodeFactory() {
2387         ForkNodeFactory forkNode = getForkNode();
2388         return forkNode == null ? new LegacyForkNodeFactory() : forkNode;
2389     }
2390 
2391     @Nonnull
2392     private ForkConfiguration createForkConfiguration(
2393             @Nonnull Platform platform, @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult)
2394             throws MojoExecutionException {
2395         File tmpDir = getSurefireTempDir();
2396 
2397         Artifact shadeFire = getShadefireArtifact();
2398 
2399         Classpath bootClasspath = getArtifactClasspath(shadeFire != null ? shadeFire : getBooterArtifact());
2400 
2401         ForkNodeFactory forkNode = getForkNodeFactory();
2402 
2403         getConsoleLogger()
2404                 .debug("Found implementation of fork node factory: "
2405                         + forkNode.getClass().getName());
2406 
2407         if (canExecuteProviderWithModularPath(platform, resolvedJavaModularityResult)) {
2408             return new ModularClasspathForkConfiguration(
2409                     bootClasspath,
2410                     tmpDir,
2411                     getEffectiveDebugForkedProcess(),
2412                     getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(),
2413                     getProject().getModel().getProperties(),
2414                     getArgLine(),
2415                     getEnvironmentVariables(),
2416                     getExcludedEnvironmentVariables(),
2417                     getConsoleLogger().isDebugEnabled(),
2418                     getEffectiveForkCount(),
2419                     reuseForks,
2420                     platform,
2421                     getConsoleLogger(),
2422                     forkNode);
2423         } else if (getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable()) {
2424             return new JarManifestForkConfiguration(
2425                     bootClasspath,
2426                     tmpDir,
2427                     getEffectiveDebugForkedProcess(),
2428                     getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(),
2429                     getProject().getModel().getProperties(),
2430                     getArgLine(),
2431                     getEnvironmentVariables(),
2432                     getExcludedEnvironmentVariables(),
2433                     getConsoleLogger().isDebugEnabled(),
2434                     getEffectiveForkCount(),
2435                     reuseForks,
2436                     platform,
2437                     getConsoleLogger(),
2438                     forkNode);
2439         } else {
2440             return new ClasspathForkConfiguration(
2441                     bootClasspath,
2442                     tmpDir,
2443                     getEffectiveDebugForkedProcess(),
2444                     getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(),
2445                     getProject().getModel().getProperties(),
2446                     getArgLine(),
2447                     getEnvironmentVariables(),
2448                     getExcludedEnvironmentVariables(),
2449                     getConsoleLogger().isDebugEnabled(),
2450                     getEffectiveForkCount(),
2451                     reuseForks,
2452                     platform,
2453                     getConsoleLogger(),
2454                     forkNode);
2455         }
2456     }
2457 
2458     private void ensureEnableProcessChecker() throws MojoFailureException {
2459         if (!ProcessCheckerType.isValid(getEnableProcessChecker())) {
2460             throw new MojoFailureException("Unexpected value '"
2461                     + getEnableProcessChecker()
2462                     + "' in the configuration parameter 'enableProcessChecker'.");
2463         }
2464     }
2465 
2466     @SuppressWarnings("checkstyle:emptyblock")
2467     protected int getEffectiveForkCount() {
2468         if (effectiveForkCount < 0) {
2469             try {
2470                 effectiveForkCount = convertWithCoreCount(forkCount);
2471             } catch (NumberFormatException ignored) {
2472             }
2473 
2474             if (effectiveForkCount < 0) {
2475                 throw new IllegalArgumentException("Fork count " + forkCount.trim() + " is not a legal value.");
2476             }
2477         }
2478 
2479         return effectiveForkCount;
2480     }
2481 
2482     protected int convertWithCoreCount(String count) {
2483         String trimmed = count.trim();
2484         if (trimmed.endsWith("C")) {
2485             double multiplier = Double.parseDouble(trimmed.substring(0, trimmed.length() - 1));
2486             double calculated = multiplier * ((double) Runtime.getRuntime().availableProcessors());
2487             return calculated > 0d ? Math.max((int) calculated, 1) : 0;
2488         } else {
2489             return parseInt(trimmed);
2490         }
2491     }
2492 
2493     private String getEffectiveDebugForkedProcess() {
2494         String debugForkedProcess = getDebugForkedProcess();
2495         if ("true".equals(debugForkedProcess)) {
2496             return "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:5005";
2497         }
2498         return debugForkedProcess;
2499     }
2500 
2501     private JdkAttributes getEffectiveJvm() throws MojoFailureException {
2502         if (isNotEmpty(getJvm())) {
2503             File pathToJava = new File(getJvm()).getAbsoluteFile();
2504             if (!endsWithJavaPath(pathToJava.getPath())) {
2505                 throw new MojoFailureException(
2506                         "Given path does not end with java executor \"" + pathToJava.getPath() + "\".");
2507             }
2508 
2509             if (!(pathToJava.isFile()
2510                     || "java".equals(pathToJava.getName())
2511                             && pathToJava.getParentFile().isDirectory())) {
2512                 throw new MojoFailureException(
2513                         "Given path to java executor does not exist \"" + pathToJava.getPath() + "\".");
2514             }
2515 
2516             File jdkHome = toJdkHomeFromJvmExec(pathToJava.getPath());
2517             if (jdkHome == null) {
2518                 getConsoleLogger().warning("Cannot determine JAVA_HOME of jvm exec path " + pathToJava);
2519             } else if (!getEnvironmentVariables().containsKey("JAVA_HOME")) {
2520                 getEnvironmentVariables().put("JAVA_HOME", jdkHome.getAbsolutePath());
2521             }
2522             BigDecimal version = jdkHome == null ? null : toJdkVersionFromReleaseFile(jdkHome);
2523             boolean javaVersion9 = version == null ? isJava9AtLeast(pathToJava.getPath()) : isJava9AtLeast(version);
2524             return new JdkAttributes(pathToJava, jdkHome, javaVersion9);
2525         }
2526 
2527         if (toolchain != null) {
2528             String jvmToUse = toolchain.findTool("java");
2529             if (isNotEmpty(jvmToUse)) {
2530                 boolean javaVersion9 = false;
2531                 String jdkHome = null;
2532 
2533                 if (toolchain instanceof DefaultToolchain) {
2534                     DefaultToolchain defaultToolchain = (DefaultToolchain) toolchain;
2535                     javaVersion9 = defaultToolchain.matchesRequirements(JAVA_9_MATCHER)
2536                             || defaultToolchain.matchesRequirements(JAVA_9_MATCHER_OLD_NOTATION);
2537                 }
2538 
2539                 if (toolchain instanceof DefaultJavaToolChain) {
2540                     DefaultJavaToolChain defaultJavaToolChain = (DefaultJavaToolChain) toolchain;
2541                     if (!getEnvironmentVariables().containsKey("JAVA_HOME")) {
2542                         jdkHome = defaultJavaToolChain.getJavaHome();
2543                         getEnvironmentVariables().put("JAVA_HOME", jdkHome);
2544                     }
2545                 }
2546 
2547                 if (!javaVersion9) {
2548                     javaVersion9 = isJava9AtLeast(jvmToUse);
2549                 }
2550 
2551                 return new JdkAttributes(
2552                         new File(jvmToUse),
2553                         jdkHome == null ? toJdkHomeFromJvmExec(jvmToUse) : new File(jdkHome),
2554                         javaVersion9);
2555             }
2556         }
2557 
2558         // use the same JVM as the one used to run Maven (the "java.home" one)
2559         String jvmToUse = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
2560         getConsoleLogger().debug("Using JVM: " + jvmToUse + " with Java version " + JAVA_RECENT);
2561 
2562         return new JdkAttributes(jvmToUse, isBuiltInJava9AtLeast());
2563     }
2564 
2565     /**
2566      * Where surefire stores its own temp files
2567      *
2568      * @return A file pointing to the location of surefire's own temp files
2569      */
2570     File getSurefireTempDir() {
2571         File result = IS_OS_WINDOWS ? createSurefireBootDirectoryInTemp() : createSurefireBootDirectoryInBuild();
2572         try {
2573             File canonical = result.getCanonicalFile();
2574             if (!result.equals(canonical)) {
2575                 getConsoleLogger().debug("Canonicalized tempDir path '" + result + "' to '" + canonical + "'");
2576             }
2577             return canonical;
2578         } catch (IOException e) {
2579             getConsoleLogger().error("Could not canonicalize tempDir path '" + result + "'", e);
2580         }
2581         return result;
2582     }
2583 
2584     /**
2585      * Operates on raw plugin parameters, not the "effective" values.
2586      *
2587      * @return The checksum
2588      */
2589     private String getConfigChecksum() {
2590         ChecksumCalculator checksum = new ChecksumCalculator();
2591         checksum.add(getPluginName());
2592         checksum.add(isSkipTests());
2593         checksum.add(isSkipExec());
2594         checksum.add(isSkip());
2595         checksum.add(getTestClassesDirectory());
2596         checksum.add(getMainBuildPath());
2597         checksum.add(getClasspathDependencyExcludes());
2598         checksum.add(getClasspathDependencyScopeExclude());
2599         checksum.add(getAdditionalClasspathElements());
2600         checksum.add(getReportsDirectory());
2601         checksum.add(getProjectBuildDirectory());
2602         checksum.add(getTestSourceDirectory());
2603         checksum.add(getTest());
2604         checksum.add(getIncludes());
2605         checksum.add(getSkipAfterFailureCount());
2606         checksum.add(getShutdown());
2607         checksum.add(getExcludes());
2608         checksum.add(getLocalRepositoryPath());
2609         checksum.add(getSystemProperties());
2610         checksum.add(getSystemPropertyVariables());
2611         checksum.add(getSystemPropertiesFile());
2612         checksum.add(getProperties());
2613         checksum.add(isPrintSummary());
2614         checksum.add(getReportFormat());
2615         checksum.add(getReportNameSuffix());
2616         checksum.add(isUseFile());
2617         checksum.add(isRedirectTestOutputToFile());
2618         checksum.add(getForkCount());
2619         checksum.add(isReuseForks());
2620         checksum.add(getJvm());
2621         checksum.add(getArgLine());
2622         checksum.add(getDebugForkedProcess());
2623         checksum.add(getForkedProcessTimeoutInSeconds());
2624         checksum.add(getParallelTestsTimeoutInSeconds());
2625         checksum.add(getParallelTestsTimeoutForcedInSeconds());
2626         checksum.add(getEnvironmentVariables());
2627         checksum.add(getExcludedEnvironmentVariables());
2628         checksum.add(getWorkingDirectory());
2629         checksum.add(isChildDelegation());
2630         checksum.add(getGroups());
2631         checksum.add(getExcludedGroups());
2632         checksum.add(getIncludeJUnit5Engines());
2633         checksum.add(getExcludeJUnit5Engines());
2634         checksum.add(getJunitArtifact());
2635         checksum.add(getTestNGArtifactName());
2636         checksum.add(getThreadCount());
2637         checksum.add(getThreadCountSuites());
2638         checksum.add(getThreadCountClasses());
2639         checksum.add(getThreadCountMethods());
2640         checksum.add(getPerCoreThreadCount());
2641         checksum.add(getUseUnlimitedThreads());
2642         checksum.add(getParallel());
2643         checksum.add(isParallelOptimized());
2644         checksum.add(isTrimStackTrace());
2645         checksum.add(disableXmlReport);
2646         checksum.add(isUseSystemClassLoader());
2647         checksum.add(isUseManifestOnlyJar());
2648         checksum.add(getEncoding());
2649         checksum.add(isEnableAssertions());
2650         checksum.add(getObjectFactory());
2651         checksum.add(getFailIfNoTests());
2652         checksum.add(getRunOrder());
2653         checksum.add(getDependenciesToScan());
2654         checksum.add(getForkedProcessExitTimeoutInSeconds());
2655         checksum.add(getRerunFailingTestsCount());
2656         checksum.add(getTempDir());
2657         checksum.add(useModulePath());
2658         checksum.add(getEnableProcessChecker());
2659         checksum.add(isEnableOutErrElements());
2660         checksum.add(isEnablePropertiesElement());
2661         addPluginSpecificChecksumItems(checksum);
2662         return checksum.getSha1();
2663     }
2664 
2665     protected void addPluginSpecificChecksumItems(ChecksumCalculator checksum) {}
2666 
2667     protected boolean hasExecutedBefore() {
2668         // A tribute to Linus Torvalds
2669         String configChecksum = getConfigChecksum();
2670         @SuppressWarnings("unchecked")
2671         Map<String, String> pluginContext = getPluginContext();
2672         if (pluginContext.containsKey(configChecksum)) {
2673             getConsoleLogger()
2674                     .info("Skipping execution of surefire because it has already been run for this configuration");
2675             return true;
2676         }
2677         pluginContext.put(configChecksum, configChecksum);
2678 
2679         return false;
2680     }
2681 
2682     @Nonnull
2683     protected ClassLoaderConfiguration getClassLoaderConfiguration() {
2684         return isForking()
2685                 ? new ClassLoaderConfiguration(isUseSystemClassLoader(), isUseManifestOnlyJar())
2686                 : new ClassLoaderConfiguration(false, false);
2687     }
2688 
2689     /**
2690      * Generates the test classpath.
2691      *
2692      * @return the classpath elements
2693      * @throws MojoFailureException
2694      */
2695     private TestClassPath generateTestClasspath(ArtifactFilter... dependencyFilters) throws MojoFailureException {
2696         Set<Artifact> classpathArtifacts = getProject().getArtifacts();
2697 
2698         if (getClasspathDependencyScopeExclude() != null
2699                 && !getClasspathDependencyScopeExclude().isEmpty()) {
2700             ArtifactFilter dependencyFilter = new ScopeArtifactFilter(getClasspathDependencyScopeExclude());
2701             classpathArtifacts = filterArtifacts(classpathArtifacts, dependencyFilter);
2702         }
2703 
2704         if (getClasspathDependencyExcludes() != null) {
2705             List<String> excludedDependencies = asList(getClasspathDependencyExcludes());
2706             ArtifactFilter dependencyFilter = new PatternIncludesArtifactFilter(excludedDependencies);
2707             classpathArtifacts = filterArtifacts(classpathArtifacts, dependencyFilter);
2708         }
2709 
2710         if (dependencyFilters != null) {
2711             for (ArtifactFilter dependencyFilter : dependencyFilters) {
2712                 if (dependencyFilter != null) {
2713                     classpathArtifacts = filterArtifacts(classpathArtifacts, dependencyFilter);
2714                 }
2715             }
2716         }
2717 
2718         Map<String, Artifact> dependencyConflictIdsProjectArtifacts = classpathArtifacts.stream()
2719                 .collect(Collectors.toMap(Artifact::getDependencyConflictId, Function.identity()));
2720         Set<String> additionalClasspathElements = new LinkedHashSet<>();
2721         if (getAdditionalClasspathElements() != null) {
2722             additionalClasspathElements.addAll(asList(getAdditionalClasspathElements()));
2723         }
2724         if (additionalClasspathDependencies != null && !additionalClasspathDependencies.isEmpty()) {
2725             Collection<Artifact> additionalArtifacts = resolveDependencies(additionalClasspathDependencies);
2726             // check for potential conflicts with project dependencies
2727             for (Artifact additionalArtifact : additionalArtifacts) {
2728                 Artifact conflictingArtifact =
2729                         dependencyConflictIdsProjectArtifacts.get(additionalArtifact.getDependencyConflictId());
2730                 if (conflictingArtifact != null
2731                         && !additionalArtifact.getVersion().equals(conflictingArtifact.getVersion())) {
2732                     getConsoleLogger()
2733                             .warning(
2734                                     "Potential classpath conflict between project dependency and resolved additionalClasspathDependency: Found multiple versions of "
2735                                             + additionalArtifact.getDependencyConflictId() + ": "
2736                                             + additionalArtifact.getVersion() + " and "
2737                                             + conflictingArtifact.getVersion());
2738                 }
2739                 additionalClasspathElements.add(additionalArtifact.getFile().getAbsolutePath());
2740             }
2741         }
2742         return new TestClassPath(
2743                 classpathArtifacts, getMainBuildPath(), getTestClassesDirectory(), additionalClasspathElements);
2744     }
2745 
2746     protected Collection<Artifact> resolveDependencies(List<Dependency> dependencies) throws MojoFailureException {
2747         Map<String, Artifact> dependencyConflictIdsAndArtifacts = new HashMap<>();
2748         try {
2749             dependencies.stream()
2750                     .map(dependency -> {
2751                         try {
2752                             return surefireDependencyResolver.resolveDependencies(
2753                                     session.getRepositorySession(), project.getRemoteProjectRepositories(), dependency);
2754                         } catch (MojoExecutionException e) {
2755                             throw new IllegalStateException(e);
2756                         }
2757                     })
2758                     .forEach(artifacts -> {
2759                         for (Artifact a : artifacts) {
2760                             Artifact conflictingArtifact =
2761                                     dependencyConflictIdsAndArtifacts.get(a.getDependencyConflictId());
2762                             if (conflictingArtifact != null
2763                                     && !a.getVersion().equals(conflictingArtifact.getVersion())) {
2764                                 getConsoleLogger()
2765                                         .warning(
2766                                                 "Potential classpath conflict among resolved additionalClasspathDependencies: Found multiple versions of "
2767                                                         + a.getDependencyConflictId() + ": " + a.getVersion() + " and "
2768                                                         + conflictingArtifact.getVersion());
2769                             } else {
2770                                 dependencyConflictIdsAndArtifacts.put(a.getDependencyConflictId(), a);
2771                             }
2772                         }
2773                     });
2774         } catch (IllegalStateException e) {
2775             throw new MojoFailureException(e.getMessage(), e.getCause());
2776         }
2777         return dependencyConflictIdsAndArtifacts.values();
2778     }
2779 
2780     /**
2781      * Return a new set containing only the artifacts not accepted by the given filter.
2782      *
2783      * @param artifacts The unfiltered artifacts
2784      * @param filter    The excludes filter to apply
2785      * @return The filtered result
2786      */
2787     private static Set<Artifact> filterArtifacts(Set<Artifact> artifacts, ArtifactFilter filter) {
2788         Set<Artifact> filteredArtifacts = new LinkedHashSet<>();
2789 
2790         for (Artifact artifact : artifacts) {
2791             if (!filter.include(artifact)) {
2792                 filteredArtifacts.add(artifact);
2793             }
2794         }
2795 
2796         return filteredArtifacts;
2797     }
2798 
2799     private void showMap(Map<?, ?> map, String setting) {
2800         for (Object o : map.keySet()) {
2801             String key = (String) o;
2802             String value = (String) map.get(key);
2803             getConsoleLogger().debug("Setting " + setting + " [" + key + "]=[" + value + "]");
2804         }
2805     }
2806 
2807     private <T> void showArray(T[] array, String setting) {
2808         for (T e : array) {
2809             getConsoleLogger().debug("Setting " + setting + " [" + e + "]");
2810         }
2811     }
2812 
2813     private Classpath getArtifactClasspath(Artifact surefireArtifact) throws MojoExecutionException {
2814 
2815         Classpath existing = classpathCache.getCachedClassPath(surefireArtifact.getArtifactId());
2816         if (existing == null) {
2817             List<String> items = new ArrayList<>();
2818             Set<Artifact> booterArtifacts = surefireDependencyResolver.resolveArtifacts(
2819                     session.getRepositorySession(), project.getRemotePluginRepositories(), surefireArtifact);
2820             for (Artifact artifact : booterArtifacts) {
2821                 items.add(artifact.getFile().getAbsolutePath());
2822             }
2823             existing = new Classpath(items);
2824             classpathCache.setCachedClasspath(surefireArtifact.getArtifactId(), existing);
2825         }
2826         return existing;
2827     }
2828 
2829     Properties getUserProperties() {
2830         return getSession().getUserProperties();
2831     }
2832 
2833     private void ensureWorkingDirectoryExists() throws MojoFailureException {
2834         if (getWorkingDirectory() == null) {
2835             throw new MojoFailureException("workingDirectory cannot be null");
2836         }
2837 
2838         if (isForking()) {
2839             // Postpone directory creation till forked JVM creation
2840             // see ForkConfiguration.createCommandLine
2841             return;
2842         }
2843 
2844         if (!getWorkingDirectory().exists()) {
2845             if (!getWorkingDirectory().mkdirs()) {
2846                 throw new MojoFailureException("Cannot create workingDirectory " + getWorkingDirectory());
2847             }
2848         }
2849 
2850         if (!getWorkingDirectory().isDirectory()) {
2851             throw new MojoFailureException(
2852                     "workingDirectory " + getWorkingDirectory() + " exists and is not a directory");
2853         }
2854     }
2855 
2856     private void ensureParallelRunningCompatibility() throws MojoFailureException {
2857         if (isMavenParallel() && isNotForking()) {
2858             throw new MojoFailureException("parallel maven execution is not compatible with surefire forkCount 0");
2859         }
2860     }
2861 
2862     private void warnIfUselessUseSystemClassLoaderParameter() {
2863         if (isUseSystemClassLoader() && isNotForking()) {
2864             getConsoleLogger().warning("useSystemClassLoader setting has no effect when not forking");
2865         }
2866     }
2867 
2868     private boolean isNotForking() {
2869         return !isForking();
2870     }
2871 
2872     private List<CommandLineOption> commandLineOptions() {
2873         return SurefireHelper.commandLineOptions(getSession(), getConsoleLogger());
2874     }
2875 
2876     private void warnIfDefunctGroupsCombinations() throws MojoFailureException, MojoExecutionException {
2877         Artifact junitArtifact = getJunitArtifact();
2878         if (isAnyGroupsSelected()) {
2879             if (getTestNgArtifact() == null) {
2880                 boolean junit47Compatible = isJunit47Compatible(junitArtifact);
2881                 boolean junit5PlatformCompatible = getJUnit5Artifact() != null;
2882                 if (!junit47Compatible && !junit5PlatformCompatible) {
2883                     throw new MojoFailureException("groups/excludedGroups are specified but JUnit version on "
2884                             + "classpath is too old to support groups. "
2885                             + "Check your dependency:tree to see if your project "
2886                             + "is picking up an old junit version");
2887                 }
2888             }
2889         }
2890     }
2891 
2892     private void warnIfRerunClashes() throws MojoFailureException {
2893         if (getRerunFailingTestsCount() < 0) {
2894             throw new MojoFailureException("Parameter \"rerunFailingTestsCount\" should not be negative.");
2895         }
2896 
2897         if (getSkipAfterFailureCount() < 0) {
2898             throw new MojoFailureException("Parameter \"skipAfterFailureCount\" should not be negative.");
2899         }
2900     }
2901 
2902     private void warnIfWrongShutdownValue() throws MojoFailureException {
2903         if (!Shutdown.isKnown(getShutdown())) {
2904             throw new MojoFailureException("Parameter \"shutdown\" should have values " + Shutdown.listParameters());
2905         }
2906     }
2907 
2908     private void warnIfIllegalTempDir() throws MojoFailureException {
2909         if (isEmpty(getTempDir())) {
2910             throw new MojoFailureException("Parameter 'tempDir' should not be blank string.");
2911         }
2912     }
2913 
2914     protected void warnIfIllegalFailOnFlakeCount() throws MojoFailureException {}
2915 
2916     private void printDefaultSeedIfNecessary() {
2917         if (getRunOrder().equals(RunOrder.RANDOM.name())) {
2918             if (getRunOrderRandomSeed() == null) {
2919                 setRunOrderRandomSeed(System.nanoTime());
2920             }
2921             getConsoleLogger()
2922                     .info("Tests will run in random order. To reproduce ordering use flag -D" + getPluginName()
2923                             + ".runOrder.random.seed=" + getRunOrderRandomSeed());
2924         }
2925     }
2926 
2927     /**
2928      * Provides the Provider information for manually configured providers.
2929      */
2930     final class DynamicProviderInfo implements ConfigurableProviderInfo {
2931         final String providerName;
2932 
2933         DynamicProviderInfo(String providerName) {
2934             this.providerName = providerName;
2935         }
2936 
2937         @Override
2938         public ProviderInfo instantiate(String providerName) {
2939             return new DynamicProviderInfo(providerName);
2940         }
2941 
2942         @Override
2943         @Nonnull
2944         public String getProviderName() {
2945             return providerName;
2946         }
2947 
2948         @Override
2949         public boolean isApplicable() {
2950             return true;
2951         }
2952 
2953         @Override
2954         public void addProviderProperties() throws MojoExecutionException {
2955             // Ok this is a bit lazy.
2956             try {
2957                 convertJunitCoreParameters();
2958                 convertTestNGParameters();
2959             } catch (MojoFailureException e) {
2960                 throw new MojoExecutionException(e.getMessage(), e);
2961             }
2962         }
2963 
2964         @Nonnull
2965         @Override
2966         public List<String[]> getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) {
2967             return emptyList();
2968         }
2969 
2970         @Override
2971         @Nonnull
2972         public Set<Artifact> getProviderClasspath() throws MojoExecutionException {
2973             Plugin plugin = getPluginDescriptor().getPlugin();
2974             Map<String, Artifact> providerArtifacts = surefireDependencyResolver.resolvePluginDependencies(
2975                     session.getRepositorySession(),
2976                     project.getRemotePluginRepositories(),
2977                     plugin,
2978                     getPluginArtifactMap());
2979             return new LinkedHashSet<>(providerArtifacts.values());
2980         }
2981     }
2982 
2983     class JUnitPlatformProviderInfo implements ProviderInfo {
2984 
2985         private static final String PROVIDER_DEP_GID = "org.junit.platform";
2986         private static final String PROVIDER_DEP_AID = "junit-platform-launcher";
2987 
2988         private final Artifact junitPlatformRunnerArtifact;
2989         private final Artifact junitPlatformArtifact;
2990         private final TestClassPath testClasspath;
2991 
2992         private final Artifact junitArtifact;
2993         private final Artifact booterArtifact;
2994         private final Artifact testNgArtifact;
2995         private final SurefireDependencyResolver surefireDependencyResolver;
2996         private final MavenSession session;
2997         private final MavenProject project;
2998         private final PluginDescriptor pluginDescriptor;
2999         private final Map<String, Artifact> pluginArtifactMap;
3000         private final ConsoleLogger consoleLogger;
3001         private ProviderInfo.Engine engine = ProviderInfo.Engine.JUNIT_PLATFORM;
3002 
3003         @SuppressWarnings("checkstyle:parameternumber")
3004         JUnitPlatformProviderInfo(
3005                 Artifact junitPlatformRunnerArtifact,
3006                 Artifact junitPlatformArtifact,
3007                 @Nonnull TestClassPath testClasspath,
3008                 Artifact junitArtifact,
3009                 Artifact booterArtifact,
3010                 SurefireDependencyResolver surefireDependencyResolver,
3011                 MavenSession session,
3012                 MavenProject project,
3013                 PluginDescriptor pluginDescriptor,
3014                 Map<String, Artifact> pluginArtifactMap,
3015                 ConsoleLogger consoleLogger,
3016                 Artifact testNgArtifact) {
3017             this.session = session;
3018             this.project = project;
3019             this.pluginDescriptor = pluginDescriptor;
3020             this.pluginArtifactMap = pluginArtifactMap;
3021             this.consoleLogger = consoleLogger;
3022             this.junitPlatformRunnerArtifact = junitPlatformRunnerArtifact;
3023             this.junitPlatformArtifact = junitPlatformArtifact;
3024             this.testClasspath = testClasspath;
3025             this.junitArtifact = junitArtifact;
3026             this.booterArtifact = booterArtifact;
3027             this.testNgArtifact = testNgArtifact;
3028             // JUnit 4.12+ is required by JUnit 5.3+
3029             // JUnit 4.13+ is required by JUnit 5.8+
3030             // JUnit 4.13.2 is required by JUnit 5.9+
3031             // JUnit 4.13.3 is required by JUnit 5.10+
3032             // JUnit 4.13.4 is required by JUnit 5.12+
3033             // https://junit.org/junit5/docs/current/user-guide/#dependency-metadata
3034 
3035             if (isAnyJunit4(junitArtifact)) {
3036                 this.engine = Engine.JUNIT4;
3037             }
3038             this.surefireDependencyResolver = surefireDependencyResolver;
3039         }
3040 
3041         @Override
3042         @Nonnull
3043         public String getProviderName() {
3044             return "org.apache.maven.surefire.junitplatform.JUnitPlatformProvider";
3045         }
3046 
3047         protected String getProviderArtifactName() {
3048             return "surefire-junit-platform";
3049         }
3050 
3051         @Override
3052         public boolean isApplicable() {
3053             return (junitPlatformRunnerArtifact == null && junitPlatformArtifact != null)
3054                     || (isAnyJunit4(junitArtifact))
3055                     || testNgArtifact != null;
3056         }
3057 
3058         @Override
3059         public void addProviderProperties() throws MojoExecutionException {
3060             try {
3061                 convertJunitEngineParameters();
3062                 convertGroupParameters();
3063                 convertJunitCoreParameters();
3064             } catch (MojoFailureException e) {
3065                 throw new MojoExecutionException(e.getMessage(), e);
3066             }
3067             // FIXME look at duplicate parameters to remove
3068             if (testNgArtifact != null) {
3069                 convertTestNGParameters();
3070             }
3071         }
3072 
3073         @Nonnull
3074         @Override
3075         public List<String[]> getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) {
3076             boolean hasTestDescriptor = forkRequirements.isModularPath() && forkRequirements.hasTestModuleDescriptor();
3077             return hasTestDescriptor ? getJpmsArgs() : Collections.emptyList();
3078         }
3079 
3080         @Override
3081         @Nonnull
3082         public Set<Artifact> getProviderClasspath() throws MojoExecutionException {
3083             String surefireVersion = booterArtifact.getBaseVersion();
3084             Map<String, Artifact> providerArtifacts = surefireDependencyResolver.getProviderClasspathAsMap(
3085                     session.getRepositorySession(),
3086                     project.getRemotePluginRepositories(),
3087                     getProviderArtifactName(),
3088                     surefireVersion);
3089             Map<String, Artifact> testDeps = testClasspath.getTestDependencies();
3090 
3091             Plugin plugin = pluginDescriptor.getPlugin();
3092             Map<String, Artifact> pluginDeps = surefireDependencyResolver.resolvePluginDependencies(
3093                     session.getRepositorySession(), project.getRemotePluginRepositories(), plugin, pluginArtifactMap);
3094 
3095             String defaultJunitVersion = getDefaultJunitVersion();
3096 
3097             String engineGroupId = "org.junit.jupiter";
3098             String engineArtifactId = "junit-jupiter-engine";
3099             String engineCoordinates = engineGroupId + ":" + engineArtifactId;
3100             String api = "org.junit.jupiter:junit-jupiter-api";
3101             // FIXME this default version should be aligned with the junit-platform-version property from pom
3102             String engineVersion = testDeps.get(api) == null
3103                     ? defaultJunitVersion
3104                     : testDeps.get(api).getBaseVersion();
3105             consoleLogger.debug(
3106                     "Test dependencies contain " + api + ". Resolving " + engineCoordinates + ":" + engineVersion);
3107 
3108             if (hasDependencyPlatformEngine(pluginDeps)) {
3109                 providerArtifacts.putAll(pluginDeps);
3110             } else {
3111                 if (hasDependencyJupiterAPI(testDeps)
3112                         && engineVersion != null
3113                         && !testDeps.containsKey(engineGroupId + ":" + engineArtifactId)) {
3114                     addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts);
3115                 }
3116 
3117                 if (isWithinVersionSpec(junitArtifact, "[4.0,4.11]")) {
3118                     String message = "Surefire is not compatible with JUnit 4.11 or older. Project version is "
3119                             + junitArtifact.getBaseVersion();
3120                     consoleLogger.error(message);
3121                     throw new MojoExecutionException(message);
3122                 }
3123             }
3124 
3125             // need to check there is at least one test engine in the classpath
3126             String junit = (String) getProperties().get("junit");
3127             boolean runJunit = Boolean.parseBoolean(StringUtils.isEmpty(junit) ? "true" : junit);
3128             if (StringUtils.isNotEmpty(junit) && !runJunit) {
3129                 consoleLogger.debug("System property 'junit' is explicity set to '" + junit + "'");
3130             } else {
3131                 if (testDeps.containsKey("junit:junit")
3132                         && !testDeps.containsKey("org.junit.vintage:junit-vintage-engine")
3133                         && !pluginDeps.containsKey("org.junit.vintage:junit-vintage-engine")) {
3134                     getProperties().setProperty(JUNIT_VINTAGE_DETECTED, "true");
3135                     if (!hasDependencyPlatformEngine(providerArtifacts)) {
3136                         // TODO exclude transitive deps (hamcrest etc...)
3137                         consoleLogger.debug("Test dependencies contain JUnit4. Resolving " + engineCoordinates + ":"
3138                                 + engineVersion);
3139                         addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts);
3140                     }
3141                     // add org.junit.vintage:junit-vintage-engine
3142                     addEngineByApi("org.junit.vintage", "junit-vintage-engine", engineVersion, providerArtifacts);
3143                 }
3144             }
3145             if (testNgArtifact != null) {
3146                 // FIXME support only from TestNG 6.14.3
3147                 // FIXME check if already present as plugin dependency or project dependency
3148                 String junitSupportGroupId = "org.junit.support";
3149                 String testNgEngineArtifactId = "testng-engine";
3150                 String testNgEngineCoordinates = junitSupportGroupId + ":" + testNgEngineArtifactId;
3151                 // FIXME configurable?
3152                 // or picked from test dependencies
3153                 String version = "1.0.6";
3154                 consoleLogger.debug("TestNG is present. Resolving " + testNgEngineCoordinates + ":" + version);
3155                 if (!testDeps.containsKey(testNgEngineCoordinates)
3156                         && !pluginDeps.containsKey(testNgEngineCoordinates)) {
3157                     addEngineByApi(junitSupportGroupId, testNgEngineArtifactId, version, providerArtifacts);
3158                 }
3159             }
3160 
3161             if (!hasDependencyPlatformEngine(providerArtifacts)) {
3162                 addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts);
3163             }
3164 
3165             if (!hasDependencyPlatformEngine(providerArtifacts)) {
3166                 addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts);
3167             }
3168 
3169             narrowDependencies(providerArtifacts, testDeps);
3170             alignProviderVersions(providerArtifacts, engineVersion, testDeps.get(api));
3171 
3172             return new LinkedHashSet<>(providerArtifacts.values());
3173         }
3174 
3175         private List<String[]> getJpmsArgs() {
3176             List<String[]> args = new ArrayList<>();
3177 
3178             args.add(new String[] {
3179                 "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.util=ALL-UNNAMED"
3180             });
3181 
3182             args.add(new String[] {
3183                 "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.logging=ALL-UNNAMED"
3184             });
3185 
3186             return args;
3187         }
3188 
3189         private void addEngineByApi(
3190                 String engineGroupId,
3191                 String engineArtifactId,
3192                 String engineVersion,
3193                 Map<String, Artifact> providerArtifacts)
3194                 throws MojoExecutionException {
3195             for (Artifact dep : resolve(engineGroupId, engineArtifactId, engineVersion, null, "jar")) {
3196                 String key = dep.getGroupId() + ":" + dep.getArtifactId();
3197                 Artifact removed = providerArtifacts.put(key, dep);
3198                 if (removed != null) {
3199                     consoleLogger.debug("Replaced provider dependency " + key + " " + removed + " with " + dep);
3200                     ComparableVersion removedVersion = new ComparableVersion(removed.getVersion());
3201                     ComparableVersion depVersion = new ComparableVersion(dep.getVersion());
3202                     // keep the highest version
3203                     if (removedVersion.compareTo(depVersion) > 0) {
3204                         providerArtifacts.put(key, removed);
3205                         consoleLogger.debug("Kept higher version " + key + " " + removed);
3206                     }
3207                 }
3208             }
3209         }
3210 
3211         private String getDefaultJunitVersion() {
3212             try (InputStream inputStream = Thread.currentThread()
3213                     .getContextClassLoader()
3214                     .getResourceAsStream("surefire.junit.version.properties")) {
3215                 Properties junitProperties = new Properties();
3216                 junitProperties.load(inputStream);
3217                 return junitProperties.getProperty("junit.version");
3218             } catch (Exception e) {
3219                 consoleLogger.warning(
3220                         "Could not load default JUnit version from junit.version.properties use default 5.14.1");
3221                 return "5.14.1";
3222             }
3223         }
3224 
3225         private void narrowDependencies(
3226                 Map<String, Artifact> providerArtifacts, Map<String, Artifact> testDependencies) {
3227             providerArtifacts.keySet().removeAll(testDependencies.keySet());
3228         }
3229 
3230         protected void alignProviderVersions(
3231                 Map<String, Artifact> providerArtifacts, String engineVersion, Artifact engineApi)
3232                 throws MojoExecutionException {
3233             if (junitPlatformArtifact == null) {
3234                 return;
3235             }
3236 
3237             String version = junitPlatformArtifact.getBaseVersion();
3238             // this might be dangerous for version before 6.x where versions were different between launcher, platform,,
3239             boolean is6x = false;
3240             if (engineApi != null) {
3241                 is6x = isWithinVersionSpec(engineApi, "[6.0.0,)");
3242             }
3243 
3244             if (!version.equals(engineVersion) && is6x) {
3245                 version = engineVersion;
3246             }
3247             for (Artifact launcherArtifact : resolve(PROVIDER_DEP_GID, PROVIDER_DEP_AID, version, null, "jar")) {
3248                 String key = launcherArtifact.getGroupId() + ":" + launcherArtifact.getArtifactId();
3249                 if (providerArtifacts.containsKey(key)) {
3250                     providerArtifacts.put(key, launcherArtifact);
3251                 }
3252             }
3253         }
3254 
3255         private Set<Artifact> resolve(String groupId, String artifactId, String version, String classifier, String type)
3256                 throws MojoExecutionException {
3257             // FIXME will be different with 3 and testng
3258             ArtifactHandler handler = junitPlatformArtifact != null
3259                     ? junitPlatformArtifact.getArtifactHandler()
3260                     : junitArtifact != null
3261                             ? junitArtifact.getArtifactHandler()
3262                             : testNgArtifact != null ? testNgArtifact.getArtifactHandler() : null;
3263             Artifact artifact = new DefaultArtifact(groupId, artifactId, version, null, type, classifier, handler);
3264             consoleLogger.debug("Resolving artifact " + groupId + ":" + artifactId + ":" + version);
3265             Set<Artifact> r = surefireDependencyResolver.resolveArtifacts(
3266                     session.getRepositorySession(), project.getRemoteProjectRepositories(), artifact);
3267             consoleLogger.debug("Resolved artifact " + groupId + ":" + artifactId + ":" + version + " to " + r);
3268             return r;
3269         }
3270 
3271         private boolean hasDependencyJupiterAPI(Map<String, Artifact> dependencies) {
3272             return dependencies.containsKey("org.junit.jupiter:junit-jupiter-api");
3273         }
3274 
3275         private boolean hasDependencyPlatformEngine(Map<String, Artifact> dependencies) {
3276             return dependencies.entrySet().stream()
3277                     .anyMatch(stringArtifactEntry ->
3278                             stringArtifactEntry.getKey().equals("org.junit.platform:junit-platform-engine"));
3279         }
3280     }
3281 
3282     // TODO why an enclosing class on the top do we real need this shading??
3283     @SuppressWarnings("checkstyle:parameternumber")
3284     final class JUnitPlatformProviderShadefireInfo extends JUnitPlatformProviderInfo {
3285 
3286         JUnitPlatformProviderShadefireInfo(
3287                 Artifact junitPlatformRunnerArtifact,
3288                 Artifact junitPlatformArtifact,
3289                 @Nonnull TestClassPath testClasspath,
3290                 Artifact junitArtifact,
3291                 Artifact booterArtifact,
3292                 SurefireDependencyResolver surefireDependencyResolver,
3293                 MavenSession session,
3294                 MavenProject project,
3295                 PluginDescriptor pluginDescriptor,
3296                 Map<String, Artifact> pluginArtifactMap,
3297                 ConsoleLogger consoleLogger,
3298                 Artifact testNgArtifact) {
3299             super(
3300                     junitPlatformRunnerArtifact,
3301                     junitPlatformArtifact,
3302                     testClasspath,
3303                     junitArtifact,
3304                     booterArtifact,
3305                     surefireDependencyResolver,
3306                     session,
3307                     project,
3308                     pluginDescriptor,
3309                     pluginArtifactMap,
3310                     consoleLogger,
3311                     testNgArtifact);
3312         }
3313 
3314         @Override
3315         public boolean isApplicable() {
3316             // newer discover this provider automatically
3317             return false;
3318         }
3319 
3320         @Override
3321         @Nonnull
3322         public String getProviderName() {
3323             return "org.apache.maven.shadefire.surefire.junitplatform.JUnitPlatformProvider";
3324         }
3325 
3326         @Override
3327         protected String getProviderArtifactName() {
3328             return "surefire-shadefire";
3329         }
3330 
3331         @Override
3332         protected void alignProviderVersions(
3333                 Map<String, Artifact> providerArtifacts, String engineVersion, Artifact engineApi)
3334                 throws MojoExecutionException {
3335             // shadefire is used as booter we can not provide additional dependencies,
3336             // so we need add a launcher here
3337             providerArtifacts.put("org.junit.platform:junit-platform-launcher", null);
3338             super.alignProviderVersions(providerArtifacts, engineVersion, engineApi);
3339         }
3340     }
3341 
3342     File createSurefireBootDirectoryInBuild() {
3343         File tmp = new File(getProjectBuildDirectory(), getTempDir());
3344         //noinspection ResultOfMethodCallIgnored
3345         tmp.mkdirs();
3346         return tmp;
3347     }
3348 
3349     File createSurefireBootDirectoryInTemp() {
3350         try {
3351             return Files.createTempDirectory(getTempDir()).toFile();
3352         } catch (IOException e) {
3353             return createSurefireBootDirectoryInBuild();
3354         }
3355     }
3356 
3357     @Override
3358     public String getLocalRepositoryPath() {
3359         return Optional.ofNullable(
3360                         session.getRepositorySession().getLocalRepository().getBasedir())
3361                 .map(File::getAbsolutePath)
3362                 .orElse(".");
3363     }
3364 
3365     public Properties getSystemProperties() {
3366         return systemProperties;
3367     }
3368 
3369     @SuppressWarnings("UnusedDeclaration")
3370     public void setSystemProperties(Properties systemProperties) {
3371         this.systemProperties = systemProperties;
3372     }
3373 
3374     public Map<String, String> getSystemPropertyVariables() {
3375         return systemPropertyVariables;
3376     }
3377 
3378     @SuppressWarnings("UnusedDeclaration")
3379     public void setSystemPropertyVariables(Map<String, String> systemPropertyVariables) {
3380         this.systemPropertyVariables = systemPropertyVariables;
3381     }
3382 
3383     /**
3384      * List of System properties, loaded from a file, to pass to the JUnit tests.
3385      *
3386      * @since 2.8.2
3387      */
3388     public abstract File getSystemPropertiesFile();
3389 
3390     @SuppressWarnings("UnusedDeclaration")
3391     public abstract void setSystemPropertiesFile(File systemPropertiesFile);
3392 
3393     private Properties getProperties() {
3394         return properties;
3395     }
3396 
3397     public void setProperties(Properties properties) {
3398         this.properties = properties;
3399     }
3400 
3401     public Map<String, Artifact> getPluginArtifactMap() {
3402         return pluginArtifactMap;
3403     }
3404 
3405     @SuppressWarnings("UnusedDeclaration")
3406     public void setPluginArtifactMap(Map<String, Artifact> pluginArtifactMap) {
3407         this.pluginArtifactMap = pluginArtifactMap;
3408     }
3409 
3410     public Map<String, Artifact> getProjectArtifactMap() {
3411         return projectArtifactMap;
3412     }
3413 
3414     @SuppressWarnings("UnusedDeclaration")
3415     public void setProjectArtifactMap(Map<String, Artifact> projectArtifactMap) {
3416         this.projectArtifactMap = projectArtifactMap;
3417     }
3418 
3419     public String getReportNameSuffix() {
3420         return reportNameSuffix;
3421     }
3422 
3423     @SuppressWarnings("UnusedDeclaration")
3424     public void setReportNameSuffix(String reportNameSuffix) {
3425         this.reportNameSuffix = reportNameSuffix;
3426     }
3427 
3428     public boolean isRedirectTestOutputToFile() {
3429         return redirectTestOutputToFile;
3430     }
3431 
3432     @SuppressWarnings("UnusedDeclaration")
3433     public void setRedirectTestOutputToFile(boolean redirectTestOutputToFile) {
3434         this.redirectTestOutputToFile = redirectTestOutputToFile;
3435     }
3436 
3437     public boolean getFailIfNoTests() {
3438         return failIfNoTests;
3439     }
3440 
3441     public void setFailIfNoTests(boolean failIfNoTests) {
3442         this.failIfNoTests = failIfNoTests;
3443     }
3444 
3445     public String getJvm() {
3446         return jvm;
3447     }
3448 
3449     public String getArgLine() {
3450         return argLine;
3451     }
3452 
3453     @SuppressWarnings("UnusedDeclaration")
3454     public void setArgLine(String argLine) {
3455         this.argLine = argLine;
3456     }
3457 
3458     public Map<String, String> getEnvironmentVariables() {
3459         return environmentVariables;
3460     }
3461 
3462     @SuppressWarnings("UnusedDeclaration")
3463     public void setEnvironmentVariables(Map<String, String> environmentVariables) {
3464         this.environmentVariables = environmentVariables;
3465     }
3466 
3467     public File getWorkingDirectory() {
3468         return workingDirectory;
3469     }
3470 
3471     @SuppressWarnings("UnusedDeclaration")
3472     public void setWorkingDirectory(File workingDirectory) {
3473         this.workingDirectory = workingDirectory;
3474     }
3475 
3476     public boolean isChildDelegation() {
3477         return childDelegation;
3478     }
3479 
3480     @SuppressWarnings("UnusedDeclaration")
3481     public void setChildDelegation(boolean childDelegation) {
3482         this.childDelegation = childDelegation;
3483     }
3484 
3485     public String getGroups() {
3486         return groups;
3487     }
3488 
3489     @SuppressWarnings("UnusedDeclaration")
3490     public void setGroups(String groups) {
3491         this.groups = groups;
3492     }
3493 
3494     public String getExcludedGroups() {
3495         return excludedGroups;
3496     }
3497 
3498     @SuppressWarnings("UnusedDeclaration")
3499     public void setExcludedGroups(String excludedGroups) {
3500         this.excludedGroups = excludedGroups;
3501     }
3502 
3503     public String getJunitArtifactName() {
3504         return junitArtifactName;
3505     }
3506 
3507     @SuppressWarnings("UnusedDeclaration")
3508     public void setJunitArtifactName(String junitArtifactName) {
3509         this.junitArtifactName = junitArtifactName;
3510     }
3511 
3512     public String getTestNGArtifactName() {
3513         return testNGArtifactName;
3514     }
3515 
3516     @SuppressWarnings("UnusedDeclaration")
3517     public void setTestNGArtifactName(String testNGArtifactName) {
3518         this.testNGArtifactName = testNGArtifactName;
3519     }
3520 
3521     public int getThreadCount() {
3522         return threadCount;
3523     }
3524 
3525     @SuppressWarnings("UnusedDeclaration")
3526     public void setThreadCount(int threadCount) {
3527         this.threadCount = threadCount;
3528     }
3529 
3530     public boolean getPerCoreThreadCount() {
3531         return perCoreThreadCount;
3532     }
3533 
3534     @SuppressWarnings("UnusedDeclaration")
3535     public void setPerCoreThreadCount(boolean perCoreThreadCount) {
3536         this.perCoreThreadCount = perCoreThreadCount;
3537     }
3538 
3539     public boolean getUseUnlimitedThreads() {
3540         return useUnlimitedThreads;
3541     }
3542 
3543     @SuppressWarnings("UnusedDeclaration")
3544     public void setUseUnlimitedThreads(boolean useUnlimitedThreads) {
3545         this.useUnlimitedThreads = useUnlimitedThreads;
3546     }
3547 
3548     public String getParallel() {
3549         return parallel;
3550     }
3551 
3552     @SuppressWarnings("UnusedDeclaration")
3553     public void setParallel(String parallel) {
3554         this.parallel = parallel;
3555     }
3556 
3557     public boolean isParallelOptimized() {
3558         return parallelOptimized;
3559     }
3560 
3561     @SuppressWarnings("UnusedDeclaration")
3562     public void setParallelOptimized(boolean parallelOptimized) {
3563         this.parallelOptimized = parallelOptimized;
3564     }
3565 
3566     public int getThreadCountSuites() {
3567         return threadCountSuites;
3568     }
3569 
3570     public void setThreadCountSuites(int threadCountSuites) {
3571         this.threadCountSuites = threadCountSuites;
3572     }
3573 
3574     public int getThreadCountClasses() {
3575         return threadCountClasses;
3576     }
3577 
3578     public void setThreadCountClasses(int threadCountClasses) {
3579         this.threadCountClasses = threadCountClasses;
3580     }
3581 
3582     public int getThreadCountMethods() {
3583         return threadCountMethods;
3584     }
3585 
3586     public void setThreadCountMethods(int threadCountMethods) {
3587         this.threadCountMethods = threadCountMethods;
3588     }
3589 
3590     public boolean isTrimStackTrace() {
3591         return trimStackTrace;
3592     }
3593 
3594     @SuppressWarnings("UnusedDeclaration")
3595     public void setTrimStackTrace(boolean trimStackTrace) {
3596         this.trimStackTrace = trimStackTrace;
3597     }
3598 
3599     public boolean isEnableAssertions() {
3600         return enableAssertions;
3601     }
3602 
3603     public boolean effectiveIsEnableAssertions() {
3604         if (getArgLine() != null) {
3605             List<String> args = asList(getArgLine().split(" "));
3606             if (args.contains("-da") || args.contains("-disableassertions")) {
3607                 return false;
3608             }
3609         }
3610         return isEnableAssertions();
3611     }
3612 
3613     @SuppressWarnings("UnusedDeclaration")
3614     public void setEnableAssertions(boolean enableAssertions) {
3615         this.enableAssertions = enableAssertions;
3616     }
3617 
3618     public boolean isEnableOutErrElements() {
3619         return enableOutErrElements;
3620     }
3621 
3622     @SuppressWarnings("UnusedDeclaration")
3623     public void setEnableOutErrElements(boolean enableOutErrElements) {
3624         this.enableOutErrElements = enableOutErrElements;
3625     }
3626 
3627     public boolean isEnablePropertiesElement() {
3628         return enablePropertiesElement;
3629     }
3630 
3631     @SuppressWarnings("UnusedDeclaration")
3632     public void setEnablePropertiesElement(boolean enablePropertiesElement) {
3633         this.enablePropertiesElement = enablePropertiesElement;
3634     }
3635 
3636     public MavenSession getSession() {
3637         return session;
3638     }
3639 
3640     @SuppressWarnings("UnusedDeclaration")
3641     public void setSession(MavenSession session) {
3642         this.session = session;
3643     }
3644 
3645     public String getObjectFactory() {
3646         return objectFactory;
3647     }
3648 
3649     @SuppressWarnings("UnusedDeclaration")
3650     public void setObjectFactory(String objectFactory) {
3651         this.objectFactory = objectFactory;
3652     }
3653 
3654     public ToolchainManager getToolchainManager() {
3655         return toolchainManager;
3656     }
3657 
3658     @SuppressWarnings("UnusedDeclaration")
3659     public void setToolchainManager(ToolchainManager toolchainManager) {
3660         this.toolchainManager = toolchainManager;
3661     }
3662 
3663     public boolean isMavenParallel() {
3664         return parallelMavenExecution != null && parallelMavenExecution;
3665     }
3666 
3667     public String[] getDependenciesToScan() {
3668         return dependenciesToScan;
3669     }
3670 
3671     public void setDependenciesToScan(String[] dependenciesToScan) {
3672         this.dependenciesToScan = dependenciesToScan;
3673     }
3674 
3675     @SuppressWarnings("UnusedDeclaration")
3676     void setPluginDescriptor(PluginDescriptor pluginDescriptor) {
3677         this.pluginDescriptor = pluginDescriptor;
3678     }
3679 
3680     public PluginDescriptor getPluginDescriptor() {
3681         return pluginDescriptor;
3682     }
3683 
3684     public MavenProject getProject() {
3685         return project;
3686     }
3687 
3688     @SuppressWarnings("UnusedDeclaration")
3689     public void setProject(MavenProject project) {
3690         this.project = project;
3691     }
3692 
3693     @Override
3694     public File getTestSourceDirectory() {
3695         return testSourceDirectory;
3696     }
3697 
3698     @Override
3699     public void setTestSourceDirectory(File testSourceDirectory) {
3700         this.testSourceDirectory = testSourceDirectory;
3701     }
3702 
3703     public String getForkCount() {
3704         return forkCount;
3705     }
3706 
3707     public boolean isReuseForks() {
3708         return reuseForks;
3709     }
3710 
3711     public String[] getAdditionalClasspathElements() {
3712         return additionalClasspathElements;
3713     }
3714 
3715     public void setAdditionalClasspathElements(String[] additionalClasspathElements) {
3716         this.additionalClasspathElements = additionalClasspathElements;
3717     }
3718 
3719     public String[] getClasspathDependencyExcludes() {
3720         return classpathDependencyExcludes;
3721     }
3722 
3723     public void setClasspathDependencyExcludes(String[] classpathDependencyExcludes) {
3724         this.classpathDependencyExcludes = classpathDependencyExcludes;
3725     }
3726 
3727     public String getClasspathDependencyScopeExclude() {
3728         return classpathDependencyScopeExclude;
3729     }
3730 
3731     public void setClasspathDependencyScopeExclude(String classpathDependencyScopeExclude) {
3732         this.classpathDependencyScopeExclude = classpathDependencyScopeExclude;
3733     }
3734 
3735     public File getProjectBuildDirectory() {
3736         return projectBuildDirectory;
3737     }
3738 
3739     public void setProjectBuildDirectory(File projectBuildDirectory) {
3740         this.projectBuildDirectory = projectBuildDirectory;
3741     }
3742 
3743     protected void logDebugOrCliShowErrors(String s) {
3744         SurefireHelper.logDebugOrCliShowErrors(s, getConsoleLogger(), cli);
3745     }
3746 
3747     public Map<String, String> getJdkToolchain() {
3748         return jdkToolchain;
3749     }
3750 
3751     public void setJdkToolchain(Map<String, String> jdkToolchain) {
3752         this.jdkToolchain = jdkToolchain;
3753     }
3754 
3755     public String getTempDir() {
3756         return tempDir;
3757     }
3758 
3759     public void setTempDir(String tempDir) {
3760         this.tempDir = tempDir;
3761     }
3762 
3763     private static final class ClasspathCache {
3764         private final Map<String, Classpath> classpaths = new HashMap<>(4);
3765 
3766         private Classpath getCachedClassPath(@Nonnull String artifactId) {
3767             return classpaths.get(artifactId);
3768         }
3769 
3770         private void setCachedClasspath(@Nonnull String key, @Nonnull Classpath classpath) {
3771             classpaths.put(key, classpath);
3772         }
3773 
3774         private Classpath setCachedClasspath(@Nonnull String key, @Nonnull Set<Artifact> artifacts) {
3775             Collection<String> files = new ArrayList<>();
3776             for (Artifact artifact : artifacts) {
3777                 files.add(artifact.getFile().getAbsolutePath());
3778             }
3779             Classpath classpath = new Classpath(files);
3780             setCachedClasspath(key, classpath);
3781             return classpath;
3782         }
3783     }
3784 
3785     /**
3786      * Determines whether the plugin should fail if no tests found to run.
3787      */
3788     enum PluginFailureReason {
3789         NONE,
3790         COULD_NOT_RUN_SPECIFIED_TESTS,
3791         COULD_NOT_RUN_DEFAULT_TESTS,
3792     }
3793 }