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