diff --git a/pitest-entry/src/main/java/org/pitest/coverage/CompoundCoverageExporterFactory.java b/pitest-entry/src/main/java/org/pitest/coverage/CompoundCoverageExporterFactory.java new file mode 100644 index 000000000..92dce9a93 --- /dev/null +++ b/pitest-entry/src/main/java/org/pitest/coverage/CompoundCoverageExporterFactory.java @@ -0,0 +1,38 @@ +package org.pitest.coverage; + +import org.pitest.plugin.Feature; +import org.pitest.plugin.FeatureSelector; +import org.pitest.plugin.FeatureSetting; +import org.pitest.util.ResultOutputStrategy; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class CompoundCoverageExporterFactory implements CoverageExporterFactory { + + private final FeatureSelector features; + + public CompoundCoverageExporterFactory(List features, Collection children) { + this.features = new FeatureSelector<>(features, children); + } + + @Override + public CoverageExporter create(ResultOutputStrategy source) { + List exporters = this.features.getActiveFeatures().stream() + .map(f -> f.create(source)) + .collect(Collectors.toList()); + return c -> exporters.stream().forEach(exporter -> exporter.recordCoverage(c)); + } + + @Override + public String description() { + throw new UnsupportedOperationException(); + } + + @Override + public Feature provides() { + throw new UnsupportedOperationException(); + } + +} \ No newline at end of file diff --git a/pitest-entry/src/main/java/org/pitest/coverage/CoverageExporterFactory.java b/pitest-entry/src/main/java/org/pitest/coverage/CoverageExporterFactory.java new file mode 100644 index 000000000..ab967e0f4 --- /dev/null +++ b/pitest-entry/src/main/java/org/pitest/coverage/CoverageExporterFactory.java @@ -0,0 +1,9 @@ +package org.pitest.coverage; + +import org.pitest.plugin.ProvidesFeature; +import org.pitest.plugin.ToolClasspathPlugin; +import org.pitest.util.ResultOutputStrategy; + +public interface CoverageExporterFactory extends ToolClasspathPlugin, ProvidesFeature { + CoverageExporter create(ResultOutputStrategy source); +} diff --git a/pitest-entry/src/main/java/org/pitest/coverage/DefaultCoverageExporterFactory.java b/pitest-entry/src/main/java/org/pitest/coverage/DefaultCoverageExporterFactory.java new file mode 100644 index 000000000..e74b69b80 --- /dev/null +++ b/pitest-entry/src/main/java/org/pitest/coverage/DefaultCoverageExporterFactory.java @@ -0,0 +1,24 @@ +package org.pitest.coverage; + +import org.pitest.coverage.export.DefaultCoverageExporter; +import org.pitest.plugin.Feature; +import org.pitest.util.ResultOutputStrategy; + +public class DefaultCoverageExporterFactory implements CoverageExporterFactory { + @Override + public CoverageExporter create(ResultOutputStrategy source) { + return new DefaultCoverageExporter(source); + } + + @Override + public Feature provides() { + return Feature.named("defaultCoverage") + .withDescription(description()) + .withOnByDefault(true); + } + + @Override + public String description() { + return "Default coverage exporter"; + } +} diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/config/PluginServices.java b/pitest-entry/src/main/java/org/pitest/mutationtest/config/PluginServices.java index f6a7c5533..8e5caff0e 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/config/PluginServices.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/config/PluginServices.java @@ -1,6 +1,7 @@ package org.pitest.mutationtest.config; import org.pitest.classpath.CodeSourceFactory; +import org.pitest.coverage.CoverageExporterFactory; import org.pitest.mutationtest.HistoryFactory; import org.pitest.mutationtest.build.CoverageTransformerFactory; import org.pitest.mutationtest.MutationEngineFactory; @@ -59,6 +60,7 @@ public Collection findToolClasspathPlugins() { l.addAll(findVerifiers()); l.addAll(findCodeSources()); l.addAll(findHistory()); + l.addAll(findCoverageExport()); return l; } @@ -133,6 +135,10 @@ public List findHistory() { return new ArrayList<>(load(HistoryFactory.class)); } + + public List findCoverageExport() { + return new ArrayList<>(load(CoverageExporterFactory.class)); + } public Collection findFeatures() { return findToolClasspathPlugins().stream() .filter(p -> p instanceof ProvidesFeature) diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/config/SettingsFactory.java b/pitest-entry/src/main/java/org/pitest/mutationtest/config/SettingsFactory.java index c6042dcab..e96dbadde 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/config/SettingsFactory.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/config/SettingsFactory.java @@ -4,12 +4,12 @@ import org.pitest.classpath.CodeSourceFactory; import org.pitest.classpath.DefaultCodeSource; import org.pitest.classpath.ProjectClassPaths; +import org.pitest.coverage.CompoundCoverageExporterFactory; import org.pitest.coverage.CoverageExporter; import org.pitest.mutationtest.HistoryFactory; 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; @@ -63,7 +63,9 @@ public ResultOutputStrategy getOutputStrategy() { public CoverageExporter createCoverageExporter() { if (this.options.shouldExportLineCoverage()) { - return new DefaultCoverageExporter(getOutputStrategy()); + final FeatureParser parser = new FeatureParser(); + return new CompoundCoverageExporterFactory(parser.parseFeatures(this.options.getFeatures()), this.plugins.findCoverageExport()) + .create(this.options.getReportDirectoryStrategy()); } else { return new NullCoverageExporter(); } diff --git a/pitest-entry/src/main/resources/META-INF/services/org.pitest.coverage.CoverageExporterFactory b/pitest-entry/src/main/resources/META-INF/services/org.pitest.coverage.CoverageExporterFactory new file mode 100644 index 000000000..7000a1c3a --- /dev/null +++ b/pitest-entry/src/main/resources/META-INF/services/org.pitest.coverage.CoverageExporterFactory @@ -0,0 +1 @@ +org.pitest.coverage.DefaultCoverageExporterFactory \ No newline at end of file diff --git a/pitest-entry/src/test/java/org/pitest/mutationtest/config/SettingsFactoryTest.java b/pitest-entry/src/test/java/org/pitest/mutationtest/config/SettingsFactoryTest.java index 0d868f487..3dc15102a 100644 --- a/pitest-entry/src/test/java/org/pitest/mutationtest/config/SettingsFactoryTest.java +++ b/pitest-entry/src/test/java/org/pitest/mutationtest/config/SettingsFactoryTest.java @@ -1,9 +1,13 @@ package org.pitest.mutationtest.config; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; +import org.pitest.coverage.CoverageExporter; import org.pitest.coverage.execute.CoverageOptions; +import org.pitest.coverage.export.DefaultCoverageExporter; import org.pitest.coverage.export.NullCoverageExporter; import org.pitest.mutationtest.engine.gregor.config.GregorEngineFactory; import org.pitest.mutationtest.incremental.DefaultHistoryFactory; @@ -12,6 +16,8 @@ import org.pitest.util.PitError; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.util.Arrays; import java.util.Collections; import java.util.function.Consumer; @@ -33,6 +39,9 @@ public class SettingsFactoryTest { private SettingsFactory testee; + @Rule + public TemporaryFolder reportDir = new TemporaryFolder(); + @Before public void setUp() { this.testee = new SettingsFactory(this.options, this.plugins); @@ -45,6 +54,31 @@ public void shouldReturnANullCoverageExporterWhenOptionSetToFalse() { assertTrue(this.testee.createCoverageExporter() instanceof NullCoverageExporter); } + @Test + public void usesDefaultCoverageExporterWhenOptionSetToTrueAndNoOtherExportersPresent() throws IOException { + this.options.setExportLineCoverage(true); + this.options.setShouldCreateTimestampedReports(false); + this.options.setReportDir(reportDir.getRoot().getAbsolutePath()); + CoverageExporter actual = this.testee.createCoverageExporter(); + actual.recordCoverage(Collections.emptyList()); + + assertThat(Files.list(reportDir.getRoot().toPath())) + .anyMatch(path -> path.getFileName().toString().equals("linecoverage.xml")); + } + + @Test + public void generatesNoCoverageWhenAllExportersDisabled() throws IOException { + this.options.setExportLineCoverage(true); + this.options.setShouldCreateTimestampedReports(false); + this.options.setReportDir(reportDir.getRoot().getAbsolutePath()); + this.options.setFeatures(Arrays.asList("-defaultCoverage")); + CoverageExporter actual = this.testee.createCoverageExporter(); + actual.recordCoverage(Collections.emptyList()); + + assertThat(Files.list(reportDir.getRoot().toPath())) + .noneMatch(path -> path.getFileName().toString().equals("linecoverage.xml")); + } + @Test public void shouldReturnEngineWhenRequestedEngineIsKnown() { assertTrue(this.testee.createEngine() instanceof GregorEngineFactory); diff --git a/pitest-entry/src/test/java/org/pitest/process/ArgLineParserTest.java b/pitest-entry/src/test/java/org/pitest/process/ArgLineParserTest.java index 65c26ddba..29dcc252c 100644 --- a/pitest-entry/src/test/java/org/pitest/process/ArgLineParserTest.java +++ b/pitest-entry/src/test/java/org/pitest/process/ArgLineParserTest.java @@ -136,4 +136,5 @@ public void handlesRealExampleArgLine() { "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.math=ALL-UNNAMED"); } + } \ No newline at end of file