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