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