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