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