diff --git a/pom.xml b/pom.xml index 9925ba6..7bfc9fc 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ mutation-analysis-plugin ch.devcon5.sonar sonar-plugin - 1.6 + 1.7 ${project.artifactId} Sonar Plugin for integrating and visualizing mutation analysis results @@ -79,6 +79,7 @@ Mutation Analysis Plugin 2015-2021 info@devcon5.ch + 7.9.5 @@ -166,6 +167,7 @@ mutationanalysis ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin Mutation Analysis + ${sonarQubeMinVersion} 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 5cdb829..74e61f4 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 @@ -118,6 +118,17 @@ private MutationMetrics(){} .setDecimalScale(1) .create(); + public static final String MUTATIONS_TEST_STRENGTH_KEY = "dc5_mutationAnalysis_mutations_test_strength"; + public static final Metric MUTATIONS_TEST_STRENGTH = new Metric.Builder(MUTATIONS_TEST_STRENGTH_KEY, "Test Strength", Metric.ValueType.PERCENT) + .setDirection(DIRECTION_BETTER) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setQualitative(true) + .setDescription("Test strength percentage") + .setBestValue(100.) + .setWorstValue(0.) + .setDecimalScale(1) + .create(); + public static final String MUTATIONS_DENSITY_KEY = "dc5_mutationAnalysis_mutations_density"; public static final Metric MUTATIONS_DENSITY=new Metric.Builder(MUTATIONS_DENSITY_KEY, "Mutation: Density", Metric.ValueType.PERCENT) .setDirection(DIRECTION_WORST) @@ -224,6 +235,7 @@ private MutationMetrics(){} MUTATIONS_DATA, MUTATIONS_TOTAL_PERCENT, MUTATIONS_COVERAGE, + MUTATIONS_TEST_STRENGTH, MUTATIONS_DENSITY, MUTATIONS_ALIVE_PERCENT, TEST_KILL_RATIO diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputer.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputer.java index 529a4ae..8371550 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputer.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputer.java @@ -46,8 +46,8 @@ public MutationScoreComputer(final Configuration config) { public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) { return defContext.newDefinitionBuilder() - .setInputMetrics(MutationMetrics.MUTATIONS_DETECTED.key(), MutationMetrics.MUTATIONS_TOTAL.key()) - .setOutputMetrics(MutationMetrics.MUTATIONS_COVERAGE.key()) + .setInputMetrics(MutationMetrics.MUTATIONS_DETECTED.key(), MutationMetrics.MUTATIONS_TOTAL.key(), MutationMetrics.MUTATIONS_SURVIVED.key()) + .setOutputMetrics(MutationMetrics.MUTATIONS_COVERAGE.key(), MutationMetrics.MUTATIONS_TEST_STRENGTH.key()) .build(); } @@ -69,12 +69,30 @@ public void compute(final MeasureComputerContext context) { final Double coverage = 100.0 * coveredElements / elements; LOG.info("Computed Mutation Coverage of {}% for {}", coverage, context.getComponent()); context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), coverage); + + final Measure aliveMutantsMeasure = context.getMeasure(MutationMetrics.MUTATIONS_SURVIVED.key()); + int aliveMutants = aliveMutantsMeasure != null ? aliveMutantsMeasure.getIntValue() : 0; + + final Measure killedMutantsMeasure = context.getMeasure(MutationMetrics.MUTATIONS_DETECTED.key()); + int killedMutants; + double testStrength = 0; + if (killedMutantsMeasure != null) { + killedMutants = killedMutantsMeasure.getIntValue(); + int allMutantsQuantity = aliveMutants + killedMutants; + if (allMutantsQuantity != 0) { + testStrength = 100.0 * killedMutants / allMutantsQuantity; + } + } + LOG.info("Computed Test Strength of {}% for {}", testStrength, context.getComponent()); + context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), testStrength); } else { //modules with no mutants (0 total) are always 100% covered (0 of 0 is 100%, right?) context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), 100.0); + context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), 100.0); } } else if(config.getBoolean(FORCE_MISSING_COVERAGE_TO_ZERO).orElse(Boolean.FALSE)){ context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), 0.0); + context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), 0.0); } } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetricsTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetricsTest.java index 6d9417e..1367e69 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetricsTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetricsTest.java @@ -34,6 +34,7 @@ public class MutationMetricsTest { public void testMetricConstants() throws Exception { assertNotNull(MutationMetrics.MUTATIONS_COVERAGE); + assertNotNull(MutationMetrics.MUTATIONS_TEST_STRENGTH); assertNotNull(MutationMetrics.MUTATIONS_DATA); assertNotNull(MutationMetrics.MUTATIONS_DETECTED); assertNotNull(MutationMetrics.MUTATIONS_KILLED); diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputerTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputerTest.java index 51b532a..638a621 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputerTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputerTest.java @@ -20,15 +20,6 @@ package ch.devcon5.sonar.plugins.mutationanalysis.metrics; -import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_COVERAGE_KEY; -import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_DETECTED_KEY; -import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_TOTAL_KEY; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; - import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; import ch.devcon5.sonar.plugins.mutationanalysis.testharness.MeasureComputerTestHarness; import org.junit.Before; @@ -37,6 +28,17 @@ import org.sonar.api.ce.measure.test.TestMeasureComputerContext; import org.sonar.api.ce.measure.test.TestMeasureComputerDefinitionContext; +import java.util.Arrays; +import java.util.HashSet; + +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_COVERAGE_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_DETECTED_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_SURVIVED_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_TEST_STRENGTH_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_TOTAL_KEY; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + public class MutationScoreComputerTest { private MutationScoreComputer computer; @@ -58,8 +60,8 @@ public void define() { final MeasureComputer.MeasureComputerDefinition def = computer.define(context); - assertTrue(def.getInputMetrics().containsAll(Arrays.asList(MUTATIONS_DETECTED_KEY, MUTATIONS_TOTAL_KEY))); - assertTrue(def.getOutputMetrics().containsAll(Arrays.asList(MUTATIONS_COVERAGE_KEY))); + assertEquals(def.getInputMetrics(), new HashSet<>(Arrays.asList(MUTATIONS_DETECTED_KEY, MUTATIONS_TOTAL_KEY, MUTATIONS_SURVIVED_KEY))); + assertEquals(def.getOutputMetrics(), new HashSet<>(Arrays.asList(MUTATIONS_COVERAGE_KEY, MUTATIONS_TEST_STRENGTH_KEY))); } @Test @@ -120,6 +122,21 @@ public void compute_3of5Mutations_to_60percentCoverage() { } + @Test + public void compute_1of5MutationsKilled_to_20percentTestStrength() { + + final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); + + measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 5); + measureContext.addInputMeasure(MUTATIONS_SURVIVED_KEY, 4); + measureContext.addInputMeasure(MUTATIONS_DETECTED_KEY, 1); + + computer.compute(measureContext); + + assertEquals(20.0, measureContext.getMeasure(MUTATIONS_TEST_STRENGTH_KEY).getDoubleValue(), 0.05); + + } + @Test public void compute_NoCoveredElements_0percentCoverage() { @@ -130,6 +147,7 @@ public void compute_NoCoveredElements_0percentCoverage() { computer.compute(measureContext); assertEquals(0.0, measureContext.getMeasure(MUTATIONS_COVERAGE_KEY).getDoubleValue(), 0.05); + assertEquals(0.0, measureContext.getMeasure(MUTATIONS_TEST_STRENGTH_KEY).getDoubleValue(), 0.05); }