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