diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetrics.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetrics.java index 42b0624..5cdb829 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetrics.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetrics.java @@ -168,6 +168,13 @@ private MutationMetrics(){} .setDescription("Kills per Test") .create(); + public static final String TEST_TOTAL_EXECUTED_KEY = "dc5_mutationAnalysis_mutations_tests_executed"; + public static final Metric TEST_TOTAL_EXECUTED=new Metric.Builder(TEST_TOTAL_EXECUTED_KEY, "Test: Executions", Metric.ValueType.INT) + .setDirection(DIRECTION_BETTER) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Number of Tests executed") + .create(); + public static final String TEST_KILL_RATIO_KEY = "dc5_mutationAnalysis_mutations_testkill_ratio"; public static final Metric TEST_KILL_RATIO=new Metric.Builder(TEST_KILL_RATIO_KEY, "Test: Kill Ratio", Metric.ValueType.PERCENT) .setDirection(DIRECTION_WORST) @@ -208,6 +215,7 @@ private MutationMetrics(){} MUTATIONS_MEMORY_ERROR, MUTATIONS_SURVIVED, TEST_KILLS, + TEST_TOTAL_EXECUTED, UTILITY_GLOBAL_MUTATIONS, UTILITY_GLOBAL_ALIVE )); diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetrics.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetrics.java index 403fc91..b227a26 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetrics.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetrics.java @@ -42,6 +42,7 @@ public class ResourceMutationMetrics { private int mutationsTimedOut; private int mutationsUnknown; private int mutationsDetected; + private int numTestsRun; private double mutationCoverage; private final InputFile resource; @@ -69,6 +70,7 @@ public void addMutant(final Mutant mutant) { mutationsDetected++; } mutationsTotal++; + numTestsRun += mutant.getNumberOfTestsRun(); switch (mutant.getState()) { case KILLED: mutationsKilled++; @@ -187,6 +189,16 @@ public double getMutationCoverage() { return mutationCoverage; } + /** + * Returns the total number of tests executed to kill the mutants (or not) + * @return + * a number >= 0 + */ + public int getNumTestsRun() { + + return numTestsRun; + } + /** * The Sonar resource to which the mutant metrics are bound. * diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java index d18c99a..e448aa7 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java @@ -47,6 +47,7 @@ public class Mutant { private final int lineNumber; private final int index; + private final int numberOfTestsRun; private final State state; private final MutationOperator mutationOperator; private final String sourceFile; @@ -79,6 +80,7 @@ private Mutant(final Builder builder) { this.mutationOperator = builder.mutationOperator; this.mutatorSuffix = builder.mutatorSuffix; this.index = builder.index; + this.numberOfTestsRun = builder.numberOfTestsRun; this.killingTest = builder.state.isDetected() ? builder.killingTest : ""; this.description = builder.description; this.toString = "Mutant [sourceFile=" @@ -95,6 +97,8 @@ private Mutant(final Builder builder) { + builder.state + ", mutationOperator=" + builder.mutationOperator.getName() + + ", numberOfTestsRun=" + + builder.numberOfTestsRun + ", killingTest=" + this.killingTest + (this.description == null ? "" : ", description=" + this.description) @@ -110,6 +114,7 @@ private Mutant(final Builder builder) { this.mutatorSuffix.hashCode(), this.sourceFile.hashCode(), this.killingTest.hashCode(), + this.numberOfTestsRun, this.description == null ? 0 : this.description.hashCode()); this.testDescriptor = new TestDescriptor(this.killingTest); @@ -220,6 +225,16 @@ public String getKillingTest() { return killingTest; } + /** + * + * @return the number of tests that had to be executed to kill the mutant. The number is + * usually >= 1. + */ + public int getNumberOfTestsRun() { + + return numberOfTestsRun; + } + /** * Newer versions of Pit produce a description containing more details about what has been mutated. * @@ -279,6 +294,9 @@ private boolean equalsMutant(final Mutant other) { // NOSONAR if (lineNumber != other.lineNumber) { return false; } + if (numberOfTestsRun != other.numberOfTestsRun) { + return false; + } if (!methodDescription.equals(other.methodDescription)) { return false; } @@ -436,6 +454,7 @@ public static class Builder { private MutationOperator mutationOperator; private String mutatorSuffix; private int index; + private int numberOfTestsRun; private String killingTest; private String description; @@ -594,6 +613,19 @@ public Builder killedBy(final String killingTest) { return this; } + /** + * + * @param numberOfTestsRun + * the number of tests that had to be executed before the mutant was killed. Pitest only executes tests that + * execute code actually cover a specific mutant to reduce the overall execution time. + * @return + * this builder + */ + public Builder numberOfTestsRun(final int numberOfTestsRun){ + this.numberOfTestsRun = numberOfTestsRun; + return this; + } + /** * Creates a new {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant} with all the parameters specified. * As the {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant} requires all parameter to diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParser.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParser.java index 6cca548..125577d 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParser.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParser.java @@ -66,6 +66,7 @@ public class PitestReportParser { private static final Logger LOG = LoggerFactory.getLogger(PitestReportParser.class); private static final String ATTR_STATUS = "status"; + private static final String ATTR_NUMBER_OF_TESTS_RUN = "numberOfTestsRun"; private static final String ELEMENT_KILLING_TEST = "killingTest"; @@ -195,7 +196,9 @@ private void startElement(final XMLStreamReader reader, final Collection */ private Mutant parseMutant(final XMLStreamReader reader) throws XMLStreamException { - final Mutant.Builder builder = Mutant.builder().mutantStatus(getMutantStatus(reader)); + final Mutant.Builder builder = Mutant.builder() + .mutantStatus(getMutantStatus(reader)) + .numberOfTestsRun(getNumberOfTestsRun(reader)); while (true) { int event = reader.next(); @@ -266,4 +269,21 @@ private String getMutantStatus(final XMLStreamReader reader) { return reader.getAttributeValue(NAMESPACE_URI, ATTR_STATUS); } + + /** + * Reads the status of {@link Mutant} from the XMLStream. + * + * @param reader + * the {@link XMLStreamReader} whose cursor is at the start element position of a <mutation> element + * + * @return the mutant status as a string + */ + private int getNumberOfTestsRun(final XMLStreamReader reader) { + + final String numberOfTestsRun = reader.getAttributeValue(NAMESPACE_URI, ATTR_NUMBER_OF_TESTS_RUN); + if(numberOfTestsRun != null){ + return Integer.parseInt(numberOfTestsRun); + } + return 0; + } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriter.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriter.java index 851dedf..5659e54 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriter.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriter.java @@ -102,15 +102,12 @@ private void saveResourceMetrics(final ResourceMutationMetrics resourceMetrics, context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_NO_COVERAGE).withValue(resourceMetrics.getMutationsNoCoverage()).save(); context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_KILLED).withValue(resourceMetrics.getMutationsKilled()).save(); context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_SURVIVED).withValue(resourceMetrics.getMutationsSurvived()).save(); - context.newMeasure() - .on(resource) - .forMetric(MutationMetrics.MUTATIONS_ALIVE) - .withValue(resourceMetrics.getMutationsTotal() - resourceMetrics.getMutationsDetected()) - .save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_ALIVE).withValue(resourceMetrics.getMutationsTotal() - resourceMetrics.getMutationsDetected()).save(); context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_MEMORY_ERROR).withValue(resourceMetrics.getMutationsMemoryError()).save(); context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_TIMED_OUT).withValue(resourceMetrics.getMutationsTimedOut()).save(); context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_UNKNOWN).withValue(resourceMetrics.getMutationsUnknown()).save(); context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_DETECTED).withValue(resourceMetrics.getMutationsDetected()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.TEST_TOTAL_EXECUTED).withValue(resourceMetrics.getNumTestsRun()).save(); } } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java index 1ebf838..a70cd54 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java @@ -37,6 +37,7 @@ public static Mutant newUndetectedMutant() { .inLine(8) .usingMutator(MutationOperators.find("INVERT_NEGS")) .atIndex(10) + .numberOfTestsRun(123) .killedBy("com.foo.bar.SomeClassKillingTest") .build(); } @@ -52,6 +53,7 @@ public static Mutant newDetectedMutant() { .inLine(17) .usingMutator(MutationOperators.find("INVERT_NEGS")) .atIndex(5) + .numberOfTestsRun(256) .killedBy("com.foo.bar.SomeClassKillingTest") .build(); } @@ -67,6 +69,7 @@ public static Mutant newSurvivedMutantWithSuffix(){ .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_EQUAL_ELSE") .atIndex(10) .killedBy("com.foo.bar.SomeClassKillingTest") + .numberOfTestsRun(42) .withDescription("removed conditional - replaced equality check with false") .build(); } @@ -146,6 +149,13 @@ public void testGetLineNumber() throws Exception { assertEquals(8, newUndetectedMutant().getLineNumber()); } + @Test + public void testGetNumberOfTestsRun() throws Exception { + + assertEquals(256, newDetectedMutant().getNumberOfTestsRun()); + } + + @Test public void testGetMutator() throws Exception { @@ -193,6 +203,7 @@ public void testToString() throws Exception { + "lineNumber=17, " + "state=KILLED, " + "mutationOperator=Invert Negs Mutator, " + + "numberOfTestsRun=256, " + "killingTest=com.foo.bar.SomeClassKillingTest]", newDetectedMutant().toString()); @@ -206,6 +217,7 @@ public void testToString_withDescription() throws Exception { + "lineNumber=8, " + "state=SURVIVED, " + "mutationOperator=Remove Conditional Mutator, " + + "numberOfTestsRun=42, " + "killingTest=, " + "description=removed conditional - replaced equality check with false]", newSurvivedMutantWithSuffix().toString()); @@ -249,6 +261,7 @@ public void testEquals_differentStatus_false() throws Exception { .withMethodParameters(expected.getMethodDescription()) .usingMutator(expected.getMutationOperator()) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy(expected.getKillingTest()) .build(); @@ -268,6 +281,7 @@ public void testEquals_differentSourceFile_false() throws Exception { .withMethodParameters(expected.getMethodDescription()) .usingMutator(expected.getMutationOperator()) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy(expected.getKillingTest()) .build(); assertNotEquals(expected, other); @@ -286,6 +300,7 @@ public void testEquals_differentMutatedClass_false() throws Exception { .withMethodParameters(expected.getMethodDescription()) .usingMutator(expected.getMutationOperator()) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy(expected.getKillingTest()) .build(); @@ -305,6 +320,7 @@ public void testEquals_differentMutatedMethod_false() throws Exception { .withMethodParameters(expected.getMethodDescription()) .usingMutator(expected.getMutationOperator()) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy(expected.getKillingTest()) .build(); assertNotEquals(expected, other); @@ -323,6 +339,7 @@ public void testEquals_differentMethodDescription_false() throws Exception { .withMethodParameters("()") .usingMutator(expected.getMutationOperator()) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy(expected.getKillingTest()) .build(); @@ -342,6 +359,7 @@ public void testEquals_differentLineNumber_false() throws Exception { .withMethodParameters(expected.getMethodDescription()) .usingMutator(expected.getMutationOperator()) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy(expected.getKillingTest()) .build(); @@ -361,6 +379,7 @@ public void testEquals_differentMutator_false() throws Exception { .withMethodParameters(expected.getMethodDescription()) .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy(expected.getKillingTest()) .build(); @@ -381,6 +400,27 @@ public void testEquals_differentSuffix_false() throws Exception { .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_EQUAL_IF") .withDescription(expected.getDescription().get()) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); + + assertNotEquals(expected, other); + } + + @Test + public void testEquals_differentNumberOfTestsRun_false() throws Exception { + + final Mutant expected = newDetectedMutant(); + final Mutant other = Mutant.builder() + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(-1) .killedBy(expected.getKillingTest()) .build(); @@ -400,6 +440,7 @@ public void testEquals_differentIndex_false() throws Exception { .withMethodParameters(expected.getMethodDescription()) .usingMutator(expected.getMutationOperator()) .atIndex(127) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy(expected.getKillingTest()) .build(); @@ -419,6 +460,7 @@ public void testEquals_differentKillingTest_false() throws Exception { .withMethodParameters(expected.getMethodDescription()) .usingMutator(expected.getMutationOperator()) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy("otherTest") .build(); @@ -438,6 +480,7 @@ public void testEquals_differentDescription_false() throws Exception { .withMethodParameters(expected.getMethodDescription()) .usingMutator(expected.getMutationOperator()) .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) .killedBy(expected.getKillingTest()) .withDescription("other Description") .build(); @@ -475,6 +518,7 @@ public void testHashCode_detected_reproducible() throws Exception { refCode = prime * refCode + mutant.getMutatorSuffix().hashCode(); refCode = prime * refCode + mutant.getSourceFile().hashCode(); refCode = prime * refCode + mutant.getKillingTest().hashCode(); + refCode = prime * refCode + mutant.getNumberOfTestsRun(); refCode = prime * refCode + mutant.getDescription().hashCode(); assertEquals(refCode, mutant.hashCode()); @@ -498,6 +542,7 @@ public void testHashCode_undetected_reproducible() throws Exception { refCode = prime * refCode + mutant.getMutatorSuffix().hashCode(); refCode = prime * refCode + mutant.getSourceFile().hashCode(); refCode = prime * refCode + mutant.getKillingTest().hashCode(); + refCode = prime * refCode + mutant.getNumberOfTestsRun(); refCode = prime * refCode + mutant.getDescription().hashCode(); assertEquals(refCode, mutant.hashCode()); diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest.java index 1073aba..d5ae861 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest.java @@ -74,6 +74,7 @@ public void parseReport_findMutants_withoutDescription() throws IOException, URI .withMethodParameters("(Ljava/lang/Object;)Z") .inLine(162) .atIndex(5) + .numberOfTestsRun(0) .usingMutator(MutationOperators.find("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator")) .killedBy( "ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)") @@ -133,6 +134,31 @@ public void parseReport_findMutants_withDescriptions() throws IOException, URISy assertEquals(expected, mutants.iterator().next()); } + @Test + public void parseReport_findMutants_withNumberOfTests() throws IOException, URISyntaxException { + + // prepare + final Path report = Paths.get(getClass().getResource("PitestReportParserTest_mutationsWithNumTests.xml").toURI()); + final Mutant expected = Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("Mutant.java") + .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") + .inMethod("equals") + .withMethodParameters("(Ljava/lang/Object;)Z") + .inLine(162) + .atIndex(5) + .numberOfTestsRun(40) + .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator") + .killedBy("ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)") + .build(); + + // act + final Collection mutants = subject.parseMutants(report); + + // assert + assertEquals(expected, mutants.iterator().next()); + } + @Test public void parseReport_emptyFile_emptyList() throws Exception { diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest.java index 712e0a3..608edad 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest.java @@ -55,6 +55,7 @@ //kills 68 mutants, 11 alive public class PitestSensorTest { + public static final int EXPECTED_QUANTITATIVE_METRICS = 12; @Rule public TemporaryFolder folder = new TemporaryFolder(); @@ -232,7 +233,7 @@ public void execute_noRuleActivated_noIssuesCreated_but_MetricsCreated() throws sensor.execute(context); assertTrue(context.getStorage().getIssues().isEmpty()); - assertEquals(11, context.getStorage().getMeasures().size()); + assertEquals(12, context.getStorage().getMeasures().size()); } @@ -266,7 +267,7 @@ public void execute_mutatorSpecificRuleActive_issueCreated() throws Exception { assertTextrangeOnLine(i.primaryLocation().textRange(), 175, 79); assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test. (WITH_SUFFIX)", i.primaryLocation().message()); }); - assertEquals(11, context.getStorage().getMeasures().size()); + assertEquals(12, context.getStorage().getMeasures().size()); } @Test @@ -290,7 +291,7 @@ public void execute_survivedMutantRuleActive_issueCreated() throws Exception { assertTextrangeOnLine(i.primaryLocation().textRange(), 175, 79); assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test. (WITH_SUFFIX)", i.primaryLocation().message()); }); - assertEquals(11, context.getStorage().getMeasures().size()); + assertEquals(12, context.getStorage().getMeasures().size()); } @@ -314,7 +315,7 @@ public void execute_unknownMutantStatusRuleActive_issueCreated() throws Exceptio assertTextrangeOnLine(i.primaryLocation().textRange(), 175, 79); assertEquals("Alive Mutant: A return value has been replaced by a method argument without being detected by a test.", i.primaryLocation().message()); }); - assertEquals(11, context.getStorage().getMeasures().size()); + assertEquals(EXPECTED_QUANTITATIVE_METRICS, context.getStorage().getMeasures().size()); } @@ -414,7 +415,7 @@ public void execute_withExperimentalFeaturesEnable_TestMetricsCreated() throws E sensor.execute(context); final List measures = context.getStorage().getMeasures(); - assertEquals(13, measures.size()); + assertEquals(EXPECTED_QUANTITATIVE_METRICS +2, measures.size()); assertEquals(3, assertContains(measures, m -> assertEquals(TEST_KILLS_KEY, m.metric().key())).value()); assertEquals(6, assertContains(measures, m -> assertEquals(UTILITY_GLOBAL_MUTATIONS_KEY, m.metric().key())).value()); } @@ -435,7 +436,7 @@ public void execute_withoutExperimentalFeaturesEnable_noTestMetricsCreated() thr sensor.execute(context); final List measures = context.getStorage().getMeasures(); - assertEquals(11, measures.size()); + assertEquals(12, measures.size()); assertNotContains(measures, m -> assertEquals(TEST_KILLS_KEY, m.metric().key())); assertNotContains(measures, m -> assertEquals(UTILITY_GLOBAL_MUTATIONS_KEY, m.metric().key())); } @@ -462,7 +463,7 @@ public void execute_onlyKotlinSensor_issueCreated() throws Exception { assertTextrangeOnLine(i.primaryLocation().textRange(), 28, 79); assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test. Mutation: negated conditional", i.primaryLocation().message()); }); - assertEquals(11, context.getStorage().getMeasures().size()); + assertEquals(12, context.getStorage().getMeasures().size()); } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriterTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriterTest.java index 0dd2c12..525f026 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriterTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriterTest.java @@ -52,6 +52,7 @@ */ public class SourceMetricsWriterTest { + public static final int EXPECTED_QUANTITATIVE_METRICS = 12; @Rule public TemporaryFolder folder = new TemporaryFolder(); @@ -88,7 +89,7 @@ public void writeMetrics_singleResourceMetrics_withGlobalMutants_metricsWritten( smw.writeMetrics(metrics, context, globalMutants); final Map measures = getMeasuresByKey("test-module:Test.java", context); - assertEquals(11, measures.size()); + assertEquals(EXPECTED_QUANTITATIVE_METRICS, measures.size()); assertEquals(15, measures.get(MutationMetrics.MUTATIONS_TOTAL.key())); assertEquals(1, measures.get(MutationMetrics.MUTATIONS_NO_COVERAGE.key())); @@ -119,7 +120,7 @@ public void writeMetrics_singleResourceMetrics_metricsWritten() throws Exception smw.writeMetrics(metrics, context, globalMutants); final Map measures = getMeasuresByKey("test-module:Test.java", context); - assertEquals(11, measures.size()); + assertEquals(EXPECTED_QUANTITATIVE_METRICS, measures.size()); assertEquals(15, measures.get(MutationMetrics.MUTATIONS_TOTAL.key())); assertEquals(1, measures.get(MutationMetrics.MUTATIONS_NO_COVERAGE.key())); @@ -197,6 +198,7 @@ public void writeMetrics_multiResourceMetrics_metricsWritten() throws Exception md.mutants.memoryError = 3; md.mutants.timedOut = 4; md.mutants.killed = 5; + md.mutants.numTestRun = 2; }), context.newResourceMutationMetrics("Test2.java", md -> { md.lines = 100; md.mutants.unknown = 1; @@ -205,13 +207,15 @@ public void writeMetrics_multiResourceMetrics_metricsWritten() throws Exception md.mutants.memoryError = 0; md.mutants.timedOut = 1; md.mutants.killed = 3; + md.mutants.numTestRun = 3; })); smw.writeMetrics(metrics, context, globalMutants); final Map values1 = getMeasuresByKey("test-module:Test1.java", context); - assertEquals(11, values1.size()); + assertEquals(EXPECTED_QUANTITATIVE_METRICS, values1.size()); assertEquals(15, values1.get(MutationMetrics.MUTATIONS_TOTAL.key())); + assertEquals(15*2, values1.get(MutationMetrics.TEST_TOTAL_EXECUTED.key())); assertEquals(1, values1.get(MutationMetrics.MUTATIONS_NO_COVERAGE.key())); assertEquals(5, values1.get(MutationMetrics.MUTATIONS_KILLED.key())); assertEquals(2, values1.get(MutationMetrics.MUTATIONS_SURVIVED.key())); @@ -223,8 +227,9 @@ public void writeMetrics_multiResourceMetrics_metricsWritten() throws Exception assertEquals(24, values1.get(MutationMetrics.UTILITY_GLOBAL_MUTATIONS.key())); assertEquals(8, values1.get(MutationMetrics.UTILITY_GLOBAL_ALIVE.key())); final Map values2 = getMeasuresByKey("test-module:Test2.java", context); - assertEquals(11, values2.size()); + assertEquals(EXPECTED_QUANTITATIVE_METRICS, values2.size()); assertEquals(9, values2.get(MutationMetrics.MUTATIONS_TOTAL.key())); + assertEquals(9*3, values2.get(MutationMetrics.TEST_TOTAL_EXECUTED.key())); assertEquals(2, values2.get(MutationMetrics.MUTATIONS_NO_COVERAGE.key())); assertEquals(3, values2.get(MutationMetrics.MUTATIONS_KILLED.key())); assertEquals(2, values2.get(MutationMetrics.MUTATIONS_SURVIVED.key())); diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestSensorContext.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestSensorContext.java index 95edaf8..ac79902 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestSensorContext.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestSensorContext.java @@ -275,14 +275,12 @@ public ResourceMutationMetrics newResourceMutationMetrics(String filename, Consu try { ResourceMutationMetrics rmm = new ResourceMutationMetrics(addTestFile(filename, metadata)); - String desc = metadata.mutants.description; - - addMutants(rmm, Mutant.State.UNKNOWN, metadata.mutants.unknown, metadata.test.name, desc); - addMutants(rmm, Mutant.State.NO_COVERAGE, metadata.mutants.noCoverage, metadata.test.name, desc); - addMutants(rmm, Mutant.State.SURVIVED, metadata.mutants.survived, metadata.test.name, desc); - addMutants(rmm, Mutant.State.TIMED_OUT, metadata.mutants.timedOut, metadata.test.name, desc); - addMutants(rmm, Mutant.State.MEMORY_ERROR, metadata.mutants.memoryError, metadata.test.name, desc); - addMutants(rmm, Mutant.State.KILLED, metadata.mutants.killed, metadata.test.name, desc); + addMutants(rmm, Mutant.State.UNKNOWN, metadata.mutants.unknown, metadata); + addMutants(rmm, Mutant.State.NO_COVERAGE, metadata.mutants.noCoverage, metadata); + addMutants(rmm, Mutant.State.SURVIVED, metadata.mutants.survived, metadata); + addMutants(rmm, Mutant.State.TIMED_OUT, metadata.mutants.timedOut, metadata); + addMutants(rmm, Mutant.State.MEMORY_ERROR, metadata.mutants.memoryError, metadata); + addMutants(rmm, Mutant.State.KILLED, metadata.mutants.killed, metadata); return rmm; } catch (IOException e) { @@ -390,16 +388,18 @@ private DefaultInputFile createNewInputFile(final String filename, TestFileMetad } - private void addMutants(final ResourceMutationMetrics rmm, final Mutant.State state, final int count, String testname, String desc) { + private void addMutants(final ResourceMutationMetrics rmm, final Mutant.State state, final int count, + TestFileMetadata metadata) { + String filename = rmm.getResource().filename(); for (int i = 0; i < count; i++) { - rmm.addMutant(newMutant(filename, state, count + i, testname, desc)); + rmm.addMutant(newMutant(filename, state, count + i, metadata)); } } - private Mutant newMutant(String file, Mutant.State state, final int line, String testName, String description) { + private Mutant newMutant(String file, Mutant.State state, final int line, TestFileMetadata metadata) { return Mutant.builder() .mutantStatus(state) @@ -409,9 +409,10 @@ private Mutant newMutant(String file, Mutant.State state, final int line, String .withMethodParameters("desc") .inLine(line) .atIndex(0) + .numberOfTestsRun(metadata.mutants.numTestRun) .usingMutator(getMutationOperatorForLine(line)) - .killedBy(testName) - .withDescription(description) + .killedBy(metadata.test.name) + .withDescription(metadata.mutants.description) .build(); } @@ -441,6 +442,7 @@ public static class MutantMetadata implements Serializable { public int timedOut = 0; public int killed = 0; public String description; + public int numTestRun; } public static class TestMetadata implements Serializable { diff --git a/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutationsWithDescriptions.xml b/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutationsWithDescriptions.xml index 7cf4df0..3231509 100644 --- a/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutationsWithDescriptions.xml +++ b/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutationsWithDescriptions.xml @@ -1,6 +1,6 @@ - + Mutant.java ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant equals diff --git a/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutationsWithNumTests.xml b/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutationsWithNumTests.xml new file mode 100644 index 0000000..4e20e74 --- /dev/null +++ b/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutationsWithNumTests.xml @@ -0,0 +1,33 @@ + + + + + + Mutant.java + ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant + equals + (Ljava/lang/Object;)Z + 162 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest) + +