org.pitest.mutationtest.config.SettingsFactory Maven / Gradle / Ivy
package org.pitest.mutationtest.config;
import org.pitest.classpath.CodeSource;
import org.pitest.classpath.CodeSourceFactory;
import org.pitest.classpath.DefaultCodeSource;
import org.pitest.classpath.ProjectClassPaths;
import org.pitest.coverage.CoverageExporter;
import org.pitest.mutationtest.build.CoverageTransformer;
import org.pitest.mutationtest.build.CoverageTransformerFactory;
import org.pitest.coverage.execute.CoverageOptions;
import org.pitest.coverage.export.DefaultCoverageExporter;
import org.pitest.coverage.export.NullCoverageExporter;
import org.pitest.functional.FCollection;
import org.pitest.mutationtest.CompoundMutationResultInterceptor;
import org.pitest.mutationtest.MutationEngineFactory;
import org.pitest.mutationtest.MutationResultInterceptor;
import org.pitest.mutationtest.MutationResultListenerFactory;
import org.pitest.mutationtest.build.CompoundInterceptorFactory;
import org.pitest.mutationtest.build.DefaultMutationGrouperFactory;
import org.pitest.mutationtest.build.DefaultTestPrioritiserFactory;
import org.pitest.mutationtest.build.MutationGrouperFactory;
import org.pitest.mutationtest.build.MutationInterceptorFactory;
import org.pitest.mutationtest.build.TestPrioritiserFactory;
import org.pitest.mutationtest.verify.BuildVerifierFactory;
import org.pitest.mutationtest.verify.CompoundBuildVerifierFactory;
import org.pitest.plugin.Feature;
import org.pitest.plugin.FeatureParser;
import org.pitest.plugin.FeatureSelector;
import org.pitest.plugin.FeatureSetting;
import org.pitest.plugin.ProvidesFeature;
import org.pitest.process.DefaultJavaExecutableLocator;
import org.pitest.process.JavaExecutableLocator;
import org.pitest.process.KnownLocationJavaExecutableLocator;
import org.pitest.util.PitError;
import org.pitest.util.ResultOutputStrategy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SettingsFactory {
private final ReportOptions options;
private final PluginServices plugins;
public SettingsFactory(final ReportOptions options,
final PluginServices plugins) {
this.options = options;
this.plugins = plugins;
}
public ResultOutputStrategy getOutputStrategy() {
return this.options.getReportDirectoryStrategy();
}
public CoverageExporter createCoverageExporter() {
if (this.options.shouldExportLineCoverage()) {
return new DefaultCoverageExporter(getOutputStrategy());
} else {
return new NullCoverageExporter();
}
}
public MutationEngineFactory createEngine() {
for (final MutationEngineFactory each : this.plugins.findMutationEngines()) {
if (each.name().equals(this.options.getMutationEngine())) {
return each;
}
}
throw new PitError("Could not load requested engine "
+ this.options.getMutationEngine());
}
public MutationResultListenerFactory createListener() {
final FeatureParser parser = new FeatureParser();
return new CompoundListenerFactory(parser.parseFeatures(this.options.getFeatures()), findListeners());
}
public ConfigurationUpdater createUpdater() {
final FeatureParser parser = new FeatureParser();
return new CompoundConfigurationUpdater(parser.parseFeatures(this.options.getFeatures()), new ArrayList<>(plugins.findConfigurationUpdaters()));
}
public JavaExecutableLocator getJavaExecutable() {
if (this.options.getJavaExecutable() != null) {
return new KnownLocationJavaExecutableLocator(
this.options.getJavaExecutable());
}
return new DefaultJavaExecutableLocator();
}
public MutationGrouperFactory getMutationGrouper() {
// Grouping behaviour is important. We cannot have more than 1 class mutated within
// a JVM or else the last mutation will poison the next. This restriction can only
// be removed if the hotswap functionality is reworked.
// Grouping behaviour is therefore hard coded for now.
return new DefaultMutationGrouperFactory();
}
public CodeSource createCodeSource(ProjectClassPaths classPath) {
List sources = this.plugins.findCodeSources();
if (sources.isEmpty()) {
return new DefaultCodeSource(classPath);
}
if (sources.size() > 1) {
throw new RuntimeException("More than CodeSource found on classpath.");
}
return sources.get(0).createCodeSource(classPath);
}
public void describeFeatures(Consumer enabled, Consumer disabled) {
final FeatureParser parser = new FeatureParser();
final Collection available = new ArrayList<>(this.plugins.findFeatures());
final List settings = parser.parseFeatures(this.options.getFeatures());
final FeatureSelector selector = new FeatureSelector<>(settings, available);
List enabledFeatures = selector.getActiveFeatures().stream()
.map(toFeature())
.filter(f -> !f.isInternal())
.distinct()
.sorted(byName())
.collect(Collectors.toList());
enabledFeatures.forEach(enabled);
available.stream()
.map(toFeature())
.filter(f -> !f.isInternal())
.distinct()
.sorted(byName())
.filter(f -> !enabledFeatures.contains(f))
.forEach(disabled);
}
public void checkRequestedFeatures() {
FeatureParser parser = new FeatureParser();
Set available = this.plugins.findFeatures().stream()
.map(f -> f.provides().name().toUpperCase())
.collect(Collectors.toSet());
Optional unknown = parser.parseFeatures(this.options.getFeatures()).stream()
.filter(f -> !available.contains(f.feature().toUpperCase()))
.findAny();
unknown.ifPresent(setting -> {
throw new IllegalArgumentException("Unknown feature " + setting.feature());
});
}
public TestPrioritiserFactory getTestPrioritiser() {
final Collection extends TestPrioritiserFactory> testPickers = this.plugins
.findTestPrioritisers();
return firstOrDefault(testPickers, new DefaultTestPrioritiserFactory());
}
public CoverageOptions createCoverageOptions() {
return new CoverageOptions(
this.options.getTargetClasses(), this.options.getExcludedClasses(),
this.options.createMinionSettings(), this.options.getVerbosity());
}
public CompoundInterceptorFactory getInterceptor() {
final Collection extends MutationInterceptorFactory> interceptors = this.plugins
.findInterceptors();
final FeatureParser parser = new FeatureParser();
return new CompoundInterceptorFactory(parser.parseFeatures(this.options.getFeatures()), new ArrayList<>(interceptors));
}
public BuildVerifierFactory createVerifier() {
return new CompoundBuildVerifierFactory(this.plugins.findVerifiers());
}
public MutationResultInterceptor getResultInterceptor() {
return new CompoundMutationResultInterceptor(this.plugins.findMutationResultInterceptor());
}
public CoverageTransformer createCoverageTransformer(CodeSource code) {
List transformers = this.plugins.findCoverageTransformers();
if (transformers.size() > 1) {
throw new RuntimeException("More than 1 coverage transformer found on classpath");
}
if (transformers.isEmpty()) {
return cov -> cov;
}
return transformers.get(0).create(code);
}
private Collection findListeners() {
final Iterable extends MutationResultListenerFactory> listeners = this.plugins
.findListeners();
final Collection matches = FCollection
.filter(listeners, nameMatches(this.options.getOutputFormats()));
if (matches.size() < this.options.getOutputFormats().size()) {
throw new PitError("Unknown listener requested in "
+ String.join(",", this.options.getOutputFormats()));
}
return matches;
}
private static Predicate nameMatches(
final Iterable outputFormats) {
// plugins can be either activated here by name
// or later via the feature mechanism
return a -> FCollection.contains(outputFormats, equalsIgnoreCase(a.name()))
|| !a.provides().equals(MutationResultListenerFactory.LEGACY_MODE);
}
private static Predicate equalsIgnoreCase(final String other) {
return a -> a.equalsIgnoreCase(other);
}
private static T firstOrDefault(final Collection extends T> found,
final T defaultInstance) {
if (found.isEmpty()) {
return defaultInstance;
}
if (found.size() > 1) {
throw new PitError(
"Multiple implementations of plugin detected on classpath");
}
return found.iterator().next();
}
private static Function toFeature() {
return ProvidesFeature::provides;
}
private Comparator byName() {
return Comparator.comparing(Feature::name);
}
}