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