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