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         if (isSkipExecution()) {
880             getConsoleLogger().info("Tests are skipped.");
881             return;
882         }
883 
884         cli = commandLineOptions();
885         // Stuff that should have been final
886         setupStuff();
887         Platform platform = PLATFORM.withJdkExecAttributesForTests(getEffectiveJvm());
888         Thread shutdownThread = new Thread(platform::setShutdownState);
889         addShutDownHook(shutdownThread);
890         try {
891             if (verifyParameters() && !hasExecutedBefore()) {
892                 DefaultScanResult scan = scanForTestClasses();
893                 if (!hasSuiteXmlFiles() && scan.isEmpty()) {
894                     switch (getEffectiveFailIfNoTests()) {
895                         case COULD_NOT_RUN_DEFAULT_TESTS:
896                             throw new MojoFailureException(
897                                     "No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.)");
898                         case COULD_NOT_RUN_SPECIFIED_TESTS:
899                             throw new MojoFailureException("No tests matching pattern \""
900                                     + getSpecificTests().toString()
901                                     + "\" were executed! (Set "
902                                     + "-D" + getPluginName()
903                                     + ".failIfNoSpecifiedTests=false to ignore this error.)");
904                         default:
905                             handleSummary(noTestsRun(), null);
906                             return;
907                     }
908                 }
909                 logReportsDirectory();
910                 executeAfterPreconditionsChecked(scan, platform);
911             }
912         } finally {
913             platform.clearShutdownState();
914             removeShutdownHook(shutdownThread);
915         }
916     }
917 
918     void setLogger(Logger logger) {
919         this.logger = logger;
920     }
921 
922     void setSurefireDependencyResolver(SurefireDependencyResolver surefireDependencyResolver) {
923         this.surefireDependencyResolver = surefireDependencyResolver;
924     }
925 
926     @Nonnull
927     protected final PluginConsoleLogger getConsoleLogger() {
928         if (consoleLogger == null) {
929             synchronized (this) {
930                 if (consoleLogger == null) {
931                     consoleLogger = new PluginConsoleLogger(logger);
932                 }
933             }
934         }
935         return consoleLogger;
936     }
937 
938     private static <T extends ToolchainManager> Toolchain getToolchainMaven33x(
939             Class<T> toolchainManagerType, T toolchainManager, MavenSession session, Map<String, String> toolchainArgs)
940             throws MojoFailureException {
941         Method getToolchainsMethod =
942                 tryGetMethod(toolchainManagerType, "getToolchains", MavenSession.class, String.class, Map.class);
943         if (getToolchainsMethod != null) {
944             //noinspection unchecked
945             List<Toolchain> tcs =
946                     invokeMethodWithArray(toolchainManager, getToolchainsMethod, session, "jdk", toolchainArgs);
947             if (tcs.isEmpty()) {
948                 throw new MojoFailureException(
949                         "Requested toolchain specification did not match any configured toolchain: " + toolchainArgs);
950             }
951             return tcs.get(0);
952         }
953         return null;
954     }
955 
956     // TODO remove the part with ToolchainManager lookup once we depend on
957     // 3.0.9 (have it as prerequisite). Define as regular component field then.
958     private Toolchain getToolchain() throws MojoFailureException {
959         Toolchain tc = null;
960 
961         if (getJdkToolchain() != null) {
962             tc = getToolchainMaven33x(ToolchainManager.class, getToolchainManager(), getSession(), getJdkToolchain());
963         }
964 
965         if (tc == null) {
966             tc = getToolchainManager().getToolchainFromBuildContext("jdk", getSession());
967         }
968 
969         return tc;
970     }
971 
972     private void setupStuff() throws MojoFailureException {
973 
974         if (getBooterArtifact() == null) {
975             throw new RuntimeException("Unable to locate surefire-booter in the list of plugin artifacts");
976         }
977 
978         if (getToolchainManager() != null) {
979             toolchain = getToolchain();
980         }
981     }
982 
983     @Nonnull
984     private DefaultScanResult scanForTestClasses() throws MojoFailureException {
985         DefaultScanResult scan = scanDirectories();
986         DefaultScanResult scanDeps = scanDependencies();
987         return scan.append(scanDeps);
988     }
989 
990     private DefaultScanResult scanDirectories() throws MojoFailureException {
991         DirectoryScanner scanner = new DirectoryScanner(getTestClassesDirectory(), getIncludedAndExcludedTests());
992         return scanner.scan();
993     }
994 
995     List<Artifact> getProjectTestArtifacts() {
996         return project.getTestArtifacts();
997     }
998 
999     DefaultScanResult scanDependencies() throws MojoFailureException {
1000         if (getDependenciesToScan() == null) {
1001             return null;
1002         } else {
1003             try {
1004                 DefaultScanResult result = null;
1005 
1006                 List<Artifact> dependenciesToScan = filter(getProjectTestArtifacts(), asList(getDependenciesToScan()));
1007 
1008                 for (Artifact artifact : dependenciesToScan) {
1009                     String type = artifact.getType();
1010                     File out = artifact.getFile();
1011                     if (out == null
1012                             || !out.exists()
1013                             || !("jar".equals(type)
1014                                     || out.isDirectory()
1015                                     || out.getName().endsWith(".jar"))) {
1016                         continue;
1017                     }
1018 
1019                     if (out.isFile()) {
1020                         DependencyScanner scanner =
1021                                 new DependencyScanner(singletonList(out), getIncludedAndExcludedTests());
1022                         result = result == null ? scanner.scan() : result.append(scanner.scan());
1023                     } else if (out.isDirectory()) {
1024                         DirectoryScanner scanner = new DirectoryScanner(out, getIncludedAndExcludedTests());
1025                         result = result == null ? scanner.scan() : result.append(scanner.scan());
1026                     }
1027                 }
1028 
1029                 return result;
1030             } catch (Exception e) {
1031                 throw new MojoFailureException(e.getLocalizedMessage(), e);
1032             }
1033         }
1034     }
1035 
1036     boolean verifyParameters() throws MojoFailureException, MojoExecutionException {
1037         setProperties(new SurefireProperties(getProperties()));
1038 
1039         String jvmToUse = getJvm();
1040         if (toolchain != null) {
1041             getConsoleLogger().info("Toolchain in maven-" + getPluginName() + "-plugin: " + toolchain);
1042             if (jvmToUse != null) {
1043                 getConsoleLogger().warning("Toolchains are ignored, 'jvm' parameter is set to " + jvmToUse);
1044             }
1045         }
1046 
1047         if (!getTestClassesDirectory().exists()
1048                 && (getDependenciesToScan() == null || getDependenciesToScan().length == 0)) {
1049             if (getFailIfNoTests()) {
1050                 throw new MojoFailureException("No tests to run!");
1051             }
1052             getConsoleLogger().info("No tests to run.");
1053         } else {
1054             ensureEnableProcessChecker();
1055             ensureWorkingDirectoryExists();
1056             ensureParallelRunningCompatibility();
1057             warnIfUselessUseSystemClassLoaderParameter();
1058             warnIfDefunctGroupsCombinations();
1059             warnIfRerunClashes();
1060             warnIfWrongShutdownValue();
1061             warnIfNotApplicableSkipAfterFailureCount();
1062             warnIfIllegalTempDir();
1063             warnIfForkCountIsZero();
1064             warnIfIllegalFailOnFlakeCount();
1065             printDefaultSeedIfNecessary();
1066         }
1067         return true;
1068     }
1069 
1070     private void warnIfForkCountIsZero() {
1071         if ("0".equals(getForkCount())) {
1072             getConsoleLogger()
1073                     .warning("The parameter forkCount should likely not be 0. Forking a JVM for tests "
1074                             + "improves test accuracy. Ensure to have a <forkCount> >= 1.");
1075         }
1076     }
1077 
1078     private void executeAfterPreconditionsChecked(@Nonnull DefaultScanResult scanResult, @Nonnull Platform platform)
1079             throws MojoExecutionException, MojoFailureException {
1080         TestClassPath testClasspath = generateTestClasspath();
1081         List<ProviderInfo> providers = createProviders(testClasspath);
1082         ResolvePathResultWrapper wrapper =
1083                 findModuleDescriptor(platform.getJdkExecAttributesForTests().getJdkHome());
1084 
1085         RunResult current = noTestsRun();
1086 
1087         Exception firstForkException = null;
1088         for (ProviderInfo provider : providers) {
1089             try {
1090                 current = current.aggregate(executeProvider(provider, scanResult, testClasspath, platform, wrapper));
1091             } catch (SurefireBooterForkException | SurefireExecutionException | TestSetFailedException e) {
1092                 if (firstForkException == null) {
1093                     firstForkException = e;
1094                 }
1095             }
1096         }
1097 
1098         if (firstForkException != null) {
1099             current = failure(current, firstForkException);
1100         }
1101 
1102         handleSummary(current, firstForkException);
1103     }
1104 
1105     protected List<ProviderInfo> createProviders(TestClassPath testClasspath) throws MojoExecutionException {
1106         Artifact junitDepArtifact = getJunitDepArtifact();
1107         return providerDetector.resolve(
1108                 new DynamicProviderInfo(null),
1109                 new JUnitPlatformProviderInfo(getJUnitPlatformRunnerArtifact(), getJUnit5Artifact(), testClasspath),
1110                 new TestNgProviderInfo(getTestNgArtifact()),
1111                 new JUnitCoreProviderInfo(getJunitArtifact(), junitDepArtifact),
1112                 new JUnit4ProviderInfo(getJunitArtifact(), junitDepArtifact),
1113                 new JUnit3ProviderInfo());
1114     }
1115 
1116     private SurefireProperties setupProperties() {
1117         SurefireProperties sysProps = null;
1118         try {
1119             sysProps = SurefireProperties.loadProperties(getSystemPropertiesFile());
1120         } catch (IOException e) {
1121             String msg = "The file '" + getSystemPropertiesFile().getAbsolutePath() + "' can't be read.";
1122             if (getConsoleLogger().isDebugEnabled()) {
1123                 getConsoleLogger().debug(msg, e);
1124             } else {
1125                 getConsoleLogger().warning(msg);
1126             }
1127         }
1128 
1129         SurefireProperties result = SurefireProperties.calculateEffectiveProperties(
1130                 getSystemProperties(), getSystemPropertyVariables(), getUserProperties(), sysProps);
1131 
1132         result.setProperty("basedir", getBasedir().getAbsolutePath());
1133         result.setProperty("localRepository", getLocalRepositoryPath());
1134         if (isForking()) {
1135             for (Object o : result.propertiesThatCannotBeSetASystemProperties()) {
1136                 if (getArgLine() == null || !getArgLine().contains("-D" + o + "=")) {
1137                     getConsoleLogger()
1138                             .warning(o + " cannot be set as system property, use <argLine>-D" + o
1139                                     + "=...</argLine> instead");
1140                 }
1141             }
1142             for (Object systemPropertyMatchingArgLine : systemPropertiesMatchingArgLine(result)) {
1143                 getConsoleLogger()
1144                         .warning("The system property "
1145                                 + systemPropertyMatchingArgLine
1146                                 + " is configured twice! "
1147                                 + "The property appears in <argLine/> and any of <systemPropertyVariables/>, "
1148                                 + "<systemProperties/> or user property.");
1149             }
1150         } else {
1151             result.setProperty("user.dir", getWorkingDirectory().getAbsolutePath());
1152         }
1153 
1154         if (getConsoleLogger().isDebugEnabled()) {
1155             showToLog(result, getConsoleLogger());
1156         }
1157 
1158         return result;
1159     }
1160 
1161     private Set<Object> systemPropertiesMatchingArgLine(SurefireProperties result) {
1162         Set<Object> intersection = new HashSet<>();
1163         if (isNotBlank(getArgLine())) {
1164             for (Object systemProperty : result.getStringKeySet()) {
1165                 if (getArgLine().contains("-D" + systemProperty + "=")) {
1166                     intersection.add(systemProperty);
1167                 }
1168             }
1169 
1170             Set<Object> ignored = result.propertiesThatCannotBeSetASystemProperties();
1171             intersection.removeAll(ignored);
1172         }
1173         return intersection;
1174     }
1175 
1176     private void showToLog(SurefireProperties props, ConsoleLogger log) {
1177         for (Object key : props.getStringKeySet()) {
1178             String value = props.getProperty((String) key);
1179             log.debug("Setting system property [" + key + "]=[" + value + "]");
1180         }
1181     }
1182 
1183     @Nonnull
1184     private RunResult executeProvider(
1185             @Nonnull ProviderInfo provider,
1186             @Nonnull DefaultScanResult scanResult,
1187             @Nonnull TestClassPath testClasspathWrapper,
1188             @Nonnull Platform platform,
1189             @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult)
1190             throws MojoExecutionException, MojoFailureException, SurefireExecutionException,
1191                     SurefireBooterForkException, TestSetFailedException {
1192         getConsoleLogger().debug("Using the provider " + provider.getProviderName());
1193         SurefireProperties effectiveProperties = setupProperties();
1194         ClassLoaderConfiguration classLoaderConfiguration = getClassLoaderConfiguration();
1195         provider.addProviderProperties();
1196         RunOrderParameters runOrderParameters =
1197                 new RunOrderParameters(getRunOrder(), getStatisticsFile(getConfigChecksum()), getRunOrderRandomSeed());
1198 
1199         if (isNotForking()) {
1200             Properties originalSystemProperties =
1201                     (Properties) System.getProperties().clone();
1202             try {
1203                 createCopyAndReplaceForkNumPlaceholder(effectiveProperties, 1).copyToSystemProperties();
1204                 getConsoleLogger().debug("Using in-process starter");
1205                 InPluginVMSurefireStarter surefireStarter = createInprocessStarter(
1206                         provider,
1207                         classLoaderConfiguration,
1208                         runOrderParameters,
1209                         scanResult,
1210                         platform,
1211                         testClasspathWrapper);
1212                 return surefireStarter.runSuitesInProcess(scanResult);
1213             } finally {
1214                 System.setProperties(originalSystemProperties);
1215             }
1216         } else {
1217             ForkConfiguration forkConfiguration = createForkConfiguration(platform, resolvedJavaModularityResult);
1218             if (getConsoleLogger().isDebugEnabled()) {
1219                 getConsoleLogger()
1220                         .debug("Using fork starter with configuration implementation "
1221                                 + forkConfiguration.getClass().getName());
1222                 showMap(getEnvironmentVariables(), "environment variable");
1223                 showArray(getExcludedEnvironmentVariables(), "excluded environment variable");
1224             }
1225 
1226             Properties originalSystemProperties =
1227                     (Properties) System.getProperties().clone();
1228             ForkStarter forkStarter = null;
1229             try {
1230                 forkStarter = createForkStarter(
1231                         provider,
1232                         forkConfiguration,
1233                         classLoaderConfiguration,
1234                         runOrderParameters,
1235                         getConsoleLogger(),
1236                         scanResult,
1237                         testClasspathWrapper,
1238                         platform,
1239                         resolvedJavaModularityResult);
1240 
1241                 return forkStarter.run(effectiveProperties, scanResult);
1242             } catch (SurefireBooterForkException e) {
1243                 forkStarter.killOrphanForks();
1244                 throw e;
1245             } finally {
1246                 System.setProperties(originalSystemProperties);
1247                 cleanupForkConfiguration(forkConfiguration);
1248             }
1249         }
1250     }
1251 
1252     public static SurefireProperties createCopyAndReplaceForkNumPlaceholder(
1253             SurefireProperties effectiveSystemProperties, int threadNumber) {
1254         SurefireProperties filteredProperties = new SurefireProperties((KeyValueSource) effectiveSystemProperties);
1255         for (Entry<Object, Object> entry : effectiveSystemProperties.entrySet()) {
1256             if (entry.getValue() instanceof String) {
1257                 String value = (String) entry.getValue();
1258                 filteredProperties.put(entry.getKey(), replaceThreadNumberPlaceholders(value, threadNumber));
1259             }
1260         }
1261         return filteredProperties;
1262     }
1263 
1264     protected void cleanupForkConfiguration(ForkConfiguration forkConfiguration) {
1265         if (!getConsoleLogger().isDebugEnabled() && forkConfiguration != null) {
1266             File tempDirectory = forkConfiguration.getTempDirectory();
1267             try {
1268                 FileUtils.deleteDirectory(tempDirectory);
1269             } catch (IOException e) {
1270                 getConsoleLogger()
1271                         .warning("Could not delete temp directory " + tempDirectory + " because " + e.getMessage());
1272             }
1273         }
1274     }
1275 
1276     protected void logReportsDirectory() {
1277         logDebugOrCliShowErrors(capitalizeFirstLetter(getPluginName()) + " report directory: " + getReportsDirectory());
1278     }
1279 
1280     private boolean existsModuleDescriptor(ResolvePathResultWrapper resolvedJavaModularityResult) {
1281         return resolvedJavaModularityResult.getResolvePathResult() != null;
1282     }
1283 
1284     private ResolvePathResultWrapper findModuleDescriptor(File jdkHome) {
1285         ResolvePathResultWrapper test = findModuleDescriptor(jdkHome, getTestClassesDirectory(), false);
1286         return test.getResolvePathResult() == null ? findModuleDescriptor(jdkHome, getMainBuildPath(), true) : test;
1287     }
1288 
1289     private ResolvePathResultWrapper findModuleDescriptor(File jdkHome, File buildPath, boolean isMainDescriptor) {
1290         boolean isJpmsModule =
1291                 buildPath.isDirectory() ? new File(buildPath, "module-info.class").exists() : isModule(buildPath);
1292 
1293         if (!isJpmsModule) {
1294             return new ResolvePathResultWrapper(null, isMainDescriptor);
1295         }
1296 
1297         try {
1298             ResolvePathRequest<?> request = ResolvePathRequest.ofFile(buildPath).setJdkHome(jdkHome);
1299             ResolvePathResult result = getLocationManager().resolvePath(request);
1300             boolean isEmpty = result.getModuleNameSource() == null;
1301             return new ResolvePathResultWrapper(isEmpty ? null : result, isMainDescriptor);
1302         } catch (Exception e) {
1303             return new ResolvePathResultWrapper(null, isMainDescriptor);
1304         }
1305     }
1306 
1307     private static boolean isModule(File jar) {
1308         try (ZipFile zip = new ZipFile(jar)) {
1309             return zip.getEntry("module-info.class") != null;
1310         } catch (IOException e) {
1311             return false;
1312         }
1313     }
1314 
1315     private boolean canExecuteProviderWithModularPath(
1316             @Nonnull Platform platform, @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult) {
1317         return useModulePath()
1318                 && platform.getJdkExecAttributesForTests().isJava9AtLeast()
1319                 && existsModuleDescriptor(resolvedJavaModularityResult);
1320     }
1321 
1322     /**
1323      * Converts old TestNG configuration parameters over to new properties based configuration
1324      * method. (if any are defined the old way)
1325      */
1326     private void convertTestNGParameters() throws MojoExecutionException {
1327         if (this.getParallel() != null) {
1328             getProperties().setProperty(ProviderParameterNames.PARALLEL_PROP, this.getParallel());
1329         }
1330         convertGroupParameters();
1331 
1332         if (this.getThreadCount() > 0) {
1333             getProperties()
1334                     .setProperty(ProviderParameterNames.THREADCOUNT_PROP, Integer.toString(this.getThreadCount()));
1335         }
1336         if (this.getObjectFactory() != null) {
1337             getProperties().setProperty("objectfactory", this.getObjectFactory());
1338         }
1339         if (this.getTestClassesDirectory() != null) {
1340             getProperties()
1341                     .setProperty(
1342                             "testng.test.classpath", getTestClassesDirectory().getAbsolutePath());
1343         }
1344 
1345         Artifact testNgArtifact = getTestNgArtifact();
1346         if (testNgArtifact != null) {
1347             DefaultArtifactVersion defaultArtifactVersion = new DefaultArtifactVersion(testNgArtifact.getVersion());
1348             getProperties()
1349                     .setProperty(
1350                             "testng.configurator", getConfiguratorName(defaultArtifactVersion, getConsoleLogger()));
1351         }
1352     }
1353 
1354     private static String getConfiguratorName(ArtifactVersion version, PluginConsoleLogger log)
1355             throws MojoExecutionException {
1356         try {
1357             VersionRange range = VersionRange.createFromVersionSpec("[4.7,5.2)");
1358             if (range.containsVersion(version)) {
1359                 return "org.apache.maven.surefire.testng.conf.TestNG4751Configurator";
1360             }
1361             range = VersionRange.createFromVersionSpec("[5.2,5.3)");
1362             if (range.containsVersion(version)) {
1363                 return "org.apache.maven.surefire.testng.conf.TestNG52Configurator";
1364             }
1365             range = VersionRange.createFromVersionSpec("[5.3,5.10)");
1366             if (range.containsVersion(version)) {
1367                 return "org.apache.maven.surefire.testng.conf.TestNGMapConfigurator";
1368             }
1369             range = VersionRange.createFromVersionSpec("[5.10,5.13)");
1370             if (range.containsVersion(version)) {
1371                 return "org.apache.maven.surefire.testng.conf.TestNG510Configurator";
1372             }
1373             range = VersionRange.createFromVersionSpec("[5.13,5.14.1)");
1374             if (range.containsVersion(version)) {
1375                 return "org.apache.maven.surefire.testng.conf.TestNG513Configurator";
1376             }
1377             range = VersionRange.createFromVersionSpec("[5.14.1,5.14.3)");
1378             if (range.containsVersion(version)) {
1379                 log.warning("The 'reporter' or 'listener' may not work properly in TestNG 5.14.1 and 5.14.2.");
1380                 return "org.apache.maven.surefire.testng.conf.TestNG5141Configurator";
1381             }
1382             range = VersionRange.createFromVersionSpec("[5.14.3,6.0)");
1383             if (range.containsVersion(version)) {
1384                 if (version.equals(new DefaultArtifactVersion("[5.14.3,5.14.5]"))) {
1385                     throw new MojoExecutionException("TestNG 5.14.3-5.14.5 is not supported. "
1386                             + "System dependency org.testng:guice missed path.");
1387                 }
1388                 return "org.apache.maven.surefire.testng.conf.TestNG5143Configurator";
1389             }
1390             range = VersionRange.createFromVersionSpec("[6.0,7.4.0)");
1391             if (range.containsVersion(version)) {
1392                 return "org.apache.maven.surefire.testng.conf.TestNG60Configurator";
1393             }
1394             range = VersionRange.createFromVersionSpec("[7.4.0,)");
1395             if (range.containsVersion(version)) {
1396                 return "org.apache.maven.surefire.testng.conf.TestNG740Configurator";
1397             }
1398 
1399             throw new MojoExecutionException("Unknown TestNG version " + version);
1400         } catch (InvalidVersionSpecificationException invsex) {
1401             throw new MojoExecutionException("Bug in plugin. Please report it with the attached stacktrace", invsex);
1402         }
1403     }
1404 
1405     private void convertGroupParameters() {
1406         if (this.getExcludedGroups() != null) {
1407             getProperties().setProperty(ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP, this.getExcludedGroups());
1408         }
1409         if (this.getGroups() != null) {
1410             getProperties().setProperty(ProviderParameterNames.TESTNG_GROUPS_PROP, this.getGroups());
1411         }
1412     }
1413 
1414     private void convertJunitEngineParameters() {
1415         if (getIncludeJUnit5Engines() != null && getIncludeJUnit5Engines().length != 0) {
1416             getProperties().setProperty(INCLUDE_JUNIT5_ENGINES_PROP, join(getIncludeJUnit5Engines()));
1417         }
1418 
1419         if (getExcludeJUnit5Engines() != null && getExcludeJUnit5Engines().length != 0) {
1420             getProperties().setProperty(EXCLUDE_JUNIT5_ENGINES_PROP, join(getExcludeJUnit5Engines()));
1421         }
1422     }
1423 
1424     private static String join(String[] array) {
1425         StringBuilder stringBuilder = new StringBuilder();
1426         for (int i = 0, length = array.length; i < length; i++) {
1427             stringBuilder.append(array[i]);
1428             if (i < length - 1) {
1429                 stringBuilder.append(',');
1430             }
1431         }
1432         return stringBuilder.toString();
1433     }
1434 
1435     protected boolean isAnyConcurrencySelected() {
1436         return getParallel() != null && !getParallel().trim().isEmpty();
1437     }
1438 
1439     protected boolean isAnyGroupsSelected() {
1440         return this.getGroups() != null || this.getExcludedGroups() != null;
1441     }
1442 
1443     /**
1444      * Converts old JUnit configuration parameters over to new properties based configuration
1445      * method. (if any are defined the old way)
1446      */
1447     private void convertJunitCoreParameters() throws MojoExecutionException {
1448         checkThreadCountEntity(getThreadCountSuites(), "suites");
1449         checkThreadCountEntity(getThreadCountClasses(), "classes");
1450         checkThreadCountEntity(getThreadCountMethods(), "methods");
1451 
1452         String usedParallel = (getParallel() != null) ? getParallel() : "none";
1453 
1454         if (!"none".equals(usedParallel)) {
1455             checkNonForkedThreads(parallel);
1456         }
1457 
1458         getProperties().setProperty(ProviderParameterNames.PARALLEL_PROP, usedParallel);
1459         if (this.getThreadCount() > 0) {
1460             getProperties().setProperty(ProviderParameterNames.THREADCOUNT_PROP, Integer.toString(getThreadCount()));
1461         }
1462         getProperties().setProperty("perCoreThreadCount", Boolean.toString(getPerCoreThreadCount()));
1463         getProperties().setProperty("useUnlimitedThreads", Boolean.toString(getUseUnlimitedThreads()));
1464         getProperties()
1465                 .setProperty(ProviderParameterNames.THREADCOUNTSUITES_PROP, Integer.toString(getThreadCountSuites()));
1466         getProperties()
1467                 .setProperty(ProviderParameterNames.THREADCOUNTCLASSES_PROP, Integer.toString(getThreadCountClasses()));
1468         getProperties()
1469                 .setProperty(ProviderParameterNames.THREADCOUNTMETHODS_PROP, Integer.toString(getThreadCountMethods()));
1470         getProperties()
1471                 .setProperty(
1472                         ProviderParameterNames.PARALLEL_TIMEOUT_PROP,
1473                         Double.toString(getParallelTestsTimeoutInSeconds()));
1474         getProperties()
1475                 .setProperty(
1476                         ProviderParameterNames.PARALLEL_TIMEOUTFORCED_PROP,
1477                         Double.toString(getParallelTestsTimeoutForcedInSeconds()));
1478         getProperties()
1479                 .setProperty(ProviderParameterNames.PARALLEL_OPTIMIZE_PROP, Boolean.toString(isParallelOptimized()));
1480 
1481         String message = "parallel='" + usedParallel + '\''
1482                 + ", perCoreThreadCount=" + getPerCoreThreadCount()
1483                 + ", threadCount=" + getThreadCount()
1484                 + ", useUnlimitedThreads=" + getUseUnlimitedThreads()
1485                 + ", threadCountSuites=" + getThreadCountSuites()
1486                 + ", threadCountClasses=" + getThreadCountClasses()
1487                 + ", threadCountMethods=" + getThreadCountMethods()
1488                 + ", parallelOptimized=" + isParallelOptimized();
1489 
1490         logDebugOrCliShowErrors(message);
1491     }
1492 
1493     private void checkNonForkedThreads(String parallel) throws MojoExecutionException {
1494         if ("suites".equals(parallel)) {
1495             if (!(getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountSuites() > 0)) {
1496                 throw new MojoExecutionException(
1497                         "Use threadCount or threadCountSuites > 0 or useUnlimitedThreads=true for parallel='suites'");
1498             }
1499             setThreadCountClasses(0);
1500             setThreadCountMethods(0);
1501         } else if ("classes".equals(parallel)) {
1502             if (!(getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountClasses() > 0)) {
1503                 throw new MojoExecutionException(
1504                         "Use threadCount or threadCountClasses > 0 or useUnlimitedThreads=true for parallel='classes'");
1505             }
1506             setThreadCountSuites(0);
1507             setThreadCountMethods(0);
1508         } else if ("methods".equals(parallel)) {
1509             if (!(getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountMethods() > 0)) {
1510                 throw new MojoExecutionException(
1511                         "Use threadCount or threadCountMethods > 0 or useUnlimitedThreads=true for parallel='methods'");
1512             }
1513             setThreadCountSuites(0);
1514             setThreadCountClasses(0);
1515         } else if ("suitesAndClasses".equals(parallel)) {
1516             if (!(getUseUnlimitedThreads()
1517                     || onlyThreadCount()
1518                     || getThreadCountSuites() > 0
1519                             && getThreadCountClasses() > 0
1520                             && getThreadCount() == 0
1521                             && getThreadCountMethods() == 0
1522                     || getThreadCount() > 0
1523                             && getThreadCountSuites() > 0
1524                             && getThreadCountClasses() > 0
1525                             && getThreadCountMethods() == 0
1526                     || getThreadCount() > 0
1527                             && getThreadCountSuites() > 0
1528                             && getThreadCount() > getThreadCountSuites()
1529                             && getThreadCountClasses() == 0
1530                             && getThreadCountMethods() == 0)) {
1531                 throw new MojoExecutionException("Use useUnlimitedThreads=true, "
1532                         + "or only threadCount > 0, "
1533                         + "or (threadCountSuites > 0 and threadCountClasses > 0), "
1534                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0) "
1535                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) "
1536                         + "for parallel='suitesAndClasses' or 'both'");
1537             }
1538             setThreadCountMethods(0);
1539         } else if ("suitesAndMethods".equals(parallel)) {
1540             if (!(getUseUnlimitedThreads()
1541                     || onlyThreadCount()
1542                     || getThreadCountSuites() > 0
1543                             && getThreadCountMethods() > 0
1544                             && getThreadCount() == 0
1545                             && getThreadCountClasses() == 0
1546                     || getThreadCount() > 0
1547                             && getThreadCountSuites() > 0
1548                             && getThreadCountMethods() > 0
1549                             && getThreadCountClasses() == 0
1550                     || getThreadCount() > 0
1551                             && getThreadCountSuites() > 0
1552                             && getThreadCount() > getThreadCountSuites()
1553                             && getThreadCountClasses() == 0
1554                             && getThreadCountMethods() == 0)) {
1555                 throw new MojoExecutionException("Use useUnlimitedThreads=true, "
1556                         + "or only threadCount > 0, "
1557                         + "or (threadCountSuites > 0 and threadCountMethods > 0), "
1558                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCountMethods > 0), "
1559                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) "
1560                         + "for parallel='suitesAndMethods'");
1561             }
1562             setThreadCountClasses(0);
1563         } else if ("both".equals(parallel) || "classesAndMethods".equals(parallel)) {
1564             if (!(getUseUnlimitedThreads()
1565                     || onlyThreadCount()
1566                     || getThreadCountClasses() > 0
1567                             && getThreadCountMethods() > 0
1568                             && getThreadCount() == 0
1569                             && getThreadCountSuites() == 0
1570                     || getThreadCount() > 0
1571                             && getThreadCountClasses() > 0
1572                             && getThreadCountMethods() > 0
1573                             && getThreadCountSuites() == 0
1574                     || getThreadCount() > 0
1575                             && getThreadCountClasses() > 0
1576                             && getThreadCount() > getThreadCountClasses()
1577                             && getThreadCountSuites() == 0
1578                             && getThreadCountMethods() == 0)) {
1579                 throw new MojoExecutionException("Use useUnlimitedThreads=true, "
1580                         + "or only threadCount > 0, "
1581                         + "or (threadCountClasses > 0 and threadCountMethods > 0), "
1582                         + "or (threadCount > 0 and threadCountClasses > 0 and threadCountMethods > 0), "
1583                         + "or (threadCount > 0 and threadCountClasses > 0 and threadCount > threadCountClasses) "
1584                         + "for parallel='both' or parallel='classesAndMethods'");
1585             }
1586             setThreadCountSuites(0);
1587         } else if ("all".equals(parallel)) {
1588             if (!(getUseUnlimitedThreads()
1589                     || onlyThreadCount()
1590                     || getThreadCountSuites() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() > 0
1591                     || getThreadCount() > 0
1592                             && getThreadCountSuites() > 0
1593                             && getThreadCountClasses() > 0
1594                             && getThreadCountMethods() == 0
1595                             && getThreadCount() > (getThreadCountSuites() + getThreadCountClasses()))) {
1596                 throw new MojoExecutionException("Use useUnlimitedThreads=true, "
1597                         + "or only threadCount > 0, "
1598                         + "or (threadCountSuites > 0 and threadCountClasses > 0 and threadCountMethods > 0), "
1599                         + "or every thread-count is specified, "
1600                         + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0 "
1601                         + "and threadCount > threadCountSuites + threadCountClasses) "
1602                         + "for parallel='all'");
1603             }
1604         } else {
1605             throw new MojoExecutionException("Illegal parallel='" + parallel + "'");
1606         }
1607     }
1608 
1609     private boolean onlyThreadCount() {
1610         return getThreadCount() > 0
1611                 && getThreadCountSuites() == 0
1612                 && getThreadCountClasses() == 0
1613                 && getThreadCountMethods() == 0;
1614     }
1615 
1616     private static void checkThreadCountEntity(int count, String entity) throws MojoExecutionException {
1617         if (count < 0) {
1618             throw new MojoExecutionException("parallel maven execution does not allow negative thread-count" + entity);
1619         }
1620     }
1621 
1622     private boolean isJunit47Compatible(Artifact artifact) {
1623         return isWithinVersionSpec(artifact, "[4.7,)");
1624     }
1625 
1626     private boolean isAnyJunit4(Artifact artifact) {
1627         return isWithinVersionSpec(artifact, "[4.0,)");
1628     }
1629 
1630     protected boolean isForking() {
1631         return 0 < getEffectiveForkCount();
1632     }
1633 
1634     private List<RunOrder> getRunOrders() {
1635         String runOrderString = getRunOrder();
1636         RunOrder[] runOrder = runOrderString == null ? RunOrder.DEFAULT : RunOrder.valueOfMulti(runOrderString);
1637         return asList(runOrder);
1638     }
1639 
1640     private boolean requiresRunHistory() {
1641         final List<RunOrder> runOrders = getRunOrders();
1642         return runOrders.contains(RunOrder.BALANCED) || runOrders.contains(RunOrder.FAILEDFIRST);
1643     }
1644 
1645     private PluginFailureReason getEffectiveFailIfNoTests() {
1646         if (isSpecificTestSpecified()) {
1647             return getFailIfNoSpecifiedTests() ? COULD_NOT_RUN_SPECIFIED_TESTS : NONE;
1648         } else {
1649             return getFailIfNoTests() ? COULD_NOT_RUN_DEFAULT_TESTS : NONE;
1650         }
1651     }
1652 
1653     private ProviderConfiguration createProviderConfiguration(RunOrderParameters runOrderParameters)
1654             throws MojoExecutionException, MojoFailureException {
1655         final ReporterConfiguration reporterConfiguration =
1656                 new ReporterConfiguration(getReportsDirectory(), isTrimStackTrace());
1657 
1658         final Artifact testNgArtifact = getTestNgArtifact();
1659         final boolean isTestNg = testNgArtifact != null;
1660         final TestArtifactInfo testNg =
1661                 isTestNg ? new TestArtifactInfo(testNgArtifact.getVersion(), testNgArtifact.getClassifier()) : null;
1662         final TestRequest testSuiteDefinition = new TestRequest(
1663                 suiteXmlFiles(), getTestSourceDirectory(), getSpecificTests(), getRerunFailingTestsCount());
1664 
1665         final boolean actualFailIfNoTests;
1666         DirectoryScannerParameters directoryScannerParameters = null;
1667         if (hasSuiteXmlFiles() && !isSpecificTestSpecified()) {
1668             actualFailIfNoTests = getFailIfNoTests();
1669             if (!isTestNg) {
1670                 throw new MojoExecutionException("suiteXmlFiles is configured, but there is no TestNG dependency");
1671             }
1672         } else {
1673             // @todo remove these three params and use DirectoryScannerParameters to pass into DirectoryScanner only
1674             // @todo or remove it in next major version :: 3.0
1675             // @todo remove deprecated methods in ProviderParameters => included|excluded|specificTests not needed here
1676 
1677             List<String> actualIncludes = getIncludeList(); // Collections.emptyList(); behaves same
1678             List<String> actualExcludes = getExcludeList(); // Collections.emptyList(); behaves same
1679             // Collections.emptyList(); behaves same
1680             List<String> specificTests = Collections.emptyList();
1681 
1682             directoryScannerParameters = new DirectoryScannerParameters(
1683                     getTestClassesDirectory(), actualIncludes, actualExcludes, specificTests, getRunOrder());
1684         }
1685 
1686         Map<String, String> providerProperties = toStringProperties(getProperties());
1687 
1688         return new ProviderConfiguration(
1689                 directoryScannerParameters,
1690                 runOrderParameters,
1691                 reporterConfiguration,
1692                 testNg, // Not really used in provider. Limited to de/serializer.
1693                 testSuiteDefinition,
1694                 providerProperties,
1695                 null,
1696                 false,
1697                 cli,
1698                 getSkipAfterFailureCount(),
1699                 Shutdown.parameterOf(getShutdown()),
1700                 getForkedProcessExitTimeoutInSeconds());
1701     }
1702 
1703     private static Map<String, String> toStringProperties(Properties properties) {
1704         Map<String, String> h = new ConcurrentHashMap<>(properties.size());
1705         for (Enumeration<?> e = properties.keys(); e.hasMoreElements(); ) {
1706             Object k = e.nextElement();
1707             Object v = properties.get(k);
1708             if (k.getClass() == String.class && v.getClass() == String.class) {
1709                 h.put((String) k, (String) v);
1710             }
1711         }
1712         return h;
1713     }
1714 
1715     private File getStatisticsFile(String configurationHash) {
1716         return new File(getBasedir(), ".surefire-" + configurationHash);
1717     }
1718 
1719     private StartupConfiguration createStartupConfiguration(
1720             @Nonnull ProviderInfo provider,
1721             boolean isForking,
1722             @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
1723             @Nonnull DefaultScanResult scanResult,
1724             @Nonnull TestClassPath testClasspathWrapper,
1725             @Nonnull Platform platform,
1726             @Nonnull ResolvePathResultWrapper resolvedJavaModularity)
1727             throws MojoExecutionException {
1728         try {
1729             if (isForking && canExecuteProviderWithModularPath(platform, resolvedJavaModularity)) {
1730                 File jdkHome = platform.getJdkExecAttributesForTests().getJdkHome();
1731                 return newStartupConfigWithModularPath(
1732                         classLoaderConfiguration,
1733                         provider,
1734                         resolvedJavaModularity,
1735                         scanResult,
1736                         jdkHome.getAbsolutePath(),
1737                         testClasspathWrapper);
1738             } else {
1739                 return newStartupConfigWithClasspath(classLoaderConfiguration, provider, testClasspathWrapper);
1740             }
1741         } catch (IOException e) {
1742             throw new MojoExecutionException(e.getMessage(), e);
1743         }
1744     }
1745 
1746     private StartupConfiguration newStartupConfigWithClasspath(
1747             @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
1748             @Nonnull ProviderInfo providerInfo,
1749             @Nonnull TestClassPath testClasspathWrapper)
1750             throws MojoExecutionException {
1751         Classpath testClasspath = testClasspathWrapper.toClasspath();
1752         Set<Artifact> providerArtifacts = providerInfo.getProviderClasspath();
1753         String providerName = providerInfo.getProviderName();
1754         Classpath providerClasspath = classpathCache.getCachedClassPath(providerName);
1755         if (providerClasspath == null) {
1756             providerClasspath = classpathCache.setCachedClasspath(providerName, providerArtifacts);
1757         }
1758 
1759         getConsoleLogger().debug(testClasspath.getLogMessage("test classpath:"));
1760         getConsoleLogger().debug(providerClasspath.getLogMessage("provider classpath:"));
1761         getConsoleLogger().debug(testClasspath.getCompactLogMessage("test(compact) classpath:"));
1762         getConsoleLogger().debug(providerClasspath.getCompactLogMessage("provider(compact) classpath:"));
1763 
1764         Artifact[] additionalInProcArtifacts = {
1765             getCommonArtifact(),
1766             getBooterArtifact(),
1767             getExtensionsArtifact(),
1768             getApiArtifact(),
1769             getSpiArtifact(),
1770             getLoggerApiArtifact(),
1771             getSurefireSharedUtilsArtifact()
1772         };
1773         Set<Artifact> inProcArtifacts = retainInProcArtifactsUnique(providerArtifacts, additionalInProcArtifacts);
1774         Classpath inProcClasspath = createInProcClasspath(providerClasspath, inProcArtifacts);
1775         getConsoleLogger().debug(inProcClasspath.getLogMessage("in-process classpath:"));
1776         getConsoleLogger().debug(inProcClasspath.getCompactLogMessage("in-process(compact) classpath:"));
1777 
1778         ClasspathConfiguration classpathConfiguration = new ClasspathConfiguration(
1779                 testClasspath, providerClasspath, inProcClasspath, effectiveIsEnableAssertions(), isChildDelegation());
1780         ProviderRequirements forkRequirements = new ProviderRequirements(false, false, false);
1781         return new StartupConfiguration(
1782                 providerName,
1783                 classpathConfiguration,
1784                 classLoaderConfiguration,
1785                 ProcessCheckerType.toEnum(getEnableProcessChecker()),
1786                 providerInfo.getJpmsArguments(forkRequirements));
1787     }
1788 
1789     private static Set<Artifact> retainInProcArtifactsUnique(
1790             Set<Artifact> providerArtifacts, Artifact... inPluginArtifacts) {
1791         Set<Artifact> result = new LinkedHashSet<>();
1792         for (Artifact inPluginArtifact : inPluginArtifacts) {
1793             boolean contains = false;
1794             for (Artifact providerArtifact : providerArtifacts) {
1795                 if (hasGroupArtifactId(
1796                         providerArtifact.getGroupId(), providerArtifact.getArtifactId(), inPluginArtifact)) {
1797                     contains = true;
1798                     break;
1799                 }
1800             }
1801             if (!contains) {
1802                 result.add(inPluginArtifact);
1803             }
1804         }
1805         return result;
1806     }
1807 
1808     private static boolean hasGroupArtifactId(String groupId, String artifactId, Artifact artifact) {
1809         return groupId.equals(artifact.getGroupId()) && artifactId.equals(artifact.getArtifactId());
1810     }
1811 
1812     private static Classpath createInProcClasspath(Classpath providerClasspath, Set<Artifact> newArtifacts) {
1813         Classpath inprocClasspath = providerClasspath.clone();
1814         for (Artifact newArtifact : newArtifacts) {
1815             inprocClasspath =
1816                     inprocClasspath.addClassPathElementUrl(newArtifact.getFile().getAbsolutePath());
1817         }
1818         return inprocClasspath;
1819     }
1820 
1821     /**
1822      * For testing purposes - Mockito.
1823      * @return plexus component
1824      */
1825     private LocationManager getLocationManager() {
1826         return locationManager;
1827     }
1828 
1829     private StartupConfiguration newStartupConfigWithModularPath(
1830             @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
1831             @Nonnull ProviderInfo providerInfo,
1832             @Nonnull ResolvePathResultWrapper moduleDescriptor,
1833             @Nonnull DefaultScanResult scanResult,
1834             @Nonnull String javaHome,
1835             @Nonnull TestClassPath testClasspathWrapper)
1836             throws MojoExecutionException, IOException {
1837         boolean isMainDescriptor = moduleDescriptor.isMainModuleDescriptor();
1838         JavaModuleDescriptor javaModuleDescriptor =
1839                 moduleDescriptor.getResolvePathResult().getModuleDescriptor();
1840         SortedSet<String> packages = new TreeSet<>();
1841 
1842         Classpath testClasspath = testClasspathWrapper.toClasspath();
1843         Set<Artifact> providerArtifacts = providerInfo.getProviderClasspath();
1844         String providerName = providerInfo.getProviderName();
1845         Classpath providerClasspath = classpathCache.getCachedClassPath(providerName);
1846         if (providerClasspath == null) {
1847             providerClasspath = classpathCache.setCachedClasspath(providerName, providerArtifacts);
1848         }
1849 
1850         final ProviderRequirements providerRequirements;
1851         final Classpath testModulepath;
1852         if (isMainDescriptor) {
1853             providerRequirements = new ProviderRequirements(true, true, false);
1854             ResolvePathsRequest<String> req = ResolvePathsRequest.ofStrings(testClasspath.getClassPath())
1855                     .setIncludeAllProviders(true)
1856                     .setJdkHome(javaHome)
1857                     .setIncludeStatic(true)
1858                     .setModuleDescriptor(javaModuleDescriptor);
1859 
1860             ResolvePathsResult<String> result = getLocationManager().resolvePaths(req);
1861             for (Entry<String, Exception> entry : result.getPathExceptions().entrySet()) {
1862                 // Probably JDK version < 9. Other known causes: passing a non-jar or a corrupted jar.
1863                 getConsoleLogger().warning("Exception for '" + entry.getKey() + "'.", entry.getValue());
1864             }
1865 
1866             testClasspath = new Classpath(result.getClasspathElements());
1867             testModulepath = new Classpath(result.getModulepathElements().keySet());
1868 
1869             for (String className : scanResult.getClasses()) {
1870                 packages.add(substringBeforeLast(className, "."));
1871             }
1872         } else {
1873             providerRequirements = new ProviderRequirements(true, false, true);
1874             testModulepath = testClasspath;
1875             testClasspath = emptyClasspath();
1876         }
1877 
1878         getConsoleLogger().debug("main module descriptor name: " + javaModuleDescriptor.name());
1879 
1880         ModularClasspath modularClasspath = new ModularClasspath(
1881                 javaModuleDescriptor.name(),
1882                 testModulepath.getClassPath(),
1883                 packages,
1884                 isMainDescriptor ? getTestClassesDirectory() : null,
1885                 isMainDescriptor);
1886 
1887         Artifact[] additionalInProcArtifacts = {
1888             getCommonArtifact(),
1889             getBooterArtifact(),
1890             getExtensionsArtifact(),
1891             getApiArtifact(),
1892             getSpiArtifact(),
1893             getLoggerApiArtifact(),
1894             getSurefireSharedUtilsArtifact()
1895         };
1896         Set<Artifact> inProcArtifacts = retainInProcArtifactsUnique(providerArtifacts, additionalInProcArtifacts);
1897         Classpath inProcClasspath = createInProcClasspath(providerClasspath, inProcArtifacts);
1898 
1899         ModularClasspathConfiguration classpathConfiguration = new ModularClasspathConfiguration(
1900                 modularClasspath,
1901                 testClasspath,
1902                 providerClasspath,
1903                 inProcClasspath,
1904                 effectiveIsEnableAssertions(),
1905                 isChildDelegation());
1906 
1907         getConsoleLogger().debug(testClasspath.getLogMessage("test classpath:"));
1908         getConsoleLogger().debug(testModulepath.getLogMessage("test modulepath:"));
1909         getConsoleLogger().debug(providerClasspath.getLogMessage("provider classpath:"));
1910         getConsoleLogger().debug(testClasspath.getCompactLogMessage("test(compact) classpath:"));
1911         getConsoleLogger().debug(testModulepath.getCompactLogMessage("test(compact) modulepath:"));
1912         getConsoleLogger().debug(providerClasspath.getCompactLogMessage("provider(compact) classpath:"));
1913         getConsoleLogger().debug(inProcClasspath.getLogMessage("in-process classpath:"));
1914         getConsoleLogger().debug(inProcClasspath.getCompactLogMessage("in-process(compact) classpath:"));
1915 
1916         ProcessCheckerType processCheckerType = ProcessCheckerType.toEnum(getEnableProcessChecker());
1917         List<String[]> jpmsArgs = providerInfo.getJpmsArguments(providerRequirements);
1918         return new StartupConfiguration(
1919                 providerName, classpathConfiguration, classLoaderConfiguration, processCheckerType, jpmsArgs);
1920     }
1921 
1922     private Artifact getCommonArtifact() {
1923         return getPluginArtifactMap().get("org.apache.maven.surefire:maven-surefire-common");
1924     }
1925 
1926     private Artifact getExtensionsArtifact() {
1927         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-extensions-api");
1928     }
1929 
1930     private Artifact getSpiArtifact() {
1931         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-extensions-spi");
1932     }
1933 
1934     private Artifact getApiArtifact() {
1935         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-api");
1936     }
1937 
1938     private Artifact getSurefireSharedUtilsArtifact() {
1939         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-shared-utils");
1940     }
1941 
1942     private Artifact getLoggerApiArtifact() {
1943         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-logger-api");
1944     }
1945 
1946     private Artifact getBooterArtifact() {
1947         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-booter");
1948     }
1949 
1950     private Artifact getShadefireArtifact() {
1951         return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-shadefire");
1952     }
1953 
1954     private StartupReportConfiguration getStartupReportConfiguration(String configChecksum, boolean isForking) {
1955         SurefireStatelessReporter xmlReporter = statelessTestsetReporter == null
1956                 ? new SurefireStatelessReporter(/*todo call def. constr.*/ isDisableXmlReport(), "3.0")
1957                 : statelessTestsetReporter;
1958 
1959         xmlReporter.setDisable(isDisableXmlReport()); // todo change to Boolean in the version 3.0.0-M6
1960 
1961         SurefireConsoleOutputReporter outReporter =
1962                 consoleOutputReporter == null ? new SurefireConsoleOutputReporter() : consoleOutputReporter;
1963 
1964         SurefireStatelessTestsetInfoReporter testsetReporter = statelessTestsetInfoReporter == null
1965                 ? new SurefireStatelessTestsetInfoReporter()
1966                 : statelessTestsetInfoReporter;
1967 
1968         return new StartupReportConfiguration(
1969                 isUseFile(),
1970                 isPrintSummary(),
1971                 getReportFormat(),
1972                 isRedirectTestOutputToFile(),
1973                 getReportsDirectory(),
1974                 isTrimStackTrace(),
1975                 getReportNameSuffix(),
1976                 getStatisticsFile(configChecksum),
1977                 requiresRunHistory(),
1978                 getRerunFailingTestsCount(),
1979                 getReportSchemaLocation(),
1980                 getEncoding(),
1981                 isForking,
1982                 xmlReporter,
1983                 outReporter,
1984                 testsetReporter);
1985     }
1986 
1987     private boolean isSpecificTestSpecified() {
1988         return isNotBlank(getTest());
1989     }
1990 
1991     @Nonnull
1992     private List<String> readListFromFile(@Nonnull final File file) {
1993         getConsoleLogger().debug("Reading list from: " + file);
1994 
1995         if (!file.exists()) {
1996             throw new RuntimeException("Failed to load list from file: " + file);
1997         }
1998 
1999         try {
2000             List<String> list = FileUtils.loadFile(file);
2001 
2002             if (getConsoleLogger().isDebugEnabled()) {
2003                 getConsoleLogger().debug("List contents:");
2004                 for (String entry : list) {
2005                     getConsoleLogger().debug("  " + entry);
2006                 }
2007             }
2008             return list;
2009         } catch (IOException e) {
2010             throw new RuntimeException("Failed to load list from file: " + file, e);
2011         }
2012     }
2013 
2014     @Nonnull
2015     private List<String> getExcludedScanList() throws MojoFailureException {
2016         return getExcludeList(true);
2017     }
2018 
2019     @Nonnull
2020     private List<String> getExcludeList() throws MojoFailureException {
2021         return getExcludeList(false);
2022     }
2023 
2024     /**
2025      * Computes a merge list of test exclusions.
2026      * Used only in {@link #getExcludeList()} and {@link #getExcludedScanList()}.
2027      * @param asScanList true if dependency or directory scanner
2028      * @return list of patterns
2029      * @throws MojoFailureException if the excludes breaks a pattern format
2030      */
2031     @Nonnull
2032     private List<String> getExcludeList(boolean asScanList) throws MojoFailureException {
2033         List<String> excludes;
2034         if (isSpecificTestSpecified()) {
2035             excludes = Collections.emptyList();
2036         } else {
2037             excludes = new ArrayList<>();
2038             if (asScanList) {
2039                 if (getExcludes() != null) {
2040                     excludes.addAll(getExcludes());
2041                 }
2042                 checkMethodFilterInIncludesExcludes(excludes);
2043             }
2044 
2045             if (getExcludesFile() != null) {
2046                 excludes.addAll(readListFromFile(getExcludesFile()));
2047             }
2048 
2049             if (asScanList && excludes.isEmpty()) {
2050                 excludes = Collections.singletonList(getDefaultExcludes());
2051             }
2052         }
2053         return filterNulls(excludes);
2054     }
2055 
2056     @Nonnull
2057     private List<String> getIncludedScanList() throws MojoFailureException {
2058         return getIncludeList(true);
2059     }
2060 
2061     @Nonnull
2062     private List<String> getIncludeList() throws MojoFailureException {
2063         return getIncludeList(false);
2064     }
2065 
2066     /**
2067      * Computes a merge list of test inclusions.
2068      * Used only in {@link #getIncludeList()} and {@link #getIncludedScanList()}.
2069      * @param asScanList true if dependency or directory scanner
2070      * @return list of patterns
2071      * @throws MojoFailureException if the includes breaks a pattern format
2072      */
2073     @Nonnull
2074     private List<String> getIncludeList(boolean asScanList) throws MojoFailureException {
2075         final List<String> includes = new ArrayList<>();
2076         if (isSpecificTestSpecified()) {
2077             addAll(includes, split(getTest(), ","));
2078         } else {
2079             if (asScanList) {
2080                 if (getIncludes() != null) {
2081                     includes.addAll(getIncludes());
2082                 }
2083                 checkMethodFilterInIncludesExcludes(includes);
2084             }
2085 
2086             if (getIncludesFile() != null) {
2087                 includes.addAll(readListFromFile(getIncludesFile()));
2088             }
2089 
2090             if (asScanList && includes.isEmpty()) {
2091                 addAll(includes, getDefaultIncludes());
2092             }
2093         }
2094 
2095         return filterNulls(includes);
2096     }
2097 
2098     private void checkMethodFilterInIncludesExcludes(Iterable<String> patterns) throws MojoFailureException {
2099         for (String pattern : patterns) {
2100             if (pattern != null && pattern.contains("#")) {
2101                 throw new MojoFailureException("Method filter prohibited in includes|excludes parameter: " + pattern);
2102             }
2103         }
2104     }
2105 
2106     private TestListResolver getIncludedAndExcludedTests() throws MojoFailureException {
2107         if (includedExcludedTests == null) {
2108             includedExcludedTests = new TestListResolver(getIncludedScanList(), getExcludedScanList());
2109             getConsoleLogger().debug("Resolved included and excluded patterns: " + includedExcludedTests);
2110         }
2111         return includedExcludedTests;
2112     }
2113 
2114     public TestListResolver getSpecificTests() throws MojoFailureException {
2115         if (specificTests == null) {
2116             specificTests = new TestListResolver(getIncludeList(), getExcludeList());
2117         }
2118         return specificTests;
2119     }
2120 
2121     @Nonnull
2122     private List<String> filterNulls(@Nonnull List<String> toFilter) {
2123         List<String> result = new ArrayList<>(toFilter.size());
2124         for (String item : toFilter) {
2125             if (item != null) {
2126                 item = item.trim();
2127                 if (!item.isEmpty()) {
2128                     result.add(item);
2129                 }
2130             }
2131         }
2132 
2133         return result;
2134     }
2135 
2136     private Artifact getTestNgArtifact() throws MojoExecutionException {
2137         Artifact artifact = getProjectArtifactMap().get(getTestNGArtifactName());
2138         Artifact projectArtifact = project.getArtifact();
2139         String projectArtifactName = projectArtifact.getGroupId() + ":" + projectArtifact.getArtifactId();
2140 
2141         if (artifact != null) {
2142             VersionRange range = createVersionRange();
2143             if (!range.containsVersion(new DefaultArtifactVersion(artifact.getVersion()))) {
2144                 throw new MojoExecutionException(
2145                         "TestNG support requires version 4.7 or above. You have declared version "
2146                                 + artifact.getVersion());
2147             }
2148         } else if (projectArtifactName.equals(getTestNGArtifactName())) {
2149             artifact = projectArtifact;
2150         }
2151 
2152         return artifact;
2153     }
2154 
2155     private VersionRange createVersionRange() {
2156         try {
2157             return VersionRange.createFromVersionSpec("[4.7,)");
2158         } catch (InvalidVersionSpecificationException e) {
2159             throw new RuntimeException(e);
2160         }
2161     }
2162 
2163     private Artifact getJunitArtifact() {
2164         Artifact artifact = getProjectArtifactMap().get(getJunitArtifactName());
2165         Artifact projectArtifact = project.getArtifact();
2166         String projectArtifactName = projectArtifact.getGroupId() + ":" + projectArtifact.getArtifactId();
2167 
2168         if (artifact == null && projectArtifactName.equals(getJunitArtifactName())) {
2169             artifact = projectArtifact;
2170         }
2171 
2172         return artifact;
2173     }
2174 
2175     private Artifact getJunitDepArtifact() {
2176         return getProjectArtifactMap().get("junit:junit-dep");
2177     }
2178 
2179     private Artifact getJUnitPlatformRunnerArtifact() {
2180         return getProjectArtifactMap().get("org.junit.platform:junit-platform-runner");
2181     }
2182 
2183     private Artifact getJUnit5Artifact() {
2184         Artifact artifact = getPluginArtifactMap().get("org.junit.platform:junit-platform-engine");
2185         if (artifact == null) {
2186             return getProjectArtifactMap().get("org.junit.platform:junit-platform-commons");
2187         }
2188 
2189         return artifact;
2190     }
2191 
2192     private ForkStarter createForkStarter(
2193             @Nonnull ProviderInfo provider,
2194             @Nonnull ForkConfiguration forkConfiguration,
2195             @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
2196             @Nonnull RunOrderParameters runOrderParameters,
2197             @Nonnull ConsoleLogger log,
2198             @Nonnull DefaultScanResult scanResult,
2199             @Nonnull TestClassPath testClasspathWrapper,
2200             @Nonnull Platform platform,
2201             @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult)
2202             throws MojoExecutionException, MojoFailureException {
2203         StartupConfiguration startupConfiguration = createStartupConfiguration(
2204                 provider,
2205                 true,
2206                 classLoaderConfiguration,
2207                 scanResult,
2208                 testClasspathWrapper,
2209                 platform,
2210                 resolvedJavaModularityResult);
2211         String configChecksum = getConfigChecksum();
2212         StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration(configChecksum, true);
2213         ProviderConfiguration providerConfiguration = createProviderConfiguration(runOrderParameters);
2214         return new ForkStarter(
2215                 providerConfiguration,
2216                 startupConfiguration,
2217                 forkConfiguration,
2218                 getForkedProcessTimeoutInSeconds(),
2219                 startupReportConfiguration,
2220                 log);
2221     }
2222 
2223     private InPluginVMSurefireStarter createInprocessStarter(
2224             @Nonnull ProviderInfo provider,
2225             @Nonnull ClassLoaderConfiguration classLoaderConfig,
2226             @Nonnull RunOrderParameters runOrderParameters,
2227             @Nonnull DefaultScanResult scanResult,
2228             @Nonnull Platform platform,
2229             @Nonnull TestClassPath testClasspathWrapper)
2230             throws MojoExecutionException, MojoFailureException {
2231         StartupConfiguration startupConfiguration = createStartupConfiguration(
2232                 provider,
2233                 false,
2234                 classLoaderConfig,
2235                 scanResult,
2236                 testClasspathWrapper,
2237                 platform,
2238                 new ResolvePathResultWrapper(null, true));
2239         String configChecksum = getConfigChecksum();
2240         StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration(configChecksum, false);
2241         ProviderConfiguration providerConfiguration = createProviderConfiguration(runOrderParameters);
2242         return new InPluginVMSurefireStarter(
2243                 startupConfiguration, providerConfiguration, startupReportConfiguration, getConsoleLogger(), platform);
2244     }
2245 
2246     // todo this is in separate method and can be better tested than whole method createForkConfiguration()
2247     @Nonnull
2248     private ForkNodeFactory getForkNodeFactory() {
2249         ForkNodeFactory forkNode = getForkNode();
2250         return forkNode == null ? new LegacyForkNodeFactory() : forkNode;
2251     }
2252 
2253     @Nonnull
2254     private ForkConfiguration createForkConfiguration(
2255             @Nonnull Platform platform, @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult)
2256             throws MojoExecutionException {
2257         File tmpDir = getSurefireTempDir();
2258 
2259         Artifact shadeFire = getShadefireArtifact();
2260 
2261         Classpath bootClasspath = getArtifactClasspath(shadeFire != null ? shadeFire : getBooterArtifact());
2262 
2263         ForkNodeFactory forkNode = getForkNodeFactory();
2264 
2265         getConsoleLogger()
2266                 .debug("Found implementation of fork node factory: "
2267                         + forkNode.getClass().getName());
2268 
2269         if (canExecuteProviderWithModularPath(platform, resolvedJavaModularityResult)) {
2270             return new ModularClasspathForkConfiguration(
2271                     bootClasspath,
2272                     tmpDir,
2273                     getEffectiveDebugForkedProcess(),
2274                     getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(),
2275                     getProject().getModel().getProperties(),
2276                     getArgLine(),
2277                     getEnvironmentVariables(),
2278                     getExcludedEnvironmentVariables(),
2279                     getConsoleLogger().isDebugEnabled(),
2280                     getEffectiveForkCount(),
2281                     reuseForks,
2282                     platform,
2283                     getConsoleLogger(),
2284                     forkNode);
2285         } else if (getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable()) {
2286             return new JarManifestForkConfiguration(
2287                     bootClasspath,
2288                     tmpDir,
2289                     getEffectiveDebugForkedProcess(),
2290                     getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(),
2291                     getProject().getModel().getProperties(),
2292                     getArgLine(),
2293                     getEnvironmentVariables(),
2294                     getExcludedEnvironmentVariables(),
2295                     getConsoleLogger().isDebugEnabled(),
2296                     getEffectiveForkCount(),
2297                     reuseForks,
2298                     platform,
2299                     getConsoleLogger(),
2300                     forkNode);
2301         } else {
2302             return new ClasspathForkConfiguration(
2303                     bootClasspath,
2304                     tmpDir,
2305                     getEffectiveDebugForkedProcess(),
2306                     getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(),
2307                     getProject().getModel().getProperties(),
2308                     getArgLine(),
2309                     getEnvironmentVariables(),
2310                     getExcludedEnvironmentVariables(),
2311                     getConsoleLogger().isDebugEnabled(),
2312                     getEffectiveForkCount(),
2313                     reuseForks,
2314                     platform,
2315                     getConsoleLogger(),
2316                     forkNode);
2317         }
2318     }
2319 
2320     private void ensureEnableProcessChecker() throws MojoFailureException {
2321         if (!ProcessCheckerType.isValid(getEnableProcessChecker())) {
2322             throw new MojoFailureException("Unexpected value '"
2323                     + getEnableProcessChecker()
2324                     + "' in the configuration parameter 'enableProcessChecker'.");
2325         }
2326     }
2327 
2328     @SuppressWarnings("checkstyle:emptyblock")
2329     protected int getEffectiveForkCount() {
2330         if (effectiveForkCount < 0) {
2331             try {
2332                 effectiveForkCount = convertWithCoreCount(forkCount);
2333             } catch (NumberFormatException ignored) {
2334             }
2335 
2336             if (effectiveForkCount < 0) {
2337                 throw new IllegalArgumentException("Fork count " + forkCount.trim() + " is not a legal value.");
2338             }
2339         }
2340 
2341         return effectiveForkCount;
2342     }
2343 
2344     protected int convertWithCoreCount(String count) {
2345         String trimmed = count.trim();
2346         if (trimmed.endsWith("C")) {
2347             double multiplier = Double.parseDouble(trimmed.substring(0, trimmed.length() - 1));
2348             double calculated = multiplier * ((double) Runtime.getRuntime().availableProcessors());
2349             return calculated > 0d ? Math.max((int) calculated, 1) : 0;
2350         } else {
2351             return parseInt(trimmed);
2352         }
2353     }
2354 
2355     private String getEffectiveDebugForkedProcess() {
2356         String debugForkedProcess = getDebugForkedProcess();
2357         if ("true".equals(debugForkedProcess)) {
2358             return "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:5005";
2359         }
2360         return debugForkedProcess;
2361     }
2362 
2363     private JdkAttributes getEffectiveJvm() throws MojoFailureException {
2364         if (isNotEmpty(getJvm())) {
2365             File pathToJava = new File(getJvm()).getAbsoluteFile();
2366             if (!endsWithJavaPath(pathToJava.getPath())) {
2367                 throw new MojoFailureException(
2368                         "Given path does not end with java executor \"" + pathToJava.getPath() + "\".");
2369             }
2370 
2371             if (!(pathToJava.isFile()
2372                     || "java".equals(pathToJava.getName())
2373                             && pathToJava.getParentFile().isDirectory())) {
2374                 throw new MojoFailureException(
2375                         "Given path to java executor does not exist \"" + pathToJava.getPath() + "\".");
2376             }
2377 
2378             File jdkHome = toJdkHomeFromJvmExec(pathToJava.getPath());
2379             if (jdkHome == null) {
2380                 getConsoleLogger().warning("Cannot determine JAVA_HOME of jvm exec path " + pathToJava);
2381             } else if (!getEnvironmentVariables().containsKey("JAVA_HOME")) {
2382                 getEnvironmentVariables().put("JAVA_HOME", jdkHome.getAbsolutePath());
2383             }
2384             BigDecimal version = jdkHome == null ? null : toJdkVersionFromReleaseFile(jdkHome);
2385             boolean javaVersion9 = version == null ? isJava9AtLeast(pathToJava.getPath()) : isJava9AtLeast(version);
2386             return new JdkAttributes(pathToJava, jdkHome, javaVersion9);
2387         }
2388 
2389         if (toolchain != null) {
2390             String jvmToUse = toolchain.findTool("java");
2391             if (isNotEmpty(jvmToUse)) {
2392                 boolean javaVersion9 = false;
2393                 String jdkHome = null;
2394 
2395                 if (toolchain instanceof DefaultToolchain) {
2396                     DefaultToolchain defaultToolchain = (DefaultToolchain) toolchain;
2397                     javaVersion9 = defaultToolchain.matchesRequirements(JAVA_9_MATCHER)
2398                             || defaultToolchain.matchesRequirements(JAVA_9_MATCHER_OLD_NOTATION);
2399                 }
2400 
2401                 if (toolchain instanceof DefaultJavaToolChain) {
2402                     DefaultJavaToolChain defaultJavaToolChain = (DefaultJavaToolChain) toolchain;
2403                     if (!getEnvironmentVariables().containsKey("JAVA_HOME")) {
2404                         jdkHome = defaultJavaToolChain.getJavaHome();
2405                         getEnvironmentVariables().put("JAVA_HOME", jdkHome);
2406                     }
2407                 }
2408 
2409                 if (!javaVersion9) {
2410                     javaVersion9 = isJava9AtLeast(jvmToUse);
2411                 }
2412 
2413                 return new JdkAttributes(
2414                         new File(jvmToUse),
2415                         jdkHome == null ? toJdkHomeFromJvmExec(jvmToUse) : new File(jdkHome),
2416                         javaVersion9);
2417             }
2418         }
2419 
2420         // use the same JVM as the one used to run Maven (the "java.home" one)
2421         String jvmToUse = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
2422         getConsoleLogger().debug("Using JVM: " + jvmToUse + " with Java version " + JAVA_RECENT);
2423 
2424         return new JdkAttributes(jvmToUse, isBuiltInJava9AtLeast());
2425     }
2426 
2427     /**
2428      * Where surefire stores its own temp files
2429      *
2430      * @return A file pointing to the location of surefire's own temp files
2431      */
2432     File getSurefireTempDir() {
2433         File result = IS_OS_WINDOWS ? createSurefireBootDirectoryInTemp() : createSurefireBootDirectoryInBuild();
2434         try {
2435             File canonical = result.getCanonicalFile();
2436             if (!result.equals(canonical)) {
2437                 getConsoleLogger().debug("Canonicalized tempDir path '" + result + "' to '" + canonical + "'");
2438             }
2439             return canonical;
2440         } catch (IOException e) {
2441             getConsoleLogger().error("Could not canonicalize tempDir path '" + result + "'", e);
2442         }
2443         return result;
2444     }
2445 
2446     /**
2447      * Operates on raw plugin parameters, not the "effective" values.
2448      *
2449      * @return The checksum
2450      */
2451     private String getConfigChecksum() {
2452         ChecksumCalculator checksum = new ChecksumCalculator();
2453         checksum.add(getPluginName());
2454         checksum.add(isSkipTests());
2455         checksum.add(isSkipExec());
2456         checksum.add(isSkip());
2457         checksum.add(getTestClassesDirectory());
2458         checksum.add(getMainBuildPath());
2459         checksum.add(getClasspathDependencyExcludes());
2460         checksum.add(getClasspathDependencyScopeExclude());
2461         checksum.add(getAdditionalClasspathElements());
2462         checksum.add(getReportsDirectory());
2463         checksum.add(getProjectBuildDirectory());
2464         checksum.add(getTestSourceDirectory());
2465         checksum.add(getTest());
2466         checksum.add(getIncludes());
2467         checksum.add(getSkipAfterFailureCount());
2468         checksum.add(getShutdown());
2469         checksum.add(getExcludes());
2470         checksum.add(getLocalRepositoryPath());
2471         checksum.add(getSystemProperties());
2472         checksum.add(getSystemPropertyVariables());
2473         checksum.add(getSystemPropertiesFile());
2474         checksum.add(getProperties());
2475         checksum.add(isPrintSummary());
2476         checksum.add(getReportFormat());
2477         checksum.add(getReportNameSuffix());
2478         checksum.add(isUseFile());
2479         checksum.add(isRedirectTestOutputToFile());
2480         checksum.add(getForkCount());
2481         checksum.add(isReuseForks());
2482         checksum.add(getJvm());
2483         checksum.add(getArgLine());
2484         checksum.add(getDebugForkedProcess());
2485         checksum.add(getForkedProcessTimeoutInSeconds());
2486         checksum.add(getParallelTestsTimeoutInSeconds());
2487         checksum.add(getParallelTestsTimeoutForcedInSeconds());
2488         checksum.add(getEnvironmentVariables());
2489         checksum.add(getExcludedEnvironmentVariables());
2490         checksum.add(getWorkingDirectory());
2491         checksum.add(isChildDelegation());
2492         checksum.add(getGroups());
2493         checksum.add(getExcludedGroups());
2494         checksum.add(getIncludeJUnit5Engines());
2495         checksum.add(getExcludeJUnit5Engines());
2496         checksum.add(getSuiteXmlFiles());
2497         checksum.add(getJunitArtifact());
2498         checksum.add(getTestNGArtifactName());
2499         checksum.add(getThreadCount());
2500         checksum.add(getThreadCountSuites());
2501         checksum.add(getThreadCountClasses());
2502         checksum.add(getThreadCountMethods());
2503         checksum.add(getPerCoreThreadCount());
2504         checksum.add(getUseUnlimitedThreads());
2505         checksum.add(getParallel());
2506         checksum.add(isParallelOptimized());
2507         checksum.add(isTrimStackTrace());
2508         checksum.add(isDisableXmlReport());
2509         checksum.add(isUseSystemClassLoader());
2510         checksum.add(isUseManifestOnlyJar());
2511         checksum.add(getEncoding());
2512         checksum.add(isEnableAssertions());
2513         checksum.add(getObjectFactory());
2514         checksum.add(getFailIfNoTests());
2515         checksum.add(getRunOrder());
2516         checksum.add(getDependenciesToScan());
2517         checksum.add(getForkedProcessExitTimeoutInSeconds());
2518         checksum.add(getRerunFailingTestsCount());
2519         checksum.add(getTempDir());
2520         checksum.add(useModulePath());
2521         checksum.add(getEnableProcessChecker());
2522         addPluginSpecificChecksumItems(checksum);
2523         return checksum.getSha1();
2524     }
2525 
2526     protected void addPluginSpecificChecksumItems(ChecksumCalculator checksum) {}
2527 
2528     protected boolean hasExecutedBefore() {
2529         // A tribute to Linus Torvalds
2530         String configChecksum = getConfigChecksum();
2531         @SuppressWarnings("unchecked")
2532         Map<String, String> pluginContext = getPluginContext();
2533         if (pluginContext.containsKey(configChecksum)) {
2534             getConsoleLogger()
2535                     .info("Skipping execution of surefire because it has already been run for this configuration");
2536             return true;
2537         }
2538         pluginContext.put(configChecksum, configChecksum);
2539 
2540         return false;
2541     }
2542 
2543     @Nonnull
2544     protected ClassLoaderConfiguration getClassLoaderConfiguration() {
2545         return isForking()
2546                 ? new ClassLoaderConfiguration(isUseSystemClassLoader(), isUseManifestOnlyJar())
2547                 : new ClassLoaderConfiguration(false, false);
2548     }
2549 
2550     /**
2551      * Generates the test classpath.
2552      *
2553      * @return the classpath elements
2554      * @throws MojoFailureException
2555      */
2556     private TestClassPath generateTestClasspath() throws MojoFailureException {
2557         Set<Artifact> classpathArtifacts = getProject().getArtifacts();
2558 
2559         if (getClasspathDependencyScopeExclude() != null
2560                 && !getClasspathDependencyScopeExclude().isEmpty()) {
2561             ArtifactFilter dependencyFilter = new ScopeArtifactFilter(getClasspathDependencyScopeExclude());
2562             classpathArtifacts = filterArtifacts(classpathArtifacts, dependencyFilter);
2563         }
2564 
2565         if (getClasspathDependencyExcludes() != null) {
2566             List<String> excludedDependencies = asList(getClasspathDependencyExcludes());
2567             ArtifactFilter dependencyFilter = new PatternIncludesArtifactFilter(excludedDependencies);
2568             classpathArtifacts = filterArtifacts(classpathArtifacts, dependencyFilter);
2569         }
2570 
2571         Map<String, Artifact> dependencyConflictIdsProjectArtifacts = classpathArtifacts.stream()
2572                 .collect(Collectors.toMap(Artifact::getDependencyConflictId, Function.identity()));
2573         Set<String> additionalClasspathElements = new LinkedHashSet<>();
2574         if (getAdditionalClasspathElements() != null) {
2575             Arrays.stream(getAdditionalClasspathElements()).forEach(additionalClasspathElements::add);
2576         }
2577         if (additionalClasspathDependencies != null && !additionalClasspathDependencies.isEmpty()) {
2578             Collection<Artifact> additionalArtifacts = resolveDependencies(additionalClasspathDependencies);
2579             // check for potential conflicts with project dependencies
2580             for (Artifact additionalArtifact : additionalArtifacts) {
2581                 Artifact conflictingArtifact =
2582                         dependencyConflictIdsProjectArtifacts.get(additionalArtifact.getDependencyConflictId());
2583                 if (conflictingArtifact != null
2584                         && !additionalArtifact.getVersion().equals(conflictingArtifact.getVersion())) {
2585                     getConsoleLogger()
2586                             .warning(
2587                                     "Potential classpath conflict between project dependency and resolved additionalClasspathDependency: Found multiple versions of "
2588                                             + additionalArtifact.getDependencyConflictId() + ": "
2589                                             + additionalArtifact.getVersion() + " and "
2590                                             + conflictingArtifact.getVersion());
2591                 }
2592                 additionalClasspathElements.add(additionalArtifact.getFile().getAbsolutePath());
2593             }
2594         }
2595         return new TestClassPath(
2596                 classpathArtifacts, getMainBuildPath(), getTestClassesDirectory(), additionalClasspathElements);
2597     }
2598 
2599     protected Collection<Artifact> resolveDependencies(List<Dependency> dependencies) throws MojoFailureException {
2600         Map<String, Artifact> dependencyConflictIdsAndArtifacts = new HashMap<>();
2601         try {
2602             dependencies.stream()
2603                     .map(dependency -> {
2604                         try {
2605                             return surefireDependencyResolver.resolveDependencies(
2606                                     session.getRepositorySession(), project.getRemoteProjectRepositories(), dependency);
2607                         } catch (MojoExecutionException e) {
2608                             throw new IllegalStateException(e);
2609                         }
2610                     })
2611                     .forEach(artifacts -> {
2612                         for (Artifact a : artifacts) {
2613                             Artifact conflictingArtifact =
2614                                     dependencyConflictIdsAndArtifacts.get(a.getDependencyConflictId());
2615                             if (conflictingArtifact != null
2616                                     && !a.getVersion().equals(conflictingArtifact.getVersion())) {
2617                                 getConsoleLogger()
2618                                         .warning(
2619                                                 "Potential classpath conflict among resolved additionalClasspathDependencies: Found multiple versions of "
2620                                                         + a.getDependencyConflictId() + ": " + a.getVersion() + " and "
2621                                                         + conflictingArtifact.getVersion());
2622                             } else {
2623                                 dependencyConflictIdsAndArtifacts.put(a.getDependencyConflictId(), a);
2624                             }
2625                         }
2626                     });
2627         } catch (IllegalStateException e) {
2628             throw new MojoFailureException(e.getMessage(), e.getCause());
2629         }
2630         return dependencyConflictIdsAndArtifacts.values();
2631     }
2632 
2633     /**
2634      * Return a new set containing only the artifacts not accepted by the given filter.
2635      *
2636      * @param artifacts The unfiltered artifacts
2637      * @param filter    The excludes filter to apply
2638      * @return The filtered result
2639      */
2640     private static Set<Artifact> filterArtifacts(Set<Artifact> artifacts, ArtifactFilter filter) {
2641         Set<Artifact> filteredArtifacts = new LinkedHashSet<>();
2642 
2643         for (Artifact artifact : artifacts) {
2644             if (!filter.include(artifact)) {
2645                 filteredArtifacts.add(artifact);
2646             }
2647         }
2648 
2649         return filteredArtifacts;
2650     }
2651 
2652     private void showMap(Map<?, ?> map, String setting) {
2653         for (Object o : map.keySet()) {
2654             String key = (String) o;
2655             String value = (String) map.get(key);
2656             getConsoleLogger().debug("Setting " + setting + " [" + key + "]=[" + value + "]");
2657         }
2658     }
2659 
2660     private <T> void showArray(T[] array, String setting) {
2661         for (T e : array) {
2662             getConsoleLogger().debug("Setting " + setting + " [" + e + "]");
2663         }
2664     }
2665 
2666     private Classpath getArtifactClasspath(Artifact surefireArtifact) throws MojoExecutionException {
2667         Classpath existing = classpathCache.getCachedClassPath(surefireArtifact.getArtifactId());
2668         if (existing == null) {
2669             List<String> items = new ArrayList<>();
2670             Set<Artifact> booterArtifacts = surefireDependencyResolver.resolveArtifacts(
2671                     session.getRepositorySession(), project.getRemotePluginRepositories(), surefireArtifact);
2672             for (Artifact artifact : booterArtifacts) {
2673                 items.add(artifact.getFile().getAbsolutePath());
2674             }
2675             existing = new Classpath(items);
2676             classpathCache.setCachedClasspath(surefireArtifact.getArtifactId(), existing);
2677         }
2678         return existing;
2679     }
2680 
2681     private Properties getUserProperties() {
2682         return getSession().getUserProperties();
2683     }
2684 
2685     private void ensureWorkingDirectoryExists() throws MojoFailureException {
2686         if (getWorkingDirectory() == null) {
2687             throw new MojoFailureException("workingDirectory cannot be null");
2688         }
2689 
2690         if (isForking()) {
2691             // Postpone directory creation till forked JVM creation
2692             // see ForkConfiguration.createCommandLine
2693             return;
2694         }
2695 
2696         if (!getWorkingDirectory().exists()) {
2697             if (!getWorkingDirectory().mkdirs()) {
2698                 throw new MojoFailureException("Cannot create workingDirectory " + getWorkingDirectory());
2699             }
2700         }
2701 
2702         if (!getWorkingDirectory().isDirectory()) {
2703             throw new MojoFailureException(
2704                     "workingDirectory " + getWorkingDirectory() + " exists and is not a directory");
2705         }
2706     }
2707 
2708     private void ensureParallelRunningCompatibility() throws MojoFailureException {
2709         if (isMavenParallel() && isNotForking()) {
2710             throw new MojoFailureException("parallel maven execution is not compatible with surefire forkCount 0");
2711         }
2712     }
2713 
2714     private void warnIfUselessUseSystemClassLoaderParameter() {
2715         if (isUseSystemClassLoader() && isNotForking()) {
2716             getConsoleLogger().warning("useSystemClassLoader setting has no effect when not forking");
2717         }
2718     }
2719 
2720     private boolean isNotForking() {
2721         return !isForking();
2722     }
2723 
2724     private List<CommandLineOption> commandLineOptions() {
2725         return SurefireHelper.commandLineOptions(getSession(), getConsoleLogger());
2726     }
2727 
2728     private void warnIfDefunctGroupsCombinations() throws MojoFailureException, MojoExecutionException {
2729         if (isAnyGroupsSelected()) {
2730             if (getTestNgArtifact() == null) {
2731                 Artifact junitArtifact = getJunitArtifact();
2732                 boolean junit47Compatible = isJunit47Compatible(junitArtifact);
2733                 boolean junit5PlatformCompatible = getJUnit5Artifact() != null;
2734                 if (!junit47Compatible && !junit5PlatformCompatible) {
2735                     if (junitArtifact != null) {
2736                         throw new MojoFailureException("groups/excludedGroups are specified but JUnit version on "
2737                                 + "classpath is too old to support groups. "
2738                                 + "Check your dependency:tree to see if your project "
2739                                 + "is picking up an old junit version");
2740                     }
2741                     throw new MojoFailureException("groups/excludedGroups require TestNG, JUnit48+ or JUnit 5 "
2742                             + "(a specific engine required on classpath) on project test classpath");
2743                 }
2744             }
2745         }
2746     }
2747 
2748     private void warnIfRerunClashes() throws MojoFailureException {
2749         if (getRerunFailingTestsCount() < 0) {
2750             throw new MojoFailureException("Parameter \"rerunFailingTestsCount\" should not be negative.");
2751         }
2752 
2753         if (getSkipAfterFailureCount() < 0) {
2754             throw new MojoFailureException("Parameter \"skipAfterFailureCount\" should not be negative.");
2755         }
2756     }
2757 
2758     private void warnIfWrongShutdownValue() throws MojoFailureException {
2759         if (!Shutdown.isKnown(getShutdown())) {
2760             throw new MojoFailureException("Parameter \"shutdown\" should have values " + Shutdown.listParameters());
2761         }
2762     }
2763 
2764     private void warnIfNotApplicableSkipAfterFailureCount() throws MojoFailureException {
2765         int skipAfterFailureCount = getSkipAfterFailureCount();
2766 
2767         if (skipAfterFailureCount < 0) {
2768             throw new MojoFailureException("Parameter \"skipAfterFailureCount\" should not be negative.");
2769         } else if (skipAfterFailureCount > 0) {
2770             try {
2771                 Artifact testng = getTestNgArtifact();
2772                 if (testng != null) {
2773                     VersionRange range = VersionRange.createFromVersionSpec("[5.10,)");
2774                     if (!range.containsVersion(new DefaultArtifactVersion(testng.getVersion()))) {
2775                         throw new MojoFailureException(
2776                                 "Parameter \"skipAfterFailureCount\" expects TestNG Version 5.10 or higher. "
2777                                         + "java.lang.NoClassDefFoundError: org/testng/IInvokedMethodListener");
2778                     }
2779                 } else {
2780                     // TestNG is dependent on JUnit
2781                     Artifact junit = getJunitArtifact();
2782                     if (junit != null) {
2783                         VersionRange range = VersionRange.createFromVersionSpec("[4.0,)");
2784                         if (!range.containsVersion(new DefaultArtifactVersion(junit.getVersion()))) {
2785                             throw new MojoFailureException(
2786                                     "Parameter \"skipAfterFailureCount\" expects JUnit Version 4.0 or higher. "
2787                                             + "java.lang.NoSuchMethodError: "
2788                                             + "org.junit.runner.notification.RunNotifier.pleaseStop()V");
2789                         }
2790                     }
2791                 }
2792             } catch (MojoExecutionException e) {
2793                 throw new MojoFailureException(e.getLocalizedMessage());
2794             } catch (InvalidVersionSpecificationException e) {
2795                 throw new RuntimeException(e);
2796             }
2797         }
2798     }
2799 
2800     private void warnIfIllegalTempDir() throws MojoFailureException {
2801         if (isEmpty(getTempDir())) {
2802             throw new MojoFailureException("Parameter 'tempDir' should not be blank string.");
2803         }
2804     }
2805 
2806     protected void warnIfIllegalFailOnFlakeCount() throws MojoFailureException {}
2807 
2808     private void printDefaultSeedIfNecessary() {
2809         if (getRunOrder().equals(RunOrder.RANDOM.name())) {
2810             if (getRunOrderRandomSeed() == null) {
2811                 setRunOrderRandomSeed(System.nanoTime());
2812             }
2813             getConsoleLogger()
2814                     .info("Tests will run in random order. To reproduce ordering use flag -D" + getPluginName()
2815                             + ".runOrder.random.seed=" + getRunOrderRandomSeed());
2816         }
2817     }
2818 
2819     final class TestNgProviderInfo implements ProviderInfo {
2820         private final Artifact testNgArtifact;
2821 
2822         TestNgProviderInfo(Artifact testNgArtifact) {
2823             this.testNgArtifact = testNgArtifact;
2824         }
2825 
2826         @Override
2827         @Nonnull
2828         public String getProviderName() {
2829             return "org.apache.maven.surefire.testng.TestNGProvider";
2830         }
2831 
2832         @Override
2833         public boolean isApplicable() {
2834             return testNgArtifact != null;
2835         }
2836 
2837         @Override
2838         public void addProviderProperties() throws MojoExecutionException {
2839             convertTestNGParameters();
2840         }
2841 
2842         @Nonnull
2843         @Override
2844         public List<String[]> getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) {
2845             return emptyList();
2846         }
2847 
2848         @Override
2849         @Nonnull
2850         public Set<Artifact> getProviderClasspath() throws MojoExecutionException {
2851             Artifact surefireArtifact = getBooterArtifact();
2852             String version = surefireArtifact.getBaseVersion();
2853             return surefireDependencyResolver.getProviderClasspath(
2854                     session.getRepositorySession(), project.getRemotePluginRepositories(), "surefire-testng", version);
2855         }
2856     }
2857 
2858     final class JUnit3ProviderInfo implements ProviderInfo {
2859         @Override
2860         @Nonnull
2861         public String getProviderName() {
2862             return "org.apache.maven.surefire.junit.JUnit3Provider";
2863         }
2864 
2865         @Override
2866         public boolean isApplicable() {
2867             return true;
2868         }
2869 
2870         @Override
2871         public void addProviderProperties() {}
2872 
2873         @Nonnull
2874         @Override
2875         public List<String[]> getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) {
2876             return emptyList();
2877         }
2878 
2879         @Override
2880         @Nonnull
2881         public Set<Artifact> getProviderClasspath() throws MojoExecutionException {
2882             // add the JUnit provider as default - it doesn't require JUnit to be present,
2883             // since it supports POJO tests.
2884             String version = getBooterArtifact().getBaseVersion();
2885             return surefireDependencyResolver.getProviderClasspath(
2886                     session.getRepositorySession(), project.getRemotePluginRepositories(), "surefire-junit3", version);
2887         }
2888     }
2889 
2890     final class JUnit4ProviderInfo implements ProviderInfo {
2891         private final Artifact junitArtifact;
2892 
2893         private final Artifact junitDepArtifact;
2894 
2895         JUnit4ProviderInfo(Artifact junitArtifact, Artifact junitDepArtifact) {
2896             this.junitArtifact = junitArtifact;
2897             this.junitDepArtifact = junitDepArtifact;
2898         }
2899 
2900         @Override
2901         @Nonnull
2902         public String getProviderName() {
2903             return "org.apache.maven.surefire.junit4.JUnit4Provider";
2904         }
2905 
2906         @Override
2907         public boolean isApplicable() {
2908             return junitDepArtifact != null || isAnyJunit4(junitArtifact);
2909         }
2910 
2911         @Override
2912         public void addProviderProperties() {}
2913 
2914         @Nonnull
2915         @Override
2916         public List<String[]> getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) {
2917             return emptyList();
2918         }
2919 
2920         @Override
2921         @Nonnull
2922         public Set<Artifact> getProviderClasspath() throws MojoExecutionException {
2923             String version = getBooterArtifact().getBaseVersion();
2924             return surefireDependencyResolver.getProviderClasspath(
2925                     session.getRepositorySession(), project.getRemotePluginRepositories(), "surefire-junit4", version);
2926         }
2927     }
2928 
2929     final class JUnitPlatformProviderInfo implements ProviderInfo {
2930         private static final String PROVIDER_DEP_GID = "org.junit.platform";
2931         private static final String PROVIDER_DEP_AID = "junit-platform-launcher";
2932 
2933         private final Artifact junitPlatformRunnerArtifact;
2934         private final Artifact junitPlatformArtifact;
2935         private final TestClassPath testClasspath;
2936 
2937         JUnitPlatformProviderInfo(
2938                 Artifact junitPlatformRunnerArtifact,
2939                 Artifact junitPlatformArtifact,
2940                 @Nonnull TestClassPath testClasspath) {
2941             this.junitPlatformRunnerArtifact = junitPlatformRunnerArtifact;
2942             this.junitPlatformArtifact = junitPlatformArtifact;
2943             this.testClasspath = testClasspath;
2944         }
2945 
2946         @Override
2947         @Nonnull
2948         public String getProviderName() {
2949             return "org.apache.maven.surefire.junitplatform.JUnitPlatformProvider";
2950         }
2951 
2952         @Override
2953         public boolean isApplicable() {
2954             return junitPlatformRunnerArtifact == null && junitPlatformArtifact != null;
2955         }
2956 
2957         @Override
2958         public void addProviderProperties() {
2959             convertGroupParameters();
2960             convertJunitEngineParameters();
2961         }
2962 
2963         @Nonnull
2964         @Override
2965         public List<String[]> getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) {
2966             boolean hasTestDescriptor = forkRequirements.isModularPath() && forkRequirements.hasTestModuleDescriptor();
2967             return hasTestDescriptor ? getJpmsArgs() : Collections.<String[]>emptyList();
2968         }
2969 
2970         @Override
2971         @Nonnull
2972         public Set<Artifact> getProviderClasspath() throws MojoExecutionException {
2973             String surefireVersion = getBooterArtifact().getBaseVersion();
2974             Map<String, Artifact> providerArtifacts = surefireDependencyResolver.getProviderClasspathAsMap(
2975                     session.getRepositorySession(),
2976                     project.getRemotePluginRepositories(),
2977                     "surefire-junit-platform",
2978                     surefireVersion);
2979             Map<String, Artifact> testDeps = testClasspath.getTestDependencies();
2980 
2981             Plugin plugin = getPluginDescriptor().getPlugin();
2982             Map<String, Artifact> pluginDeps = surefireDependencyResolver.resolvePluginDependencies(
2983                     session.getRepositorySession(),
2984                     project.getRemotePluginRepositories(),
2985                     plugin,
2986                     getPluginArtifactMap());
2987 
2988             if (hasDependencyPlatformEngine(pluginDeps)) {
2989                 providerArtifacts.putAll(pluginDeps);
2990             } else {
2991                 String engineVersion = null;
2992                 if (hasDependencyJupiterAPI(testDeps)
2993                         && !testDeps.containsKey("org.junit.jupiter:junit-jupiter-engine")) {
2994                     String engineGroupId = "org.junit.jupiter";
2995                     String engineArtifactId = "junit-jupiter-engine";
2996                     String engineCoordinates = engineGroupId + ":" + engineArtifactId;
2997                     String api = "org.junit.jupiter:junit-jupiter-api";
2998                     engineVersion = testDeps.get(api).getBaseVersion();
2999                     getConsoleLogger()
3000                             .debug("Test dependencies contain " + api + ". Resolving " + engineCoordinates + ":"
3001                                     + engineVersion);
3002                     addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts);
3003                 }
3004 
3005                 if ((testDeps.containsKey("junit:junit") || testDeps.containsKey("junit:junit-dep"))
3006                         && !testDeps.containsKey("org.junit.vintage:junit-vintage-engine")) {
3007                     String engineGroupId = "org.junit.vintage";
3008                     String engineArtifactId = "junit-vintage-engine";
3009                     String engineCoordinates = engineGroupId + ":" + engineArtifactId;
3010 
3011                     if (engineVersion != null) {
3012                         getConsoleLogger()
3013                                 .debug("Test dependencies contain JUnit4. Resolving " + engineCoordinates + ":"
3014                                         + engineVersion);
3015                         addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts);
3016                     }
3017                 }
3018             }
3019 
3020             narrowDependencies(providerArtifacts, testDeps);
3021             alignProviderVersions(providerArtifacts);
3022 
3023             return new LinkedHashSet<>(providerArtifacts.values());
3024         }
3025 
3026         private List<String[]> getJpmsArgs() {
3027             List<String[]> args = new ArrayList<>();
3028 
3029             args.add(new String[] {
3030                 "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.util=ALL-UNNAMED"
3031             });
3032 
3033             args.add(new String[] {
3034                 "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.logging=ALL-UNNAMED"
3035             });
3036 
3037             return args;
3038         }
3039 
3040         private void addEngineByApi(
3041                 String engineGroupId,
3042                 String engineArtifactId,
3043                 String engineVersion,
3044                 Map<String, Artifact> providerArtifacts)
3045                 throws MojoExecutionException {
3046             for (Artifact dep : resolve(engineGroupId, engineArtifactId, engineVersion, null, "jar")) {
3047                 String key = dep.getGroupId() + ":" + dep.getArtifactId();
3048                 providerArtifacts.put(key, dep);
3049             }
3050         }
3051 
3052         private void narrowDependencies(
3053                 Map<String, Artifact> providerArtifacts, Map<String, Artifact> testDependencies) {
3054             providerArtifacts.keySet().removeAll(testDependencies.keySet());
3055         }
3056 
3057         private void alignProviderVersions(Map<String, Artifact> providerArtifacts) throws MojoExecutionException {
3058             String version = junitPlatformArtifact.getBaseVersion();
3059             for (Artifact launcherArtifact : resolve(PROVIDER_DEP_GID, PROVIDER_DEP_AID, version, null, "jar")) {
3060                 String key = launcherArtifact.getGroupId() + ":" + launcherArtifact.getArtifactId();
3061                 if (providerArtifacts.containsKey(key)) {
3062                     providerArtifacts.put(key, launcherArtifact);
3063                 }
3064             }
3065         }
3066 
3067         private Set<Artifact> resolve(String g, String a, String v, String c, String t) throws MojoExecutionException {
3068             ArtifactHandler handler = junitPlatformArtifact.getArtifactHandler();
3069             Artifact artifact = new DefaultArtifact(g, a, v, null, t, c, handler);
3070             getConsoleLogger().debug("Resolving artifact " + g + ":" + a + ":" + v);
3071             Set<Artifact> r = surefireDependencyResolver.resolveArtifacts(
3072                     session.getRepositorySession(), project.getRemoteProjectRepositories(), artifact);
3073             getConsoleLogger().debug("Resolved artifact " + g + ":" + a + ":" + v + " to " + r);
3074             return r;
3075         }
3076 
3077         private boolean hasDependencyJupiterAPI(Map<String, Artifact> dependencies) {
3078             return dependencies.containsKey("org.junit.jupiter:junit-jupiter-api");
3079         }
3080 
3081         private boolean hasDependencyPlatformEngine(Map<String, Artifact> dependencies) {
3082             for (Entry<String, Artifact> dependency : dependencies.entrySet()) {
3083                 if (dependency.getKey().equals("org.junit.platform:junit-platform-engine")) {
3084                     return true;
3085                 }
3086             }
3087 
3088             return false;
3089         }
3090     }
3091 
3092     final class JUnitCoreProviderInfo implements ProviderInfo {
3093         private final Artifact junitArtifact;
3094 
3095         private final Artifact junitDepArtifact;
3096 
3097         JUnitCoreProviderInfo(Artifact junitArtifact, Artifact junitDepArtifact) {
3098             this.junitArtifact = junitArtifact;
3099             this.junitDepArtifact = junitDepArtifact;
3100         }
3101 
3102         @Override
3103         @Nonnull
3104         public String getProviderName() {
3105             return "org.apache.maven.surefire.junitcore.JUnitCoreProvider";
3106         }
3107 
3108         private boolean is47CompatibleJunitDep() {
3109             return isJunit47Compatible(junitDepArtifact);
3110         }
3111 
3112         @Override
3113         public boolean isApplicable() {
3114             final boolean isJunitArtifact47 = isAnyJunit4(junitArtifact) && isJunit47Compatible(junitArtifact);
3115             final boolean isAny47ProvidersForces = isAnyConcurrencySelected() || isAnyGroupsSelected();
3116             return isAny47ProvidersForces && (isJunitArtifact47 || is47CompatibleJunitDep());
3117         }
3118 
3119         @Override
3120         public void addProviderProperties() throws MojoExecutionException {
3121             convertJunitCoreParameters();
3122             convertGroupParameters();
3123             convertJunitEngineParameters();
3124         }
3125 
3126         @Nonnull
3127         @Override
3128         public List<String[]> getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) {
3129             return emptyList();
3130         }
3131 
3132         @Override
3133         @Nonnull
3134         public Set<Artifact> getProviderClasspath() throws MojoExecutionException {
3135             String version = getBooterArtifact().getBaseVersion();
3136             return surefireDependencyResolver.getProviderClasspath(
3137                     session.getRepositorySession(), project.getRemotePluginRepositories(), "surefire-junit47", version);
3138         }
3139     }
3140 
3141     /**
3142      * Provides the Provider information for manually configured providers.
3143      */
3144     final class DynamicProviderInfo implements ConfigurableProviderInfo {
3145         final String providerName;
3146 
3147         DynamicProviderInfo(String providerName) {
3148             this.providerName = providerName;
3149         }
3150 
3151         @Override
3152         public ProviderInfo instantiate(String providerName) {
3153             return new DynamicProviderInfo(providerName);
3154         }
3155 
3156         @Override
3157         @Nonnull
3158         public String getProviderName() {
3159             return providerName;
3160         }
3161 
3162         @Override
3163         public boolean isApplicable() {
3164             return true;
3165         }
3166 
3167         @Override
3168         public void addProviderProperties() throws MojoExecutionException {
3169             // Ok this is a bit lazy.
3170             convertJunitCoreParameters();
3171             convertTestNGParameters();
3172         }
3173 
3174         @Nonnull
3175         @Override
3176         public List<String[]> getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) {
3177             return emptyList();
3178         }
3179 
3180         @Override
3181         @Nonnull
3182         public Set<Artifact> getProviderClasspath() throws MojoExecutionException {
3183             Plugin plugin = getPluginDescriptor().getPlugin();
3184             Map<String, Artifact> providerArtifacts = surefireDependencyResolver.resolvePluginDependencies(
3185                     session.getRepositorySession(),
3186                     project.getRemotePluginRepositories(),
3187                     plugin,
3188                     getPluginArtifactMap());
3189             return new LinkedHashSet<>(providerArtifacts.values());
3190         }
3191     }
3192 
3193     File createSurefireBootDirectoryInBuild() {
3194         File tmp = new File(getProjectBuildDirectory(), getTempDir());
3195         //noinspection ResultOfMethodCallIgnored
3196         tmp.mkdirs();
3197         return tmp;
3198     }
3199 
3200     File createSurefireBootDirectoryInTemp() {
3201         try {
3202             return Files.createTempDirectory(getTempDir()).toFile();
3203         } catch (IOException e) {
3204             return createSurefireBootDirectoryInBuild();
3205         }
3206     }
3207 
3208     @Override
3209     public String getLocalRepositoryPath() {
3210         return Optional.ofNullable(
3211                         session.getRepositorySession().getLocalRepository().getBasedir())
3212                 .map(File::getAbsolutePath)
3213                 .orElse(".");
3214     }
3215 
3216     public Properties getSystemProperties() {
3217         return systemProperties;
3218     }
3219 
3220     @SuppressWarnings("UnusedDeclaration")
3221     public void setSystemProperties(Properties systemProperties) {
3222         this.systemProperties = systemProperties;
3223     }
3224 
3225     public Map<String, String> getSystemPropertyVariables() {
3226         return systemPropertyVariables;
3227     }
3228 
3229     @SuppressWarnings("UnusedDeclaration")
3230     public void setSystemPropertyVariables(Map<String, String> systemPropertyVariables) {
3231         this.systemPropertyVariables = systemPropertyVariables;
3232     }
3233 
3234     /**
3235      * List of System properties, loaded from a file, to pass to the JUnit tests.
3236      *
3237      * @since 2.8.2
3238      */
3239     public abstract File getSystemPropertiesFile();
3240 
3241     @SuppressWarnings("UnusedDeclaration")
3242     public abstract void setSystemPropertiesFile(File systemPropertiesFile);
3243 
3244     private Properties getProperties() {
3245         return properties;
3246     }
3247 
3248     public void setProperties(Properties properties) {
3249         this.properties = properties;
3250     }
3251 
3252     public Map<String, Artifact> getPluginArtifactMap() {
3253         return pluginArtifactMap;
3254     }
3255 
3256     @SuppressWarnings("UnusedDeclaration")
3257     public void setPluginArtifactMap(Map<String, Artifact> pluginArtifactMap) {
3258         this.pluginArtifactMap = pluginArtifactMap;
3259     }
3260 
3261     public Map<String, Artifact> getProjectArtifactMap() {
3262         return projectArtifactMap;
3263     }
3264 
3265     @SuppressWarnings("UnusedDeclaration")
3266     public void setProjectArtifactMap(Map<String, Artifact> projectArtifactMap) {
3267         this.projectArtifactMap = projectArtifactMap;
3268     }
3269 
3270     public String getReportNameSuffix() {
3271         return reportNameSuffix;
3272     }
3273 
3274     @SuppressWarnings("UnusedDeclaration")
3275     public void setReportNameSuffix(String reportNameSuffix) {
3276         this.reportNameSuffix = reportNameSuffix;
3277     }
3278 
3279     public boolean isRedirectTestOutputToFile() {
3280         return redirectTestOutputToFile;
3281     }
3282 
3283     @SuppressWarnings("UnusedDeclaration")
3284     public void setRedirectTestOutputToFile(boolean redirectTestOutputToFile) {
3285         this.redirectTestOutputToFile = redirectTestOutputToFile;
3286     }
3287 
3288     public boolean getFailIfNoTests() {
3289         return failIfNoTests;
3290     }
3291 
3292     public void setFailIfNoTests(boolean failIfNoTests) {
3293         this.failIfNoTests = failIfNoTests;
3294     }
3295 
3296     public String getJvm() {
3297         return jvm;
3298     }
3299 
3300     public String getArgLine() {
3301         return argLine;
3302     }
3303 
3304     @SuppressWarnings("UnusedDeclaration")
3305     public void setArgLine(String argLine) {
3306         this.argLine = argLine;
3307     }
3308 
3309     public Map<String, String> getEnvironmentVariables() {
3310         return environmentVariables;
3311     }
3312 
3313     @SuppressWarnings("UnusedDeclaration")
3314     public void setEnvironmentVariables(Map<String, String> environmentVariables) {
3315         this.environmentVariables = environmentVariables;
3316     }
3317 
3318     public File getWorkingDirectory() {
3319         return workingDirectory;
3320     }
3321 
3322     @SuppressWarnings("UnusedDeclaration")
3323     public void setWorkingDirectory(File workingDirectory) {
3324         this.workingDirectory = workingDirectory;
3325     }
3326 
3327     public boolean isChildDelegation() {
3328         return childDelegation;
3329     }
3330 
3331     @SuppressWarnings("UnusedDeclaration")
3332     public void setChildDelegation(boolean childDelegation) {
3333         this.childDelegation = childDelegation;
3334     }
3335 
3336     public String getGroups() {
3337         return groups;
3338     }
3339 
3340     @SuppressWarnings("UnusedDeclaration")
3341     public void setGroups(String groups) {
3342         this.groups = groups;
3343     }
3344 
3345     public String getExcludedGroups() {
3346         return excludedGroups;
3347     }
3348 
3349     @SuppressWarnings("UnusedDeclaration")
3350     public void setExcludedGroups(String excludedGroups) {
3351         this.excludedGroups = excludedGroups;
3352     }
3353 
3354     public String getJunitArtifactName() {
3355         return junitArtifactName;
3356     }
3357 
3358     @SuppressWarnings("UnusedDeclaration")
3359     public void setJunitArtifactName(String junitArtifactName) {
3360         this.junitArtifactName = junitArtifactName;
3361     }
3362 
3363     public String getTestNGArtifactName() {
3364         return testNGArtifactName;
3365     }
3366 
3367     @SuppressWarnings("UnusedDeclaration")
3368     public void setTestNGArtifactName(String testNGArtifactName) {
3369         this.testNGArtifactName = testNGArtifactName;
3370     }
3371 
3372     public int getThreadCount() {
3373         return threadCount;
3374     }
3375 
3376     @SuppressWarnings("UnusedDeclaration")
3377     public void setThreadCount(int threadCount) {
3378         this.threadCount = threadCount;
3379     }
3380 
3381     public boolean getPerCoreThreadCount() {
3382         return perCoreThreadCount;
3383     }
3384 
3385     @SuppressWarnings("UnusedDeclaration")
3386     public void setPerCoreThreadCount(boolean perCoreThreadCount) {
3387         this.perCoreThreadCount = perCoreThreadCount;
3388     }
3389 
3390     public boolean getUseUnlimitedThreads() {
3391         return useUnlimitedThreads;
3392     }
3393 
3394     @SuppressWarnings("UnusedDeclaration")
3395     public void setUseUnlimitedThreads(boolean useUnlimitedThreads) {
3396         this.useUnlimitedThreads = useUnlimitedThreads;
3397     }
3398 
3399     public String getParallel() {
3400         return parallel;
3401     }
3402 
3403     @SuppressWarnings("UnusedDeclaration")
3404     public void setParallel(String parallel) {
3405         this.parallel = parallel;
3406     }
3407 
3408     public boolean isParallelOptimized() {
3409         return parallelOptimized;
3410     }
3411 
3412     @SuppressWarnings("UnusedDeclaration")
3413     public void setParallelOptimized(boolean parallelOptimized) {
3414         this.parallelOptimized = parallelOptimized;
3415     }
3416 
3417     public int getThreadCountSuites() {
3418         return threadCountSuites;
3419     }
3420 
3421     public void setThreadCountSuites(int threadCountSuites) {
3422         this.threadCountSuites = threadCountSuites;
3423     }
3424 
3425     public int getThreadCountClasses() {
3426         return threadCountClasses;
3427     }
3428 
3429     public void setThreadCountClasses(int threadCountClasses) {
3430         this.threadCountClasses = threadCountClasses;
3431     }
3432 
3433     public int getThreadCountMethods() {
3434         return threadCountMethods;
3435     }
3436 
3437     public void setThreadCountMethods(int threadCountMethods) {
3438         this.threadCountMethods = threadCountMethods;
3439     }
3440 
3441     public boolean isTrimStackTrace() {
3442         return trimStackTrace;
3443     }
3444 
3445     @SuppressWarnings("UnusedDeclaration")
3446     public void setTrimStackTrace(boolean trimStackTrace) {
3447         this.trimStackTrace = trimStackTrace;
3448     }
3449 
3450     public boolean isDisableXmlReport() {
3451         return disableXmlReport;
3452     }
3453 
3454     @SuppressWarnings("UnusedDeclaration")
3455     public void setDisableXmlReport(boolean disableXmlReport) {
3456         this.disableXmlReport = disableXmlReport;
3457     }
3458 
3459     public boolean isEnableAssertions() {
3460         return enableAssertions;
3461     }
3462 
3463     public boolean effectiveIsEnableAssertions() {
3464         if (getArgLine() != null) {
3465             List<String> args = asList(getArgLine().split(" "));
3466             if (args.contains("-da") || args.contains("-disableassertions")) {
3467                 return false;
3468             }
3469         }
3470         return isEnableAssertions();
3471     }
3472 
3473     @SuppressWarnings("UnusedDeclaration")
3474     public void setEnableAssertions(boolean enableAssertions) {
3475         this.enableAssertions = enableAssertions;
3476     }
3477 
3478     public MavenSession getSession() {
3479         return session;
3480     }
3481 
3482     @SuppressWarnings("UnusedDeclaration")
3483     public void setSession(MavenSession session) {
3484         this.session = session;
3485     }
3486 
3487     public String getObjectFactory() {
3488         return objectFactory;
3489     }
3490 
3491     @SuppressWarnings("UnusedDeclaration")
3492     public void setObjectFactory(String objectFactory) {
3493         this.objectFactory = objectFactory;
3494     }
3495 
3496     public ToolchainManager getToolchainManager() {
3497         return toolchainManager;
3498     }
3499 
3500     @SuppressWarnings("UnusedDeclaration")
3501     public void setToolchainManager(ToolchainManager toolchainManager) {
3502         this.toolchainManager = toolchainManager;
3503     }
3504 
3505     public boolean isMavenParallel() {
3506         return parallelMavenExecution != null && parallelMavenExecution;
3507     }
3508 
3509     public String[] getDependenciesToScan() {
3510         return dependenciesToScan;
3511     }
3512 
3513     public void setDependenciesToScan(String[] dependenciesToScan) {
3514         this.dependenciesToScan = dependenciesToScan;
3515     }
3516 
3517     @SuppressWarnings("UnusedDeclaration")
3518     void setPluginDescriptor(PluginDescriptor pluginDescriptor) {
3519         this.pluginDescriptor = pluginDescriptor;
3520     }
3521 
3522     public PluginDescriptor getPluginDescriptor() {
3523         return pluginDescriptor;
3524     }
3525 
3526     public MavenProject getProject() {
3527         return project;
3528     }
3529 
3530     @SuppressWarnings("UnusedDeclaration")
3531     public void setProject(MavenProject project) {
3532         this.project = project;
3533     }
3534 
3535     @Override
3536     public File getTestSourceDirectory() {
3537         return testSourceDirectory;
3538     }
3539 
3540     @Override
3541     public void setTestSourceDirectory(File testSourceDirectory) {
3542         this.testSourceDirectory = testSourceDirectory;
3543     }
3544 
3545     public String getForkCount() {
3546         return forkCount;
3547     }
3548 
3549     public boolean isReuseForks() {
3550         return reuseForks;
3551     }
3552 
3553     public String[] getAdditionalClasspathElements() {
3554         return additionalClasspathElements;
3555     }
3556 
3557     public void setAdditionalClasspathElements(String[] additionalClasspathElements) {
3558         this.additionalClasspathElements = additionalClasspathElements;
3559     }
3560 
3561     public String[] getClasspathDependencyExcludes() {
3562         return classpathDependencyExcludes;
3563     }
3564 
3565     public void setClasspathDependencyExcludes(String[] classpathDependencyExcludes) {
3566         this.classpathDependencyExcludes = classpathDependencyExcludes;
3567     }
3568 
3569     public String getClasspathDependencyScopeExclude() {
3570         return classpathDependencyScopeExclude;
3571     }
3572 
3573     public void setClasspathDependencyScopeExclude(String classpathDependencyScopeExclude) {
3574         this.classpathDependencyScopeExclude = classpathDependencyScopeExclude;
3575     }
3576 
3577     public File getProjectBuildDirectory() {
3578         return projectBuildDirectory;
3579     }
3580 
3581     public void setProjectBuildDirectory(File projectBuildDirectory) {
3582         this.projectBuildDirectory = projectBuildDirectory;
3583     }
3584 
3585     protected void logDebugOrCliShowErrors(String s) {
3586         SurefireHelper.logDebugOrCliShowErrors(s, getConsoleLogger(), cli);
3587     }
3588 
3589     public Map<String, String> getJdkToolchain() {
3590         return jdkToolchain;
3591     }
3592 
3593     public void setJdkToolchain(Map<String, String> jdkToolchain) {
3594         this.jdkToolchain = jdkToolchain;
3595     }
3596 
3597     public String getTempDir() {
3598         return tempDir;
3599     }
3600 
3601     public void setTempDir(String tempDir) {
3602         this.tempDir = tempDir;
3603     }
3604 
3605     private static final class ClasspathCache {
3606         private final Map<String, Classpath> classpaths = new HashMap<>(4);
3607 
3608         private Classpath getCachedClassPath(@Nonnull String artifactId) {
3609             return classpaths.get(artifactId);
3610         }
3611 
3612         private void setCachedClasspath(@Nonnull String key, @Nonnull Classpath classpath) {
3613             classpaths.put(key, classpath);
3614         }
3615 
3616         private Classpath setCachedClasspath(@Nonnull String key, @Nonnull Set<Artifact> artifacts) {
3617             Collection<String> files = new ArrayList<>();
3618             for (Artifact artifact : artifacts) {
3619                 files.add(artifact.getFile().getAbsolutePath());
3620             }
3621             Classpath classpath = new Classpath(files);
3622             setCachedClasspath(key, classpath);
3623             return classpath;
3624         }
3625     }
3626 
3627     /**
3628      * Determines whether the plugin should fail if no tests found to run.
3629      */
3630     enum PluginFailureReason {
3631         NONE,
3632         COULD_NOT_RUN_SPECIFIED_TESTS,
3633         COULD_NOT_RUN_DEFAULT_TESTS,
3634     }
3635 }