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