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