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