All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.plugin.surefire.AbstractSurefireMojo Maven / Gradle / Ivy

There is a newer version: 3.5.2
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.maven.plugin.surefire;

import javax.annotation.Nonnull;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.ZipFile;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugin.surefire.booterclient.ChecksumCalculator;
import org.apache.maven.plugin.surefire.booterclient.ClasspathForkConfiguration;
import org.apache.maven.plugin.surefire.booterclient.ForkConfiguration;
import org.apache.maven.plugin.surefire.booterclient.ForkStarter;
import org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfiguration;
import org.apache.maven.plugin.surefire.booterclient.ModularClasspathForkConfiguration;
import org.apache.maven.plugin.surefire.booterclient.Platform;
import org.apache.maven.plugin.surefire.extensions.LegacyForkNodeFactory;
import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
import org.apache.maven.plugin.surefire.log.PluginConsoleLogger;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.plugin.surefire.util.DependencyScanner;
import org.apache.maven.plugin.surefire.util.DirectoryScanner;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
import org.apache.maven.surefire.api.booter.ProviderParameterNames;
import org.apache.maven.surefire.api.booter.Shutdown;
import org.apache.maven.surefire.api.cli.CommandLineOption;
import org.apache.maven.surefire.api.report.ReporterConfiguration;
import org.apache.maven.surefire.api.suite.RunResult;
import org.apache.maven.surefire.api.testset.DirectoryScannerParameters;
import org.apache.maven.surefire.api.testset.RunOrderParameters;
import org.apache.maven.surefire.api.testset.TestArtifactInfo;
import org.apache.maven.surefire.api.testset.TestListResolver;
import org.apache.maven.surefire.api.testset.TestRequest;
import org.apache.maven.surefire.api.testset.TestSetFailedException;
import org.apache.maven.surefire.api.util.DefaultScanResult;
import org.apache.maven.surefire.api.util.RunOrder;
import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
import org.apache.maven.surefire.booter.Classpath;
import org.apache.maven.surefire.booter.ClasspathConfiguration;
import org.apache.maven.surefire.booter.KeyValueSource;
import org.apache.maven.surefire.booter.ModularClasspath;
import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
import org.apache.maven.surefire.booter.ProcessCheckerType;
import org.apache.maven.surefire.booter.ProviderConfiguration;
import org.apache.maven.surefire.booter.StartupConfiguration;
import org.apache.maven.surefire.booter.SurefireBooterForkException;
import org.apache.maven.surefire.booter.SurefireExecutionException;
import org.apache.maven.surefire.extensions.ForkNodeFactory;
import org.apache.maven.surefire.providerapi.ConfigurableProviderInfo;
import org.apache.maven.surefire.providerapi.ProviderDetector;
import org.apache.maven.surefire.providerapi.ProviderInfo;
import org.apache.maven.surefire.providerapi.ProviderRequirements;
import org.apache.maven.surefire.shared.utils.io.FileUtils;
import org.apache.maven.toolchain.DefaultToolchain;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.apache.maven.toolchain.java.DefaultJavaToolChain;
import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
import org.codehaus.plexus.languages.java.jpms.LocationManager;
import org.codehaus.plexus.languages.java.jpms.ResolvePathRequest;
import org.codehaus.plexus.languages.java.jpms.ResolvePathResult;
import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
import org.codehaus.plexus.logging.Logger;

import static java.lang.Integer.parseInt;
import static java.util.Arrays.asList;
import static java.util.Collections.addAll;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.PluginFailureReason.COULD_NOT_RUN_DEFAULT_TESTS;
import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.PluginFailureReason.COULD_NOT_RUN_SPECIFIED_TESTS;
import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.PluginFailureReason.NONE;
import static org.apache.maven.plugin.surefire.SurefireDependencyResolver.isWithinVersionSpec;
import static org.apache.maven.plugin.surefire.SurefireHelper.replaceThreadNumberPlaceholders;
import static org.apache.maven.plugin.surefire.util.DependencyScanner.filter;
import static org.apache.maven.surefire.api.booter.ProviderParameterNames.EXCLUDE_JUNIT5_ENGINES_PROP;
import static org.apache.maven.surefire.api.booter.ProviderParameterNames.INCLUDE_JUNIT5_ENGINES_PROP;
import static org.apache.maven.surefire.api.suite.RunResult.failure;
import static org.apache.maven.surefire.api.suite.RunResult.noTestsRun;
import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray;
import static org.apache.maven.surefire.api.util.ReflectionUtils.tryGetMethod;
import static org.apache.maven.surefire.booter.Classpath.emptyClasspath;
import static org.apache.maven.surefire.booter.SystemUtils.endsWithJavaPath;
import static org.apache.maven.surefire.booter.SystemUtils.isBuiltInJava9AtLeast;
import static org.apache.maven.surefire.booter.SystemUtils.isJava9AtLeast;
import static org.apache.maven.surefire.booter.SystemUtils.toJdkHomeFromJvmExec;
import static org.apache.maven.surefire.booter.SystemUtils.toJdkVersionFromReleaseFile;
import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT;
import static org.apache.maven.surefire.shared.lang3.StringUtils.substringBeforeLast;
import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
import static org.apache.maven.surefire.shared.utils.StringUtils.capitalizeFirstLetter;
import static org.apache.maven.surefire.shared.utils.StringUtils.isEmpty;
import static org.apache.maven.surefire.shared.utils.StringUtils.isNotBlank;
import static org.apache.maven.surefire.shared.utils.StringUtils.isNotEmpty;
import static org.apache.maven.surefire.shared.utils.StringUtils.split;
import static org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils.addShutDownHook;
import static org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils.removeShutdownHook;

/**
 * Abstract base class for running tests using Surefire.
 *
 * @author Stephen Connolly
 * @version $Id: SurefirePlugin.java 945065 2010-05-17 10:26:22Z stephenc $
 */
public abstract class AbstractSurefireMojo extends AbstractMojo implements SurefireExecutionParameters {
    private static final Map JAVA_9_MATCHER_OLD_NOTATION = singletonMap("version", "[1.9,)");
    private static final Map JAVA_9_MATCHER = singletonMap("version", "[9,)");
    private static final Platform PLATFORM = new Platform();

    private final ClasspathCache classpathCache = new ClasspathCache();

    /**
     * Note: use the legacy system property disableXmlReport set to {@code true} to disable the report.
     */
    @Parameter
    private SurefireStatelessReporter statelessTestsetReporter;

    @Parameter
    private SurefireConsoleOutputReporter consoleOutputReporter;

    @Parameter
    private SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter;

    /**
     * Information about this plugin, mainly used to lookup this plugin's configuration from the currently executing
     * project.
     *
     * @since 2.12
     */
    @Parameter(defaultValue = "${plugin}", readonly = true, required = true)
    private PluginDescriptor pluginDescriptor;

    /**
     * Set this to "true" to skip running tests, but still compile them. Its use is NOT RECOMMENDED, but quite
     * convenient on occasion.
* Failsafe plugin deprecated the parameter {@code skipTests} and the parameter will be removed in * Failsafe 3.0.0 as it is a source of conflicts between Failsafe and Surefire plugin. * * @since 2.4 */ @Parameter(property = "skipTests", defaultValue = "false") protected boolean skipTests; /** * This old parameter is just like {@code skipTests}, but bound to the old property "maven.test.skip.exec". * * @since 2.3 * @deprecated Use skipTests instead. */ @Deprecated @Parameter(property = "maven.test.skip.exec") protected boolean skipExec; /** * Set this to "true" to bypass unit tests entirely. Its use is NOT RECOMMENDED, especially if you enable it using * the "maven.test.skip" property, because maven.test.skip disables both running the tests and compiling the tests. * Consider using the {@code skipTests} parameter instead. */ @Parameter(property = "maven.test.skip", defaultValue = "false") protected boolean skip; /** * The Maven Project Object. */ @Parameter(defaultValue = "${project}", required = true, readonly = true) private MavenProject project; /** * The base directory of the project being tested. This can be obtained in your integration test via * System.getProperty("basedir"). */ @Parameter(defaultValue = "${basedir}", readonly = true, required = true) protected File basedir; /** * The directory containing generated test classes of the project being tested. This will be included at the * beginning of the test classpath. */ @Parameter(defaultValue = "${project.build.testOutputDirectory}") protected File testClassesDirectory; /** * List of dependencies to exclude from the test classpath at runtime. * Each item is passed as pattern to {@link PatternIncludesArtifactFilter}. * The pattern is matched against the following artifact ids: *
    *
  • {@code groupId:artifactId} (Short ID)
  • *
  • {@code groupId:artifactId:type:classifier} (Dependency Conflict ID)
  • *
  • {@code groupId:artifactId:type:classifier:version} (Full ID)
  • *
* The matching algorithm is described in detail in Advanced Artifact-Matching for the maven-assembly-plugin. This parameter behaves the same as the {@code excludes} pattern described there. * The dependency matching is applied to the project dependency IDs (including transitive ones) after resolving, i.e. excluding * one dependency will not exclude its transitive dependencies! * @since 2.6 */ @Parameter(property = "maven.test.dependency.excludes") private String[] classpathDependencyExcludes; /** * A dependency scope to exclude from the test classpath at runtime. The scope should be one of the scopes defined by * org.apache.maven.artifact.Artifact. This includes the following: *
*
    *
  • compile - system, provided, compile *
  • runtime - compile, runtime *
  • compile+runtime - system, provided, compile, runtime *
  • runtime+system - system, compile, runtime *
  • test - system, provided, compile, runtime, test *
* * @since 2.6 */ @Parameter(defaultValue = "") private String classpathDependencyScopeExclude; /** * Additional elements to be appended to the test classpath at runtime. * Each element must be a file system path to a JAR file or a directory containing classes. * No wildcards are allowed here. * * @since 2.4 */ @Parameter(property = "maven.test.additionalClasspath") private String[] additionalClasspathElements; /** * Additional Maven dependencies to be added to the test classpath at runtime. * Each element supports the parametrization like documented in POM Reference: Dependencies. *

* Those dependencies are automatically collected (i.e. have their full dependency tree calculated) and then all underlying artifacts are resolved from the repository (including their transitive dependencies). * Afterwards the resolved artifacts are filtered to only contain {@code compile} and {@code runtime} scoped ones and appended to the test classpath at runtime * (after the ones from {@link #additionalClasspathElements}). *

* The following differences to regular project dependency resolving apply: *

    *
  • The dependency management from the project is not taken into account.
  • *
  • Conflicts between the different items and the project dependencies are not resolved.
  • *
  • Only external dependencies (outside the current Maven reactor) are supported.
  • *
* * @since 3.2 */ @Parameter(property = "maven.test.additionalClasspathDependencies") private List additionalClasspathDependencies; /** * The test source directory containing test class sources. * Important only for TestNG HTML reports. * * @since 2.2 */ @Parameter(defaultValue = "${project.build.testSourceDirectory}") private File testSourceDirectory; /** * List of System properties to pass to a provider. * * @deprecated Use systemPropertyVariables instead. */ @Deprecated @Parameter private Properties systemProperties; /** * List of System properties to pass to a provider. * * @since 2.5 */ @Parameter private Map systemPropertyVariables; /** * List of properties for configuring all TestNG related configurations. This is the new preferred method of * configuring TestNG. * * @since 2.4 */ @Parameter private Properties properties; /** * Map of plugin artifacts. */ @Parameter(property = "plugin.artifactMap", required = true, readonly = true) private Map pluginArtifactMap; /** * Map of project artifacts. */ @Parameter(property = "project.artifactMap", readonly = true, required = true) private Map projectArtifactMap; /** * Add custom text into report filename: TEST-testClassName-reportNameSuffix.xml, * testClassName-reportNameSuffix.txt and testClassName-reportNameSuffix-output.txt. * File TEST-testClassName-reportNameSuffix.xml has changed attributes 'testsuite'--'name' * and 'testcase'--'classname' - reportNameSuffix is added to the attribute value. */ @Parameter(property = "surefire.reportNameSuffix", defaultValue = "") private String reportNameSuffix; /** * Set this to "true" to redirect the unit test standard output to a file (found in * reportsDirectory/testName-output.txt). * * @since 2.3 */ @Parameter(property = "maven.test.redirectTestOutputToFile", defaultValue = "false") private boolean redirectTestOutputToFile; /** * Set this to "true" to cause a failure if there are no tests to run. Defaults to "false". * * @since 2.4 */ @Parameter(property = "failIfNoTests", defaultValue = "false") private boolean failIfNoTests; /** * Relative path to temporary-surefire-boot directory containing internal Surefire temporary files. *
* The temporary-surefire-boot directory is project.build.directory on most platforms or * system default temporary-directory specified by the system property {@code java.io.tmpdir} * on Windows (see SUREFIRE-1400). *
* It is deleted after the test set has completed. * * @since 2.20 */ @Parameter(property = "tempDir", defaultValue = "surefire") private String tempDir; /** * Option to specify the jvm (or path to the java executable) to use with the forking options. For the default, the * jvm will be a new instance of the same VM as the one used to run Maven. JVM settings are not inherited from * MAVEN_OPTS. * * @since 2.1 */ @Parameter(property = "jvm") private String jvm; /** * Arbitrary JVM options to set on the command line. *
*
* Since the Version 2.17 using an alternate syntax for {@code argLine}, @{...} allows late replacement * of properties when the plugin is executed, so properties that have been modified by other plugins will be picked * up correctly. * See the Frequently Asked Questions page with more details:
* * http://maven.apache.org/surefire/maven-surefire-plugin/faq.html *
* * http://maven.apache.org/surefire/maven-failsafe-plugin/faq.html * * @since 2.1 */ @Parameter(property = "argLine") private String argLine; /** * Additional environment variables to set on the command line. * * @since 2.1.3 */ @Parameter private Map environmentVariables = new HashMap<>(); /** * Command line working directory. * * @since 2.1.3 */ @Parameter(property = "basedir") private File workingDirectory; /** * When false it makes tests run using the standard classloader delegation instead of the default Maven isolated * classloader. Only used when forking ({@code forkCount} is greater than zero).
* Setting it to false helps with some problems caused by conflicts between xml parsers in the classpath and the * Java 5 provider parser. * * @since 2.1 */ @Parameter(property = "childDelegation", defaultValue = "false") private boolean childDelegation; /** * (TestNG/JUnit47 provider with JUnit4.8+ only and JUnit5+ provider since 2.22.0) Groups/categories/tags for this * test. Only classes/methods/etc decorated with one of the groups/categories/tags specified here will be included * in test run, if specified.
* For JUnit4 tests, this parameter forces the use of the 4.7 provider. For JUnit5 tests, this parameter forces * the use of the JUnit platform provider.
* This parameter is ignored if the {@code suiteXmlFiles} parameter is specified.
* Since version 2.18.1 and JUnit 4.12, the {@code @Category} annotation type is automatically inherited from * superclasses, see {@code @java.lang.annotation.Inherited}. Make sure that test class inheritance still makes * sense together with {@code @Category} annotation of the JUnit 4.12 or higher appeared in superclass. * * @since 2.2 */ @Parameter(property = "groups") private String groups; /** * (TestNG/JUnit47 provider with JUnit4.8+ only and JUnit5+ provider since 2.22.0) Excluded groups/categories/tags. * Any methods/classes/etc with one of the groups/categories/tags specified in this list will specifically not be * run.
* For JUnit4, this parameter forces the use of the 4.7 provider. For JUnit5, this parameter forces the use of the * JUnit platform provider.
* This parameter is ignored if the {@code suiteXmlFiles} parameter is specified.
* Since version 2.18.1 and JUnit 4.12, the {@code @Category} annotation type is automatically inherited from * superclasses, see {@code @java.lang.annotation.Inherited}. Make sure that test class inheritance still makes * sense together with {@code @Category} annotation of the JUnit 4.12 or higher appeared in superclass. * * @since 2.2 */ @Parameter(property = "excludedGroups") private String excludedGroups; /** * Allows you to specify the name of the JUnit artifact. If not set, {@code junit:junit} will be used. * * @since 2.3.1 */ @Parameter(property = "junitArtifactName", defaultValue = "junit:junit") private String junitArtifactName; /** * Allows you to specify the name of the TestNG artifact. If not set, {@code org.testng:testng} will be used. * * @since 2.3.1 */ @Parameter(property = "testNGArtifactName", defaultValue = "org.testng:testng") private String testNGArtifactName; /** * (TestNG/JUnit 4.7 provider) The attribute thread-count allows you to specify how many threads should be * allocated for this execution. Only makes sense to use in conjunction with the {@code parallel} parameter. * * @since 2.2 */ @Parameter(property = "threadCount") private int threadCount; /** * Option to specify the number of VMs to fork in parallel in order to execute the tests. When terminated with "C", * the number part is multiplied with the number of CPU cores. Floating point value are only accepted together with * "C". If set to "0", no VM is forked and all tests are executed within the main process.
*
* Example values: "1.5C", "4"
*
* The system properties and the {@code argLine} of the forked processes may contain the place holder string * ${surefire.forkNumber}, which is replaced with a fixed number for each of the parallel forks, * ranging from 1 to the effective value of {@code forkCount} times the maximum number of parallel * Surefire executions in maven parallel builds, i.e. the effective value of the -T command line * argument of maven core. * * @since 2.14 */ @Parameter(property = "forkCount", defaultValue = "1") private String forkCount; /** * Indicates if forked VMs can be reused. If set to "false", a new VM is forked for each test class to be executed. * If set to "true", up to {@code forkCount} VMs will be forked and then reused to execute all tests. * * @since 2.13 */ @Parameter(property = "reuseForks", defaultValue = "true") private boolean reuseForks; /** * (JUnit 4.7 provider) Indicates that threadCount, threadCountSuites, threadCountClasses, threadCountMethods * are per cpu core. * * @since 2.5 */ @Parameter(property = "perCoreThreadCount", defaultValue = "true") private boolean perCoreThreadCount; /** * (JUnit 4.7 provider) Indicates that the thread pool will be unlimited. The {@code parallel} parameter and * the actual number of classes/methods will decide. Setting this to "true" effectively disables * {@code perCoreThreadCount} and {@code threadCount}. Defaults to "false". * * @since 2.5 */ @Parameter(property = "useUnlimitedThreads", defaultValue = "false") private boolean useUnlimitedThreads; /** * (TestNG provider) When you use the parameter {@code parallel}, TestNG will try to run all your test methods * in separate threads, except for methods that depend on each other, which will be run in the same thread in order * to respect their order of execution. Supports two values: {@code classes} or {@code methods}. *
* (JUnit 4.7 provider) Supports values {@code classes}, {@code methods}, {@code both} to run * in separate threads been controlled by {@code threadCount}. *
*
* Since version 2.16 (JUnit 4.7 provider), the value {@code both} is DEPRECATED. * Use {@code classesAndMethods} instead. *
*
* Since version 2.16 (JUnit 4.7 provider), additional vales are available: *
* {@code suites}, {@code suitesAndClasses}, {@code suitesAndMethods}, {@code classesAndMethods}, {@code all}. *
* By default, Surefire does not execute tests in parallel. You can set the parameter {@code parallel} to * {@code none} to explicitly disable parallel execution (e.g. when disabling parallel execution in special Maven * profiles when executing coverage analysis). * * @since 2.2 */ @Parameter(property = "parallel") private String parallel; /** * (JUnit 4.7 / provider only) The thread counts do not exceed the number of parallel suite, class runners and * average number of methods per class if set to true. *
* True by default. * * @since 2.17 */ @Parameter(property = "parallelOptimized", defaultValue = "true") private boolean parallelOptimized; /** * (JUnit 4.7 provider) This attribute allows you to specify the concurrency in test suites, i.e.: *
    *
  • number of concurrent suites if {@code threadCount} is 0 or unspecified
  • *
  • limited suites concurrency if {@code useUnlimitedThreads} is set to true
  • *
  • if {@code threadCount} and certain thread-count parameters are > 0 for {@code parallel}, the * concurrency is computed from ratio. For instance {@code parallel=all} and the ratio between * {@code threadCountSuites}:{@code threadCountClasses}:{@code threadCountMethods} is * 2:3:5, there is 20% of {@code threadCount} which appeared in concurrent suites.
  • *
* * Only makes sense to use in conjunction with the {@code parallel} parameter. * The default value 0 behaves same as unspecified one. * * @since 2.16 */ @Parameter(property = "threadCountSuites", defaultValue = "0") private int threadCountSuites; /** * (JUnit 4.7 provider) This attribute allows you to specify the concurrency in test classes, i.e.: *
    *
  • number of concurrent classes if {@code threadCount} is 0 or unspecified
  • *
  • limited classes concurrency if {@code useUnlimitedThreads} is set to true
  • *
  • if {@code threadCount} and certain thread-count parameters are > 0 for {@code parallel}, the * concurrency is computed from ratio. For instance {@code parallel=all} and the ratio between * {@code threadCountSuites}:{@code threadCountClasses}:{@code threadCountMethods} is * 2:3:5, there is 30% of {@code threadCount} in concurrent classes.
  • *
  • as in the previous case but without this leaf thread-count. Example: {@code parallel=suitesAndClasses}, * {@code threadCount=16}, {@code threadCountSuites=5}, {@code threadCountClasses} is unspecified leaf, the number * of concurrent classes is varying from >= 11 to 14 or 15. The {@code threadCountSuites} become * given number of threads.
  • *
* * Only makes sense to use in conjunction with the {@code parallel} parameter. * The default value 0 behaves same as unspecified one. * * @since 2.16 */ @Parameter(property = "threadCountClasses", defaultValue = "0") private int threadCountClasses; /** * (JUnit 4.7 provider) This attribute allows you to specify the concurrency in test methods, i.e.: *
    *
  • number of concurrent methods if {@code threadCount} is 0 or unspecified
  • *
  • limited concurrency of methods if {@code useUnlimitedThreads} is set to true
  • *
  • if {@code threadCount} and certain thread-count parameters are > 0 for {@code parallel}, the * concurrency is computed from ratio. For instance parallel=all and the ratio between * {@code threadCountSuites}:{@code threadCountClasses}:{@code threadCountMethods} is 2:3:5, * there is 50% of {@code threadCount} which appears in concurrent methods.
  • *
  • as in the previous case but without this leaf thread-count. Example: {@code parallel=all}, * {@code threadCount=16}, {@code threadCountSuites=2}, {@code threadCountClasses=3}, but {@code threadCountMethods} * is unspecified leaf, the number of concurrent methods is varying from >= 11 to 14 or 15. * The {@code threadCountSuites} and {@code threadCountClasses} become given number of threads.
  • *
* Only makes sense to use in conjunction with the {@code parallel} parameter. The default value 0 * behaves same as unspecified one. * * @since 2.16 */ @Parameter(property = "threadCountMethods", defaultValue = "0") private int threadCountMethods; /** * Whether to trim the stack trace in the reports to just the lines within the test, or show the full trace. * * @since 2.2 */ @Parameter(property = "trimStackTrace", defaultValue = "false") private boolean trimStackTrace; /** * Flag to disable the generation of report files in xml format. * Deprecated since 3.0.0-M4. * Instead use disable within {@code statelessTestsetReporter} since of 3.0.0-M6. * @since 2.2 */ @Deprecated // todo make readonly to handle system property @Parameter(property = "disableXmlReport", defaultValue = "false") private boolean disableXmlReport; /** * By default, Surefire enables JVM assertions for the execution of your test cases. To disable the assertions, set * this flag to "false". * * @since 2.3.1 */ @Parameter(property = "enableAssertions", defaultValue = "true") private boolean enableAssertions; /** * The current build session instance. */ @Parameter(defaultValue = "${session}", required = true, readonly = true) private MavenSession session; @Component private Logger logger; /** * (TestNG only) Define the factory class used to create all test instances. * * @since 2.5 */ @Parameter(property = "objectFactory") private String objectFactory; /** * Parallel Maven Execution. */ @Parameter(defaultValue = "${session.parallel}", readonly = true) private Boolean parallelMavenExecution; /** * Read-only parameter with value of Maven property project.build.directory. * @since 2.20 */ @Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true) private File projectBuildDirectory; /** * List of dependencies to scan for test classes to include in the test run. * The child elements of this element must be <dependency> elements, and the * contents of each of these elements must be a string which follows the general form: * *

{@code groupId[:artifactId[:type[:classifier][:version]]]}

* *

The wildcard character * can be used within the sub parts of those composite identifiers to * do glob-like pattern matching. The classifier may be omitted when matching dependencies without a classifier.

* *

Examples:

* *
    *
  • {@code group} or, equivalently, {@code group:*}
  • *
  • {@code g*p:*rtifac*}
  • *
  • {@code group:*:jar}
  • *
  • {@code group:artifact:*:1.0.0} (no classifier)
  • *
  • {@code group:*:test-jar:tests}
  • *
  • {@code *:artifact:*:*:1.0.0}
  • *
* *

Since version 2.22.0 you can scan for test classes from a project * dependency of your multi-module project.

* *

In versions before 3.0.0-M4, only groupId:artifactId is supported.

* * @since 2.15 */ @Parameter(property = "dependenciesToScan") private String[] dependenciesToScan; /** *

* Allow for configuration of the test jvm via maven toolchains. * This permits a configuration where the project is built with one jvm and tested with another. * This is similar to {@link #jvm}, but avoids hardcoding paths. * The two parameters are mutually exclusive (jvm wins) *

* *

Examples:

* (see * Guide to Toolchains for more info) * *
     * {@code
     *    
     *        ...
     *        
     *            1.11
     *        
     *    
     *
     *    
     *        ...
     *        
     *            1.8
     *            zulu
     *        
     *    
     *    }
     * 
* * @since 3.0.0-M5 and Maven 3.3.x */ @Parameter private Map jdkToolchain; /** * */ @Component private ToolchainManager toolchainManager; @Component private LocationManager locationManager; @Component private ProviderDetector providerDetector; private Toolchain toolchain; private int effectiveForkCount = -1; protected abstract String getPluginName(); protected abstract int getRerunFailingTestsCount(); @Override public abstract List getIncludes(); public abstract File getIncludesFile(); @Override public abstract void setIncludes(List includes); public abstract File getExcludesFile(); /** * Calls {@link #getSuiteXmlFiles()} as {@link List list}. * Never returns null. * * @return list of TestNG suite XML files provided by MOJO */ protected abstract List suiteXmlFiles(); /** * @return {@code true} if {@link #getSuiteXmlFiles() suite-xml files array} is not empty. */ protected abstract boolean hasSuiteXmlFiles(); protected abstract String[] getExcludedEnvironmentVariables(); public abstract File[] getSuiteXmlFiles(); public abstract void setSuiteXmlFiles(File[] suiteXmlFiles); public abstract String getRunOrder(); public abstract void setRunOrder(String runOrder); public abstract Long getRunOrderRandomSeed(); public abstract void setRunOrderRandomSeed(Long runOrderRandomSeed); protected abstract void handleSummary(RunResult summary, Exception firstForkException) throws MojoExecutionException, MojoFailureException; protected abstract boolean isSkipExecution(); protected abstract String[] getDefaultIncludes(); protected abstract String getReportSchemaLocation(); protected abstract boolean useModulePath(); protected abstract void setUseModulePath(boolean useModulePath); protected abstract String getEnableProcessChecker(); protected abstract ForkNodeFactory getForkNode(); /** * This plugin MOJO artifact. * * @return non-null plugin artifact */ protected Artifact getMojoArtifact() { return getPluginDescriptor().getPluginArtifact(); } private String getDefaultExcludes() { return "**/*$*"; } @Component(role = SurefireDependencyResolver.class) private SurefireDependencyResolver surefireDependencyResolver; private TestListResolver specificTests; private TestListResolver includedExcludedTests; private List cli; private volatile PluginConsoleLogger consoleLogger; @Override public void execute() throws MojoExecutionException, MojoFailureException { if (isSkipExecution()) { getConsoleLogger().info("Tests are skipped."); return; } cli = commandLineOptions(); // Stuff that should have been final setupStuff(); Platform platform = PLATFORM.withJdkExecAttributesForTests(getEffectiveJvm()); Thread shutdownThread = new Thread(platform::setShutdownState); addShutDownHook(shutdownThread); try { if (verifyParameters() && !hasExecutedBefore()) { DefaultScanResult scan = scanForTestClasses(); if (!hasSuiteXmlFiles() && scan.isEmpty()) { switch (getEffectiveFailIfNoTests()) { case COULD_NOT_RUN_DEFAULT_TESTS: throw new MojoFailureException( "No tests were executed! (Set -DfailIfNoTests=false to ignore this error.)"); case COULD_NOT_RUN_SPECIFIED_TESTS: throw new MojoFailureException("No tests matching pattern \"" + getSpecificTests().toString() + "\" were executed! (Set " + "-D" + getPluginName() + ".failIfNoSpecifiedTests=false to ignore this error.)"); default: handleSummary(noTestsRun(), null); return; } } logReportsDirectory(); executeAfterPreconditionsChecked(scan, platform); } } finally { platform.clearShutdownState(); removeShutdownHook(shutdownThread); } } void setLogger(Logger logger) { this.logger = logger; } void setSurefireDependencyResolver(SurefireDependencyResolver surefireDependencyResolver) { this.surefireDependencyResolver = surefireDependencyResolver; } @Nonnull protected final PluginConsoleLogger getConsoleLogger() { if (consoleLogger == null) { synchronized (this) { if (consoleLogger == null) { consoleLogger = new PluginConsoleLogger(logger); } } } return consoleLogger; } private static Toolchain getToolchainMaven33x( Class toolchainManagerType, T toolchainManager, MavenSession session, Map toolchainArgs) throws MojoFailureException { Method getToolchainsMethod = tryGetMethod(toolchainManagerType, "getToolchains", MavenSession.class, String.class, Map.class); if (getToolchainsMethod != null) { //noinspection unchecked List tcs = invokeMethodWithArray(toolchainManager, getToolchainsMethod, session, "jdk", toolchainArgs); if (tcs.isEmpty()) { throw new MojoFailureException( "Requested toolchain specification did not match any configured toolchain: " + toolchainArgs); } return tcs.get(0); } return null; } // TODO remove the part with ToolchainManager lookup once we depend on // 3.0.9 (have it as prerequisite). Define as regular component field then. private Toolchain getToolchain() throws MojoFailureException { Toolchain tc = null; if (getJdkToolchain() != null) { tc = getToolchainMaven33x(ToolchainManager.class, getToolchainManager(), getSession(), getJdkToolchain()); } if (tc == null) { tc = getToolchainManager().getToolchainFromBuildContext("jdk", getSession()); } return tc; } private void setupStuff() throws MojoFailureException { if (getBooterArtifact() == null) { throw new RuntimeException("Unable to locate surefire-booter in the list of plugin artifacts"); } if (getToolchainManager() != null) { toolchain = getToolchain(); } } @Nonnull private DefaultScanResult scanForTestClasses() throws MojoFailureException { DefaultScanResult scan = scanDirectories(); DefaultScanResult scanDeps = scanDependencies(); return scan.append(scanDeps); } private DefaultScanResult scanDirectories() throws MojoFailureException { DirectoryScanner scanner = new DirectoryScanner(getTestClassesDirectory(), getIncludedAndExcludedTests()); return scanner.scan(); } List getProjectTestArtifacts() { return project.getTestArtifacts(); } DefaultScanResult scanDependencies() throws MojoFailureException { if (getDependenciesToScan() == null) { return null; } else { try { DefaultScanResult result = null; List dependenciesToScan = filter(getProjectTestArtifacts(), asList(getDependenciesToScan())); for (Artifact artifact : dependenciesToScan) { String type = artifact.getType(); File out = artifact.getFile(); if (out == null || !out.exists() || !("jar".equals(type) || out.isDirectory() || out.getName().endsWith(".jar"))) { continue; } if (out.isFile()) { DependencyScanner scanner = new DependencyScanner(singletonList(out), getIncludedAndExcludedTests()); result = result == null ? scanner.scan() : result.append(scanner.scan()); } else if (out.isDirectory()) { DirectoryScanner scanner = new DirectoryScanner(out, getIncludedAndExcludedTests()); result = result == null ? scanner.scan() : result.append(scanner.scan()); } } return result; } catch (Exception e) { throw new MojoFailureException(e.getLocalizedMessage(), e); } } } boolean verifyParameters() throws MojoFailureException, MojoExecutionException { setProperties(new SurefireProperties(getProperties())); String jvmToUse = getJvm(); if (toolchain != null) { getConsoleLogger().info("Toolchain in maven-" + getPluginName() + "-plugin: " + toolchain); if (jvmToUse != null) { getConsoleLogger().warning("Toolchains are ignored, 'jvm' parameter is set to " + jvmToUse); } } if (!getTestClassesDirectory().exists() && (getDependenciesToScan() == null || getDependenciesToScan().length == 0)) { if (getFailIfNoTests()) { throw new MojoFailureException("No tests to run!"); } getConsoleLogger().info("No tests to run."); } else { ensureEnableProcessChecker(); ensureWorkingDirectoryExists(); ensureParallelRunningCompatibility(); warnIfUselessUseSystemClassLoaderParameter(); warnIfDefunctGroupsCombinations(); warnIfRerunClashes(); warnIfWrongShutdownValue(); warnIfNotApplicableSkipAfterFailureCount(); warnIfIllegalTempDir(); warnIfForkCountIsZero(); warnIfIllegalFailOnFlakeCount(); printDefaultSeedIfNecessary(); } return true; } private void warnIfForkCountIsZero() { if ("0".equals(getForkCount())) { getConsoleLogger() .warning("The parameter forkCount should likely not be 0. Forking a JVM for tests " + "improves test accuracy. Ensure to have a >= 1."); } } private void executeAfterPreconditionsChecked(@Nonnull DefaultScanResult scanResult, @Nonnull Platform platform) throws MojoExecutionException, MojoFailureException { TestClassPath testClasspath = generateTestClasspath(); List providers = createProviders(testClasspath); ResolvePathResultWrapper wrapper = findModuleDescriptor(platform.getJdkExecAttributesForTests().getJdkHome()); RunResult current = noTestsRun(); Exception firstForkException = null; for (ProviderInfo provider : providers) { try { current = current.aggregate(executeProvider(provider, scanResult, testClasspath, platform, wrapper)); } catch (SurefireBooterForkException | SurefireExecutionException | TestSetFailedException e) { if (firstForkException == null) { firstForkException = e; } } } if (firstForkException != null) { current = failure(current, firstForkException); } handleSummary(current, firstForkException); } protected List createProviders(TestClassPath testClasspath) throws MojoExecutionException { Artifact junitDepArtifact = getJunitDepArtifact(); return providerDetector.resolve( new DynamicProviderInfo(null), new JUnitPlatformProviderInfo(getJUnitPlatformRunnerArtifact(), getJUnit5Artifact(), testClasspath), new TestNgProviderInfo(getTestNgArtifact()), new JUnitCoreProviderInfo(getJunitArtifact(), junitDepArtifact), new JUnit4ProviderInfo(getJunitArtifact(), junitDepArtifact), new JUnit3ProviderInfo()); } private SurefireProperties setupProperties() { SurefireProperties sysProps = null; try { sysProps = SurefireProperties.loadProperties(getSystemPropertiesFile()); } catch (IOException e) { String msg = "The file '" + getSystemPropertiesFile().getAbsolutePath() + "' can't be read."; if (getConsoleLogger().isDebugEnabled()) { getConsoleLogger().debug(msg, e); } else { getConsoleLogger().warning(msg); } } SurefireProperties result = SurefireProperties.calculateEffectiveProperties( getSystemProperties(), getSystemPropertyVariables(), getUserProperties(), sysProps); result.setProperty("basedir", getBasedir().getAbsolutePath()); result.setProperty("localRepository", getLocalRepositoryPath()); if (isForking()) { for (Object o : result.propertiesThatCannotBeSetASystemProperties()) { if (getArgLine() == null || !getArgLine().contains("-D" + o + "=")) { getConsoleLogger() .warning(o + " cannot be set as system property, use -D" + o + "=... instead"); } } for (Object systemPropertyMatchingArgLine : systemPropertiesMatchingArgLine(result)) { getConsoleLogger() .warning("The system property " + systemPropertyMatchingArgLine + " is configured twice! " + "The property appears in and any of , " + " or user property."); } } else { result.setProperty("user.dir", getWorkingDirectory().getAbsolutePath()); } if (getConsoleLogger().isDebugEnabled()) { showToLog(result, getConsoleLogger()); } return result; } private Set systemPropertiesMatchingArgLine(SurefireProperties result) { Set intersection = new HashSet<>(); if (isNotBlank(getArgLine())) { for (Object systemProperty : result.getStringKeySet()) { if (getArgLine().contains("-D" + systemProperty + "=")) { intersection.add(systemProperty); } } Set ignored = result.propertiesThatCannotBeSetASystemProperties(); intersection.removeAll(ignored); } return intersection; } private void showToLog(SurefireProperties props, ConsoleLogger log) { for (Object key : props.getStringKeySet()) { String value = props.getProperty((String) key); log.debug("Setting system property [" + key + "]=[" + value + "]"); } } @Nonnull private RunResult executeProvider( @Nonnull ProviderInfo provider, @Nonnull DefaultScanResult scanResult, @Nonnull TestClassPath testClasspathWrapper, @Nonnull Platform platform, @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult) throws MojoExecutionException, MojoFailureException, SurefireExecutionException, SurefireBooterForkException, TestSetFailedException { getConsoleLogger().debug("Using the provider " + provider.getProviderName()); SurefireProperties effectiveProperties = setupProperties(); ClassLoaderConfiguration classLoaderConfiguration = getClassLoaderConfiguration(); provider.addProviderProperties(); RunOrderParameters runOrderParameters = new RunOrderParameters(getRunOrder(), getStatisticsFile(getConfigChecksum()), getRunOrderRandomSeed()); if (isNotForking()) { Properties originalSystemProperties = (Properties) System.getProperties().clone(); try { createCopyAndReplaceForkNumPlaceholder(effectiveProperties, 1).copyToSystemProperties(); getConsoleLogger().debug("Using in-process starter"); InPluginVMSurefireStarter surefireStarter = createInprocessStarter( provider, classLoaderConfiguration, runOrderParameters, scanResult, platform, testClasspathWrapper); return surefireStarter.runSuitesInProcess(scanResult); } finally { System.setProperties(originalSystemProperties); } } else { ForkConfiguration forkConfiguration = createForkConfiguration(platform, resolvedJavaModularityResult); if (getConsoleLogger().isDebugEnabled()) { getConsoleLogger() .debug("Using fork starter with configuration implementation " + forkConfiguration.getClass().getName()); showMap(getEnvironmentVariables(), "environment variable"); showArray(getExcludedEnvironmentVariables(), "excluded environment variable"); } Properties originalSystemProperties = (Properties) System.getProperties().clone(); ForkStarter forkStarter = null; try { forkStarter = createForkStarter( provider, forkConfiguration, classLoaderConfiguration, runOrderParameters, getConsoleLogger(), scanResult, testClasspathWrapper, platform, resolvedJavaModularityResult); return forkStarter.run(effectiveProperties, scanResult); } catch (SurefireBooterForkException e) { forkStarter.killOrphanForks(); throw e; } finally { System.setProperties(originalSystemProperties); cleanupForkConfiguration(forkConfiguration); } } } public static SurefireProperties createCopyAndReplaceForkNumPlaceholder( SurefireProperties effectiveSystemProperties, int threadNumber) { SurefireProperties filteredProperties = new SurefireProperties((KeyValueSource) effectiveSystemProperties); for (Entry entry : effectiveSystemProperties.entrySet()) { if (entry.getValue() instanceof String) { String value = (String) entry.getValue(); filteredProperties.put(entry.getKey(), replaceThreadNumberPlaceholders(value, threadNumber)); } } return filteredProperties; } protected void cleanupForkConfiguration(ForkConfiguration forkConfiguration) { if (!getConsoleLogger().isDebugEnabled() && forkConfiguration != null) { File tempDirectory = forkConfiguration.getTempDirectory(); try { FileUtils.deleteDirectory(tempDirectory); } catch (IOException e) { getConsoleLogger() .warning("Could not delete temp directory " + tempDirectory + " because " + e.getMessage()); } } } protected void logReportsDirectory() { logDebugOrCliShowErrors(capitalizeFirstLetter(getPluginName()) + " report directory: " + getReportsDirectory()); } private boolean existsModuleDescriptor(ResolvePathResultWrapper resolvedJavaModularityResult) { return resolvedJavaModularityResult.getResolvePathResult() != null; } private ResolvePathResultWrapper findModuleDescriptor(File jdkHome) { ResolvePathResultWrapper test = findModuleDescriptor(jdkHome, getTestClassesDirectory(), false); return test.getResolvePathResult() == null ? findModuleDescriptor(jdkHome, getMainBuildPath(), true) : test; } private ResolvePathResultWrapper findModuleDescriptor(File jdkHome, File buildPath, boolean isMainDescriptor) { boolean isJpmsModule = buildPath.isDirectory() ? new File(buildPath, "module-info.class").exists() : isModule(buildPath); if (!isJpmsModule) { return new ResolvePathResultWrapper(null, isMainDescriptor); } try { ResolvePathRequest request = ResolvePathRequest.ofFile(buildPath).setJdkHome(jdkHome); ResolvePathResult result = getLocationManager().resolvePath(request); boolean isEmpty = result.getModuleNameSource() == null; return new ResolvePathResultWrapper(isEmpty ? null : result, isMainDescriptor); } catch (Exception e) { return new ResolvePathResultWrapper(null, isMainDescriptor); } } private static boolean isModule(File jar) { try (ZipFile zip = new ZipFile(jar)) { return zip.getEntry("module-info.class") != null; } catch (IOException e) { return false; } } private boolean canExecuteProviderWithModularPath( @Nonnull Platform platform, @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult) { return useModulePath() && platform.getJdkExecAttributesForTests().isJava9AtLeast() && existsModuleDescriptor(resolvedJavaModularityResult); } /** * Converts old TestNG configuration parameters over to new properties based configuration * method. (if any are defined the old way) */ private void convertTestNGParameters() throws MojoExecutionException { if (this.getParallel() != null) { getProperties().setProperty(ProviderParameterNames.PARALLEL_PROP, this.getParallel()); } convertGroupParameters(); if (this.getThreadCount() > 0) { getProperties() .setProperty(ProviderParameterNames.THREADCOUNT_PROP, Integer.toString(this.getThreadCount())); } if (this.getObjectFactory() != null) { getProperties().setProperty("objectfactory", this.getObjectFactory()); } if (this.getTestClassesDirectory() != null) { getProperties() .setProperty( "testng.test.classpath", getTestClassesDirectory().getAbsolutePath()); } Artifact testNgArtifact = getTestNgArtifact(); if (testNgArtifact != null) { DefaultArtifactVersion defaultArtifactVersion = new DefaultArtifactVersion(testNgArtifact.getVersion()); getProperties() .setProperty( "testng.configurator", getConfiguratorName(defaultArtifactVersion, getConsoleLogger())); } } private static String getConfiguratorName(ArtifactVersion version, PluginConsoleLogger log) throws MojoExecutionException { try { VersionRange range = VersionRange.createFromVersionSpec("[4.7,5.2)"); if (range.containsVersion(version)) { return "org.apache.maven.surefire.testng.conf.TestNG4751Configurator"; } range = VersionRange.createFromVersionSpec("[5.2,5.3)"); if (range.containsVersion(version)) { return "org.apache.maven.surefire.testng.conf.TestNG52Configurator"; } range = VersionRange.createFromVersionSpec("[5.3,5.10)"); if (range.containsVersion(version)) { return "org.apache.maven.surefire.testng.conf.TestNGMapConfigurator"; } range = VersionRange.createFromVersionSpec("[5.10,5.13)"); if (range.containsVersion(version)) { return "org.apache.maven.surefire.testng.conf.TestNG510Configurator"; } range = VersionRange.createFromVersionSpec("[5.13,5.14.1)"); if (range.containsVersion(version)) { return "org.apache.maven.surefire.testng.conf.TestNG513Configurator"; } range = VersionRange.createFromVersionSpec("[5.14.1,5.14.3)"); if (range.containsVersion(version)) { log.warning("The 'reporter' or 'listener' may not work properly in TestNG 5.14.1 and 5.14.2."); return "org.apache.maven.surefire.testng.conf.TestNG5141Configurator"; } range = VersionRange.createFromVersionSpec("[5.14.3,6.0)"); if (range.containsVersion(version)) { if (version.equals(new DefaultArtifactVersion("[5.14.3,5.14.5]"))) { throw new MojoExecutionException("TestNG 5.14.3-5.14.5 is not supported. " + "System dependency org.testng:guice missed path."); } return "org.apache.maven.surefire.testng.conf.TestNG5143Configurator"; } range = VersionRange.createFromVersionSpec("[6.0,7.4.0)"); if (range.containsVersion(version)) { return "org.apache.maven.surefire.testng.conf.TestNG60Configurator"; } range = VersionRange.createFromVersionSpec("[7.4.0,)"); if (range.containsVersion(version)) { return "org.apache.maven.surefire.testng.conf.TestNG740Configurator"; } throw new MojoExecutionException("Unknown TestNG version " + version); } catch (InvalidVersionSpecificationException invsex) { throw new MojoExecutionException("Bug in plugin. Please report it with the attached stacktrace", invsex); } } private void convertGroupParameters() { if (this.getExcludedGroups() != null) { getProperties().setProperty(ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP, this.getExcludedGroups()); } if (this.getGroups() != null) { getProperties().setProperty(ProviderParameterNames.TESTNG_GROUPS_PROP, this.getGroups()); } } private void convertJunitEngineParameters() { if (getIncludeJUnit5Engines() != null && getIncludeJUnit5Engines().length != 0) { getProperties().setProperty(INCLUDE_JUNIT5_ENGINES_PROP, join(getIncludeJUnit5Engines())); } if (getExcludeJUnit5Engines() != null && getExcludeJUnit5Engines().length != 0) { getProperties().setProperty(EXCLUDE_JUNIT5_ENGINES_PROP, join(getExcludeJUnit5Engines())); } } private static String join(String[] array) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0, length = array.length; i < length; i++) { stringBuilder.append(array[i]); if (i < length - 1) { stringBuilder.append(','); } } return stringBuilder.toString(); } protected boolean isAnyConcurrencySelected() { return getParallel() != null && !getParallel().trim().isEmpty(); } protected boolean isAnyGroupsSelected() { return this.getGroups() != null || this.getExcludedGroups() != null; } /** * Converts old JUnit configuration parameters over to new properties based configuration * method. (if any are defined the old way) */ private void convertJunitCoreParameters() throws MojoExecutionException { checkThreadCountEntity(getThreadCountSuites(), "suites"); checkThreadCountEntity(getThreadCountClasses(), "classes"); checkThreadCountEntity(getThreadCountMethods(), "methods"); String usedParallel = (getParallel() != null) ? getParallel() : "none"; if (!"none".equals(usedParallel)) { checkNonForkedThreads(parallel); } getProperties().setProperty(ProviderParameterNames.PARALLEL_PROP, usedParallel); if (this.getThreadCount() > 0) { getProperties().setProperty(ProviderParameterNames.THREADCOUNT_PROP, Integer.toString(getThreadCount())); } getProperties().setProperty("perCoreThreadCount", Boolean.toString(getPerCoreThreadCount())); getProperties().setProperty("useUnlimitedThreads", Boolean.toString(getUseUnlimitedThreads())); getProperties() .setProperty(ProviderParameterNames.THREADCOUNTSUITES_PROP, Integer.toString(getThreadCountSuites())); getProperties() .setProperty(ProviderParameterNames.THREADCOUNTCLASSES_PROP, Integer.toString(getThreadCountClasses())); getProperties() .setProperty(ProviderParameterNames.THREADCOUNTMETHODS_PROP, Integer.toString(getThreadCountMethods())); getProperties() .setProperty( ProviderParameterNames.PARALLEL_TIMEOUT_PROP, Double.toString(getParallelTestsTimeoutInSeconds())); getProperties() .setProperty( ProviderParameterNames.PARALLEL_TIMEOUTFORCED_PROP, Double.toString(getParallelTestsTimeoutForcedInSeconds())); getProperties() .setProperty(ProviderParameterNames.PARALLEL_OPTIMIZE_PROP, Boolean.toString(isParallelOptimized())); String message = "parallel='" + usedParallel + '\'' + ", perCoreThreadCount=" + getPerCoreThreadCount() + ", threadCount=" + getThreadCount() + ", useUnlimitedThreads=" + getUseUnlimitedThreads() + ", threadCountSuites=" + getThreadCountSuites() + ", threadCountClasses=" + getThreadCountClasses() + ", threadCountMethods=" + getThreadCountMethods() + ", parallelOptimized=" + isParallelOptimized(); logDebugOrCliShowErrors(message); } private void checkNonForkedThreads(String parallel) throws MojoExecutionException { if ("suites".equals(parallel)) { if (!(getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountSuites() > 0)) { throw new MojoExecutionException( "Use threadCount or threadCountSuites > 0 or useUnlimitedThreads=true for parallel='suites'"); } setThreadCountClasses(0); setThreadCountMethods(0); } else if ("classes".equals(parallel)) { if (!(getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountClasses() > 0)) { throw new MojoExecutionException( "Use threadCount or threadCountClasses > 0 or useUnlimitedThreads=true for parallel='classes'"); } setThreadCountSuites(0); setThreadCountMethods(0); } else if ("methods".equals(parallel)) { if (!(getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountMethods() > 0)) { throw new MojoExecutionException( "Use threadCount or threadCountMethods > 0 or useUnlimitedThreads=true for parallel='methods'"); } setThreadCountSuites(0); setThreadCountClasses(0); } else if ("suitesAndClasses".equals(parallel)) { if (!(getUseUnlimitedThreads() || onlyThreadCount() || getThreadCountSuites() > 0 && getThreadCountClasses() > 0 && getThreadCount() == 0 && getThreadCountMethods() == 0 || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() == 0 || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCount() > getThreadCountSuites() && getThreadCountClasses() == 0 && getThreadCountMethods() == 0)) { throw new MojoExecutionException("Use useUnlimitedThreads=true, " + "or only threadCount > 0, " + "or (threadCountSuites > 0 and threadCountClasses > 0), " + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0) " + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " + "for parallel='suitesAndClasses' or 'both'"); } setThreadCountMethods(0); } else if ("suitesAndMethods".equals(parallel)) { if (!(getUseUnlimitedThreads() || onlyThreadCount() || getThreadCountSuites() > 0 && getThreadCountMethods() > 0 && getThreadCount() == 0 && getThreadCountClasses() == 0 || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCountMethods() > 0 && getThreadCountClasses() == 0 || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCount() > getThreadCountSuites() && getThreadCountClasses() == 0 && getThreadCountMethods() == 0)) { throw new MojoExecutionException("Use useUnlimitedThreads=true, " + "or only threadCount > 0, " + "or (threadCountSuites > 0 and threadCountMethods > 0), " + "or (threadCount > 0 and threadCountSuites > 0 and threadCountMethods > 0), " + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " + "for parallel='suitesAndMethods'"); } setThreadCountClasses(0); } else if ("both".equals(parallel) || "classesAndMethods".equals(parallel)) { if (!(getUseUnlimitedThreads() || onlyThreadCount() || getThreadCountClasses() > 0 && getThreadCountMethods() > 0 && getThreadCount() == 0 && getThreadCountSuites() == 0 || getThreadCount() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() > 0 && getThreadCountSuites() == 0 || getThreadCount() > 0 && getThreadCountClasses() > 0 && getThreadCount() > getThreadCountClasses() && getThreadCountSuites() == 0 && getThreadCountMethods() == 0)) { throw new MojoExecutionException("Use useUnlimitedThreads=true, " + "or only threadCount > 0, " + "or (threadCountClasses > 0 and threadCountMethods > 0), " + "or (threadCount > 0 and threadCountClasses > 0 and threadCountMethods > 0), " + "or (threadCount > 0 and threadCountClasses > 0 and threadCount > threadCountClasses) " + "for parallel='both' or parallel='classesAndMethods'"); } setThreadCountSuites(0); } else if ("all".equals(parallel)) { if (!(getUseUnlimitedThreads() || onlyThreadCount() || getThreadCountSuites() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() > 0 || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() == 0 && getThreadCount() > (getThreadCountSuites() + getThreadCountClasses()))) { throw new MojoExecutionException("Use useUnlimitedThreads=true, " + "or only threadCount > 0, " + "or (threadCountSuites > 0 and threadCountClasses > 0 and threadCountMethods > 0), " + "or every thread-count is specified, " + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0 " + "and threadCount > threadCountSuites + threadCountClasses) " + "for parallel='all'"); } } else { throw new MojoExecutionException("Illegal parallel='" + parallel + "'"); } } private boolean onlyThreadCount() { return getThreadCount() > 0 && getThreadCountSuites() == 0 && getThreadCountClasses() == 0 && getThreadCountMethods() == 0; } private static void checkThreadCountEntity(int count, String entity) throws MojoExecutionException { if (count < 0) { throw new MojoExecutionException("parallel maven execution does not allow negative thread-count" + entity); } } private boolean isJunit47Compatible(Artifact artifact) { return isWithinVersionSpec(artifact, "[4.7,)"); } private boolean isAnyJunit4(Artifact artifact) { return isWithinVersionSpec(artifact, "[4.0,)"); } protected boolean isForking() { return 0 < getEffectiveForkCount(); } private List getRunOrders() { String runOrderString = getRunOrder(); RunOrder[] runOrder = runOrderString == null ? RunOrder.DEFAULT : RunOrder.valueOfMulti(runOrderString); return asList(runOrder); } private boolean requiresRunHistory() { final List runOrders = getRunOrders(); return runOrders.contains(RunOrder.BALANCED) || runOrders.contains(RunOrder.FAILEDFIRST); } private PluginFailureReason getEffectiveFailIfNoTests() { if (isSpecificTestSpecified()) { return getFailIfNoSpecifiedTests() ? COULD_NOT_RUN_SPECIFIED_TESTS : NONE; } else { return getFailIfNoTests() ? COULD_NOT_RUN_DEFAULT_TESTS : NONE; } } private ProviderConfiguration createProviderConfiguration(RunOrderParameters runOrderParameters) throws MojoExecutionException, MojoFailureException { final ReporterConfiguration reporterConfiguration = new ReporterConfiguration(getReportsDirectory(), isTrimStackTrace()); final Artifact testNgArtifact = getTestNgArtifact(); final boolean isTestNg = testNgArtifact != null; final TestArtifactInfo testNg = isTestNg ? new TestArtifactInfo(testNgArtifact.getVersion(), testNgArtifact.getClassifier()) : null; final TestRequest testSuiteDefinition = new TestRequest( suiteXmlFiles(), getTestSourceDirectory(), getSpecificTests(), getRerunFailingTestsCount()); final boolean actualFailIfNoTests; DirectoryScannerParameters directoryScannerParameters = null; if (hasSuiteXmlFiles() && !isSpecificTestSpecified()) { actualFailIfNoTests = getFailIfNoTests(); if (!isTestNg) { throw new MojoExecutionException("suiteXmlFiles is configured, but there is no TestNG dependency"); } } else { // @todo remove these three params and use DirectoryScannerParameters to pass into DirectoryScanner only // @todo or remove it in next major version :: 3.0 // @todo remove deprecated methods in ProviderParameters => included|excluded|specificTests not needed here List actualIncludes = getIncludeList(); // Collections.emptyList(); behaves same List actualExcludes = getExcludeList(); // Collections.emptyList(); behaves same // Collections.emptyList(); behaves same List specificTests = Collections.emptyList(); directoryScannerParameters = new DirectoryScannerParameters( getTestClassesDirectory(), actualIncludes, actualExcludes, specificTests, getRunOrder()); } Map providerProperties = toStringProperties(getProperties()); return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, reporterConfiguration, testNg, // Not really used in provider. Limited to de/serializer. testSuiteDefinition, providerProperties, null, false, cli, getSkipAfterFailureCount(), Shutdown.parameterOf(getShutdown()), getForkedProcessExitTimeoutInSeconds()); } private static Map toStringProperties(Properties properties) { Map h = new ConcurrentHashMap<>(properties.size()); for (Enumeration e = properties.keys(); e.hasMoreElements(); ) { Object k = e.nextElement(); Object v = properties.get(k); if (k.getClass() == String.class && v.getClass() == String.class) { h.put((String) k, (String) v); } } return h; } private File getStatisticsFile(String configurationHash) { return new File(getBasedir(), ".surefire-" + configurationHash); } private StartupConfiguration createStartupConfiguration( @Nonnull ProviderInfo provider, boolean isForking, @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull DefaultScanResult scanResult, @Nonnull TestClassPath testClasspathWrapper, @Nonnull Platform platform, @Nonnull ResolvePathResultWrapper resolvedJavaModularity) throws MojoExecutionException { try { if (isForking && canExecuteProviderWithModularPath(platform, resolvedJavaModularity)) { File jdkHome = platform.getJdkExecAttributesForTests().getJdkHome(); return newStartupConfigWithModularPath( classLoaderConfiguration, provider, resolvedJavaModularity, scanResult, jdkHome.getAbsolutePath(), testClasspathWrapper); } else { return newStartupConfigWithClasspath(classLoaderConfiguration, provider, testClasspathWrapper); } } catch (IOException e) { throw new MojoExecutionException(e.getMessage(), e); } } private StartupConfiguration newStartupConfigWithClasspath( @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull ProviderInfo providerInfo, @Nonnull TestClassPath testClasspathWrapper) throws MojoExecutionException { Classpath testClasspath = testClasspathWrapper.toClasspath(); Set providerArtifacts = providerInfo.getProviderClasspath(); String providerName = providerInfo.getProviderName(); Classpath providerClasspath = classpathCache.getCachedClassPath(providerName); if (providerClasspath == null) { providerClasspath = classpathCache.setCachedClasspath(providerName, providerArtifacts); } getConsoleLogger().debug(testClasspath.getLogMessage("test classpath:")); getConsoleLogger().debug(providerClasspath.getLogMessage("provider classpath:")); getConsoleLogger().debug(testClasspath.getCompactLogMessage("test(compact) classpath:")); getConsoleLogger().debug(providerClasspath.getCompactLogMessage("provider(compact) classpath:")); Artifact[] additionalInProcArtifacts = { getCommonArtifact(), getBooterArtifact(), getExtensionsArtifact(), getApiArtifact(), getSpiArtifact(), getLoggerApiArtifact(), getSurefireSharedUtilsArtifact() }; Set inProcArtifacts = retainInProcArtifactsUnique(providerArtifacts, additionalInProcArtifacts); Classpath inProcClasspath = createInProcClasspath(providerClasspath, inProcArtifacts); getConsoleLogger().debug(inProcClasspath.getLogMessage("in-process classpath:")); getConsoleLogger().debug(inProcClasspath.getCompactLogMessage("in-process(compact) classpath:")); ClasspathConfiguration classpathConfiguration = new ClasspathConfiguration( testClasspath, providerClasspath, inProcClasspath, effectiveIsEnableAssertions(), isChildDelegation()); ProviderRequirements forkRequirements = new ProviderRequirements(false, false, false); return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration, ProcessCheckerType.toEnum(getEnableProcessChecker()), providerInfo.getJpmsArguments(forkRequirements)); } private static Set retainInProcArtifactsUnique( Set providerArtifacts, Artifact... inPluginArtifacts) { Set result = new LinkedHashSet<>(); for (Artifact inPluginArtifact : inPluginArtifacts) { boolean contains = false; for (Artifact providerArtifact : providerArtifacts) { if (hasGroupArtifactId( providerArtifact.getGroupId(), providerArtifact.getArtifactId(), inPluginArtifact)) { contains = true; break; } } if (!contains) { result.add(inPluginArtifact); } } return result; } private static boolean hasGroupArtifactId(String groupId, String artifactId, Artifact artifact) { return groupId.equals(artifact.getGroupId()) && artifactId.equals(artifact.getArtifactId()); } private static Classpath createInProcClasspath(Classpath providerClasspath, Set newArtifacts) { Classpath inprocClasspath = providerClasspath.clone(); for (Artifact newArtifact : newArtifacts) { inprocClasspath = inprocClasspath.addClassPathElementUrl(newArtifact.getFile().getAbsolutePath()); } return inprocClasspath; } /** * For testing purposes - Mockito. * @return plexus component */ private LocationManager getLocationManager() { return locationManager; } private StartupConfiguration newStartupConfigWithModularPath( @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull ProviderInfo providerInfo, @Nonnull ResolvePathResultWrapper moduleDescriptor, @Nonnull DefaultScanResult scanResult, @Nonnull String javaHome, @Nonnull TestClassPath testClasspathWrapper) throws MojoExecutionException, IOException { boolean isMainDescriptor = moduleDescriptor.isMainModuleDescriptor(); JavaModuleDescriptor javaModuleDescriptor = moduleDescriptor.getResolvePathResult().getModuleDescriptor(); SortedSet packages = new TreeSet<>(); Classpath testClasspath = testClasspathWrapper.toClasspath(); Set providerArtifacts = providerInfo.getProviderClasspath(); String providerName = providerInfo.getProviderName(); Classpath providerClasspath = classpathCache.getCachedClassPath(providerName); if (providerClasspath == null) { providerClasspath = classpathCache.setCachedClasspath(providerName, providerArtifacts); } final ProviderRequirements providerRequirements; final Classpath testModulepath; if (isMainDescriptor) { providerRequirements = new ProviderRequirements(true, true, false); ResolvePathsRequest req = ResolvePathsRequest.ofStrings(testClasspath.getClassPath()) .setIncludeAllProviders(true) .setJdkHome(javaHome) .setIncludeStatic(true) .setModuleDescriptor(javaModuleDescriptor); ResolvePathsResult result = getLocationManager().resolvePaths(req); for (Entry entry : result.getPathExceptions().entrySet()) { // Probably JDK version < 9. Other known causes: passing a non-jar or a corrupted jar. getConsoleLogger().warning("Exception for '" + entry.getKey() + "'.", entry.getValue()); } testClasspath = new Classpath(result.getClasspathElements()); testModulepath = new Classpath(result.getModulepathElements().keySet()); for (String className : scanResult.getClasses()) { packages.add(substringBeforeLast(className, ".")); } } else { providerRequirements = new ProviderRequirements(true, false, true); testModulepath = testClasspath; testClasspath = emptyClasspath(); } getConsoleLogger().debug("main module descriptor name: " + javaModuleDescriptor.name()); ModularClasspath modularClasspath = new ModularClasspath( javaModuleDescriptor.name(), testModulepath.getClassPath(), packages, isMainDescriptor ? getTestClassesDirectory() : null, isMainDescriptor); Artifact[] additionalInProcArtifacts = { getCommonArtifact(), getBooterArtifact(), getExtensionsArtifact(), getApiArtifact(), getSpiArtifact(), getLoggerApiArtifact(), getSurefireSharedUtilsArtifact() }; Set inProcArtifacts = retainInProcArtifactsUnique(providerArtifacts, additionalInProcArtifacts); Classpath inProcClasspath = createInProcClasspath(providerClasspath, inProcArtifacts); ModularClasspathConfiguration classpathConfiguration = new ModularClasspathConfiguration( modularClasspath, testClasspath, providerClasspath, inProcClasspath, effectiveIsEnableAssertions(), isChildDelegation()); getConsoleLogger().debug(testClasspath.getLogMessage("test classpath:")); getConsoleLogger().debug(testModulepath.getLogMessage("test modulepath:")); getConsoleLogger().debug(providerClasspath.getLogMessage("provider classpath:")); getConsoleLogger().debug(testClasspath.getCompactLogMessage("test(compact) classpath:")); getConsoleLogger().debug(testModulepath.getCompactLogMessage("test(compact) modulepath:")); getConsoleLogger().debug(providerClasspath.getCompactLogMessage("provider(compact) classpath:")); getConsoleLogger().debug(inProcClasspath.getLogMessage("in-process classpath:")); getConsoleLogger().debug(inProcClasspath.getCompactLogMessage("in-process(compact) classpath:")); ProcessCheckerType processCheckerType = ProcessCheckerType.toEnum(getEnableProcessChecker()); List jpmsArgs = providerInfo.getJpmsArguments(providerRequirements); return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration, processCheckerType, jpmsArgs); } private Artifact getCommonArtifact() { return getPluginArtifactMap().get("org.apache.maven.surefire:maven-surefire-common"); } private Artifact getExtensionsArtifact() { return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-extensions-api"); } private Artifact getSpiArtifact() { return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-extensions-spi"); } private Artifact getApiArtifact() { return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-api"); } private Artifact getSurefireSharedUtilsArtifact() { return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-shared-utils"); } private Artifact getLoggerApiArtifact() { return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-logger-api"); } private Artifact getBooterArtifact() { return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-booter"); } private Artifact getShadefireArtifact() { return getPluginArtifactMap().get("org.apache.maven.surefire:surefire-shadefire"); } private StartupReportConfiguration getStartupReportConfiguration(String configChecksum, boolean isForking) { SurefireStatelessReporter xmlReporter = statelessTestsetReporter == null ? new SurefireStatelessReporter(/*todo call def. constr.*/ isDisableXmlReport(), "3.0") : statelessTestsetReporter; xmlReporter.setDisable(isDisableXmlReport()); // todo change to Boolean in the version 3.0.0-M6 SurefireConsoleOutputReporter outReporter = consoleOutputReporter == null ? new SurefireConsoleOutputReporter() : consoleOutputReporter; SurefireStatelessTestsetInfoReporter testsetReporter = statelessTestsetInfoReporter == null ? new SurefireStatelessTestsetInfoReporter() : statelessTestsetInfoReporter; return new StartupReportConfiguration( isUseFile(), isPrintSummary(), getReportFormat(), isRedirectTestOutputToFile(), getReportsDirectory(), isTrimStackTrace(), getReportNameSuffix(), getStatisticsFile(configChecksum), requiresRunHistory(), getRerunFailingTestsCount(), getReportSchemaLocation(), getEncoding(), isForking, xmlReporter, outReporter, testsetReporter); } private boolean isSpecificTestSpecified() { return isNotBlank(getTest()); } @Nonnull private List readListFromFile(@Nonnull final File file) { getConsoleLogger().debug("Reading list from: " + file); if (!file.exists()) { throw new RuntimeException("Failed to load list from file: " + file); } try { List list = FileUtils.loadFile(file); if (getConsoleLogger().isDebugEnabled()) { getConsoleLogger().debug("List contents:"); for (String entry : list) { getConsoleLogger().debug(" " + entry); } } return list; } catch (IOException e) { throw new RuntimeException("Failed to load list from file: " + file, e); } } @Nonnull private List getExcludedScanList() throws MojoFailureException { return getExcludeList(true); } @Nonnull private List getExcludeList() throws MojoFailureException { return getExcludeList(false); } /** * Computes a merge list of test exclusions. * Used only in {@link #getExcludeList()} and {@link #getExcludedScanList()}. * @param asScanList true if dependency or directory scanner * @return list of patterns * @throws MojoFailureException if the excludes breaks a pattern format */ @Nonnull private List getExcludeList(boolean asScanList) throws MojoFailureException { List excludes; if (isSpecificTestSpecified()) { excludes = Collections.emptyList(); } else { excludes = new ArrayList<>(); if (asScanList) { if (getExcludes() != null) { excludes.addAll(getExcludes()); } checkMethodFilterInIncludesExcludes(excludes); } if (getExcludesFile() != null) { excludes.addAll(readListFromFile(getExcludesFile())); } if (asScanList && excludes.isEmpty()) { excludes = Collections.singletonList(getDefaultExcludes()); } } return filterNulls(excludes); } @Nonnull private List getIncludedScanList() throws MojoFailureException { return getIncludeList(true); } @Nonnull private List getIncludeList() throws MojoFailureException { return getIncludeList(false); } /** * Computes a merge list of test inclusions. * Used only in {@link #getIncludeList()} and {@link #getIncludedScanList()}. * @param asScanList true if dependency or directory scanner * @return list of patterns * @throws MojoFailureException if the includes breaks a pattern format */ @Nonnull private List getIncludeList(boolean asScanList) throws MojoFailureException { final List includes = new ArrayList<>(); if (isSpecificTestSpecified()) { addAll(includes, split(getTest(), ",")); } else { if (asScanList) { if (getIncludes() != null) { includes.addAll(getIncludes()); } checkMethodFilterInIncludesExcludes(includes); } if (getIncludesFile() != null) { includes.addAll(readListFromFile(getIncludesFile())); } if (asScanList && includes.isEmpty()) { addAll(includes, getDefaultIncludes()); } } return filterNulls(includes); } private void checkMethodFilterInIncludesExcludes(Iterable patterns) throws MojoFailureException { for (String pattern : patterns) { if (pattern != null && pattern.contains("#")) { throw new MojoFailureException("Method filter prohibited in includes|excludes parameter: " + pattern); } } } private TestListResolver getIncludedAndExcludedTests() throws MojoFailureException { if (includedExcludedTests == null) { includedExcludedTests = new TestListResolver(getIncludedScanList(), getExcludedScanList()); getConsoleLogger().debug("Resolved included and excluded patterns: " + includedExcludedTests); } return includedExcludedTests; } public TestListResolver getSpecificTests() throws MojoFailureException { if (specificTests == null) { specificTests = new TestListResolver(getIncludeList(), getExcludeList()); } return specificTests; } @Nonnull private List filterNulls(@Nonnull List toFilter) { List result = new ArrayList<>(toFilter.size()); for (String item : toFilter) { if (item != null) { item = item.trim(); if (!item.isEmpty()) { result.add(item); } } } return result; } private Artifact getTestNgArtifact() throws MojoExecutionException { Artifact artifact = getProjectArtifactMap().get(getTestNGArtifactName()); Artifact projectArtifact = project.getArtifact(); String projectArtifactName = projectArtifact.getGroupId() + ":" + projectArtifact.getArtifactId(); if (artifact != null) { VersionRange range = createVersionRange(); if (!range.containsVersion(new DefaultArtifactVersion(artifact.getVersion()))) { throw new MojoExecutionException( "TestNG support requires version 4.7 or above. You have declared version " + artifact.getVersion()); } } else if (projectArtifactName.equals(getTestNGArtifactName())) { artifact = projectArtifact; } return artifact; } private VersionRange createVersionRange() { try { return VersionRange.createFromVersionSpec("[4.7,)"); } catch (InvalidVersionSpecificationException e) { throw new RuntimeException(e); } } private Artifact getJunitArtifact() { Artifact artifact = getProjectArtifactMap().get(getJunitArtifactName()); Artifact projectArtifact = project.getArtifact(); String projectArtifactName = projectArtifact.getGroupId() + ":" + projectArtifact.getArtifactId(); if (artifact == null && projectArtifactName.equals(getJunitArtifactName())) { artifact = projectArtifact; } return artifact; } private Artifact getJunitDepArtifact() { return getProjectArtifactMap().get("junit:junit-dep"); } private Artifact getJUnitPlatformRunnerArtifact() { return getProjectArtifactMap().get("org.junit.platform:junit-platform-runner"); } private Artifact getJUnit5Artifact() { Artifact artifact = getPluginArtifactMap().get("org.junit.platform:junit-platform-engine"); if (artifact == null) { return getProjectArtifactMap().get("org.junit.platform:junit-platform-commons"); } return artifact; } private ForkStarter createForkStarter( @Nonnull ProviderInfo provider, @Nonnull ForkConfiguration forkConfiguration, @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull RunOrderParameters runOrderParameters, @Nonnull ConsoleLogger log, @Nonnull DefaultScanResult scanResult, @Nonnull TestClassPath testClasspathWrapper, @Nonnull Platform platform, @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult) throws MojoExecutionException, MojoFailureException { StartupConfiguration startupConfiguration = createStartupConfiguration( provider, true, classLoaderConfiguration, scanResult, testClasspathWrapper, platform, resolvedJavaModularityResult); String configChecksum = getConfigChecksum(); StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration(configChecksum, true); ProviderConfiguration providerConfiguration = createProviderConfiguration(runOrderParameters); return new ForkStarter( providerConfiguration, startupConfiguration, forkConfiguration, getForkedProcessTimeoutInSeconds(), startupReportConfiguration, log); } private InPluginVMSurefireStarter createInprocessStarter( @Nonnull ProviderInfo provider, @Nonnull ClassLoaderConfiguration classLoaderConfig, @Nonnull RunOrderParameters runOrderParameters, @Nonnull DefaultScanResult scanResult, @Nonnull Platform platform, @Nonnull TestClassPath testClasspathWrapper) throws MojoExecutionException, MojoFailureException { StartupConfiguration startupConfiguration = createStartupConfiguration( provider, false, classLoaderConfig, scanResult, testClasspathWrapper, platform, new ResolvePathResultWrapper(null, true)); String configChecksum = getConfigChecksum(); StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration(configChecksum, false); ProviderConfiguration providerConfiguration = createProviderConfiguration(runOrderParameters); return new InPluginVMSurefireStarter( startupConfiguration, providerConfiguration, startupReportConfiguration, getConsoleLogger(), platform); } // todo this is in separate method and can be better tested than whole method createForkConfiguration() @Nonnull private ForkNodeFactory getForkNodeFactory() { ForkNodeFactory forkNode = getForkNode(); return forkNode == null ? new LegacyForkNodeFactory() : forkNode; } @Nonnull private ForkConfiguration createForkConfiguration( @Nonnull Platform platform, @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult) throws MojoExecutionException { File tmpDir = getSurefireTempDir(); Artifact shadeFire = getShadefireArtifact(); Classpath bootClasspath = getArtifactClasspath(shadeFire != null ? shadeFire : getBooterArtifact()); ForkNodeFactory forkNode = getForkNodeFactory(); getConsoleLogger() .debug("Found implementation of fork node factory: " + forkNode.getClass().getName()); if (canExecuteProviderWithModularPath(platform, resolvedJavaModularityResult)) { return new ModularClasspathForkConfiguration( bootClasspath, tmpDir, getEffectiveDebugForkedProcess(), getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(), getProject().getModel().getProperties(), getArgLine(), getEnvironmentVariables(), getExcludedEnvironmentVariables(), getConsoleLogger().isDebugEnabled(), getEffectiveForkCount(), reuseForks, platform, getConsoleLogger(), forkNode); } else if (getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable()) { return new JarManifestForkConfiguration( bootClasspath, tmpDir, getEffectiveDebugForkedProcess(), getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(), getProject().getModel().getProperties(), getArgLine(), getEnvironmentVariables(), getExcludedEnvironmentVariables(), getConsoleLogger().isDebugEnabled(), getEffectiveForkCount(), reuseForks, platform, getConsoleLogger(), forkNode); } else { return new ClasspathForkConfiguration( bootClasspath, tmpDir, getEffectiveDebugForkedProcess(), getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(), getProject().getModel().getProperties(), getArgLine(), getEnvironmentVariables(), getExcludedEnvironmentVariables(), getConsoleLogger().isDebugEnabled(), getEffectiveForkCount(), reuseForks, platform, getConsoleLogger(), forkNode); } } private void ensureEnableProcessChecker() throws MojoFailureException { if (!ProcessCheckerType.isValid(getEnableProcessChecker())) { throw new MojoFailureException("Unexpected value '" + getEnableProcessChecker() + "' in the configuration parameter 'enableProcessChecker'."); } } @SuppressWarnings("checkstyle:emptyblock") protected int getEffectiveForkCount() { if (effectiveForkCount < 0) { try { effectiveForkCount = convertWithCoreCount(forkCount); } catch (NumberFormatException ignored) { } if (effectiveForkCount < 0) { throw new IllegalArgumentException("Fork count " + forkCount.trim() + " is not a legal value."); } } return effectiveForkCount; } protected int convertWithCoreCount(String count) { String trimmed = count.trim(); if (trimmed.endsWith("C")) { double multiplier = Double.parseDouble(trimmed.substring(0, trimmed.length() - 1)); double calculated = multiplier * ((double) Runtime.getRuntime().availableProcessors()); return calculated > 0d ? Math.max((int) calculated, 1) : 0; } else { return parseInt(trimmed); } } private String getEffectiveDebugForkedProcess() { String debugForkedProcess = getDebugForkedProcess(); if ("true".equals(debugForkedProcess)) { return "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:5005"; } return debugForkedProcess; } private JdkAttributes getEffectiveJvm() throws MojoFailureException { if (isNotEmpty(getJvm())) { File pathToJava = new File(getJvm()).getAbsoluteFile(); if (!endsWithJavaPath(pathToJava.getPath())) { throw new MojoFailureException( "Given path does not end with java executor \"" + pathToJava.getPath() + "\"."); } if (!(pathToJava.isFile() || "java".equals(pathToJava.getName()) && pathToJava.getParentFile().isDirectory())) { throw new MojoFailureException( "Given path to java executor does not exist \"" + pathToJava.getPath() + "\"."); } File jdkHome = toJdkHomeFromJvmExec(pathToJava.getPath()); if (jdkHome == null) { getConsoleLogger().warning("Cannot determine JAVA_HOME of jvm exec path " + pathToJava); } else if (!getEnvironmentVariables().containsKey("JAVA_HOME")) { getEnvironmentVariables().put("JAVA_HOME", jdkHome.getAbsolutePath()); } BigDecimal version = jdkHome == null ? null : toJdkVersionFromReleaseFile(jdkHome); boolean javaVersion9 = version == null ? isJava9AtLeast(pathToJava.getPath()) : isJava9AtLeast(version); return new JdkAttributes(pathToJava, jdkHome, javaVersion9); } if (toolchain != null) { String jvmToUse = toolchain.findTool("java"); if (isNotEmpty(jvmToUse)) { boolean javaVersion9 = false; String jdkHome = null; if (toolchain instanceof DefaultToolchain) { DefaultToolchain defaultToolchain = (DefaultToolchain) toolchain; javaVersion9 = defaultToolchain.matchesRequirements(JAVA_9_MATCHER) || defaultToolchain.matchesRequirements(JAVA_9_MATCHER_OLD_NOTATION); } if (toolchain instanceof DefaultJavaToolChain) { DefaultJavaToolChain defaultJavaToolChain = (DefaultJavaToolChain) toolchain; if (!getEnvironmentVariables().containsKey("JAVA_HOME")) { jdkHome = defaultJavaToolChain.getJavaHome(); getEnvironmentVariables().put("JAVA_HOME", jdkHome); } } if (!javaVersion9) { javaVersion9 = isJava9AtLeast(jvmToUse); } return new JdkAttributes( new File(jvmToUse), jdkHome == null ? toJdkHomeFromJvmExec(jvmToUse) : new File(jdkHome), javaVersion9); } } // use the same JVM as the one used to run Maven (the "java.home" one) String jvmToUse = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; getConsoleLogger().debug("Using JVM: " + jvmToUse + " with Java version " + JAVA_RECENT); return new JdkAttributes(jvmToUse, isBuiltInJava9AtLeast()); } /** * Where surefire stores its own temp files * * @return A file pointing to the location of surefire's own temp files */ File getSurefireTempDir() { File result = IS_OS_WINDOWS ? createSurefireBootDirectoryInTemp() : createSurefireBootDirectoryInBuild(); try { File canonical = result.getCanonicalFile(); if (!result.equals(canonical)) { getConsoleLogger().debug("Canonicalized tempDir path '" + result + "' to '" + canonical + "'"); } return canonical; } catch (IOException e) { getConsoleLogger().error("Could not canonicalize tempDir path '" + result + "'", e); } return result; } /** * Operates on raw plugin parameters, not the "effective" values. * * @return The checksum */ private String getConfigChecksum() { ChecksumCalculator checksum = new ChecksumCalculator(); checksum.add(getPluginName()); checksum.add(isSkipTests()); checksum.add(isSkipExec()); checksum.add(isSkip()); checksum.add(getTestClassesDirectory()); checksum.add(getMainBuildPath()); checksum.add(getClasspathDependencyExcludes()); checksum.add(getClasspathDependencyScopeExclude()); checksum.add(getAdditionalClasspathElements()); checksum.add(getReportsDirectory()); checksum.add(getProjectBuildDirectory()); checksum.add(getTestSourceDirectory()); checksum.add(getTest()); checksum.add(getIncludes()); checksum.add(getSkipAfterFailureCount()); checksum.add(getShutdown()); checksum.add(getExcludes()); checksum.add(getLocalRepositoryPath()); checksum.add(getSystemProperties()); checksum.add(getSystemPropertyVariables()); checksum.add(getSystemPropertiesFile()); checksum.add(getProperties()); checksum.add(isPrintSummary()); checksum.add(getReportFormat()); checksum.add(getReportNameSuffix()); checksum.add(isUseFile()); checksum.add(isRedirectTestOutputToFile()); checksum.add(getForkCount()); checksum.add(isReuseForks()); checksum.add(getJvm()); checksum.add(getArgLine()); checksum.add(getDebugForkedProcess()); checksum.add(getForkedProcessTimeoutInSeconds()); checksum.add(getParallelTestsTimeoutInSeconds()); checksum.add(getParallelTestsTimeoutForcedInSeconds()); checksum.add(getEnvironmentVariables()); checksum.add(getExcludedEnvironmentVariables()); checksum.add(getWorkingDirectory()); checksum.add(isChildDelegation()); checksum.add(getGroups()); checksum.add(getExcludedGroups()); checksum.add(getIncludeJUnit5Engines()); checksum.add(getExcludeJUnit5Engines()); checksum.add(getSuiteXmlFiles()); checksum.add(getJunitArtifact()); checksum.add(getTestNGArtifactName()); checksum.add(getThreadCount()); checksum.add(getThreadCountSuites()); checksum.add(getThreadCountClasses()); checksum.add(getThreadCountMethods()); checksum.add(getPerCoreThreadCount()); checksum.add(getUseUnlimitedThreads()); checksum.add(getParallel()); checksum.add(isParallelOptimized()); checksum.add(isTrimStackTrace()); checksum.add(isDisableXmlReport()); checksum.add(isUseSystemClassLoader()); checksum.add(isUseManifestOnlyJar()); checksum.add(getEncoding()); checksum.add(isEnableAssertions()); checksum.add(getObjectFactory()); checksum.add(getFailIfNoTests()); checksum.add(getRunOrder()); checksum.add(getDependenciesToScan()); checksum.add(getForkedProcessExitTimeoutInSeconds()); checksum.add(getRerunFailingTestsCount()); checksum.add(getTempDir()); checksum.add(useModulePath()); checksum.add(getEnableProcessChecker()); addPluginSpecificChecksumItems(checksum); return checksum.getSha1(); } protected void addPluginSpecificChecksumItems(ChecksumCalculator checksum) {} protected boolean hasExecutedBefore() { // A tribute to Linus Torvalds String configChecksum = getConfigChecksum(); @SuppressWarnings("unchecked") Map pluginContext = getPluginContext(); if (pluginContext.containsKey(configChecksum)) { getConsoleLogger() .info("Skipping execution of surefire because it has already been run for this configuration"); return true; } pluginContext.put(configChecksum, configChecksum); return false; } @Nonnull protected ClassLoaderConfiguration getClassLoaderConfiguration() { return isForking() ? new ClassLoaderConfiguration(isUseSystemClassLoader(), isUseManifestOnlyJar()) : new ClassLoaderConfiguration(false, false); } /** * Generates the test classpath. * * @return the classpath elements * @throws MojoFailureException */ private TestClassPath generateTestClasspath() throws MojoFailureException { Set classpathArtifacts = getProject().getArtifacts(); if (getClasspathDependencyScopeExclude() != null && !getClasspathDependencyScopeExclude().isEmpty()) { ArtifactFilter dependencyFilter = new ScopeArtifactFilter(getClasspathDependencyScopeExclude()); classpathArtifacts = filterArtifacts(classpathArtifacts, dependencyFilter); } if (getClasspathDependencyExcludes() != null) { List excludedDependencies = asList(getClasspathDependencyExcludes()); ArtifactFilter dependencyFilter = new PatternIncludesArtifactFilter(excludedDependencies); classpathArtifacts = filterArtifacts(classpathArtifacts, dependencyFilter); } Map dependencyConflictIdsProjectArtifacts = classpathArtifacts.stream() .collect(Collectors.toMap(Artifact::getDependencyConflictId, Function.identity())); Set additionalClasspathElements = new LinkedHashSet<>(); if (getAdditionalClasspathElements() != null) { Arrays.stream(getAdditionalClasspathElements()).forEach(additionalClasspathElements::add); } if (additionalClasspathDependencies != null && !additionalClasspathDependencies.isEmpty()) { Collection additionalArtifacts = resolveDependencies(additionalClasspathDependencies); // check for potential conflicts with project dependencies for (Artifact additionalArtifact : additionalArtifacts) { Artifact conflictingArtifact = dependencyConflictIdsProjectArtifacts.get(additionalArtifact.getDependencyConflictId()); if (conflictingArtifact != null && !additionalArtifact.getVersion().equals(conflictingArtifact.getVersion())) { getConsoleLogger() .warning( "Potential classpath conflict between project dependency and resolved additionalClasspathDependency: Found multiple versions of " + additionalArtifact.getDependencyConflictId() + ": " + additionalArtifact.getVersion() + " and " + conflictingArtifact.getVersion()); } additionalClasspathElements.add(additionalArtifact.getFile().getAbsolutePath()); } } return new TestClassPath( classpathArtifacts, getMainBuildPath(), getTestClassesDirectory(), additionalClasspathElements); } protected Collection resolveDependencies(List dependencies) throws MojoFailureException { Map dependencyConflictIdsAndArtifacts = new HashMap<>(); try { dependencies.stream() .map(dependency -> { try { return surefireDependencyResolver.resolveDependencies( session.getRepositorySession(), project.getRemoteProjectRepositories(), dependency); } catch (MojoExecutionException e) { throw new IllegalStateException(e); } }) .forEach(artifacts -> { for (Artifact a : artifacts) { Artifact conflictingArtifact = dependencyConflictIdsAndArtifacts.get(a.getDependencyConflictId()); if (conflictingArtifact != null && !a.getVersion().equals(conflictingArtifact.getVersion())) { getConsoleLogger() .warning( "Potential classpath conflict among resolved additionalClasspathDependencies: Found multiple versions of " + a.getDependencyConflictId() + ": " + a.getVersion() + " and " + conflictingArtifact.getVersion()); } else { dependencyConflictIdsAndArtifacts.put(a.getDependencyConflictId(), a); } } }); } catch (IllegalStateException e) { throw new MojoFailureException(e.getMessage(), e.getCause()); } return dependencyConflictIdsAndArtifacts.values(); } /** * Return a new set containing only the artifacts not accepted by the given filter. * * @param artifacts The unfiltered artifacts * @param filter The excludes filter to apply * @return The filtered result */ private static Set filterArtifacts(Set artifacts, ArtifactFilter filter) { Set filteredArtifacts = new LinkedHashSet<>(); for (Artifact artifact : artifacts) { if (!filter.include(artifact)) { filteredArtifacts.add(artifact); } } return filteredArtifacts; } private void showMap(Map map, String setting) { for (Object o : map.keySet()) { String key = (String) o; String value = (String) map.get(key); getConsoleLogger().debug("Setting " + setting + " [" + key + "]=[" + value + "]"); } } private void showArray(T[] array, String setting) { for (T e : array) { getConsoleLogger().debug("Setting " + setting + " [" + e + "]"); } } private Classpath getArtifactClasspath(Artifact surefireArtifact) throws MojoExecutionException { Classpath existing = classpathCache.getCachedClassPath(surefireArtifact.getArtifactId()); if (existing == null) { List items = new ArrayList<>(); Set booterArtifacts = surefireDependencyResolver.resolveArtifacts( session.getRepositorySession(), project.getRemotePluginRepositories(), surefireArtifact); for (Artifact artifact : booterArtifacts) { items.add(artifact.getFile().getAbsolutePath()); } existing = new Classpath(items); classpathCache.setCachedClasspath(surefireArtifact.getArtifactId(), existing); } return existing; } private Properties getUserProperties() { return getSession().getUserProperties(); } private void ensureWorkingDirectoryExists() throws MojoFailureException { if (getWorkingDirectory() == null) { throw new MojoFailureException("workingDirectory cannot be null"); } if (isForking()) { // Postpone directory creation till forked JVM creation // see ForkConfiguration.createCommandLine return; } if (!getWorkingDirectory().exists()) { if (!getWorkingDirectory().mkdirs()) { throw new MojoFailureException("Cannot create workingDirectory " + getWorkingDirectory()); } } if (!getWorkingDirectory().isDirectory()) { throw new MojoFailureException( "workingDirectory " + getWorkingDirectory() + " exists and is not a directory"); } } private void ensureParallelRunningCompatibility() throws MojoFailureException { if (isMavenParallel() && isNotForking()) { throw new MojoFailureException("parallel maven execution is not compatible with surefire forkCount 0"); } } private void warnIfUselessUseSystemClassLoaderParameter() { if (isUseSystemClassLoader() && isNotForking()) { getConsoleLogger().warning("useSystemClassLoader setting has no effect when not forking"); } } private boolean isNotForking() { return !isForking(); } private List commandLineOptions() { return SurefireHelper.commandLineOptions(getSession(), getConsoleLogger()); } private void warnIfDefunctGroupsCombinations() throws MojoFailureException, MojoExecutionException { if (isAnyGroupsSelected()) { if (getTestNgArtifact() == null) { Artifact junitArtifact = getJunitArtifact(); boolean junit47Compatible = isJunit47Compatible(junitArtifact); boolean junit5PlatformCompatible = getJUnit5Artifact() != null; if (!junit47Compatible && !junit5PlatformCompatible) { if (junitArtifact != null) { throw new MojoFailureException("groups/excludedGroups are specified but JUnit version on " + "classpath is too old to support groups. " + "Check your dependency:tree to see if your project " + "is picking up an old junit version"); } throw new MojoFailureException("groups/excludedGroups require TestNG, JUnit48+ or JUnit 5 " + "(a specific engine required on classpath) on project test classpath"); } } } } private void warnIfRerunClashes() throws MojoFailureException { if (getRerunFailingTestsCount() < 0) { throw new MojoFailureException("Parameter \"rerunFailingTestsCount\" should not be negative."); } if (getSkipAfterFailureCount() < 0) { throw new MojoFailureException("Parameter \"skipAfterFailureCount\" should not be negative."); } } private void warnIfWrongShutdownValue() throws MojoFailureException { if (!Shutdown.isKnown(getShutdown())) { throw new MojoFailureException("Parameter \"shutdown\" should have values " + Shutdown.listParameters()); } } private void warnIfNotApplicableSkipAfterFailureCount() throws MojoFailureException { int skipAfterFailureCount = getSkipAfterFailureCount(); if (skipAfterFailureCount < 0) { throw new MojoFailureException("Parameter \"skipAfterFailureCount\" should not be negative."); } else if (skipAfterFailureCount > 0) { try { Artifact testng = getTestNgArtifact(); if (testng != null) { VersionRange range = VersionRange.createFromVersionSpec("[5.10,)"); if (!range.containsVersion(new DefaultArtifactVersion(testng.getVersion()))) { throw new MojoFailureException( "Parameter \"skipAfterFailureCount\" expects TestNG Version 5.10 or higher. " + "java.lang.NoClassDefFoundError: org/testng/IInvokedMethodListener"); } } else { // TestNG is dependent on JUnit Artifact junit = getJunitArtifact(); if (junit != null) { VersionRange range = VersionRange.createFromVersionSpec("[4.0,)"); if (!range.containsVersion(new DefaultArtifactVersion(junit.getVersion()))) { throw new MojoFailureException( "Parameter \"skipAfterFailureCount\" expects JUnit Version 4.0 or higher. " + "java.lang.NoSuchMethodError: " + "org.junit.runner.notification.RunNotifier.pleaseStop()V"); } } } } catch (MojoExecutionException e) { throw new MojoFailureException(e.getLocalizedMessage()); } catch (InvalidVersionSpecificationException e) { throw new RuntimeException(e); } } } private void warnIfIllegalTempDir() throws MojoFailureException { if (isEmpty(getTempDir())) { throw new MojoFailureException("Parameter 'tempDir' should not be blank string."); } } protected void warnIfIllegalFailOnFlakeCount() throws MojoFailureException {} private void printDefaultSeedIfNecessary() { if (getRunOrder().equals(RunOrder.RANDOM.name())) { if (getRunOrderRandomSeed() == null) { setRunOrderRandomSeed(System.nanoTime()); } getConsoleLogger() .info("Tests will run in random order. To reproduce ordering use flag -D" + getPluginName() + ".runOrder.random.seed=" + getRunOrderRandomSeed()); } } final class TestNgProviderInfo implements ProviderInfo { private final Artifact testNgArtifact; TestNgProviderInfo(Artifact testNgArtifact) { this.testNgArtifact = testNgArtifact; } @Override @Nonnull public String getProviderName() { return "org.apache.maven.surefire.testng.TestNGProvider"; } @Override public boolean isApplicable() { return testNgArtifact != null; } @Override public void addProviderProperties() throws MojoExecutionException { convertTestNGParameters(); } @Nonnull @Override public List getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) { return emptyList(); } @Override @Nonnull public Set getProviderClasspath() throws MojoExecutionException { Artifact surefireArtifact = getBooterArtifact(); String version = surefireArtifact.getBaseVersion(); return surefireDependencyResolver.getProviderClasspath( session.getRepositorySession(), project.getRemotePluginRepositories(), "surefire-testng", version); } } final class JUnit3ProviderInfo implements ProviderInfo { @Override @Nonnull public String getProviderName() { return "org.apache.maven.surefire.junit.JUnit3Provider"; } @Override public boolean isApplicable() { return true; } @Override public void addProviderProperties() {} @Nonnull @Override public List getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) { return emptyList(); } @Override @Nonnull public Set getProviderClasspath() throws MojoExecutionException { // add the JUnit provider as default - it doesn't require JUnit to be present, // since it supports POJO tests. String version = getBooterArtifact().getBaseVersion(); return surefireDependencyResolver.getProviderClasspath( session.getRepositorySession(), project.getRemotePluginRepositories(), "surefire-junit3", version); } } final class JUnit4ProviderInfo implements ProviderInfo { private final Artifact junitArtifact; private final Artifact junitDepArtifact; JUnit4ProviderInfo(Artifact junitArtifact, Artifact junitDepArtifact) { this.junitArtifact = junitArtifact; this.junitDepArtifact = junitDepArtifact; } @Override @Nonnull public String getProviderName() { return "org.apache.maven.surefire.junit4.JUnit4Provider"; } @Override public boolean isApplicable() { return junitDepArtifact != null || isAnyJunit4(junitArtifact); } @Override public void addProviderProperties() {} @Nonnull @Override public List getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) { return emptyList(); } @Override @Nonnull public Set getProviderClasspath() throws MojoExecutionException { String version = getBooterArtifact().getBaseVersion(); return surefireDependencyResolver.getProviderClasspath( session.getRepositorySession(), project.getRemotePluginRepositories(), "surefire-junit4", version); } } final class JUnitPlatformProviderInfo implements ProviderInfo { private static final String PROVIDER_DEP_GID = "org.junit.platform"; private static final String PROVIDER_DEP_AID = "junit-platform-launcher"; private final Artifact junitPlatformRunnerArtifact; private final Artifact junitPlatformArtifact; private final TestClassPath testClasspath; JUnitPlatformProviderInfo( Artifact junitPlatformRunnerArtifact, Artifact junitPlatformArtifact, @Nonnull TestClassPath testClasspath) { this.junitPlatformRunnerArtifact = junitPlatformRunnerArtifact; this.junitPlatformArtifact = junitPlatformArtifact; this.testClasspath = testClasspath; } @Override @Nonnull public String getProviderName() { return "org.apache.maven.surefire.junitplatform.JUnitPlatformProvider"; } @Override public boolean isApplicable() { return junitPlatformRunnerArtifact == null && junitPlatformArtifact != null; } @Override public void addProviderProperties() { convertGroupParameters(); convertJunitEngineParameters(); } @Nonnull @Override public List getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) { boolean hasTestDescriptor = forkRequirements.isModularPath() && forkRequirements.hasTestModuleDescriptor(); return hasTestDescriptor ? getJpmsArgs() : Collections.emptyList(); } @Override @Nonnull public Set getProviderClasspath() throws MojoExecutionException { String surefireVersion = getBooterArtifact().getBaseVersion(); Map providerArtifacts = surefireDependencyResolver.getProviderClasspathAsMap( session.getRepositorySession(), project.getRemotePluginRepositories(), "surefire-junit-platform", surefireVersion); Map testDeps = testClasspath.getTestDependencies(); Plugin plugin = getPluginDescriptor().getPlugin(); Map pluginDeps = surefireDependencyResolver.resolvePluginDependencies( session.getRepositorySession(), project.getRemotePluginRepositories(), plugin, getPluginArtifactMap()); if (hasDependencyPlatformEngine(pluginDeps)) { providerArtifacts.putAll(pluginDeps); } else { String engineVersion = null; if (hasDependencyJupiterAPI(testDeps) && !testDeps.containsKey("org.junit.jupiter:junit-jupiter-engine")) { String engineGroupId = "org.junit.jupiter"; String engineArtifactId = "junit-jupiter-engine"; String engineCoordinates = engineGroupId + ":" + engineArtifactId; String api = "org.junit.jupiter:junit-jupiter-api"; engineVersion = testDeps.get(api).getBaseVersion(); getConsoleLogger() .debug("Test dependencies contain " + api + ". Resolving " + engineCoordinates + ":" + engineVersion); addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts); } if ((testDeps.containsKey("junit:junit") || testDeps.containsKey("junit:junit-dep")) && !testDeps.containsKey("org.junit.vintage:junit-vintage-engine")) { String engineGroupId = "org.junit.vintage"; String engineArtifactId = "junit-vintage-engine"; String engineCoordinates = engineGroupId + ":" + engineArtifactId; if (engineVersion != null) { getConsoleLogger() .debug("Test dependencies contain JUnit4. Resolving " + engineCoordinates + ":" + engineVersion); addEngineByApi(engineGroupId, engineArtifactId, engineVersion, providerArtifacts); } } } narrowDependencies(providerArtifacts, testDeps); alignProviderVersions(providerArtifacts); return new LinkedHashSet<>(providerArtifacts.values()); } private List getJpmsArgs() { List args = new ArrayList<>(); args.add(new String[] { "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.util=ALL-UNNAMED" }); args.add(new String[] { "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.logging=ALL-UNNAMED" }); return args; } private void addEngineByApi( String engineGroupId, String engineArtifactId, String engineVersion, Map providerArtifacts) throws MojoExecutionException { for (Artifact dep : resolve(engineGroupId, engineArtifactId, engineVersion, null, "jar")) { String key = dep.getGroupId() + ":" + dep.getArtifactId(); providerArtifacts.put(key, dep); } } private void narrowDependencies( Map providerArtifacts, Map testDependencies) { providerArtifacts.keySet().removeAll(testDependencies.keySet()); } private void alignProviderVersions(Map providerArtifacts) throws MojoExecutionException { String version = junitPlatformArtifact.getBaseVersion(); for (Artifact launcherArtifact : resolve(PROVIDER_DEP_GID, PROVIDER_DEP_AID, version, null, "jar")) { String key = launcherArtifact.getGroupId() + ":" + launcherArtifact.getArtifactId(); if (providerArtifacts.containsKey(key)) { providerArtifacts.put(key, launcherArtifact); } } } private Set resolve(String g, String a, String v, String c, String t) throws MojoExecutionException { ArtifactHandler handler = junitPlatformArtifact.getArtifactHandler(); Artifact artifact = new DefaultArtifact(g, a, v, null, t, c, handler); getConsoleLogger().debug("Resolving artifact " + g + ":" + a + ":" + v); Set r = surefireDependencyResolver.resolveArtifacts( session.getRepositorySession(), project.getRemoteProjectRepositories(), artifact); getConsoleLogger().debug("Resolved artifact " + g + ":" + a + ":" + v + " to " + r); return r; } private boolean hasDependencyJupiterAPI(Map dependencies) { return dependencies.containsKey("org.junit.jupiter:junit-jupiter-api"); } private boolean hasDependencyPlatformEngine(Map dependencies) { for (Entry dependency : dependencies.entrySet()) { if (dependency.getKey().equals("org.junit.platform:junit-platform-engine")) { return true; } } return false; } } final class JUnitCoreProviderInfo implements ProviderInfo { private final Artifact junitArtifact; private final Artifact junitDepArtifact; JUnitCoreProviderInfo(Artifact junitArtifact, Artifact junitDepArtifact) { this.junitArtifact = junitArtifact; this.junitDepArtifact = junitDepArtifact; } @Override @Nonnull public String getProviderName() { return "org.apache.maven.surefire.junitcore.JUnitCoreProvider"; } private boolean is47CompatibleJunitDep() { return isJunit47Compatible(junitDepArtifact); } @Override public boolean isApplicable() { final boolean isJunitArtifact47 = isAnyJunit4(junitArtifact) && isJunit47Compatible(junitArtifact); final boolean isAny47ProvidersForces = isAnyConcurrencySelected() || isAnyGroupsSelected(); return isAny47ProvidersForces && (isJunitArtifact47 || is47CompatibleJunitDep()); } @Override public void addProviderProperties() throws MojoExecutionException { convertJunitCoreParameters(); convertGroupParameters(); convertJunitEngineParameters(); } @Nonnull @Override public List getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) { return emptyList(); } @Override @Nonnull public Set getProviderClasspath() throws MojoExecutionException { String version = getBooterArtifact().getBaseVersion(); return surefireDependencyResolver.getProviderClasspath( session.getRepositorySession(), project.getRemotePluginRepositories(), "surefire-junit47", version); } } /** * Provides the Provider information for manually configured providers. */ final class DynamicProviderInfo implements ConfigurableProviderInfo { final String providerName; DynamicProviderInfo(String providerName) { this.providerName = providerName; } @Override public ProviderInfo instantiate(String providerName) { return new DynamicProviderInfo(providerName); } @Override @Nonnull public String getProviderName() { return providerName; } @Override public boolean isApplicable() { return true; } @Override public void addProviderProperties() throws MojoExecutionException { // Ok this is a bit lazy. convertJunitCoreParameters(); convertTestNGParameters(); } @Nonnull @Override public List getJpmsArguments(@Nonnull ProviderRequirements forkRequirements) { return emptyList(); } @Override @Nonnull public Set getProviderClasspath() throws MojoExecutionException { Plugin plugin = getPluginDescriptor().getPlugin(); Map providerArtifacts = surefireDependencyResolver.resolvePluginDependencies( session.getRepositorySession(), project.getRemotePluginRepositories(), plugin, getPluginArtifactMap()); return new LinkedHashSet<>(providerArtifacts.values()); } } File createSurefireBootDirectoryInBuild() { File tmp = new File(getProjectBuildDirectory(), getTempDir()); //noinspection ResultOfMethodCallIgnored tmp.mkdirs(); return tmp; } File createSurefireBootDirectoryInTemp() { try { return Files.createTempDirectory(getTempDir()).toFile(); } catch (IOException e) { return createSurefireBootDirectoryInBuild(); } } @Override public String getLocalRepositoryPath() { return Optional.ofNullable( session.getRepositorySession().getLocalRepository().getBasedir()) .map(File::getAbsolutePath) .orElse("."); } public Properties getSystemProperties() { return systemProperties; } @SuppressWarnings("UnusedDeclaration") public void setSystemProperties(Properties systemProperties) { this.systemProperties = systemProperties; } public Map getSystemPropertyVariables() { return systemPropertyVariables; } @SuppressWarnings("UnusedDeclaration") public void setSystemPropertyVariables(Map systemPropertyVariables) { this.systemPropertyVariables = systemPropertyVariables; } /** * List of System properties, loaded from a file, to pass to the JUnit tests. * * @since 2.8.2 */ public abstract File getSystemPropertiesFile(); @SuppressWarnings("UnusedDeclaration") public abstract void setSystemPropertiesFile(File systemPropertiesFile); private Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } public Map getPluginArtifactMap() { return pluginArtifactMap; } @SuppressWarnings("UnusedDeclaration") public void setPluginArtifactMap(Map pluginArtifactMap) { this.pluginArtifactMap = pluginArtifactMap; } public Map getProjectArtifactMap() { return projectArtifactMap; } @SuppressWarnings("UnusedDeclaration") public void setProjectArtifactMap(Map projectArtifactMap) { this.projectArtifactMap = projectArtifactMap; } public String getReportNameSuffix() { return reportNameSuffix; } @SuppressWarnings("UnusedDeclaration") public void setReportNameSuffix(String reportNameSuffix) { this.reportNameSuffix = reportNameSuffix; } public boolean isRedirectTestOutputToFile() { return redirectTestOutputToFile; } @SuppressWarnings("UnusedDeclaration") public void setRedirectTestOutputToFile(boolean redirectTestOutputToFile) { this.redirectTestOutputToFile = redirectTestOutputToFile; } public boolean getFailIfNoTests() { return failIfNoTests; } public void setFailIfNoTests(boolean failIfNoTests) { this.failIfNoTests = failIfNoTests; } public String getJvm() { return jvm; } public String getArgLine() { return argLine; } @SuppressWarnings("UnusedDeclaration") public void setArgLine(String argLine) { this.argLine = argLine; } public Map getEnvironmentVariables() { return environmentVariables; } @SuppressWarnings("UnusedDeclaration") public void setEnvironmentVariables(Map environmentVariables) { this.environmentVariables = environmentVariables; } public File getWorkingDirectory() { return workingDirectory; } @SuppressWarnings("UnusedDeclaration") public void setWorkingDirectory(File workingDirectory) { this.workingDirectory = workingDirectory; } public boolean isChildDelegation() { return childDelegation; } @SuppressWarnings("UnusedDeclaration") public void setChildDelegation(boolean childDelegation) { this.childDelegation = childDelegation; } public String getGroups() { return groups; } @SuppressWarnings("UnusedDeclaration") public void setGroups(String groups) { this.groups = groups; } public String getExcludedGroups() { return excludedGroups; } @SuppressWarnings("UnusedDeclaration") public void setExcludedGroups(String excludedGroups) { this.excludedGroups = excludedGroups; } public String getJunitArtifactName() { return junitArtifactName; } @SuppressWarnings("UnusedDeclaration") public void setJunitArtifactName(String junitArtifactName) { this.junitArtifactName = junitArtifactName; } public String getTestNGArtifactName() { return testNGArtifactName; } @SuppressWarnings("UnusedDeclaration") public void setTestNGArtifactName(String testNGArtifactName) { this.testNGArtifactName = testNGArtifactName; } public int getThreadCount() { return threadCount; } @SuppressWarnings("UnusedDeclaration") public void setThreadCount(int threadCount) { this.threadCount = threadCount; } public boolean getPerCoreThreadCount() { return perCoreThreadCount; } @SuppressWarnings("UnusedDeclaration") public void setPerCoreThreadCount(boolean perCoreThreadCount) { this.perCoreThreadCount = perCoreThreadCount; } public boolean getUseUnlimitedThreads() { return useUnlimitedThreads; } @SuppressWarnings("UnusedDeclaration") public void setUseUnlimitedThreads(boolean useUnlimitedThreads) { this.useUnlimitedThreads = useUnlimitedThreads; } public String getParallel() { return parallel; } @SuppressWarnings("UnusedDeclaration") public void setParallel(String parallel) { this.parallel = parallel; } public boolean isParallelOptimized() { return parallelOptimized; } @SuppressWarnings("UnusedDeclaration") public void setParallelOptimized(boolean parallelOptimized) { this.parallelOptimized = parallelOptimized; } public int getThreadCountSuites() { return threadCountSuites; } public void setThreadCountSuites(int threadCountSuites) { this.threadCountSuites = threadCountSuites; } public int getThreadCountClasses() { return threadCountClasses; } public void setThreadCountClasses(int threadCountClasses) { this.threadCountClasses = threadCountClasses; } public int getThreadCountMethods() { return threadCountMethods; } public void setThreadCountMethods(int threadCountMethods) { this.threadCountMethods = threadCountMethods; } public boolean isTrimStackTrace() { return trimStackTrace; } @SuppressWarnings("UnusedDeclaration") public void setTrimStackTrace(boolean trimStackTrace) { this.trimStackTrace = trimStackTrace; } public boolean isDisableXmlReport() { return disableXmlReport; } @SuppressWarnings("UnusedDeclaration") public void setDisableXmlReport(boolean disableXmlReport) { this.disableXmlReport = disableXmlReport; } public boolean isEnableAssertions() { return enableAssertions; } public boolean effectiveIsEnableAssertions() { if (getArgLine() != null) { List args = asList(getArgLine().split(" ")); if (args.contains("-da") || args.contains("-disableassertions")) { return false; } } return isEnableAssertions(); } @SuppressWarnings("UnusedDeclaration") public void setEnableAssertions(boolean enableAssertions) { this.enableAssertions = enableAssertions; } public MavenSession getSession() { return session; } @SuppressWarnings("UnusedDeclaration") public void setSession(MavenSession session) { this.session = session; } public String getObjectFactory() { return objectFactory; } @SuppressWarnings("UnusedDeclaration") public void setObjectFactory(String objectFactory) { this.objectFactory = objectFactory; } public ToolchainManager getToolchainManager() { return toolchainManager; } @SuppressWarnings("UnusedDeclaration") public void setToolchainManager(ToolchainManager toolchainManager) { this.toolchainManager = toolchainManager; } public boolean isMavenParallel() { return parallelMavenExecution != null && parallelMavenExecution; } public String[] getDependenciesToScan() { return dependenciesToScan; } public void setDependenciesToScan(String[] dependenciesToScan) { this.dependenciesToScan = dependenciesToScan; } @SuppressWarnings("UnusedDeclaration") void setPluginDescriptor(PluginDescriptor pluginDescriptor) { this.pluginDescriptor = pluginDescriptor; } public PluginDescriptor getPluginDescriptor() { return pluginDescriptor; } public MavenProject getProject() { return project; } @SuppressWarnings("UnusedDeclaration") public void setProject(MavenProject project) { this.project = project; } @Override public File getTestSourceDirectory() { return testSourceDirectory; } @Override public void setTestSourceDirectory(File testSourceDirectory) { this.testSourceDirectory = testSourceDirectory; } public String getForkCount() { return forkCount; } public boolean isReuseForks() { return reuseForks; } public String[] getAdditionalClasspathElements() { return additionalClasspathElements; } public void setAdditionalClasspathElements(String[] additionalClasspathElements) { this.additionalClasspathElements = additionalClasspathElements; } public String[] getClasspathDependencyExcludes() { return classpathDependencyExcludes; } public void setClasspathDependencyExcludes(String[] classpathDependencyExcludes) { this.classpathDependencyExcludes = classpathDependencyExcludes; } public String getClasspathDependencyScopeExclude() { return classpathDependencyScopeExclude; } public void setClasspathDependencyScopeExclude(String classpathDependencyScopeExclude) { this.classpathDependencyScopeExclude = classpathDependencyScopeExclude; } public File getProjectBuildDirectory() { return projectBuildDirectory; } public void setProjectBuildDirectory(File projectBuildDirectory) { this.projectBuildDirectory = projectBuildDirectory; } protected void logDebugOrCliShowErrors(String s) { SurefireHelper.logDebugOrCliShowErrors(s, getConsoleLogger(), cli); } public Map getJdkToolchain() { return jdkToolchain; } public void setJdkToolchain(Map jdkToolchain) { this.jdkToolchain = jdkToolchain; } public String getTempDir() { return tempDir; } public void setTempDir(String tempDir) { this.tempDir = tempDir; } private static final class ClasspathCache { private final Map classpaths = new HashMap<>(4); private Classpath getCachedClassPath(@Nonnull String artifactId) { return classpaths.get(artifactId); } private void setCachedClasspath(@Nonnull String key, @Nonnull Classpath classpath) { classpaths.put(key, classpath); } private Classpath setCachedClasspath(@Nonnull String key, @Nonnull Set artifacts) { Collection files = new ArrayList<>(); for (Artifact artifact : artifacts) { files.add(artifact.getFile().getAbsolutePath()); } Classpath classpath = new Classpath(files); setCachedClasspath(key, classpath); return classpath; } } /** * Determines whether the plugin should fail if no tests found to run. */ enum PluginFailureReason { NONE, COULD_NOT_RUN_SPECIFIED_TESTS, COULD_NOT_RUN_DEFAULT_TESTS, } }