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