org.bluestemsoftware.open.eoa.plugin.surefire.AbstractSurefireMojo Maven / Gradle / Ivy
/**
* Copyright 2008 Bluestem Software LLC. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
package org.bluestemsoftware.open.eoa.plugin.surefire;
/*
* Adapted from org.apache.maven.plugin.surefire.SurefirePlugin which was released under the following
* license:
*
* Copyright 2001-2005 The Apache Software Foundation.
*
* 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.
*/
import java.io.File;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.resolver.ArtifactCollector;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ResolutionNode;
import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Exclusion;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.surefire.report.BriefFileReporter;
import org.apache.maven.surefire.report.ConsoleReporter;
import org.apache.maven.surefire.report.FileReporter;
import org.apache.maven.surefire.report.XMLReporter;
import org.bluestemsoftware.open.eoa.plugin.AbstractDeploymentMojo;
import org.bluestemsoftware.open.eoa.plugin.SurefireBooter;
import org.bluestemsoftware.open.eoa.plugin.util.ClasspathHelper;
import org.bluestemsoftware.open.eoa.plugin.util.DependencyHelper;
/**
* Partitions classloaders as required by EOA specification for extension testing and invokes
* SurefireBooter helper class which forks JVM as appropriate (see class for comments.)
*/
public abstract class AbstractSurefireMojo extends AbstractDeploymentMojo {
static final String LINE_BREAK = System.getProperty("line.separator");
/**
* Base directory where all reports are written to.
*
* @parameter expression="${project.build.directory}/surefire-reports"
*/
protected File reportsDirectory;
/**
* Map of plugin artifacts.
*
* @parameter expression="${plugin.artifactMap}"
* @required
* @readonly
*/
protected Map, ?> pluginArtifactMap;
/**
* Option to print summary of test suites or just print the test cases that has errors.
*
* @parameter expression="${surefire.printSummary}" default-value="true"
*/
protected boolean printSummary;
/**
* Selects the formatting for the test report to be generated. Can be set as brief or
* plain.
*
* @parameter expression="${surefire.reportFormat}" default-value="brief"
*/
protected String reportFormat;
/**
* Whether to trim the stack trace in the reports to just the lines within the test, or
* show the full trace.
*
* @parameter expression="${trimStackTrace}" default-value="true"
*/
protected boolean trimStackTrace;
/**
* Collects artifacts.
*
* @component
*/
protected ArtifactCollector artifactCollector;
/**
* For retrieval of artifact's metadata.
*
* @component
*/
protected ArtifactMetadataSource metadataSource;
/**
* Set this to true to redirect the unit test standard output to a file (found in
* reportsDirectory/testName-output.txt).
*
* @parameter expression="${maven.test.redirectTestOutputToFile}" default-value="false"
*/
protected boolean redirectTestOutputToFile;
private ClasspathHelper cph;
private Map surefireBooterClasspath;
private Map surefireClasspath;
public void execute() throws MojoExecutionException, MojoFailureException {
if (!getSystemTestClassesDirectory().exists()) {
getLog().info("No tests to run.");
return;
}
cph = new ClasspathHelper(logger, basedir, project, true);
cph.setArtifactCollector(artifactCollector);
cph.setArtifactFactory(artifactFactory);
cph.setArtifactResolver(artifactResolver);
cph.setLocalRepository(localRepository);
cph.setMetadataSource(metadataSource);
cph.setRemoteRepositories(remoteRepositories);
cph.setSystemTestClassesDirectory(getSystemTestClassesDirectory());
cph.setClientTestClassesDirectory(getClientTestClassesDirectory());
cph.setTest(test);
Set projectDependencies = null;
try {
projectDependencies = getProjectDependencies();
} catch (Exception ex) {
throw new MojoExecutionException("Error retrieving project dependencies. " + ex);
}
Map pcp = cph.getParentClasspath(projectDependencies, new HashSet());
if (logger.isDebugEnabled()) {
logger.debug(LINE_BREAK);
logger.debug("parent classpath: ");
for (Map.Entry entry : pcp.entrySet()) {
logger.debug("constituent: "
+ new File(entry.getKey()).getName()
+ " dependency trail: "
+ entry.getValue());
}
logger.debug("end classpath");
}
// set isProjectClasspath to false when retrieving shared artifacts, i.e.
// we do not want any artifacts flagged as optional. these are provided
// by engine deployment that implements application
Map scp = cph.getSharedClasspath(projectDependencies, new HashSet(), false);
if (logger.isDebugEnabled()) {
logger.debug(LINE_BREAK);
logger.debug("shared classpath: ");
for (Map.Entry entry : scp.entrySet()) {
logger.debug("constituent: "
+ new File(entry.getKey()).getName()
+ " dependency trail: "
+ entry.getValue());
}
logger.debug("end classpath");
}
Map dcp = cph.getSystemTestClasspath(projectDependencies, new HashSet());
if (logger.isDebugEnabled()) {
logger.debug(LINE_BREAK);
logger.debug("system test classpath: ");
for (Map.Entry entry : dcp.entrySet()) {
logger.debug("constituent: "
+ new File(entry.getKey()).getName()
+ " dependency trail: "
+ entry.getValue());
}
logger.debug("end classpath");
}
Map ctp = cph.getClientTestClasspath(projectDependencies, new HashSet());
if (logger.isDebugEnabled()) {
logger.debug(LINE_BREAK);
logger.debug("client test classpath: ");
for (Map.Entry entry : ctp.entrySet()) {
logger.debug("constituent: "
+ new File(entry.getKey()).getName()
+ " dependency trail: "
+ entry.getValue());
}
logger.debug("end classpath");
}
// take a snapshot of system props as is and then add
// properties defined within pom as a system prop so
// that they will be available to system tests
Properties snapshot = System.getProperties();
Properties pomProperties = project.getModel().getProperties();
for (String key : pomProperties.stringPropertyNames()) {
System.setProperty(key, pomProperties.getProperty(key));
}
SurefireBooter surefireBooter = new SurefireBooter();
surefireBooter.setGroupID(project.getGroupId());
surefireBooter.setArtifactID(project.getArtifactId());
surefireBooter.setPackaging(project.getPackaging());
surefireBooter.setVersion(project.getVersion());
surefireBooter.setTestClassesDirectory(getSystemTestClassesDirectory().getAbsolutePath());
surefireBooter.setRedirectTestOutputToFile(redirectTestOutputToFile);
surefireBooter.setBasedir(basedir.getAbsolutePath());
surefireBooter.setReportsDirectory(getTestReportsDirectory());
surefireBooter.setParentClasspath(pcp.keySet());
surefireBooter.setSharedClasspath(scp.keySet());
surefireBooter.setDeploymentClasspath(dcp.keySet());
surefireBooter.setClientClasspath(ctp.keySet());
surefireBooter.setSurefireBooterClasspath(getSurefireBooterClasspath());
surefireBooter.setSurefireClasspath(getSurefireClasspath());
surefireBooter.setWorkingDirectory(getSystemTestClassesDirectory().getParentFile().getAbsolutePath());
surefireBooter.setIntegrationTest(isIntegrationTest());
addTestSuiteDefinitions(surefireBooter);
addReportDefinitions(surefireBooter);
boolean success;
try {
success = surefireBooter.run();
} catch (Exception ex) {
throw new MojoFailureException(ex.toString());
}
// restore system properties to saved off values
System.setProperties(snapshot);
System.out.println(LINE_BREAK);
if (!success) {
throw new MojoFailureException("There are test failures.");
}
}
protected abstract File getSystemTestClassesDirectory();
protected abstract File getClientTestClassesDirectory();
protected abstract File getTestReportsDirectory();
protected abstract boolean isIntegrationTest();
protected Set getSurefireBooterClasspath() throws MojoExecutionException {
if (surefireBooterClasspath != null) {
if (getLog().isDebugEnabled()) {
getLog().debug(LINE_BREAK);
getLog().debug("surefire bootstrap classpath: ");
for (Map.Entry entry : surefireBooterClasspath.entrySet()) {
logger.debug("constituent: "
+ new File(entry.getKey()).getName()
+ " dependency trail: "
+ entry.getValue());
}
getLog().debug("end classpath");
}
return surefireBooterClasspath.keySet();
} else {
surefireBooterClasspath = new HashMap();
}
Artifact booterArtifact = (Artifact)pluginArtifactMap
.get("org.bluestemsoftware.open.maven.plugin:surefire-booter");
if (booterArtifact == null) {
throw new MojoExecutionException("Error configuring surefire bootstrap classpath."
+ " Failed to resolve required artifact"
+ "'org.bluestemsoftware.open.eoa.plugin:surefire-booter'.");
}
try {
Set result = resolveArtifactTransitively(booterArtifact);
for (Iterator i = result.iterator(); i.hasNext();) {
Artifact artifact = i.next();
if (!artifact.getType().equals("jar")) {
continue;
}
// used in -classpath argument. strip scheme and decode
String constituent = artifact.getFile().toURI().getPath();
String dependencyTrail = artifact.getDependencyTrail() == null ? "none" : artifact
.getDependencyTrail().toString();
surefireBooterClasspath.put(constituent, dependencyTrail);
}
} catch (Exception ex) {
throw new MojoExecutionException("Error configuring surefire bootstrap classpath." + ex);
}
if (getLog().isDebugEnabled()) {
getLog().debug(LINE_BREAK);
getLog().debug("surefire bootstrap classpath: ");
for (Map.Entry entry : surefireBooterClasspath.entrySet()) {
logger.debug("constituent: "
+ new File(entry.getKey()).getName()
+ " dependency trail: "
+ entry.getValue());
}
getLog().debug("end classpath");
}
return surefireBooterClasspath.keySet();
}
protected Set getSurefireClasspath() throws MojoExecutionException, MojoFailureException {
if (surefireClasspath != null) {
if (getLog().isDebugEnabled()) {
getLog().debug(LINE_BREAK);
getLog().debug("surefirefire classpath: ");
for (Map.Entry entry : surefireClasspath.entrySet()) {
logger.debug("constituent: "
+ new File(entry.getKey()).getName()
+ " dependency trail: "
+ entry.getValue());
}
getLog().debug("end classpath");
}
return surefireClasspath.keySet();
} else {
surefireClasspath = new HashMap();
}
Artifact surefireArtifact = (Artifact)pluginArtifactMap.get("org.apache.maven.surefire:surefire-api");
try {
Artifact providerArtifact = artifactFactory.createDependencyArtifact("org.apache.maven.surefire",
"surefire-junit4", VersionRange.createFromVersion(surefireArtifact.getBaseVersion()), "jar",
null, Artifact.SCOPE_TEST);
Set result = resolveArtifactTransitively(providerArtifact);
for (Iterator i = result.iterator(); i.hasNext();) {
Artifact artifact = i.next();
if (!artifact.getType().equals("jar")) {
continue;
}
try {
String constituent = artifact.getFile().toURI().toURL().toExternalForm();
String dependencyTrail = artifact.getDependencyTrail() == null ? "none" : artifact
.getDependencyTrail().toString();
surefireClasspath.put(constituent, dependencyTrail);
} catch (MalformedURLException me) {
throw new ArtifactResolutionException(me.toString(), artifact);
}
}
} catch (Exception ex) {
throw new MojoExecutionException("Error configuring surefire classpath." + ex);
}
if (getLog().isDebugEnabled()) {
getLog().debug(LINE_BREAK);
getLog().debug("surefirefire classpath: ");
for (Map.Entry entry : surefireClasspath.entrySet()) {
logger.debug("constituent: "
+ new File(entry.getKey()).getName()
+ " dependency trail: "
+ entry.getValue());
}
getLog().debug("end classpath");
}
return surefireClasspath.keySet();
}
private void addTestSuiteDefinitions(SurefireBooter surefireBooter) {
List includes = new ArrayList(Arrays.asList(new String[] { "**/Test*.java",
"**/*Test.java", "**/*TestCase.java" }));
List excludes = new ArrayList(Arrays.asList(new String[] { "**/Abstract*Test.java",
"**/Abstract*TestCase.java", "**/*$*" }));
String className = "org.apache.maven.surefire.junit4.JUnit4DirectoryTestSuite";
Object[] parms = new Object[] { getSystemTestClassesDirectory(), includes, excludes };
surefireBooter.addTestSuite(className, parms);
}
private void addReportDefinitions(SurefireBooter surefireBooter) {
Boolean trimStackTrace = Boolean.valueOf(this.trimStackTrace);
if (printSummary) {
Object[] parms = new Object[] { trimStackTrace };
surefireBooter.addReport(ConsoleReporter.class.getName(), parms);
}
if ("brief".equals(reportFormat)) {
Object[] parms = new Object[] { getTestReportsDirectory(), trimStackTrace };
surefireBooter.addReport(BriefFileReporter.class.getName(), parms);
} else if ("plain".equals(reportFormat)) {
Object[] parms = new Object[] { getTestReportsDirectory(), trimStackTrace };
surefireBooter.addReport(FileReporter.class.getName(), parms);
}
Object[] parms = new Object[] { getTestReportsDirectory(), trimStackTrace };
surefireBooter.addReport(XMLReporter.class.getName(), parms);
}
/*
* we build our own set of dependency artifacts rather than retrieving from project because
* exclusion filters set on scoped artifacts may mask system scoped dependencies and/or
* provided scoped dependences.
*/
private Set getProjectDependencies() throws Exception {
Set projectDependencies = new HashSet();
for (Object dependencyObj : project.getDependencies()) {
Dependency dependency = (Dependency)dependencyObj;
String groupID = dependency.getGroupId();
String artifactID = dependency.getArtifactId();
VersionRange versionRange = VersionRange.createFromVersionSpec(dependency.getVersion());
String scope = dependency.getScope() == null ? Artifact.SCOPE_COMPILE : dependency.getScope();
String type = dependency.getType() == null ? "jar" : dependency.getType();
String classifier = dependency.getClassifier();
boolean isOptional = dependency.isOptional();
Artifact artifact = artifactFactory.createDependencyArtifact(groupID, artifactID, versionRange, type,
classifier, scope, isOptional);
projectDependencies.add(artifact);
if (scope.equals(Artifact.SCOPE_SYSTEM)) {
File file = new File(dependency.getSystemPath());
artifact.setFile(file);
}
if (dependency.getExclusions() != null) {
List patterns = new ArrayList();
for (Object exclusionObj : dependency.getExclusions()) {
Exclusion exclusion = (Exclusion)exclusionObj;
StringBuilder sb = new StringBuilder();
sb.append(exclusion.getGroupId());
sb.append(":");
sb.append(exclusion.getArtifactId());
patterns.add(sb.toString());
}
ExcludesArtifactFilter excludesFilter = new ExcludesArtifactFilter(patterns);
artifact.setDependencyFilter(excludesFilter);
}
}
return projectDependencies;
}
protected Set resolveArtifactTransitively(Artifact artifact) throws Exception {
ArtifactResolutionResult arr;
try {
Map managedVersionMap = createManagedVersionMap(project);
Artifact dummy = artifactFactory.createBuildArtifact("dummy", "dummy", "1.0", "jar");
arr = artifactCollector.collect(Collections.singleton(artifact), dummy, managedVersionMap,
localRepository, remoteRepositories, metadataSource, null, Collections.EMPTY_LIST);
} catch (Exception ex) {
throw new MojoExecutionException("Error resolving artifact. " + ex);
}
Set resolvedArtifacts = new HashSet();
Iterator> itr = arr.getArtifactResolutionNodes().iterator();
while (itr.hasNext()) {
ResolutionNode node = (ResolutionNode)itr.next();
Artifact ra = node.getArtifact();
DependencyHelper.resolveArtifact(ra, node.getRemoteRepositories(), artifactResolver, localRepository);
resolvedArtifacts.add(ra);
}
return resolvedArtifacts;
}
protected Map createManagedVersionMap(MavenProject extensionFactoryProject) throws InvalidVersionSpecificationException {
DependencyManagement dependencyManagement = extensionFactoryProject.getDependencyManagement();
if (dependencyManagement == null) {
return new HashMap();
}
if (dependencyManagement.getDependencies() == null) {
return new HashMap();
}
Map managedVersionMap = new HashMap();
Iterator> itr = dependencyManagement.getDependencies().iterator();
while (itr.hasNext()) {
Dependency d = (Dependency)itr.next();
VersionRange versionRange = VersionRange.createFromVersionSpec(d.getVersion());
Artifact artifact = artifactFactory.createDependencyArtifact(d.getGroupId(), d.getArtifactId(),
versionRange, d.getType(), d.getClassifier(), d.getScope(), d.isOptional());
managedVersionMap.put(d.getManagementKey(), artifact);
}
return managedVersionMap;
}
}