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