/*
* Copyright 2011 Henry Coles
*
* 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.
*/
package org.pitest.maven;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.pitest.classinfo.ClassName;
import org.pitest.classpath.DirectoryClassPathRoot;
import org.pitest.functional.F;
import org.pitest.functional.FCollection;
import org.pitest.functional.predicate.Predicate;
import org.pitest.mutationtest.config.ReportOptions;
import org.pitest.testapi.TestGroupConfig;
import org.pitest.util.Glob;
public class MojoToReportOptionsConverter {
private final AbstractPitMojo mojo;
private final Predicate<Artifact> dependencyFilter;
private final Log log;
private final SurefireConfigConverter surefireConverter;
public MojoToReportOptionsConverter(final AbstractPitMojo mojo,
SurefireConfigConverter surefireConverter,
Predicate<Artifact> dependencyFilter) {
this.mojo = mojo;
this.dependencyFilter = dependencyFilter;
this.log = mojo.getLog();
this.surefireConverter = surefireConverter;
}
@SuppressWarnings("unchecked")
public ReportOptions convert() {
final List<String> classPath = new ArrayList<String>();
try {
classPath.addAll(this.mojo.getProject().getTestClasspathElements());
} catch (final DependencyResolutionRequiredException e1) {
this.log.info(e1);
}
addOwnDependenciesToClassPath(classPath);
classPath.addAll(this.mojo.getAdditionalClasspathElements());
for (Object artifact : this.mojo.getProject().getArtifacts()) {
final Artifact dependency = (Artifact) artifact;
if (this.mojo.getClasspathDependencyExcludes().contains(
dependency.getGroupId() + ":" + dependency.getArtifactId())) {
classPath.remove(dependency.getFile().getPath());
}
}
ReportOptions option = parseReportOptions(classPath);
return updateFromSurefire(option);
}
@SuppressWarnings("unchecked")
private ReportOptions parseReportOptions(final List<String> classPath) {
final ReportOptions data = new ReportOptions();
if (this.mojo.getProject().getBuild() != null) {
this.log.info("Mutating from "
+ this.mojo.getProject().getBuild().getOutputDirectory());
data.setCodePaths(Collections.singleton(this.mojo.getProject().getBuild()
.getOutputDirectory()));
}
data.setClassPathElements(classPath);
data.setDependencyAnalysisMaxDistance(this.mojo.getMaxDependencyDistance());
data.setFailWhenNoMutations(shouldFailWhenNoMutations());
data.setTargetClasses(determineTargetClasses());
data.setTargetTests(determineTargetTests());
data.setMutateStaticInitializers(this.mojo.isMutateStaticInitializers());
data.setExcludedMethods(globStringsToPredicates(this.mojo
.getExcludedMethods()));
data.setExcludedClasses(globStringsToPredicates(this.mojo
.getExcludedClasses()));
data.setNumberOfThreads(this.mojo.getThreads());
data.setMaxMutationsPerClass(this.mojo.getMaxMutationsPerClass());
data.setExcludedRunners(this.mojo.getExcludedRunners());
data.setReportDir(this.mojo.getReportsDirectory().getAbsolutePath());
data.setVerbose(this.mojo.isVerbose());
if (this.mojo.getJvmArgs() != null) {
data.addChildJVMArgs(this.mojo.getJvmArgs());
}
data.setMutators(determineMutators());
data.setTimeoutConstant(this.mojo.getTimeoutConstant());
data.setTimeoutFactor(this.mojo.getTimeoutFactor());
if (hasValue(this.mojo.getAvoidCallsTo())) {
data.setLoggingClasses(this.mojo.getAvoidCallsTo());
}
final List<String> sourceRoots = new ArrayList<String>();
sourceRoots.addAll(this.mojo.getProject().getCompileSourceRoots());
sourceRoots.addAll(this.mojo.getProject().getTestCompileSourceRoots());
data.setSourceDirs(stringsTofiles(sourceRoots));
data.addOutputFormats(determineOutputFormats());
setTestGroups(data);
data.setMutationUnitSize(this.mojo.getMutationUnitSize());
data.setShouldCreateTimestampedReports(this.mojo.isTimestampedReports());
data.setDetectInlinedCode(this.mojo.isDetectInlinedCode());
determineHistory(data);
data.setExportLineCoverage(this.mojo.isExportLineCoverage());
data.setMutationEngine(this.mojo.getMutationEngine());
data.setJavaExecutable(this.mojo.getJavaExecutable());
data.setFreeFormProperties(createPluginProperties());
return data;
}
private void determineHistory(final ReportOptions data) {
if (this.mojo.useHistory()) {
useHistoryFileInTempDir(data);
} else {
data.setHistoryInputLocation(this.mojo.getHistoryInputFile());
data.setHistoryOutputLocation(this.mojo.getHistoryOutputFile());
}
}
private void useHistoryFileInTempDir(final ReportOptions data) {
String tempDir = System.getProperty("java.io.tmpdir");
MavenProject project = this.mojo.project;
String name = project.getGroupId() + "."
+ project.getArtifactId() + "."
+ project.getVersion() + "_pitest_history.bin";
File historyFile = new File(tempDir, name);
log.info("Will read and write history at " + historyFile);
if (this.mojo.getHistoryInputFile() == null) {
data.setHistoryInputLocation(historyFile);
}
if (this.mojo.getHistoryOutputFile() == null) {
data.setHistoryOutputLocation(historyFile);
}
}
private ReportOptions updateFromSurefire(ReportOptions option) {
Collection<Plugin> plugins = lookupPlugin("org.apache.maven.plugins:maven-surefire-plugin");
if (!this.mojo.isParseSurefireConfig()) {
return option;
} else if (plugins.isEmpty()) {
this.log.warn("Could not find surefire configuration in pom");
return option;
}
Plugin surefire = plugins.iterator().next();
if (surefire != null) {
return this.surefireConverter.update(option,
(Xpp3Dom) surefire.getConfiguration());
} else {
return option;
}
}
private Collection<Plugin> lookupPlugin(String key) {
@SuppressWarnings("unchecked")
List<Plugin> plugins = this.mojo.getProject().getBuildPlugins();
return FCollection.filter(plugins, hasKey(key));
}
private static F<Plugin, Boolean> hasKey(final String key) {
return new F<Plugin, Boolean>() {
@Override
public Boolean apply(Plugin a) {
return a.getKey().equals(key);
}
};
}
private boolean shouldFailWhenNoMutations() {
return this.mojo.isFailWhenNoMutations();
}
private void setTestGroups(final ReportOptions data) {
final TestGroupConfig conf = new TestGroupConfig(
this.mojo.getExcludedGroups(), this.mojo.getIncludedGroups());
data.setGroupConfig(conf);
}
private void addOwnDependenciesToClassPath(final List<String> classPath) {
for (final Artifact dependency : filteredDependencies()) {
this.log.info("Adding " + dependency.getGroupId() + ":"
+ dependency.getArtifactId() + " to SUT classpath");
classPath.add(dependency.getFile().getAbsolutePath());
}
}
private Collection<Predicate<String>> globStringsToPredicates(
final List<String> excludedMethods) {
return FCollection.map(excludedMethods, Glob.toGlobPredicate());
}
private Collection<Predicate<String>> determineTargetTests() {
return FCollection.map(this.mojo.getTargetTests(), Glob.toGlobPredicate());
}
private Collection<Artifact> filteredDependencies() {
return FCollection.filter(this.mojo.getPluginArtifactMap().values(),
this.dependencyFilter);
}
private Collection<String> determineMutators() {
if (this.mojo.getMutators() != null) {
return this.mojo.getMutators();
} else {
return Collections.emptyList();
}
}
private Collection<Predicate<String>> determineTargetClasses() {
return useConfiguredTargetClassesOrFindOccupiedPackages(this.mojo.getTargetClasses());
}
private Collection<Predicate<String>> useConfiguredTargetClassesOrFindOccupiedPackages(
final Collection<String> filters) {
if (!hasValue(filters)) {
this.mojo.getLog().info("Defaulting target classes to match packages in build directory");
return FCollection.map(findOccupiedPackages(), Glob.toGlobPredicate());
} else {
return FCollection.map(filters, Glob.toGlobPredicate());
}
}
private Collection<String> findOccupiedPackages() {
String outputDirName = this.mojo.getProject().getBuild()
.getOutputDirectory();
File outputDir = new File(outputDirName);
if (outputDir.exists()) {
DirectoryClassPathRoot root = new DirectoryClassPathRoot(outputDir);
Set<String> occupiedPackages = new HashSet<String>();
FCollection.mapTo(root.classNames(), classToPackageGlob(),
occupiedPackages);
return occupiedPackages;
}
return Collections.emptyList();
}
private static F<String,String> classToPackageGlob() {
return new F<String,String>() {
@Override
public String apply(String a) {
return ClassName.fromString(a).getPackage().asJavaName() + ".*";
}
};
}
private Collection<File> stringsTofiles(final List<String> sourceRoots) {
return FCollection.map(sourceRoots, stringToFile());
}
private F<String, File> stringToFile() {
return new F<String, File>() {
@Override
public File apply(final String a) {
return new File(a);
}
};
}
private Collection<String> determineOutputFormats() {
if (hasValue(this.mojo.getOutputFormats())) {
return this.mojo.getOutputFormats();
} else {
return Arrays.asList("HTML");
}
}
private boolean hasValue(final Collection<?> collection) {
return (collection != null) && !collection.isEmpty();
}
private Properties createPluginProperties() {
Properties p = new Properties();
if (this.mojo.getPluginProperties() != null) {
p.putAll(this.mojo.getPluginProperties());
}
return p;
}
}