]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7782 Compute Reliability Rating on New Code
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 30 Sep 2016 09:27:24 +0000 (11:27 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 3 Oct 2016 16:19:37 +0000 (18:19 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitor.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForReportTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorForViewsTest.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java

index d95f7a231dc6145d56c986fcba0a9fd0f9ed6188..a46218e8d1c0c11adbff3708281896bf07738d02 100644 (file)
@@ -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<ReliabilityAndSecurityRatingMeasuresVisitor.Counter> {
@@ -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<String, Metric> 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<Counter> path) {
-    Optional<Measure> reliabilityRatingMeasure = measureRepository.getRawMeasure(projectView, reliabilityRatingMetric);
-    if (reliabilityRatingMeasure.isPresent()) {
-      path.parent().reliabilityRating.increment(valueOf(reliabilityRatingMeasure.get().getData()));
-    }
-    Optional<Measure> securityRatingMeasure = measureRepository.getRawMeasure(projectView, securityRatingMetric);
-    if (securityRatingMeasure.isPresent()) {
-      path.parent().securityRating.increment(valueOf(securityRatingMeasure.get().getData()));
-    }
+    path.parent().ratingValueByMetric.entrySet().forEach(entry -> {
+      Optional<Measure> ratingMeasure = measureRepository.getRawMeasure(projectView, metricsByKey.get(entry.getKey()));
+      if (ratingMeasure.isPresent()) {
+        entry.getValue().increment(valueOf(ratingMeasure.get().getData()));
+      }
+    });
   }
 
   private void computeAndSaveMeasures(Component component, Path<Counter> 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<Counter> 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<Counter> path) {
-    Rating rating = path.current().reliabilityRating.getValue();
-    measureRepository.add(component, reliabilityRatingMetric, newMeasureBuilder().create(rating.getIndex(), rating.name()));
-  }
-
-  private void addSecurityRatingMeasure(Component component, Path<Counter> path) {
-    Rating rating = path.current().securityRating.getValue();
-    measureRepository.add(component, securityRatingMetric, newMeasureBuilder().create(rating.getIndex(), rating.name()));
-  }
-
-  private void addNewSecurityRatingMeasure(Component component, Path<Counter> path) {
-    java.util.Optional<MeasureVariations> 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<Counter> 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<String, RatingVariationValue> ratingValueByMetric = ImmutableMap.of(
+      RELIABILITY_RATING_KEY, new RatingVariationValue(),
+      SECURITY_RATING_KEY, new RatingVariationValue());
+    private Map<String, RatingVariationValue.Array> 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);
         }
       }
     }
index 24bfa5574d125949bdfa2f3cf270e4d00b2f3023..e56fa5d646be22ebbd5fea9ab24a31f3dfff07a2 100644 (file)
@@ -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);
index 1a437fc9f048fec4e518841d934b3352832a142a..9c2a1b6fbe1b1a49a03546f0f5c1c40e00d91209 100644 (file)
@@ -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);
index 72336fdd7c7f76099ae6e36a371bd6eb50c7cf97..538d6ed97bf297c7971bc229f87844840f09acb7 100644 (file)
@@ -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
index f000227c474afae095914686f0a424b591dbdb99..3be47d98602c9bd23d3a428866cc63eb925d68ee 100644 (file)
@@ -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<Integer> 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