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