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