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