From 328d1094ff11072c9b4448949428d4cd6318bc16 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 30 Sep 2016 11:27:24 +0200 Subject: [PATCH] SONAR-7782 Compute Reliability Rating on New Code --- ...ilityAndSecurityRatingMeasuresVisitor.java | 98 ++++++++++--------- ...ityRatingMeasuresVisitorForReportTest.java | 30 +++++- ...rityRatingMeasuresVisitorForViewsTest.java | 4 +- .../resources/org/sonar/l10n/core.properties | 2 + .../org/sonar/api/measures/CoreMetrics.java | 19 ++++ 5 files changed, 104 insertions(+), 49 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitor.java index d95f7a231dc..a46218e8d1c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitor.java @@ -32,12 +32,12 @@ import org.sonar.server.computation.task.projectanalysis.formula.counter.RatingV import org.sonar.server.computation.task.projectanalysis.issue.ComponentIssuesRepository; import org.sonar.server.computation.task.projectanalysis.measure.Measure; import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository; -import org.sonar.server.computation.task.projectanalysis.measure.MeasureVariations; import org.sonar.server.computation.task.projectanalysis.metric.Metric; import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository; import org.sonar.server.computation.task.projectanalysis.period.Period; import org.sonar.server.computation.task.projectanalysis.period.PeriodsHolder; +import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.RELIABILITY_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_RATING_KEY; @@ -63,6 +63,7 @@ import static org.sonar.server.computation.task.projectanalysis.qualitymodel.Rat * Compute following measures : * {@link CoreMetrics#RELIABILITY_RATING_KEY} * {@link CoreMetrics#SECURITY_RATING_KEY} + * {@link CoreMetrics#NEW_RELIABILITY_RATING_KEY} * {@link CoreMetrics#NEW_SECURITY_RATING_KEY} */ public class ReliabilityAndSecurityRatingMeasuresVisitor extends PathAwareVisitorAdapter { @@ -81,8 +82,11 @@ public class ReliabilityAndSecurityRatingMeasuresVisitor extends PathAwareVisito // Output metrics private final Metric reliabilityRatingMetric; private final Metric securityRatingMetric; + private final Metric newReliabilityRatingMetric; private final Metric newSecurityRatingMetric; + private final Map metricsByKey; + public ReliabilityAndSecurityRatingMeasuresVisitor(MetricRepository metricRepository, MeasureRepository measureRepository, ComponentIssuesRepository componentIssuesRepository, PeriodsHolder periodsHolder) { super(LEAVES, POST_ORDER, CounterFactory.INSTANCE); @@ -93,7 +97,14 @@ public class ReliabilityAndSecurityRatingMeasuresVisitor extends PathAwareVisito // Output metrics this.reliabilityRatingMetric = metricRepository.getByKey(RELIABILITY_RATING_KEY); this.securityRatingMetric = metricRepository.getByKey(SECURITY_RATING_KEY); + this.newReliabilityRatingMetric = metricRepository.getByKey(NEW_RELIABILITY_RATING_KEY); this.newSecurityRatingMetric = metricRepository.getByKey(NEW_SECURITY_RATING_KEY); + + this.metricsByKey = ImmutableMap.of( + RELIABILITY_RATING_KEY, reliabilityRatingMetric, + SECURITY_RATING_KEY, securityRatingMetric, + NEW_RELIABILITY_RATING_KEY, newReliabilityRatingMetric, + NEW_SECURITY_RATING_KEY, newSecurityRatingMetric); } @Override @@ -128,51 +139,36 @@ public class ReliabilityAndSecurityRatingMeasuresVisitor extends PathAwareVisito @Override public void visitProjectView(Component projectView, Path path) { - Optional reliabilityRatingMeasure = measureRepository.getRawMeasure(projectView, reliabilityRatingMetric); - if (reliabilityRatingMeasure.isPresent()) { - path.parent().reliabilityRating.increment(valueOf(reliabilityRatingMeasure.get().getData())); - } - Optional securityRatingMeasure = measureRepository.getRawMeasure(projectView, securityRatingMetric); - if (securityRatingMeasure.isPresent()) { - path.parent().securityRating.increment(valueOf(securityRatingMeasure.get().getData())); - } + path.parent().ratingValueByMetric.entrySet().forEach(entry -> { + Optional ratingMeasure = measureRepository.getRawMeasure(projectView, metricsByKey.get(entry.getKey())); + if (ratingMeasure.isPresent()) { + entry.getValue().increment(valueOf(ratingMeasure.get().getData())); + } + }); } private void computeAndSaveMeasures(Component component, Path path) { processIssues(component, path); - addReliabilityRatingMeasure(component, path); - addSecurityRatingMeasure(component, path); - addNewSecurityRatingMeasure(component, path); + path.current().ratingValueByMetric.entrySet().forEach( + entry -> measureRepository.add(component, metricsByKey.get(entry.getKey()), createRatingMeasure(entry.getValue().getValue()))); + path.current().newRatingValueByMetric.entrySet().forEach( + entry -> entry.getValue().toMeasureVariations() + .ifPresent(measureVariations -> measureRepository.add( + component, + metricsByKey.get(entry.getKey()), + newMeasureBuilder().setVariations(measureVariations).createNoValue()))); addToParent(path); } private void processIssues(Component component, Path path) { - for (Issue issue : componentIssuesRepository.getIssues(component)) { - if (issue.resolution() == null) { + componentIssuesRepository.getIssues(component) + .stream() + .filter(issue -> issue.resolution() == null) + .filter(issue -> issue.type().equals(BUG) || issue.type().equals(VULNERABILITY)) + .forEach(issue -> { path.current().processIssue(issue); - for (Period period : periodsHolder.getPeriods()) { - path.current().processIssue(issue, period); - } - } - } - } - - private void addReliabilityRatingMeasure(Component component, Path path) { - Rating rating = path.current().reliabilityRating.getValue(); - measureRepository.add(component, reliabilityRatingMetric, newMeasureBuilder().create(rating.getIndex(), rating.name())); - } - - private void addSecurityRatingMeasure(Component component, Path path) { - Rating rating = path.current().securityRating.getValue(); - measureRepository.add(component, securityRatingMetric, newMeasureBuilder().create(rating.getIndex(), rating.name())); - } - - private void addNewSecurityRatingMeasure(Component component, Path path) { - java.util.Optional measureVariations = path.current().newSecurityRating.toMeasureVariations(); - if (measureVariations.isPresent()) { - Measure measure = newMeasureBuilder().setVariations(measureVariations.get()).createNoValue(); - measureRepository.add(component, newSecurityRatingMetric, measure); - } + periodsHolder.getPeriods().forEach(period -> path.current().processIssue(issue, period)); + }); } private static void addToParent(Path path) { @@ -181,35 +177,43 @@ public class ReliabilityAndSecurityRatingMeasuresVisitor extends PathAwareVisito } } + private static Measure createRatingMeasure(Rating rating) { + return newMeasureBuilder().create(rating.getIndex(), rating.name()); + } + static final class Counter { - private RatingVariationValue reliabilityRating = new RatingVariationValue(); - private RatingVariationValue securityRating = new RatingVariationValue(); - private RatingVariationValue.Array newSecurityRating = new RatingVariationValue.Array(); + private Map ratingValueByMetric = ImmutableMap.of( + RELIABILITY_RATING_KEY, new RatingVariationValue(), + SECURITY_RATING_KEY, new RatingVariationValue()); + private Map newRatingValueByMetric = ImmutableMap.of( + NEW_RELIABILITY_RATING_KEY, new RatingVariationValue.Array(), + NEW_SECURITY_RATING_KEY, new RatingVariationValue.Array()); private Counter() { // prevents instantiation } void add(Counter otherCounter) { - reliabilityRating.increment(otherCounter.reliabilityRating); - securityRating.increment(otherCounter.securityRating); - newSecurityRating.incrementAll(otherCounter.newSecurityRating); + ratingValueByMetric.entrySet().forEach(e -> e.getValue().increment(otherCounter.ratingValueByMetric.get(e.getKey()))); + newRatingValueByMetric.entrySet().forEach(e -> e.getValue().incrementAll(otherCounter.newRatingValueByMetric.get(e.getKey()))); } void processIssue(Issue issue) { Rating rating = RATING_BY_SEVERITY.get(issue.severity()); if (issue.type().equals(BUG)) { - reliabilityRating.increment(rating); + ratingValueByMetric.get(RELIABILITY_RATING_KEY).increment(rating); } else if (issue.type().equals(VULNERABILITY)) { - securityRating.increment(rating); + ratingValueByMetric.get(SECURITY_RATING_KEY).increment(rating); } } void processIssue(Issue issue, Period period) { if (isOnPeriod((DefaultIssue) issue, period)) { Rating rating = RATING_BY_SEVERITY.get(issue.severity()); - if (issue.type().equals(VULNERABILITY)) { - newSecurityRating.increment(period, rating); + if (issue.type().equals(BUG)) { + newRatingValueByMetric.get(NEW_RELIABILITY_RATING_KEY).increment(period, rating); + } else if (issue.type().equals(VULNERABILITY)) { + newRatingValueByMetric.get(NEW_SECURITY_RATING_KEY).increment(period, rating); } } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForReportTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForReportTest.java index 24bfa5574d1..e56fa5d646b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForReportTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForReportTest.java @@ -44,6 +44,8 @@ import org.sonar.server.computation.task.projectanalysis.period.PeriodsHolderRul import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.api.issue.Issue.RESOLUTION_FIXED; +import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING; +import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.RELIABILITY_RATING; @@ -107,7 +109,8 @@ public class ReliabilityAndSecurityRatingMeasuresVisitorForReportTest { public MetricRepositoryRule metricRepository = new MetricRepositoryRule() .add(RELIABILITY_RATING) .add(SECURITY_RATING) - .add(NEW_SECURITY_RATING); + .add(NEW_SECURITY_RATING) + .add(NEW_RELIABILITY_RATING); @Rule public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); @@ -242,6 +245,31 @@ public class ReliabilityAndSecurityRatingMeasuresVisitorForReportTest { verifyAddedRawMeasure(PROJECT_REF, SECURITY_RATING_KEY, A); } + @Test + public void compute_new_reliability_rating() throws Exception { + treeRootHolder.setRoot(ROOT_PROJECT); + fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, + newBugIssue(10L, MAJOR).setCreationDate(AFTER_LEAK_PERIOD_DATE), + // Should not be taken into account + newBugIssue(1L, MAJOR).setCreationDate(BEFORE_LEAK_PERIOD_DATE), + newVulnerabilityIssue(1L, MAJOR).setCreationDate(AFTER_LEAK_PERIOD_DATE)); + fillComponentIssuesVisitorRule.setIssues(FILE_2_REF, + newBugIssue(2L, CRITICAL).setCreationDate(AFTER_LEAK_PERIOD_DATE), + newBugIssue(3L, MINOR).setCreationDate(AFTER_LEAK_PERIOD_DATE), + // Should not be taken into account + newBugIssue(10L, BLOCKER).setCreationDate(AFTER_LEAK_PERIOD_DATE).setResolution(RESOLUTION_FIXED)); + fillComponentIssuesVisitorRule.setIssues(MODULE_REF, + newBugIssue(7L, BLOCKER).setCreationDate(AFTER_LEAK_PERIOD_DATE)); + + underTest.visit(ROOT_PROJECT); + + verifyAddedRawMeasureOnLeakPeriod(FILE_1_REF, NEW_RELIABILITY_RATING_KEY, C); + verifyAddedRawMeasureOnLeakPeriod(FILE_2_REF, NEW_RELIABILITY_RATING_KEY, D); + verifyAddedRawMeasureOnLeakPeriod(DIRECTORY_REF, NEW_RELIABILITY_RATING_KEY, D); + verifyAddedRawMeasureOnLeakPeriod(MODULE_REF, NEW_RELIABILITY_RATING_KEY, E); + verifyAddedRawMeasureOnLeakPeriod(PROJECT_REF, NEW_RELIABILITY_RATING_KEY, E); + } + @Test public void compute_new_security_rating() throws Exception { treeRootHolder.setRoot(ROOT_PROJECT); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForViewsTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForViewsTest.java index 1a437fc9f04..9c2a1b6fbe1 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForViewsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForViewsTest.java @@ -33,6 +33,7 @@ import org.sonar.server.computation.task.projectanalysis.period.PeriodsHolderRul import org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING; import static org.sonar.api.measures.CoreMetrics.RELIABILITY_RATING; import static org.sonar.api.measures.CoreMetrics.RELIABILITY_RATING_KEY; @@ -85,7 +86,8 @@ public class ReliabilityAndSecurityRatingMeasuresVisitorForViewsTest { public MetricRepositoryRule metricRepository = new MetricRepositoryRule() .add(RELIABILITY_RATING) .add(SECURITY_RATING) - .add(NEW_SECURITY_RATING); + .add(NEW_SECURITY_RATING) + .add(NEW_RELIABILITY_RATING); @Rule public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 72336fdd7c7..538d6ed97bf 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -410,6 +410,8 @@ metric.new_overall_uncovered_conditions.description=New conditions that are not metric.new_overall_uncovered_conditions.name=Overall Uncovered Conditions on New Code metric.new_overall_uncovered_lines.description=New lines that are not covered by any tests metric.new_overall_uncovered_lines.name=Overall Uncovered Lines on New Code +metric.new_reliability_rating.description=Reliability rating on new code +metric.new_reliability_rating.name=Reliability Rating on New Code metric.new_reliability_remediation_effort.description=Reliability remediation effort on new code metric.new_reliability_remediation_effort.name=Reliability Remediation Effort on New Code metric.new_security_rating.description=Security rating on new code diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java index f000227c474..3be47d98602 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java @@ -2302,6 +2302,25 @@ public final class CoreMetrics { .setWorstValue(5.0) .create(); + /** + * @since 6.2 + */ + public static final String NEW_RELIABILITY_RATING_KEY = "new_reliability_rating"; + + /** + * @since 6.2 + */ + public static final Metric NEW_RELIABILITY_RATING = new Metric.Builder(NEW_RELIABILITY_RATING_KEY, "Reliability Rating on New Code", Metric.ValueType.RATING) + .setDescription("Reliability rating on new code") + .setDomain(DOMAIN_RELIABILITY) + .setDirection(Metric.DIRECTION_WORST) + .setDeleteHistoricalData(true) + .setOptimizedBestValue(true) + .setQualitative(true) + .setBestValue(1.0) + .setWorstValue(5.0) + .create(); + // -------------------------------------------------------------------------------------------------------------------- // // SECURITY CHARACTERISTIC -- 2.39.5