/*
* #%L
* Native ARchive plugin for Maven
* %%
* Copyright (C) 2002 - 2014 NAR Maven Plugin developers.
* %%
* Licensed 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.
* #L%
*/
package com.github.maven_nar;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
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.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
import org.apache.maven.surefire.booter.ForkConfiguration;
import org.apache.maven.surefire.booter.SurefireBooter;
import org.apache.maven.surefire.booter.SurefireBooterForkException;
import org.apache.maven.surefire.booter.SurefireExecutionException;
import org.apache.maven.surefire.report.BriefConsoleReporter;
import org.apache.maven.surefire.report.BriefFileReporter;
import org.apache.maven.surefire.report.ConsoleReporter;
import org.apache.maven.surefire.report.DetailedConsoleReporter;
import org.apache.maven.surefire.report.FileReporter;
import org.apache.maven.surefire.report.ForkingConsoleReporter;
import org.apache.maven.surefire.report.XMLReporter;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.StringUtils;
// Copied from Maven maven-surefire-plugin 2.4.3
/**
* Run integration tests using Surefire. This goal was copied from Maven's
* surefire plugin to accomodate a few things
* for the NAR plugin:
* <P>
* 1. To test a jar file with its native module we can only run after the
* package phase, so we use the integration-test phase.
* </P>
* <P>
* 2. We need to set java.library.path to an AOL (architecture-os-linker)
* specific value, but AOL is only known in the NAR plugin and thus cannot be
* set from the pom.
* </P>
* <P>
* 3. To have the java.library.path definition picked up by java we need the
* "pertest" forkmode. To use this goal you need to put the test sources in the
* regular test directories but disable the running of the tests by the
* maven-surefire-plugin by setting maven.test.skip.exec to false in your pom.
* </P>
*
* @author Jason van Zyl (modified by Mark Donszelmann, noted by DUNS)
* @version $Id: SurefirePlugin.java 652773 2008-05-02 05:58:54Z dfabulich $
* Mods by Duns for NAR
*/
// DUNS, changed class name, inheritance, goal and phase
@Mojo(name = "nar-integration-test", defaultPhase = LifecyclePhase.INTEGRATION_TEST,
requiresDependencyResolution = ResolutionScope.TEST)
public class NarIntegrationTestMojo extends AbstractDependencyMojo {
private static final String BRIEF_REPORT_FORMAT = "brief";
private static final String PLAIN_REPORT_FORMAT = "plain";
// DUNS added because of naming conflict
/**
* Skip running of NAR integration test plugin
*/
@Parameter(property = "skipNar")
private boolean skipNar;
// DUNS changed to nar. because of naming conflict
/**
* Set this to 'true' to skip running tests, but still compile them. Its use
* is NOT RECOMMENDED, but quite
* convenient on occasion.
*
* @since 2.4
*/
@Parameter(property = "skipNarTests")
private boolean skipNarTests;
// DUNS changed to nar. because of naming conflict
/**
* DEPRECATED This old parameter is just like skipTests, but bound to the old
* property maven.test.skip.exec. Use
* -DskipTests instead; it's shorter.
*
* @deprecated
* @since 2.3
*/
@Deprecated
@Parameter(property = "nar.test.skip.exec")
private boolean skipNarExec;
// DUNS changed to nar. because of naming conflict
/**
* Set this to true to ignore a failure during testing. Its use is NOT
* RECOMMENDED, but quite convenient on
* occasion.
*
*/
@Parameter(property = "nar.test.failure.ignore")
private boolean testFailureIgnore;
/**
* The base directory of the project being tested. This can be obtained in
* your unit test by
* System.getProperty("basedir").
*
*/
@Parameter(property = "basedir", required = true)
private File basedir;
/**
* The directory containing generated test classes of the project being
* tested.
*/
@Parameter(property = "project.build.testOutputDirectory", required = true)
private File testClassesDirectory;
/**
* The directory containing generated classes of the project being tested.
*/
@Parameter(property = "project.build.outputDirectory", required = true)
private File classesDirectory;
/**
* The Maven Project Object
*/
// DUNS, made private
@Component
private MavenProject project;
/**
* The classpath elements of the project being tested.
*/
@Parameter(property = "project.testClasspathElements", required = true, readonly = true)
private List classpathElements;
/**
* Additional elements to be appended to the classpath.
*
* @since 2.4
*/
@Parameter
private List additionalClasspathElements;
/**
* Base directory where all reports are written to.
*/
@Parameter(defaultValue = "${project.build.directory}/surefire-reports")
private File reportsDirectory;
/**
* The test source directory containing test class sources.
*
* @since 2.2
*/
@Parameter(property = "project.build.testSourceDirectory", required = true)
private File testSourceDirectory;
/**
* Specify this parameter to run individual tests by file name, overriding the
* <code>includes/excludes</code> parameters. Each pattern you specify here
* will be used to create an include pattern formatted like
* <code>**/${test}.java</code>, so you can just type "-Dtest=MyTest" to
* run a single test called
* "foo/MyTest.java". This parameter will override the TestNG suiteXmlFiles
* parameter.
*/
@Parameter(property = "test")
private String test;
/**
* List of patterns (separated by commas) used to specify the tests that
* should be included in testing. When not
* specified and when the <code>test</code> parameter is not specified, the
* default includes will be
* <code>**/Test*.java **/*Test.java **/*TestCase.java</code>.
* This parameter is ignored if TestNG
* suiteXmlFiles are specified.
*/
@Parameter
private List includes;
/**
* List of patterns (separated by commas) used to specify the tests that
* should be excluded in testing. When not
* specified and when the <code>test</code> parameter is not specified, the
* default excludes will be <code>**/*$*</code> (which excludes all inner
* classes). This parameter is ignored if TestNG suiteXmlFiles are
* specified.
*/
@Parameter
private List excludes;
/**
* List of System properties to pass to the JUnit tests.
*
*/
@Parameter
private Properties systemProperties;
/**
* List of properties for configuring all TestNG related configurations. This
* is the new preferred method of
* configuring TestNG.
*/
@Parameter
private Properties properties;
/**
* Map of of plugin artifacts.
*/
@Parameter(property = "plugin.artifactMap", required = true, readonly = true)
private Map pluginArtifactMap;
/**
* Map of of project artifacts.
*/
@Parameter(property = "project.artifactMap", readonly = true, required = true)
private Map projectArtifactMap;
/**
* Option to print summary of test suites or just print the test cases that
* has errors.
*/
@Parameter(property = "surefire.printSummary", defaultValue = "true")
private boolean printSummary = true;
/**
* Selects the formatting for the test report to be generated. Can be set as
* brief or plain.
*/
@Parameter(property = "surefire.reportFormat", defaultValue = "brief")
private String reportFormat;
/**
* Option to generate a file test report or just output the test report to the
* console.
*
*/
@Parameter(property = "surefire.useFile", defaultValue = "true")
private boolean useFile;
// DUNS changed to nar. because of naming conflict
/**
* When forking, 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 = "nar.test.redirectTestOutputToFile")
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")
private Boolean failIfNoTests;
/**
* Option to specify the forking mode. Can be "never", "once" or "always".
* "none" and "pertest" are also accepted
* for backwards compatibility.
*
* @since 2.1
*/
@Parameter(property = "forkMode", defaultValue = "once")
private String forkMode;
/**
* Option to specify the jvm (or path to the java executable) to use with the
* forking options. For the default, the
* jvm will be the same as the one used to run Maven.
*
* @since 2.1
*/
@Parameter(property = "jvm")
private String jvm;
/**
* Arbitrary JVM options to set on the command line.
*
* @since 2.1
*/
@Parameter(property = "argLine")
private String argLine;
/**
* Attach a debugger to the forked JVM. If set to "true", the process will
* suspend and wait for a debugger to attach
* on port 5005. If set to some other string, that string will be appended to
* the argLine, allowing you to configure
* arbitrary debuggability options (without overwriting the other options
* specified in the argLine).
*
* @since 2.4
*/
@Parameter(property = "maven.surefire.debug")
private String debugForkedProcess;
/**
* Kill the forked test process after a certain number of seconds. If set to
* 0, wait forever for the process, never
* timing out.
*
* @since 2.4
*/
@Parameter(property = "surefire.timeout")
private int forkedProcessTimeoutInSeconds;
/**
* Additional environments 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 (forkMode is not "none").<br/>
* 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")
private boolean childDelegation;
/**
* (TestNG only) Groups for this test. Only classes/methods/etc decorated with
* one of the groups specified here will
* be included in test run, if specified. This parameter is overridden if
* suiteXmlFiles are specified.
*
* @since 2.2
*/
@Parameter(property = "groups")
private String groups;
/**
* (TestNG only) Excluded groups. Any methods/classes/etc with one of the
* groups specified in this list will
* specifically not be run. This parameter is overridden if suiteXmlFiles are
* specified.
*
* @since 2.2
*/
@Parameter(property = "excludedGroups")
private String excludedGroups;
/**
* (TestNG only) List of TestNG suite xml file locations, seperated by commas.
* Note that suiteXmlFiles is
* incompatible with several other parameters on this plugin, like
* includes/excludes. This parameter is ignored if
* the "test" parameter is specified (allowing you to run a single test
* instead of an entire suite).
*
* @since 2.2
*/
@Parameter
private File[] suiteXmlFiles;
/**
* Allows you to specify the name of the JUnit artifact. If not set,
* <code>junit:junit</code> 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</code> will be used.
*
* @since 2.3.1
*/
@Parameter(property = "testNGArtifactName", defaultValue = "org.testng:testng")
private String testNGArtifactName;
/**
* (TestNG only) 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 parallel.
*
* @since 2.2
*/
@Parameter(property = "threadCount")
private int threadCount;
/**
* (TestNG only) When you use the parallel attribute, 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.
*
* @todo test how this works with forking, and console/file output parallelism
* @since 2.2
*/
@Parameter(property = "parallel")
private String parallel;
/**
* 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 = "true")
private boolean trimStackTrace = true;
/**
* Creates the artifact
*/
@Component
private ArtifactFactory artifactFactory;
/**
* For retrieval of artifact's metadata.
*/
@Component
private ArtifactMetadataSource metadataSource;
private Properties originalSystemProperties;
/**
* Flag to disable the generation of report files in xml format.
*
* @since 2.2
*/
@Parameter(property = "disableXmlReport")
private boolean disableXmlReport;
/**
* Option to pass dependencies to the system's classloader instead of using an
* isolated class loader when forking.
* Prevents problems with JDKs which implement the service provider lookup
* mechanism by using the system's
* classloader. Default value is "true".
*
* @since 2.3
*/
@Parameter(property = "surefire.useSystemClassLoader")
private Boolean useSystemClassLoader;
/**
* By default, Surefire forks your tests using a manifest-only jar; set this
* parameter to "false" to force it to
* launch your tests with a plain old Java classpath. (See
* http://maven.apache.org/plugins/maven-surefire-plugin/examples/class-
* loading.html for a more detailed explanation
* of manifest-only jars and their benefits.) Default value is "true". Beware,
* setting this to "false" may cause
* your tests to fail on Windows if your classpath is too long.
*
* @since 2.4.3
*/
@Parameter(property = "surefire.useManifestOnlyJar", defaultValue = "true")
private boolean useManifestOnlyJar = true;
/**
* By default, Surefire enables JVM assertions for the execution of your test
* cases. To disable the assertions, set
* this flag to <code>false</code>.
*
* @since 2.3.1
*/
@Parameter(property = "enableAssertions", defaultValue = "true")
private boolean enableAssertions;
/**
* The current build session instance.
*/
@Component
private MavenSession session;
private void addArtifact(final SurefireBooter surefireBooter, final Artifact surefireArtifact)
throws ArtifactNotFoundException, ArtifactResolutionException {
final ArtifactResolutionResult result = resolveArtifact(null, surefireArtifact);
for (final Object element : result.getArtifacts()) {
final Artifact artifact = (Artifact) element;
getLog().debug("Adding to surefire booter test classpath: " + artifact.getFile().getAbsolutePath());
surefireBooter.addSurefireBootClassPathUrl(artifact.getFile().getAbsolutePath());
}
}
private void addProvider(final SurefireBooter surefireBooter, final String provider, final String version,
final Artifact filteredArtifact) throws ArtifactNotFoundException, ArtifactResolutionException {
final Artifact providerArtifact = this.artifactFactory.createDependencyArtifact("org.apache.maven.surefire",
provider, VersionRange.createFromVersion(version), "jar", null, Artifact.SCOPE_TEST);
final ArtifactResolutionResult result = resolveArtifact(filteredArtifact, providerArtifact);
for (final Object element : result.getArtifacts()) {
final Artifact artifact = (Artifact) element;
getLog().debug("Adding to surefire test classpath: " + artifact.getFile().getAbsolutePath());
surefireBooter.addSurefireClassPathUrl(artifact.getFile().getAbsolutePath());
}
}
/**
* <p>
* Adds Reporters that will generate reports with different formatting.
* <p>
* The Reporter that will be added will be based on the value of the parameter
* useFile, reportFormat, and printSummary.
*
* @param surefireBooter
* The surefire booter that will run tests.
* @param forking
*/
private void addReporters(final SurefireBooter surefireBooter, final boolean forking) {
final Boolean trimStackTrace = Boolean.valueOf(this.trimStackTrace);
if (this.useFile) {
if (this.printSummary) {
if (forking) {
surefireBooter.addReport(ForkingConsoleReporter.class.getName(), new Object[] {
trimStackTrace
});
} else {
surefireBooter.addReport(ConsoleReporter.class.getName(), new Object[] {
trimStackTrace
});
}
}
if (BRIEF_REPORT_FORMAT.equals(this.reportFormat)) {
surefireBooter.addReport(BriefFileReporter.class.getName(), new Object[] {
this.reportsDirectory, trimStackTrace
});
} else if (PLAIN_REPORT_FORMAT.equals(this.reportFormat)) {
surefireBooter.addReport(FileReporter.class.getName(), new Object[] {
this.reportsDirectory, trimStackTrace
});
}
} else {
if (BRIEF_REPORT_FORMAT.equals(this.reportFormat)) {
surefireBooter.addReport(BriefConsoleReporter.class.getName(), new Object[] {
trimStackTrace
});
} else if (PLAIN_REPORT_FORMAT.equals(this.reportFormat)) {
surefireBooter.addReport(DetailedConsoleReporter.class.getName(), new Object[] {
trimStackTrace
});
}
}
if (!this.disableXmlReport) {
surefireBooter.addReport(XMLReporter.class.getName(), new Object[] {
this.reportsDirectory, trimStackTrace
});
}
}
private SurefireBooter constructSurefireBooter() throws MojoExecutionException, MojoFailureException {
final SurefireBooter surefireBooter = new SurefireBooter();
final Artifact surefireArtifact = (Artifact) this.pluginArtifactMap
.get("org.apache.maven.surefire:surefire-booter");
if (surefireArtifact == null) {
throw new MojoExecutionException("Unable to locate surefire-booter in the list of plugin artifacts");
}
surefireArtifact.isSnapshot(); // TODO: this is ridiculous, but it fixes
// getBaseVersion to be -SNAPSHOT if
// needed
Artifact junitArtifact;
Artifact testNgArtifact;
try {
addArtifact(surefireBooter, surefireArtifact);
junitArtifact = (Artifact) this.projectArtifactMap.get(this.junitArtifactName);
// SUREFIRE-378, junit can have an alternate artifact name
if (junitArtifact == null && "junit:junit".equals(this.junitArtifactName)) {
junitArtifact = (Artifact) this.projectArtifactMap.get("junit:junit-dep");
}
// TODO: this is pretty manual, but I'd rather not require the plugin >
// dependencies section right now
testNgArtifact = (Artifact) this.projectArtifactMap.get(this.testNGArtifactName);
if (testNgArtifact != null) {
final VersionRange range = VersionRange.createFromVersionSpec("[4.7,)");
if (!range.containsVersion(new DefaultArtifactVersion(testNgArtifact.getVersion()))) {
throw new MojoFailureException("TestNG support requires version 4.7 or above. You have declared version "
+ testNgArtifact.getVersion());
}
convertTestNGParameters();
if (this.testClassesDirectory != null) {
this.properties.setProperty("testng.test.classpath", this.testClassesDirectory.getAbsolutePath());
}
addArtifact(surefireBooter, testNgArtifact);
// The plugin uses a JDK based profile to select the right testng. We
// might be explicity using a
// different one since its based on the source level, not the JVM. Prune
// using the filter.
addProvider(surefireBooter, "surefire-testng", surefireArtifact.getBaseVersion(), testNgArtifact);
} else if (junitArtifact != null && junitArtifact.getBaseVersion().startsWith("4")) {
addProvider(surefireBooter, "surefire-junit4", surefireArtifact.getBaseVersion(), null);
} else {
// add the JUnit provider as default - it doesn't require JUnit to be
// present,
// since it supports POJO tests.
addProvider(surefireBooter, "surefire-junit", surefireArtifact.getBaseVersion(), null);
}
} catch (final ArtifactNotFoundException e) {
throw new MojoExecutionException("Unable to locate required surefire provider dependency: " + e.getMessage(), e);
} catch (final InvalidVersionSpecificationException e) {
throw new MojoExecutionException("Error determining the TestNG version requested: " + e.getMessage(), e);
} catch (final ArtifactResolutionException e) {
throw new MojoExecutionException("Error to resolving surefire provider dependency: " + e.getMessage(), e);
}
if (this.suiteXmlFiles != null && this.suiteXmlFiles.length > 0 && this.test == null) {
if (testNgArtifact == null) {
throw new MojoExecutionException("suiteXmlFiles is configured, but there is no TestNG dependency");
}
// TODO: properties should be passed in here too
surefireBooter.addTestSuite("org.apache.maven.surefire.testng.TestNGXmlTestSuite", new Object[] {
this.suiteXmlFiles, this.testSourceDirectory.getAbsolutePath(), testNgArtifact.getBaseVersion(),
testNgArtifact.getClassifier(), this.properties, this.reportsDirectory
});
} else {
List includeList;
List excludeList;
if (this.test != null) {
// Check to see if we are running a single test. The raw parameter will
// come through if it has not been set.
// FooTest -> **/FooTest.java
includeList = new ArrayList();
excludeList = new ArrayList();
if (this.failIfNoTests == null) {
this.failIfNoTests = Boolean.TRUE;
}
final String[] testRegexes = StringUtils.split(this.test, ",");
for (final String testRegexe : testRegexes) {
String testRegex = testRegexe;
if (testRegex.endsWith(".java")) {
testRegex = testRegex.substring(0, testRegex.length() - 5);
}
// Allow paths delimited by '.' or '/'
testRegex = testRegex.replace('.', '/');
includeList.add("**/" + testRegex + ".java");
}
} else {
includeList = this.includes;
excludeList = this.excludes;
// defaults here, qdox doesn't like the end javadoc value
// Have to wrap in an ArrayList as surefire expects an ArrayList instead
// of a List for some reason
if (includeList == null || includeList.size() == 0) {
includeList = new ArrayList(Arrays.asList(new String[] {
"**/Test*.java", "**/*Test.java", "**/*TestCase.java"
}));
}
if (excludeList == null || excludeList.size() == 0) {
excludeList = new ArrayList(Arrays.asList(new String[] {
"**/*$*"
}));
}
}
if (testNgArtifact != null) {
surefireBooter.addTestSuite("org.apache.maven.surefire.testng.TestNGDirectoryTestSuite", new Object[] {
this.testClassesDirectory, includeList, excludeList, this.testSourceDirectory.getAbsolutePath(),
testNgArtifact.getBaseVersion(), testNgArtifact.getClassifier(), this.properties, this.reportsDirectory
});
} else {
String junitDirectoryTestSuite;
if (junitArtifact != null && junitArtifact.getBaseVersion() != null
&& junitArtifact.getBaseVersion().startsWith("4")) {
junitDirectoryTestSuite = "org.apache.maven.surefire.junit4.JUnit4DirectoryTestSuite";
} else {
junitDirectoryTestSuite = "org.apache.maven.surefire.junit.JUnitDirectoryTestSuite";
}
// fall back to JUnit, which also contains POJO support. Also it can run
// classes compiled against JUnit since it has a dependency on JUnit
// itself.
surefireBooter.addTestSuite(junitDirectoryTestSuite, new Object[] {
this.testClassesDirectory, includeList, excludeList
});
}
}
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
getLog().debug("Test Classpath :");
// Check if we need to add configured classes/test classes directories here.
// If they are configured, we should remove the default to avoid conflicts.
if (!this.project.getBuild().getOutputDirectory().equals(this.classesDirectory.getAbsolutePath())) {
this.classpathElements.remove(this.project.getBuild().getOutputDirectory());
this.classpathElements.add(this.classesDirectory.getAbsolutePath());
}
if (!this.project.getBuild().getTestOutputDirectory().equals(this.testClassesDirectory.getAbsolutePath())) {
this.classpathElements.remove(this.project.getBuild().getTestOutputDirectory());
this.classpathElements.add(this.testClassesDirectory.getAbsolutePath());
}
for (final Object classpathElement1 : this.classpathElements) {
final String classpathElement = (String) classpathElement1;
getLog().debug(" " + classpathElement);
surefireBooter.addClassPathUrl(classpathElement);
}
final Toolchain tc = getToolchain();
if (tc != null) {
getLog().info("Toolchain in surefire-plugin: " + tc);
if (ForkConfiguration.FORK_NEVER.equals(this.forkMode)) {
this.forkMode = ForkConfiguration.FORK_ONCE;
}
if (this.jvm != null) {
getLog().warn("Toolchains are ignored, 'executable' parameter is set to " + this.jvm);
} else {
this.jvm = tc.findTool("java"); // NOI18N
}
}
if (this.additionalClasspathElements != null) {
for (final Object additionalClasspathElement : this.additionalClasspathElements) {
final String classpathElement = (String) additionalClasspathElement;
getLog().debug(" " + classpathElement);
surefireBooter.addClassPathUrl(classpathElement);
}
}
// ----------------------------------------------------------------------
// Forking
// ----------------------------------------------------------------------
final ForkConfiguration fork = new ForkConfiguration();
// DUNS
if (this.project.getPackaging().equals("nar") || getNarArtifacts().size() > 0) {
this.forkMode = "pertest";
}
fork.setForkMode(this.forkMode);
processSystemProperties(!fork.isForking());
if (getLog().isDebugEnabled()) {
showMap(this.systemProperties, "system property");
}
if (fork.isForking()) {
this.useSystemClassLoader = this.useSystemClassLoader == null ? Boolean.TRUE : this.useSystemClassLoader;
fork.setUseSystemClassLoader(this.useSystemClassLoader.booleanValue());
fork.setUseManifestOnlyJar(this.useManifestOnlyJar);
fork.setSystemProperties(this.systemProperties);
if ("true".equals(this.debugForkedProcess)) {
this.debugForkedProcess = "-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005";
}
fork.setDebugLine(this.debugForkedProcess);
if (this.jvm == null || "".equals(this.jvm)) {
// use the same JVM as the one used to run Maven (the "java.home" one)
this.jvm = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
getLog().debug("Using JVM: " + this.jvm);
}
fork.setJvmExecutable(this.jvm);
if (this.workingDirectory != null) {
fork.setWorkingDirectory(this.workingDirectory);
} else {
fork.setWorkingDirectory(this.basedir);
}
// BEGINDUNS
if (this.argLine == null) {
this.argLine = "";
}
final StringBuffer javaLibraryPath = new StringBuffer();
if (testJNIModule()) {
// Add libraries to java.library.path for testing
final File jniLibraryPathEntry = getLayout().getLibDirectory(getTargetDirectory(),
getMavenProject().getArtifactId(), getMavenProject().getVersion(), getAOL().toString(), Library.JNI);
if (jniLibraryPathEntry.exists()) {
getLog().debug("Adding library directory to java.library.path: " + jniLibraryPathEntry);
if (javaLibraryPath.length() > 0) {
javaLibraryPath.append(File.pathSeparator);
}
javaLibraryPath.append(jniLibraryPathEntry);
}
final File sharedLibraryPathEntry = getLayout().getLibDirectory(getTargetDirectory(),
getMavenProject().getArtifactId(), getMavenProject().getVersion(), getAOL().toString(), Library.SHARED);
if (sharedLibraryPathEntry.exists()) {
getLog().debug("Adding library directory to java.library.path: " + sharedLibraryPathEntry);
if (javaLibraryPath.length() > 0) {
javaLibraryPath.append(File.pathSeparator);
}
javaLibraryPath.append(sharedLibraryPathEntry);
}
// add jar file to classpath, as one may want to read a
// properties file for artifactId and version
final String narFile = "target/" + this.project.getArtifactId() + "-" + this.project.getVersion() + ".jar";
getLog().debug("Adding to surefire test classpath: " + narFile);
surefireBooter.addClassPathUrl(narFile);
}
final List dependencies = getNarArtifacts(); // TODO: get seems heavy, not
// sure if we can push this
// up to before the fork to
// use it multiple times.
for (final Object dependency1 : dependencies) {
final NarArtifact dependency = (NarArtifact) dependency1;
// FIXME this should be overridable
// NarInfo info = dependency.getNarInfo();
// String binding = info.getBinding(getAOL(), Library.STATIC);
// NOTE: fixed to shared, jni
final String[] bindings = { Library.SHARED, Library.JNI
};
for (final String binding2 : bindings) {
final String binding = binding2;
if (!binding.equals(Library.STATIC)) {
final File depLibPathEntry = getLayout()
.getLibDirectory(getUnpackDirectory(), dependency.getArtifactId(), dependency.getBaseVersion(),
getAOL().toString(), binding);
//dependency.getVersion() calls the maven super class, which is not used when
//unpacking the NarDependencies in AbstractDependencyMojo. This causes
//the path to not exist and not be added to the library path.
if (depLibPathEntry.exists()) {
getLog().debug("Adding dependency directory to java.library.path: " + depLibPathEntry);
if (javaLibraryPath.length() > 0) {
javaLibraryPath.append(File.pathSeparator);
}
javaLibraryPath.append(depLibPathEntry);
}
}
}
}
// add final javalibrary path
if (javaLibraryPath.length() > 0) {
// NOTE java.library.path only works for the jni lib itself, and
// not for its dependent shareables.
// NOTE: java.library.path does not work with arguments with
// spaces as
// SureFireBooter splits the line in parts and then quotes
// it wrongly
NarUtil.addLibraryPathToEnv(javaLibraryPath.toString(), this.environmentVariables, getOS());
}
// necessary to find WinSxS
if (getOS().equals(OS.WINDOWS)) {
this.environmentVariables.put("SystemRoot", NarUtil.getEnv("SystemRoot", "SystemRoot", "C:\\Windows"));
}
// ENDDUNS
fork.setArgLine(this.argLine);
fork.setEnvironmentVariables(this.environmentVariables);
if (getLog().isDebugEnabled()) {
showMap(this.environmentVariables, "environment variable");
fork.setDebug(true);
}
if (this.argLine != null) {
final List args = Arrays.asList(this.argLine.split(" "));
if (args.contains("-da") || args.contains("-disableassertions")) {
this.enableAssertions = false;
}
}
}
surefireBooter.setFailIfNoTests(this.failIfNoTests == null ? false : this.failIfNoTests.booleanValue());
surefireBooter.setForkedProcessTimeoutInSeconds(this.forkedProcessTimeoutInSeconds);
surefireBooter.setRedirectTestOutputToFile(this.redirectTestOutputToFile);
surefireBooter.setForkConfiguration(fork);
surefireBooter.setChildDelegation(this.childDelegation);
surefireBooter.setEnableAssertions(this.enableAssertions);
surefireBooter.setReportsDirectory(this.reportsDirectory);
addReporters(surefireBooter, fork.isForking());
return surefireBooter;
}
/**
* Converts old TestNG configuration parameters over to new properties based
* configuration method. (if any are
* defined the old way)
*/
private void convertTestNGParameters() {
if (this.properties == null) {
this.properties = new Properties();
}
if (this.parallel != null) {
this.properties.setProperty("parallel", this.parallel);
}
if (this.excludedGroups != null) {
this.properties.setProperty("excludegroups", this.excludedGroups);
}
if (this.groups != null) {
this.properties.setProperty("groups", this.groups);
}
if (this.threadCount > 0) {
this.properties.setProperty("threadcount", Integer.toString(this.threadCount));
}
}
/**
* List the dependencies needed for integration tests executions, those
* dependencies are used to declare the paths
* of shared and jni libraries in java.library.path
*/
@Override
protected ScopeFilter getArtifactScopeFilter() {
return new ScopeFilter( Artifact.SCOPE_TEST, 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() {
Toolchain tc = null;
try {
if (this.session != null) // session is null in tests..
{
final ToolchainManager toolchainManager = (ToolchainManager) this.session.getContainer().lookup(
ToolchainManager.ROLE);
if (toolchainManager != null) {
tc = toolchainManager.getToolchainFromBuildContext("jdk", this.session);
}
}
} catch (final ComponentLookupException componentLookupException) {
// just ignore, could happen in pre-3.0.9 builds..
}
return tc;
}
@Override
protected File getUnpackDirectory() {
return getTestUnpackDirectory() == null ? super.getUnpackDirectory() : getTestUnpackDirectory();
}
/**
* @return SurefirePlugin Returns the skipExec.
*/
public boolean isSkipExec() {
return this.skipNarTests;
}
// DUNS, changed name
@Override
public void narExecute() throws MojoExecutionException, MojoFailureException {
if (this.skipTests) {
getLog().info("Tests are skipped");
} else if (verifyParameters()) {
final SurefireBooter surefireBooter = constructSurefireBooter();
getLog().info("Surefire report directory: " + this.reportsDirectory);
int result;
try {
result = surefireBooter.run();
} catch (final SurefireBooterForkException | SurefireExecutionException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
if (this.originalSystemProperties != null && !surefireBooter.isForking()) {
// restore system properties, only makes sense when not forking..
System.setProperties(this.originalSystemProperties);
}
if (result == 0) {
return;
}
String msg;
if (result == SurefireBooter.NO_TESTS_EXIT_CODE) {
if (this.failIfNoTests == null || !this.failIfNoTests.booleanValue()) {
return;
}
// TODO: i18n
throw new MojoFailureException("No tests were executed! (Set -DfailIfNoTests=false to ignore this error.)");
} else {
// TODO: i18n
msg = "There are test failures.\n\nPlease refer to " + this.reportsDirectory
+ " for the individual test results.";
}
if (this.testFailureIgnore) {
getLog().error(msg);
} else {
throw new MojoFailureException(msg);
}
}
}
protected void processSystemProperties(final boolean setInSystem) {
if (this.systemProperties == null) {
this.systemProperties = new Properties();
}
this.originalSystemProperties = (Properties) System.getProperties().clone();
// We used to take all of our system properties and dump them in with the
// user specified properties for SUREFIRE-121, causing SUREFIRE-491.
// Not gonna do THAT any more... but I'm leaving this code here in case
// we need it later when we try to fix SUREFIRE-121 again.
// Get the properties from the MavenSession instance to make embedded use
// work correctly
final Properties userSpecifiedProperties = (Properties) this.session.getExecutionProperties().clone();
userSpecifiedProperties.putAll(this.systemProperties);
// systemProperties = userSpecifiedProperties;
this.systemProperties.setProperty("basedir", this.basedir.getAbsolutePath());
this.systemProperties.setProperty("user.dir", this.workingDirectory.getAbsolutePath());
// DUNS, use access method rather than "localRepository" field.
this.systemProperties.setProperty("localRepository", getLocalRepository().getBasedir());
if (setInSystem) {
// Add all system properties configured by the user
for (final Object o : this.systemProperties.keySet()) {
final String key = (String) o;
final String value = this.systemProperties.getProperty(key);
System.setProperty(key, value);
}
}
}
private ArtifactResolutionResult resolveArtifact(final Artifact filteredArtifact, final Artifact providerArtifact)
throws ArtifactResolutionException, ArtifactNotFoundException {
ArtifactFilter filter = null;
if (filteredArtifact != null) {
filter = new ExcludesArtifactFilter(Collections.singletonList(filteredArtifact.getGroupId() + ":"
+ filteredArtifact.getArtifactId()));
}
final Artifact originatingArtifact = this.artifactFactory.createBuildArtifact("dummy", "dummy", "1.0", "jar");
// DUNS, use access method rather than "localRepository" field.
return this.artifactResolver.resolveTransitively(Collections.singleton(providerArtifact), originatingArtifact,
getLocalRepository(), getRemoteRepositories(), this.metadataSource, filter);
}
/**
* @param skipExec
* the skipExec to set
*/
public void setSkipExec(final boolean skipExec) {
this.skipNarTests = skipExec;
}
private void showMap(final Map map, final String setting) {
for (final Object o : map.keySet()) {
final String key = (String) o;
final String value = (String) map.get(key);
getLog().debug("Setting " + setting + " [" + key + "]=[" + value + "]");
}
}
// DUNS added test for JNI module
private boolean testJNIModule() {
for (final Object element : getLibraries()) {
final Library lib = (Library) element;
final String type = lib.getType();
if (type.equals(Library.JNI) || type.equals(Library.SHARED)) {
return true;
}
}
return false;
}
private boolean verifyParameters() throws MojoFailureException {
// DUNS
if (this.skipNar || this.skipNarTests || this.skipNarExec) {
getLog().info("Tests are skipped.");
return false;
}
if (!this.testClassesDirectory.exists()) {
if (this.failIfNoTests != null && this.failIfNoTests.booleanValue()) {
throw new MojoFailureException("No tests to run!");
}
getLog().info("No tests to run.");
return false;
}
if (this.useSystemClassLoader != null && ForkConfiguration.FORK_NEVER.equals(this.forkMode)) {
getLog().warn("useSystemClassloader setting has no effect when not forking");
}
return true;
}
}