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                 // minimum testng version tested via getTestNgArtifact
3163                 String junitSupportGroupId = "org.junit.support";
3164                 String testNgEngineArtifactId = "testng-engine";
3165                 String testNgEngineCoordinates = junitSupportGroupId + ":" + testNgEngineArtifactId;
3166                 String version = "1.1.0";
3167                 if (!testDeps.containsKey(testNgEngineCoordinates)
3168                         && !pluginDeps.containsKey(testNgEngineCoordinates)) {
3169                     addEngineByApi(junitSupportGroupId, testNgEngineArtifactId, version, providerArtifacts);
3170                     consoleLogger.info(
3171                             "TestNG is present. Adding per default " + testNgEngineCoordinates + ":" + version);
3172                 }
3173             }
3174 
3175             if (!hasDependencyPlatformEngine(providerArtifacts)) {
3176                 addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts);
3177             }
3178 
3179             if (!hasDependencyPlatformEngine(providerArtifacts)) {
3180                 addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts);
3181             }
3182 
3183             narrowDependencies(providerArtifacts, testDeps);
3184             alignProviderVersions(providerArtifacts, engineVersion, testDeps.get(api));
3185 
3186             return new LinkedHashSet<>(providerArtifacts.values());
3187         }
3188 
3189         private List<String[]> getJpmsArgs() {
3190             List<String[]> args = new ArrayList<>();
3191 
3192             args.add(new String[] {
3193                 "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.util=ALL-UNNAMED"
3194             });
3195 
3196             args.add(new String[] {
3197                 "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.logging=ALL-UNNAMED"
3198             });
3199 
3200             return args;
3201         }
3202 
3203         private void addEngineByApi(
3204                 String engineGroupId,
3205                 String engineArtifactId,
3206                 String engineVersion,
3207                 Map<String, Artifact> providerArtifacts)
3208                 throws MojoExecutionException {
3209             for (Artifact dep : resolve(engineGroupId, engineArtifactId, engineVersion, null, "jar")) {
3210                 String key = dep.getGroupId() + ":" + dep.getArtifactId();
3211                 Artifact removed = providerArtifacts.put(key, dep);
3212                 if (removed != null) {
3213                     consoleLogger.debug("Replaced provider dependency " + key + " " + removed + " with " + dep);
3214                     ComparableVersion removedVersion = new ComparableVersion(removed.getVersion());
3215                     ComparableVersion depVersion = new ComparableVersion(dep.getVersion());
3216                     // keep the highest version
3217                     if (removedVersion.compareTo(depVersion) > 0) {
3218                         providerArtifacts.put(key, removed);
3219                         consoleLogger.debug("Kept higher version " + key + " " + removed);
3220                     }
3221                 }
3222             }
3223         }
3224 
3225         private String getDefaultJunitVersion() {
3226             try (InputStream inputStream = Thread.currentThread()
3227                     .getContextClassLoader()
3228                     .getResourceAsStream("surefire.junit.version.properties")) {
3229                 Properties junitProperties = new Properties();
3230                 junitProperties.load(inputStream);
3231                 return junitProperties.getProperty("junit.version");
3232             } catch (Exception e) {
3233                 consoleLogger.warning(
3234                         "Could not load default JUnit version from junit.version.properties use default 5.14.1");
3235                 return "5.14.1";
3236             }
3237         }
3238 
3239         private void narrowDependencies(
3240                 Map<String, Artifact> providerArtifacts, Map<String, Artifact> testDependencies) {
3241             providerArtifacts.keySet().removeAll(testDependencies.keySet());
3242         }
3243 
3244         protected void alignProviderVersions(
3245                 Map<String, Artifact> providerArtifacts, String engineVersion, Artifact engineApi)
3246                 throws MojoExecutionException {
3247             if (junitPlatformArtifact == null) {
3248                 return;
3249             }
3250 
3251             String version = junitPlatformArtifact.getBaseVersion();
3252             // this might be dangerous for version before 6.x where versions were different between launcher, platform,,
3253             boolean is6x = false;
3254             if (engineApi != null) {
3255                 is6x = isWithinVersionSpec(engineApi, "[6.0.0,)");
3256             }
3257 
3258             if (!version.equals(engineVersion) && is6x) {
3259                 version = engineVersion;
3260             }
3261             for (Artifact launcherArtifact : resolve(PROVIDER_DEP_GID, PROVIDER_DEP_AID, version, null, "jar")) {
3262                 String key = launcherArtifact.getGroupId() + ":" + launcherArtifact.getArtifactId();
3263                 if (providerArtifacts.containsKey(key)) {
3264                     providerArtifacts.put(key, launcherArtifact);
3265                 }
3266             }
3267         }
3268 
3269         private Set<Artifact> resolve(String groupId, String artifactId, String version, String classifier, String type)
3270                 throws MojoExecutionException {
3271             // FIXME will be different with 3 and testng
3272             ArtifactHandler handler = junitPlatformArtifact != null
3273                     ? junitPlatformArtifact.getArtifactHandler()
3274                     : junitArtifact != null
3275                             ? junitArtifact.getArtifactHandler()
3276                             : testNgArtifact != null ? testNgArtifact.getArtifactHandler() : null;
3277             Artifact artifact = new DefaultArtifact(groupId, artifactId, version, null, type, classifier, handler);
3278             consoleLogger.debug("Resolving artifact " + groupId + ":" + artifactId + ":" + version);
3279             Set<Artifact> r = surefireDependencyResolver.resolveArtifacts(
3280                     session.getRepositorySession(), project.getRemoteProjectRepositories(), artifact);
3281             consoleLogger.debug("Resolved artifact " + groupId + ":" + artifactId + ":" + version + " to " + r);
3282             return r;
3283         }
3284 
3285         private boolean hasDependencyJupiterAPI(Map<String, Artifact> dependencies) {
3286             return dependencies.containsKey("org.junit.jupiter:junit-jupiter-api");
3287         }
3288 
3289         private boolean hasDependencyPlatformEngine(Map<String, Artifact> dependencies) {
3290             return dependencies.entrySet().stream()
3291                     .anyMatch(stringArtifactEntry ->
3292                             stringArtifactEntry.getKey().equals("org.junit.platform:junit-platform-engine"));
3293         }
3294     }
3295 
3296     // TODO why an enclosing class on the top do we real need this shading??
3297     @SuppressWarnings("checkstyle:parameternumber")
3298     final class JUnitPlatformProviderShadefireInfo extends JUnitPlatformProviderInfo {
3299 
3300         JUnitPlatformProviderShadefireInfo(
3301                 Artifact junitPlatformRunnerArtifact,
3302                 Artifact junitPlatformArtifact,
3303                 @Nonnull TestClassPath testClasspath,
3304                 Artifact junitArtifact,
3305                 Artifact booterArtifact,
3306                 SurefireDependencyResolver surefireDependencyResolver,
3307                 MavenSession session,
3308                 MavenProject project,
3309                 PluginDescriptor pluginDescriptor,
3310                 Map<String, Artifact> pluginArtifactMap,
3311                 ConsoleLogger consoleLogger,
3312                 Artifact testNgArtifact) {
3313             super(
3314                     junitPlatformRunnerArtifact,
3315                     junitPlatformArtifact,
3316                     testClasspath,
3317                     junitArtifact,
3318                     booterArtifact,
3319                     surefireDependencyResolver,
3320                     session,
3321                     project,
3322                     pluginDescriptor,
3323                     pluginArtifactMap,
3324                     consoleLogger,
3325                     testNgArtifact);
3326         }
3327 
3328         @Override
3329         public boolean isApplicable() {
3330             // newer discover this provider automatically
3331             return false;
3332         }
3333 
3334         @Override
3335         @Nonnull
3336         public String getProviderName() {
3337             return "org.apache.maven.shadefire.surefire.junitplatform.JUnitPlatformProvider";
3338         }
3339 
3340         @Override
3341         protected String getProviderArtifactName() {
3342             return "surefire-shadefire";
3343         }
3344 
3345         @Override
3346         protected void alignProviderVersions(
3347                 Map<String, Artifact> providerArtifacts, String engineVersion, Artifact engineApi)
3348                 throws MojoExecutionException {
3349             // shadefire is used as booter we can not provide additional dependencies,
3350             // so we need add a launcher here
3351             providerArtifacts.put("org.junit.platform:junit-platform-launcher", null);
3352             super.alignProviderVersions(providerArtifacts, engineVersion, engineApi);
3353         }
3354     }
3355 
3356     File createSurefireBootDirectoryInBuild() {
3357         File tmp = new File(getProjectBuildDirectory(), getTempDir());
3358         //noinspection ResultOfMethodCallIgnored
3359         tmp.mkdirs();
3360         return tmp;
3361     }
3362 
3363     File createSurefireBootDirectoryInTemp() {
3364         try {
3365             return Files.createTempDirectory(getTempDir()).toFile();
3366         } catch (IOException e) {
3367             return createSurefireBootDirectoryInBuild();
3368         }
3369     }
3370 
3371     @Override
3372     public String getLocalRepositoryPath() {
3373         return Optional.ofNullable(
3374                         session.getRepositorySession().getLocalRepository().getBasedir())
3375                 .map(File::getAbsolutePath)
3376                 .orElse(".");
3377     }
3378 
3379     public Properties getSystemProperties() {
3380         return systemProperties;
3381     }
3382 
3383     @SuppressWarnings("UnusedDeclaration")
3384     public void setSystemProperties(Properties systemProperties) {
3385         this.systemProperties = systemProperties;
3386     }
3387 
3388     public Map<String, String> getSystemPropertyVariables() {
3389         return systemPropertyVariables;
3390     }
3391 
3392     @SuppressWarnings("UnusedDeclaration")
3393     public void setSystemPropertyVariables(Map<String, String> systemPropertyVariables) {
3394         this.systemPropertyVariables = systemPropertyVariables;
3395     }
3396 
3397     /**
3398      * List of System properties, loaded from a file, to pass to the JUnit tests.
3399      *
3400      * @since 2.8.2
3401      */
3402     public abstract File getSystemPropertiesFile();
3403 
3404     @SuppressWarnings("UnusedDeclaration")
3405     public abstract void setSystemPropertiesFile(File systemPropertiesFile);
3406 
3407     private Properties getProperties() {
3408         return properties;
3409     }
3410 
3411     public void setProperties(Properties properties) {
3412         this.properties = properties;
3413     }
3414 
3415     public Map<String, Artifact> getPluginArtifactMap() {
3416         return pluginArtifactMap;
3417     }
3418 
3419     @SuppressWarnings("UnusedDeclaration")
3420     public void setPluginArtifactMap(Map<String, Artifact> pluginArtifactMap) {
3421         this.pluginArtifactMap = pluginArtifactMap;
3422     }
3423 
3424     public Map<String, Artifact> getProjectArtifactMap() {
3425         return projectArtifactMap;
3426     }
3427 
3428     @SuppressWarnings("UnusedDeclaration")
3429     public void setProjectArtifactMap(Map<String, Artifact> projectArtifactMap) {
3430         this.projectArtifactMap = projectArtifactMap;
3431     }
3432 
3433     public String getReportNameSuffix() {
3434         return reportNameSuffix;
3435     }
3436 
3437     @SuppressWarnings("UnusedDeclaration")
3438     public void setReportNameSuffix(String reportNameSuffix) {
3439         this.reportNameSuffix = reportNameSuffix;
3440     }
3441 
3442     public boolean isRedirectTestOutputToFile() {
3443         return redirectTestOutputToFile;
3444     }
3445 
3446     @SuppressWarnings("UnusedDeclaration")
3447     public void setRedirectTestOutputToFile(boolean redirectTestOutputToFile) {
3448         this.redirectTestOutputToFile = redirectTestOutputToFile;
3449     }
3450 
3451     public boolean getFailIfNoTests() {
3452         return failIfNoTests;
3453     }
3454 
3455     public void setFailIfNoTests(boolean failIfNoTests) {
3456         this.failIfNoTests = failIfNoTests;
3457     }
3458 
3459     public String getJvm() {
3460         return jvm;
3461     }
3462 
3463     public String getArgLine() {
3464         return argLine;
3465     }
3466 
3467     @SuppressWarnings("UnusedDeclaration")
3468     public void setArgLine(String argLine) {
3469         this.argLine = argLine;
3470     }
3471 
3472     public Map<String, String> getEnvironmentVariables() {
3473         return environmentVariables;
3474     }
3475 
3476     @SuppressWarnings("UnusedDeclaration")
3477     public void setEnvironmentVariables(Map<String, String> environmentVariables) {
3478         this.environmentVariables = environmentVariables;
3479     }
3480 
3481     public File getWorkingDirectory() {
3482         return workingDirectory;
3483     }
3484 
3485     @SuppressWarnings("UnusedDeclaration")
3486     public void setWorkingDirectory(File workingDirectory) {
3487         this.workingDirectory = workingDirectory;
3488     }
3489 
3490     public boolean isChildDelegation() {
3491         return childDelegation;
3492     }
3493 
3494     @SuppressWarnings("UnusedDeclaration")
3495     public void setChildDelegation(boolean childDelegation) {
3496         this.childDelegation = childDelegation;
3497     }
3498 
3499     public String getGroups() {
3500         return groups;
3501     }
3502 
3503     @SuppressWarnings("UnusedDeclaration")
3504     public void setGroups(String groups) {
3505         this.groups = groups;
3506     }
3507 
3508     public String getExcludedGroups() {
3509         return excludedGroups;
3510     }
3511 
3512     @SuppressWarnings("UnusedDeclaration")
3513     public void setExcludedGroups(String excludedGroups) {
3514         this.excludedGroups = excludedGroups;
3515     }
3516 
3517     public String getJunitArtifactName() {
3518         return junitArtifactName;
3519     }
3520 
3521     @SuppressWarnings("UnusedDeclaration")
3522     public void setJunitArtifactName(String junitArtifactName) {
3523         this.junitArtifactName = junitArtifactName;
3524     }
3525 
3526     public String getTestNGArtifactName() {
3527         return testNGArtifactName;
3528     }
3529 
3530     @SuppressWarnings("UnusedDeclaration")
3531     public void setTestNGArtifactName(String testNGArtifactName) {
3532         this.testNGArtifactName = testNGArtifactName;
3533     }
3534 
3535     public int getThreadCount() {
3536         return threadCount;
3537     }
3538 
3539     @SuppressWarnings("UnusedDeclaration")
3540     public void setThreadCount(int threadCount) {
3541         this.threadCount = threadCount;
3542     }
3543 
3544     public boolean getPerCoreThreadCount() {
3545         return perCoreThreadCount;
3546     }
3547 
3548     @SuppressWarnings("UnusedDeclaration")
3549     public void setPerCoreThreadCount(boolean perCoreThreadCount) {
3550         this.perCoreThreadCount = perCoreThreadCount;
3551     }
3552 
3553     public boolean getUseUnlimitedThreads() {
3554         return useUnlimitedThreads;
3555     }
3556 
3557     @SuppressWarnings("UnusedDeclaration")
3558     public void setUseUnlimitedThreads(boolean useUnlimitedThreads) {
3559         this.useUnlimitedThreads = useUnlimitedThreads;
3560     }
3561 
3562     public String getParallel() {
3563         return parallel;
3564     }
3565 
3566     @SuppressWarnings("UnusedDeclaration")
3567     public void setParallel(String parallel) {
3568         this.parallel = parallel;
3569     }
3570 
3571     public boolean isParallelOptimized() {
3572         return parallelOptimized;
3573     }
3574 
3575     @SuppressWarnings("UnusedDeclaration")
3576     public void setParallelOptimized(boolean parallelOptimized) {
3577         this.parallelOptimized = parallelOptimized;
3578     }
3579 
3580     public int getThreadCountSuites() {
3581         return threadCountSuites;
3582     }
3583 
3584     public void setThreadCountSuites(int threadCountSuites) {
3585         this.threadCountSuites = threadCountSuites;
3586     }
3587 
3588     public int getThreadCountClasses() {
3589         return threadCountClasses;
3590     }
3591 
3592     public void setThreadCountClasses(int threadCountClasses) {
3593         this.threadCountClasses = threadCountClasses;
3594     }
3595 
3596     public int getThreadCountMethods() {
3597         return threadCountMethods;
3598     }
3599 
3600     public void setThreadCountMethods(int threadCountMethods) {
3601         this.threadCountMethods = threadCountMethods;
3602     }
3603 
3604     public boolean isTrimStackTrace() {
3605         return trimStackTrace;
3606     }
3607 
3608     @SuppressWarnings("UnusedDeclaration")
3609     public void setTrimStackTrace(boolean trimStackTrace) {
3610         this.trimStackTrace = trimStackTrace;
3611     }
3612 
3613     public boolean isEnableAssertions() {
3614         return enableAssertions;
3615     }
3616 
3617     public boolean effectiveIsEnableAssertions() {
3618         if (getArgLine() != null) {
3619             List<String> args = asList(getArgLine().split(" "));
3620             if (args.contains("-da") || args.contains("-disableassertions")) {
3621                 return false;
3622             }
3623         }
3624         return isEnableAssertions();
3625     }
3626 
3627     @SuppressWarnings("UnusedDeclaration")
3628     public void setEnableAssertions(boolean enableAssertions) {
3629         this.enableAssertions = enableAssertions;
3630     }
3631 
3632     public boolean isEnableOutErrElements() {
3633         return enableOutErrElements;
3634     }
3635 
3636     @SuppressWarnings("UnusedDeclaration")
3637     public void setEnableOutErrElements(boolean enableOutErrElements) {
3638         this.enableOutErrElements = enableOutErrElements;
3639     }
3640 
3641     public boolean isEnablePropertiesElement() {
3642         return enablePropertiesElement;
3643     }
3644 
3645     public boolean isReportTestTimestamp() {
3646         return reportTestTimestamp;
3647     }
3648 
3649     @SuppressWarnings("UnusedDeclaration")
3650     public void setEnablePropertiesElement(boolean enablePropertiesElement) {
3651         this.enablePropertiesElement = enablePropertiesElement;
3652     }
3653 
3654     public MavenSession getSession() {
3655         return session;
3656     }
3657 
3658     @SuppressWarnings("UnusedDeclaration")
3659     public void setSession(MavenSession session) {
3660         this.session = session;
3661     }
3662 
3663     public String getObjectFactory() {
3664         return objectFactory;
3665     }
3666 
3667     @SuppressWarnings("UnusedDeclaration")
3668     public void setObjectFactory(String objectFactory) {
3669         this.objectFactory = objectFactory;
3670     }
3671 
3672     public ToolchainManager getToolchainManager() {
3673         return toolchainManager;
3674     }
3675 
3676     @SuppressWarnings("UnusedDeclaration")
3677     public void setToolchainManager(ToolchainManager toolchainManager) {
3678         this.toolchainManager = toolchainManager;
3679     }
3680 
3681     public boolean isMavenParallel() {
3682         return parallelMavenExecution != null && parallelMavenExecution;
3683     }
3684 
3685     public String[] getDependenciesToScan() {
3686         return dependenciesToScan;
3687     }
3688 
3689     public void setDependenciesToScan(String[] dependenciesToScan) {
3690         this.dependenciesToScan = dependenciesToScan;
3691     }
3692 
3693     @SuppressWarnings("UnusedDeclaration")
3694     void setPluginDescriptor(PluginDescriptor pluginDescriptor) {
3695         this.pluginDescriptor = pluginDescriptor;
3696     }
3697 
3698     public PluginDescriptor getPluginDescriptor() {
3699         return pluginDescriptor;
3700     }
3701 
3702     public MavenProject getProject() {
3703         return project;
3704     }
3705 
3706     @SuppressWarnings("UnusedDeclaration")
3707     public void setProject(MavenProject project) {
3708         this.project = project;
3709     }
3710 
3711     @Override
3712     public File getTestSourceDirectory() {
3713         return testSourceDirectory;
3714     }
3715 
3716     @Override
3717     public void setTestSourceDirectory(File testSourceDirectory) {
3718         this.testSourceDirectory = testSourceDirectory;
3719     }
3720 
3721     public String getForkCount() {
3722         return forkCount;
3723     }
3724 
3725     public boolean isReuseForks() {
3726         return reuseForks;
3727     }
3728 
3729     public String[] getAdditionalClasspathElements() {
3730         return additionalClasspathElements;
3731     }
3732 
3733     public void setAdditionalClasspathElements(String[] additionalClasspathElements) {
3734         this.additionalClasspathElements = additionalClasspathElements;
3735     }
3736 
3737     public String[] getClasspathDependencyExcludes() {
3738         return classpathDependencyExcludes;
3739     }
3740 
3741     public void setClasspathDependencyExcludes(String[] classpathDependencyExcludes) {
3742         this.classpathDependencyExcludes = classpathDependencyExcludes;
3743     }
3744 
3745     public String getClasspathDependencyScopeExclude() {
3746         return classpathDependencyScopeExclude;
3747     }
3748 
3749     public void setClasspathDependencyScopeExclude(String classpathDependencyScopeExclude) {
3750         this.classpathDependencyScopeExclude = classpathDependencyScopeExclude;
3751     }
3752 
3753     public File getProjectBuildDirectory() {
3754         return projectBuildDirectory;
3755     }
3756 
3757     public void setProjectBuildDirectory(File projectBuildDirectory) {
3758         this.projectBuildDirectory = projectBuildDirectory;
3759     }
3760 
3761     protected void logDebugOrCliShowErrors(String s) {
3762         SurefireHelper.logDebugOrCliShowErrors(s, getConsoleLogger(), cli);
3763     }
3764 
3765     public Map<String, String> getJdkToolchain() {
3766         return jdkToolchain;
3767     }
3768 
3769     public void setJdkToolchain(Map<String, String> jdkToolchain) {
3770         this.jdkToolchain = jdkToolchain;
3771     }
3772 
3773     public String getTempDir() {
3774         return tempDir;
3775     }
3776 
3777     public void setTempDir(String tempDir) {
3778         this.tempDir = tempDir;
3779     }
3780 
3781     private static final class ClasspathCache {
3782         private final Map<String, Classpath> classpaths = new HashMap<>(4);
3783 
3784         private Classpath getCachedClassPath(@Nonnull String artifactId) {
3785             return classpaths.get(artifactId);
3786         }
3787 
3788         private void setCachedClasspath(@Nonnull String key, @Nonnull Classpath classpath) {
3789             classpaths.put(key, classpath);
3790         }
3791 
3792         private Classpath setCachedClasspath(@Nonnull String key, @Nonnull Set<Artifact> artifacts) {
3793             Collection<String> files = new ArrayList<>();
3794             for (Artifact artifact : artifacts) {
3795                 files.add(artifact.getFile().getAbsolutePath());
3796             }
3797             Classpath classpath = new Classpath(files);
3798             setCachedClasspath(key, classpath);
3799             return classpath;
3800         }
3801     }
3802 
3803     /**
3804      * Determines whether the plugin should fail if no tests found to run.
3805      */
3806     enum PluginFailureReason {
3807         NONE,
3808         COULD_NOT_RUN_SPECIFIED_TESTS,
3809         COULD_NOT_RUN_DEFAULT_TESTS,
3810     }
3811 }