]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12962 Compute Security Review measures on Applications
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 3 Feb 2020 13:10:24 +0000 (14:10 +0100)
committerSonarTech <sonartech@sonarsource.com>
Tue, 11 Feb 2020 19:46:12 +0000 (20:46 +0100)
14 files changed:
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewCounter.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitor.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfolios.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplications.java [deleted file]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitorTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitorTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest.java [deleted file]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosTest.java [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityReviewRating.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImpl.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImplTest.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java

index 5c48a1648053873c1918b011158d6df4bb98ee09..806915dded5e3979bf97911a06e6e04d18f156ca 100644 (file)
@@ -30,6 +30,8 @@ import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
 import org.sonar.ce.task.projectanalysis.period.PeriodHolder;
 
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY;
 import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
 import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
@@ -45,6 +47,8 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
   private final AnalysisMetadataHolder analysisMetadataHolder;
   private final Metric newSecurityReviewRatingMetric;
   private final Metric newSecurityHotspotsReviewedMetric;
+  private final Metric newSecurityHotspotsReviewedStatusMetric;
+  private final Metric newSecurityHotspotsToReviewStatusMetric;
 
   public NewSecurityReviewMeasuresVisitor(ComponentIssuesRepository componentIssuesRepository, MeasureRepository measureRepository, PeriodHolder periodHolder,
     AnalysisMetadataHolder analysisMetadataHolder, MetricRepository metricRepository) {
@@ -55,11 +59,19 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
     this.analysisMetadataHolder = analysisMetadataHolder;
     this.newSecurityReviewRatingMetric = metricRepository.getByKey(NEW_SECURITY_REVIEW_RATING_KEY);
     this.newSecurityHotspotsReviewedMetric = metricRepository.getByKey(NEW_SECURITY_HOTSPOTS_REVIEWED_KEY);
+    this.newSecurityHotspotsReviewedStatusMetric = metricRepository.getByKey(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY);
+    this.newSecurityHotspotsToReviewStatusMetric = metricRepository.getByKey(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY);
   }
 
   @Override
   public void visitProject(Component project, Path<SecurityReviewCounter> path) {
     computeMeasure(project, path);
+    if (!periodHolder.hasPeriod() && !analysisMetadataHolder.isPullRequest()) {
+      return;
+    }
+    // The following measures are only computed on projects level as they are required to compute the others measures on applications
+    measureRepository.add(project, newSecurityHotspotsReviewedStatusMetric, Measure.newMeasureBuilder().setVariation(path.current().getHotspotsReviewed()).createNoValue());
+    measureRepository.add(project, newSecurityHotspotsToReviewStatusMetric, Measure.newMeasureBuilder().setVariation(path.current().getHotspotsToReview()).createNoValue());
   }
 
   @Override
@@ -79,10 +91,10 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
     componentIssuesRepository.getIssues(component)
       .stream()
       .filter(issue -> issue.type().equals(SECURITY_HOTSPOT))
-      .filter(issue -> analysisMetadataHolder.isPullRequest() || periodHolder.getPeriod().isOnPeriod(issue.creationDate()) )
+      .filter(issue -> analysisMetadataHolder.isPullRequest() || periodHolder.getPeriod().isOnPeriod(issue.creationDate()))
       .forEach(issue -> path.current().processHotspot(issue));
 
-    Double percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
+    double percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
     measureRepository.add(component, newSecurityHotspotsReviewedMetric, Measure.newMeasureBuilder().setVariation(percent).createNoValue());
     measureRepository.add(component, newSecurityReviewRatingMetric, Measure.newMeasureBuilder().setVariation(computeRating(percent).getIndex()).createNoValue());
 
index 07d35e3a98c1145e25228458bcbf7b11a8a03fcd..d09f039b98d048bada83e7d2aa12a490c98ab9d3 100644 (file)
@@ -25,8 +25,8 @@ import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
 
 final class SecurityReviewCounter {
-  private long hotspotsReviewed;
-  private long hotspotsToReview;
+  private int hotspotsReviewed;
+  private int hotspotsToReview;
 
   SecurityReviewCounter() {
     // prevents instantiation
@@ -45,11 +45,11 @@ final class SecurityReviewCounter {
     hotspotsToReview += otherCounter.hotspotsToReview;
   }
 
-  public long getHotspotsReviewed() {
+  public int getHotspotsReviewed() {
     return hotspotsReviewed;
   }
 
-  public long getHotspotsToReview() {
+  public int getHotspotsToReview() {
     return hotspotsToReview;
   }
 }
index 2bb1fdc3d3fc26b1d7ead0568599d88426b7897d..574d53a74e3c9a5b4dc2af85c181a3e6f4f3a3c8 100644 (file)
@@ -30,6 +30,8 @@ import org.sonar.ce.task.projectanalysis.metric.Metric;
 import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
 
 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY;
 import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
 import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
@@ -43,6 +45,8 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Secur
   private final MeasureRepository measureRepository;
   private final Metric securityReviewRatingMetric;
   private final Metric securityHotspotsReviewedMetric;
+  private final Metric securityHotspotsReviewedStatusMetric;
+  private final Metric securityHotspotsToReviewStatusMetric;
 
   public SecurityReviewMeasuresVisitor(ComponentIssuesRepository componentIssuesRepository, MeasureRepository measureRepository, MetricRepository metricRepository) {
     super(FILE, POST_ORDER, SecurityReviewMeasuresVisitor.CounterFactory.INSTANCE);
@@ -50,11 +54,16 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Secur
     this.measureRepository = measureRepository;
     this.securityReviewRatingMetric = metricRepository.getByKey(SECURITY_REVIEW_RATING_KEY);
     this.securityHotspotsReviewedMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_REVIEWED_KEY);
+    this.securityHotspotsReviewedStatusMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY);
+    this.securityHotspotsToReviewStatusMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY);
   }
 
   @Override
   public void visitProject(Component project, Path<SecurityReviewCounter> path) {
     computeMeasure(project, path);
+    // The following measures are only computed on projects level as they are required to compute the others measures on applications
+    measureRepository.add(project, securityHotspotsReviewedStatusMetric, Measure.newMeasureBuilder().create(path.current().getHotspotsReviewed()));
+    measureRepository.add(project, securityHotspotsToReviewStatusMetric, Measure.newMeasureBuilder().create(path.current().getHotspotsToReview()));
   }
 
   @Override
@@ -73,7 +82,7 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Secur
       .filter(issue -> issue.type().equals(SECURITY_HOTSPOT))
       .forEach(issue -> path.current().processHotspot(issue));
 
-    Double percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
+    double percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
     measureRepository.add(component, securityHotspotsReviewedMetric, Measure.newMeasureBuilder().create(percent, securityHotspotsReviewedMetric.getDecimalScale()));
     measureRepository.add(component, securityReviewRatingMetric, RatingMeasures.get(computeRating(percent)));
 
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfolios.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfolios.java
new file mode 100644 (file)
index 0000000..a88e3d0
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.qualitymodel;
+
+import java.util.Optional;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
+import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter;
+import org.sonar.ce.task.projectanalysis.measure.Measure;
+import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
+import org.sonar.ce.task.projectanalysis.measure.RatingMeasures;
+import org.sonar.ce.task.projectanalysis.metric.Metric;
+import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
+import org.sonar.server.measure.Rating;
+import org.sonar.server.security.SecurityReviewRating;
+
+import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY;
+import static org.sonar.ce.task.projectanalysis.component.ViewAttributes.Type.APPLICATION;
+
+public class SecurityReviewRatingVisitorForPortfolios extends TypeAwareVisitorAdapter {
+
+  private final MeasureRepository measureRepository;
+  private final Metric nclocMetric;
+  private final Metric securityHostspotsMetric;
+  private final Metric securityReviewRatingMetric;
+
+  public SecurityReviewRatingVisitorForPortfolios(MeasureRepository measureRepository, MetricRepository metricRepository) {
+    super(CrawlerDepthLimit.SUBVIEW, Order.POST_ORDER);
+    this.measureRepository = measureRepository;
+    this.nclocMetric = metricRepository.getByKey(NCLOC_KEY);
+    this.securityHostspotsMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_KEY);
+    this.securityReviewRatingMetric = metricRepository.getByKey(SECURITY_REVIEW_RATING_KEY);
+  }
+
+  @Override
+  public void visitView(Component view) {
+    if (view.getViewAttributes().getType().equals(APPLICATION)) {
+      return;
+    }
+    computeMeasure(view);
+  }
+
+  @Override
+  public void visitSubView(Component subView) {
+    computeMeasure(subView);
+  }
+
+  private void computeMeasure(Component component) {
+    Optional<Measure> nclocMeasure = measureRepository.getRawMeasure(component, nclocMetric);
+    Optional<Measure> securityHostspotsMeasure = measureRepository.getRawMeasure(component, securityHostspotsMetric);
+    if (!nclocMeasure.isPresent() || !securityHostspotsMeasure.isPresent()) {
+      return;
+    }
+    int ncloc = nclocMeasure.get().getIntValue();
+    int securityHotspots = securityHostspotsMeasure.get().getIntValue();
+    Rating rating = SecurityReviewRating.computeForPortfolios(ncloc, securityHotspots);
+    measureRepository.add(component, securityReviewRatingMetric, RatingMeasures.get(rating));
+  }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplications.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplications.java
deleted file mode 100644 (file)
index 382606a..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.qualitymodel;
-
-import java.util.Optional;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
-import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter;
-import org.sonar.ce.task.projectanalysis.measure.Measure;
-import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
-import org.sonar.ce.task.projectanalysis.measure.RatingMeasures;
-import org.sonar.ce.task.projectanalysis.metric.Metric;
-import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
-import org.sonar.server.measure.Rating;
-import org.sonar.server.security.SecurityReviewRating;
-
-import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
-import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
-import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY;
-
-public class SecurityReviewRatingVisitorForPortfoliosAndApplications extends TypeAwareVisitorAdapter {
-
-  private final MeasureRepository measureRepository;
-  private final Metric nclocMetric;
-  private final Metric securityHostspotsMetric;
-  private final Metric securityReviewRatingMetric;
-
-  public SecurityReviewRatingVisitorForPortfoliosAndApplications(MeasureRepository measureRepository, MetricRepository metricRepository) {
-    super(CrawlerDepthLimit.SUBVIEW, Order.POST_ORDER);
-    this.measureRepository = measureRepository;
-    this.nclocMetric = metricRepository.getByKey(NCLOC_KEY);
-    this.securityHostspotsMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_KEY);
-    this.securityReviewRatingMetric = metricRepository.getByKey(SECURITY_REVIEW_RATING_KEY);
-  }
-
-  @Override
-  public void visitProject(Component project) {
-    // Do nothing
-  }
-
-  @Override
-  public void visitView(Component view) {
-    computeMeasure(view);
-  }
-
-  @Override
-  public void visitSubView(Component subView) {
-    computeMeasure(subView);
-  }
-
-  private void computeMeasure(Component component) {
-    Optional<Measure> nclocMeasure = measureRepository.getRawMeasure(component, nclocMetric);
-    Optional<Measure> securityHostspotsMeasure = measureRepository.getRawMeasure(component, securityHostspotsMetric);
-    if (!nclocMeasure.isPresent() || !securityHostspotsMeasure.isPresent()) {
-      return;
-    }
-    int ncloc = nclocMeasure.get().getIntValue();
-    int securityHotspots = securityHostspotsMeasure.get().getIntValue();
-    Rating rating = SecurityReviewRating.computeForPortfolios(ncloc, securityHotspots);
-    measureRepository.add(component, securityReviewRatingMetric, RatingMeasures.get(rating));
-  }
-
-}
index 08b85755a8d6d0f27c0fb5521f1d4155f1d1fb8b..c39752eb7752410a039096d5598ec1bfd74a6564 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.ce.task.projectanalysis.qualitymodel;
 import java.util.Arrays;
 import java.util.Date;
 import javax.annotation.Nullable;
+import org.assertj.core.api.Assertions;
 import org.assertj.core.data.Offset;
 import org.junit.Rule;
 import org.junit.Test;
@@ -34,7 +35,6 @@ import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
 import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler;
 import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepositoryRule;
 import org.sonar.ce.task.projectanalysis.issue.FillComponentIssuesVisitorRule;
-import org.sonar.ce.task.projectanalysis.measure.MeasureAssert;
 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
 import org.sonar.ce.task.projectanalysis.period.Period;
@@ -52,6 +52,10 @@ import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY;
 import static org.sonar.api.rule.Severity.MAJOR;
@@ -59,6 +63,7 @@ import static org.sonar.api.rule.Severity.MINOR;
 import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
 import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.ce.task.projectanalysis.measure.MeasureAssert.assertThat;
 import static org.sonar.server.measure.Rating.A;
 import static org.sonar.server.measure.Rating.B;
 import static org.sonar.server.measure.Rating.C;
@@ -99,7 +104,9 @@ public class NewSecurityReviewMeasuresVisitorTest {
   @Rule
   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
     .add(NEW_SECURITY_REVIEW_RATING)
-    .add(NEW_SECURITY_HOTSPOTS_REVIEWED);
+    .add(NEW_SECURITY_HOTSPOTS_REVIEWED)
+    .add(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS)
+    .add(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
   @Rule
   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
   @Rule
@@ -131,11 +138,11 @@ public class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, A, 100.0);
-    verifyMeasures(FILE_2_REF, A, 100.0);
-    verifyMeasures(DIRECTORY_REF, A, 100.0);
-    verifyMeasures(ROOT_DIR_REF, A, 100.0);
-    verifyMeasures(PROJECT_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
   }
 
   @Test
@@ -160,11 +167,11 @@ public class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, A, 100.0);
-    verifyMeasures(FILE_2_REF, A, 80.0);
-    verifyMeasures(DIRECTORY_REF, A, 87.5);
-    verifyMeasures(ROOT_DIR_REF, A, 87.5);
-    verifyMeasures(PROJECT_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, A, 80.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 87.5);
   }
 
   @Test
@@ -189,11 +196,11 @@ public class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, A, 100.0);
-    verifyMeasures(FILE_2_REF, B, 71.42);
-    verifyMeasures(DIRECTORY_REF, B, 75.0);
-    verifyMeasures(ROOT_DIR_REF, B, 75.0);
-    verifyMeasures(PROJECT_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, B, 71.42);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, B, 75.0);
   }
 
   @Test
@@ -217,11 +224,11 @@ public class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, C, 50.0);
-    verifyMeasures(FILE_2_REF, C, 60.0);
-    verifyMeasures(DIRECTORY_REF, C, 57.14);
-    verifyMeasures(ROOT_DIR_REF, C, 57.14);
-    verifyMeasures(PROJECT_REF, C, 57.14);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, C, 60.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 57.14);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 57.14);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, C, 57.14);
   }
 
   @Test
@@ -246,11 +253,11 @@ public class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, D, 33.33);
-    verifyMeasures(FILE_2_REF, D, 40.0);
-    verifyMeasures(DIRECTORY_REF, D, 37.5);
-    verifyMeasures(ROOT_DIR_REF, D, 37.5);
-    verifyMeasures(PROJECT_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.33);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, D, 40.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, D, 37.5);
   }
 
   @Test
@@ -273,11 +280,11 @@ public class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, D, 33.33);
-    verifyMeasures(FILE_2_REF, E, 0.0);
-    verifyMeasures(DIRECTORY_REF, E, 16.66);
-    verifyMeasures(ROOT_DIR_REF, E, 16.66);
-    verifyMeasures(PROJECT_REF, E, 16.66);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.33);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, E, 0.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, 16.66);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, 16.66);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, E, 16.66);
   }
 
   @Test
@@ -290,7 +297,7 @@ public class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(PROJECT_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
   }
 
   @Test
@@ -317,11 +324,45 @@ public class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, C, 50.0);
-    verifyMeasures(FILE_2_REF, C, 57.14);
-    verifyMeasures(DIRECTORY_REF, C, 55.55);
-    verifyMeasures(ROOT_DIR_REF, C, 55.55);
-    verifyMeasures(PROJECT_REF, C, 55.55);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, C, 57.14);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 55.55);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 55.55);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, C, 55.55);
+  }
+
+  @Test
+  public void compute_status_related_measures() {
+    treeRootHolder.setRoot(ROOT_PROJECT);
+    fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
+      newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
+      newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
+      // Should not be taken into account
+      newIssue());
+    fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
+      newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
+      newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
+      newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
+      newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
+      newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
+      newIssue());
+
+    underTest.visit(ROOT_PROJECT);
+
+    verifyHotspotStatusMeasures(FILE_1_REF, null, null);
+    verifyHotspotStatusMeasures(FILE_2_REF, null, null);
+    verifyHotspotStatusMeasures(DIRECTORY_REF, null, null);
+    verifyHotspotStatusMeasures(ROOT_DIR_REF, null, null);
+    verifyHotspotStatusMeasures(PROJECT_REF, 4, 3);
+  }
+
+  @Test
+  public void compute_0_status_related_measures_when_no_hotspot() {
+    treeRootHolder.setRoot(ROOT_PROJECT);
+
+    underTest.visit(ROOT_PROJECT);
+
+    verifyHotspotStatusMeasures(PROJECT_REF, 0, 0);
   }
 
   @Test
@@ -337,12 +378,25 @@ public class NewSecurityReviewMeasuresVisitorTest {
     assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).values()).isEmpty();
   }
 
-  private void verifyMeasures(int componentRef, Rating expectedReviewRating, double expectedHotspotsReviewed) {
-    MeasureAssert.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_REVIEW_RATING_KEY)).hasVariation(expectedReviewRating.getIndex());
-    MeasureAssert.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY)).hasVariation(expectedHotspotsReviewed,
+  private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, double expectedHotspotsReviewed) {
+    assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_REVIEW_RATING_KEY)).hasVariation(expectedReviewRating.getIndex());
+    assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY)).hasVariation(expectedHotspotsReviewed,
       VARIATION_COMPARISON_OFFSET);
   }
 
+  private void verifyHotspotStatusMeasures(int componentRef, @Nullable Integer hotspotsReviewed, @Nullable Integer hotspotsToReview) {
+    if (hotspotsReviewed == null) {
+      Assertions.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).isEmpty();
+    } else {
+      assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).hasVariation(hotspotsReviewed);
+    }
+    if (hotspotsReviewed == null) {
+      Assertions.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).isEmpty();
+    } else {
+      assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).hasVariation(hotspotsToReview);
+    }
+  }
+
   private static DefaultIssue newHotspot(String status, @Nullable String resolution) {
     return new DefaultIssue()
       .setKey(Uuids.create())
index 7e259ea5998d40ff2dd8e5ab858e36961320a23d..9780f4e72f340d721ae76459559fc43035001bd7 100644 (file)
@@ -41,6 +41,10 @@ import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
 import static org.sonar.api.issue.Issue.RESOLUTION_SAFE;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY;
 import static org.sonar.api.rule.Severity.MAJOR;
@@ -81,7 +85,9 @@ public class SecurityReviewMeasuresVisitorTest {
   @Rule
   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
     .add(SECURITY_REVIEW_RATING)
-    .add(SECURITY_HOTSPOTS_REVIEWED);
+    .add(SECURITY_HOTSPOTS_REVIEWED)
+    .add(SECURITY_HOTSPOTS_REVIEWED_STATUS)
+    .add(SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
   @Rule
   public ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder);
   @Rule
@@ -93,7 +99,7 @@ public class SecurityReviewMeasuresVisitorTest {
     new SecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, metricRepository)));
 
   @Test
-  public void compute_measures_when_100_percent_hotspots_reviewed() {
+  public void compute_rating_and_reviewed_measures_when_100_percent_hotspots_reviewed() {
     treeRootHolder.setRoot(ROOT_PROJECT);
     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
@@ -106,15 +112,15 @@ public class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, A, 100.0);
-    verifyMeasures(FILE_2_REF, A, 100.0);
-    verifyMeasures(DIRECTORY_REF, A, 100.0);
-    verifyMeasures(ROOT_DIR_REF, A, 100.0);
-    verifyMeasures(PROJECT_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
   }
 
   @Test
-  public void compute_measures_when_more_than_80_percent_hotspots_reviewed() {
+  public void compute_rating_and_reviewed__measures_when_more_than_80_percent_hotspots_reviewed() {
     treeRootHolder.setRoot(ROOT_PROJECT);
     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
@@ -132,15 +138,15 @@ public class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, A, 100.0);
-    verifyMeasures(FILE_2_REF, A, 80.0);
-    verifyMeasures(DIRECTORY_REF, A, 87.5);
-    verifyMeasures(ROOT_DIR_REF, A, 87.5);
-    verifyMeasures(PROJECT_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, A, 80.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 87.5);
   }
 
   @Test
-  public void compute_measures_when_more_than_70_percent_hotspots_reviewed() {
+  public void compute_rating_and_reviewed__measures_when_more_than_70_percent_hotspots_reviewed() {
     treeRootHolder.setRoot(ROOT_PROJECT);
     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
@@ -158,15 +164,15 @@ public class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, A, 100.0);
-    verifyMeasures(FILE_2_REF, B, 71.4);
-    verifyMeasures(DIRECTORY_REF, B, 75.0);
-    verifyMeasures(ROOT_DIR_REF, B, 75.0);
-    verifyMeasures(PROJECT_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, B, 71.4);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, B, 75.0);
   }
 
   @Test
-  public void compute_measures_when_more_than_50_percent_hotspots_reviewed() {
+  public void compute_rating_and_reviewed__measures_when_more_than_50_percent_hotspots_reviewed() {
     treeRootHolder.setRoot(ROOT_PROJECT);
     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
       newHotspot(STATUS_TO_REVIEW, null),
@@ -183,15 +189,15 @@ public class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, C, 50.0);
-    verifyMeasures(FILE_2_REF, C, 60.0);
-    verifyMeasures(DIRECTORY_REF, C, 57.1);
-    verifyMeasures(ROOT_DIR_REF, C, 57.1);
-    verifyMeasures(PROJECT_REF, C, 57.1);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, C, 60.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 57.1);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 57.1);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, C, 57.1);
   }
 
   @Test
-  public void compute_measures_when_more_30_than_percent_hotspots_reviewed() {
+  public void compute_rating_and_reviewed__measures_when_more_30_than_percent_hotspots_reviewed() {
     treeRootHolder.setRoot(ROOT_PROJECT);
     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
       newHotspot(STATUS_TO_REVIEW, null),
@@ -209,15 +215,15 @@ public class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, D, 33.3);
-    verifyMeasures(FILE_2_REF, D, 40.0);
-    verifyMeasures(DIRECTORY_REF, D, 37.5);
-    verifyMeasures(ROOT_DIR_REF, D, 37.5);
-    verifyMeasures(PROJECT_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.3);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, D, 40.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, D, 37.5);
   }
 
   @Test
-  public void compute_measures_when_less_than_30_percent_hotspots_reviewed() {
+  public void compute_rating_and_reviewed__measures_when_less_than_30_percent_hotspots_reviewed() {
     treeRootHolder.setRoot(ROOT_PROJECT);
     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
       newHotspot(STATUS_TO_REVIEW, null),
@@ -233,11 +239,11 @@ public class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(FILE_1_REF, D, 33.3);
-    verifyMeasures(FILE_2_REF, E, 0.0);
-    verifyMeasures(DIRECTORY_REF, E, 16.7);
-    verifyMeasures(ROOT_DIR_REF, E, 16.7);
-    verifyMeasures(PROJECT_REF, E, 16.7);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.3);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, E, 0.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, 16.7);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, 16.7);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, E, 16.7);
   }
 
   @Test
@@ -246,10 +252,44 @@ public class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyMeasures(PROJECT_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
   }
 
-  private void verifyMeasures(int componentRef, Rating expectedReviewRating, double expectedHotspotsReviewed) {
+  @Test
+  public void compute_status_related_measures() {
+    treeRootHolder.setRoot(ROOT_PROJECT);
+    fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
+      newHotspot(STATUS_TO_REVIEW, null),
+      newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
+      // Should not be taken into account
+      newIssue());
+    fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
+      newHotspot(STATUS_TO_REVIEW, null),
+      newHotspot(STATUS_TO_REVIEW, null),
+      newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
+      newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
+      newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
+      newIssue());
+
+    underTest.visit(ROOT_PROJECT);
+
+    verifyHotspotStatusMeasures(FILE_1_REF, null, null);
+    verifyHotspotStatusMeasures(FILE_2_REF, null, null);
+    verifyHotspotStatusMeasures(DIRECTORY_REF, null, null);
+    verifyHotspotStatusMeasures(ROOT_DIR_REF, null, null);
+    verifyHotspotStatusMeasures(PROJECT_REF, 4, 3);
+  }
+
+  @Test
+  public void compute_0_status_related_measures_when_no_hotspot() {
+    treeRootHolder.setRoot(ROOT_PROJECT);
+
+    underTest.visit(ROOT_PROJECT);
+
+    verifyHotspotStatusMeasures(PROJECT_REF, 0, 0);
+  }
+
+  private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, double expectedHotspotsReviewed) {
     verifySecurityReviewRating(componentRef, expectedReviewRating);
     verifySecurityHotspotsReviewed(componentRef, expectedHotspotsReviewed);
   }
@@ -264,6 +304,19 @@ public class SecurityReviewMeasuresVisitorTest {
     assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_KEY).get().getDoubleValue()).isEqualTo(percent);
   }
 
+  private void verifyHotspotStatusMeasures(int componentRef, @Nullable Integer hotspotsReviewed, @Nullable Integer hotspotsToReview) {
+    if (hotspotsReviewed == null){
+      assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).isEmpty();
+    } else {
+      assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsReviewed);
+    }
+    if (hotspotsReviewed == null){
+      assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).isEmpty();
+    } else {
+      assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsToReview);
+    }
+  }
+
   private static DefaultIssue newHotspot(String status, @Nullable String resolution) {
     return new DefaultIssue()
       .setKey(Uuids.create())
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest.java
deleted file mode 100644 (file)
index a55e2db..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.qualitymodel;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.ce.task.projectanalysis.component.ViewAttributes;
-import org.sonar.ce.task.projectanalysis.component.ViewsComponent;
-import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler;
-import org.sonar.ce.task.projectanalysis.measure.Measure;
-import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
-import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
-
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.api.measures.CoreMetrics.NCLOC;
-import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
-import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS;
-import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
-import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING;
-import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY;
-import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
-import static org.sonar.server.measure.Rating.B;
-import static org.sonar.server.measure.Rating.C;
-
-public class SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest {
-
-  private static final int PORTFOLIO_REF = 10;
-  private static final int SUB_PORTFOLIO_1_REF = 11;
-  private static final int SUB_PORTFOLIO_2_REF = 12;
-  private static final Component PORTFOLIO = ViewsComponent.builder(Component.Type.VIEW, Integer.toString(PORTFOLIO_REF))
-    .addChildren(
-      ViewsComponent.builder(Component.Type.SUBVIEW, Integer.toString(SUB_PORTFOLIO_1_REF)).build(),
-      ViewsComponent.builder(Component.Type.SUBVIEW, Integer.toString(SUB_PORTFOLIO_2_REF)).build())
-    .build();
-
-  private static final int APPLICATION_REF = 20;
-  private static final Component APPLICATION = ViewsComponent.builder(Component.Type.VIEW, Integer.toString(APPLICATION_REF))
-    .setViewAttributes(new ViewAttributes(ViewAttributes.Type.APPLICATION))
-    .build();
-
-  @Rule
-  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
-  @Rule
-  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
-    .add(NCLOC)
-    .add(SECURITY_HOTSPOTS)
-    .add(SECURITY_REVIEW_RATING);
-
-  @Rule
-  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
-
-  private VisitorsCrawler underTest = new VisitorsCrawler(singletonList(new SecurityReviewRatingVisitorForPortfoliosAndApplications(measureRepository, metricRepository)));
-
-  @Test
-  public void compute_security_review_rating_on_portfolio() {
-    treeRootHolder.setRoot(PORTFOLIO);
-    measureRepository.addRawMeasure(PORTFOLIO_REF, NCLOC_KEY, newMeasureBuilder().create(2000));
-    measureRepository.addRawMeasure(PORTFOLIO_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(20));
-    measureRepository.addRawMeasure(SUB_PORTFOLIO_1_REF, NCLOC_KEY, newMeasureBuilder().create(1000));
-    measureRepository.addRawMeasure(SUB_PORTFOLIO_1_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(5));
-    measureRepository.addRawMeasure(SUB_PORTFOLIO_2_REF, NCLOC_KEY, newMeasureBuilder().create(1000));
-    measureRepository.addRawMeasure(SUB_PORTFOLIO_2_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(15));
-
-    underTest.visit(PORTFOLIO);
-
-    assertThat(measureRepository.getAddedRawMeasure(SUB_PORTFOLIO_1_REF, SECURITY_REVIEW_RATING_KEY).get().getIntValue()).isEqualTo(B.getIndex());
-    assertThat(measureRepository.getAddedRawMeasure(SUB_PORTFOLIO_2_REF, SECURITY_REVIEW_RATING_KEY).get().getIntValue()).isEqualTo(C.getIndex());
-    assertThat(measureRepository.getAddedRawMeasure(PORTFOLIO_REF, SECURITY_REVIEW_RATING_KEY).get().getIntValue()).isEqualTo(B.getIndex());
-  }
-
-  @Test
-  public void compute_security_review_rating_on_application() {
-    treeRootHolder.setRoot(APPLICATION);
-    measureRepository.addRawMeasure(APPLICATION_REF, NCLOC_KEY, newMeasureBuilder().create(1000));
-    measureRepository.addRawMeasure(APPLICATION_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(12));
-
-    underTest.visit(APPLICATION);
-
-    Measure measure = measureRepository.getAddedRawMeasure(APPLICATION_REF, SECURITY_REVIEW_RATING_KEY).get();
-    assertThat(measure.getIntValue()).isEqualTo(C.getIndex());
-    assertThat(measure.getData()).isEqualTo(C.name());
-  }
-
-  @Test
-  public void compute_nothing_when_no_ncloc() {
-    treeRootHolder.setRoot(PORTFOLIO);
-    measureRepository.addRawMeasure(PORTFOLIO_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(2));
-
-    underTest.visit(PORTFOLIO);
-
-    assertThat(measureRepository.getAddedRawMeasure(PORTFOLIO_REF, SECURITY_REVIEW_RATING_KEY)).isEmpty();
-  }
-
-  @Test
-  public void compute_nothing_when_no_security_hotspot() {
-    treeRootHolder.setRoot(PORTFOLIO);
-    measureRepository.addRawMeasure(PORTFOLIO_REF, NCLOC_KEY, newMeasureBuilder().create(1000));
-
-    underTest.visit(PORTFOLIO);
-
-    assertThat(measureRepository.getAddedRawMeasure(PORTFOLIO_REF, SECURITY_REVIEW_RATING_KEY)).isEmpty();
-  }
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosTest.java
new file mode 100644 (file)
index 0000000..099113d
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.qualitymodel;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.component.ViewAttributes;
+import org.sonar.ce.task.projectanalysis.component.ViewsComponent;
+import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler;
+import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
+import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.measures.CoreMetrics.NCLOC;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY;
+import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
+import static org.sonar.server.measure.Rating.B;
+import static org.sonar.server.measure.Rating.C;
+
+public class SecurityReviewRatingVisitorForPortfoliosTest {
+
+  private static final int PORTFOLIO_REF = 10;
+  private static final int SUB_PORTFOLIO_1_REF = 11;
+  private static final int SUB_PORTFOLIO_2_REF = 12;
+  private static final Component PORTFOLIO = ViewsComponent.builder(Component.Type.VIEW, Integer.toString(PORTFOLIO_REF))
+    .setViewAttributes(new ViewAttributes(ViewAttributes.Type.PORTFOLIO))
+    .addChildren(
+      ViewsComponent.builder(Component.Type.SUBVIEW, Integer.toString(SUB_PORTFOLIO_1_REF)).build(),
+      ViewsComponent.builder(Component.Type.SUBVIEW, Integer.toString(SUB_PORTFOLIO_2_REF)).build())
+    .build();
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+  @Rule
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+    .add(NCLOC)
+    .add(SECURITY_HOTSPOTS)
+    .add(SECURITY_REVIEW_RATING);
+
+  @Rule
+  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+  private VisitorsCrawler underTest = new VisitorsCrawler(singletonList(new SecurityReviewRatingVisitorForPortfolios(measureRepository, metricRepository)));
+
+  @Test
+  public void compute_security_review_rating_on_portfolio() {
+    treeRootHolder.setRoot(PORTFOLIO);
+    measureRepository.addRawMeasure(PORTFOLIO_REF, NCLOC_KEY, newMeasureBuilder().create(2000));
+    measureRepository.addRawMeasure(PORTFOLIO_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(20));
+    measureRepository.addRawMeasure(SUB_PORTFOLIO_1_REF, NCLOC_KEY, newMeasureBuilder().create(1000));
+    measureRepository.addRawMeasure(SUB_PORTFOLIO_1_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(5));
+    measureRepository.addRawMeasure(SUB_PORTFOLIO_2_REF, NCLOC_KEY, newMeasureBuilder().create(1000));
+    measureRepository.addRawMeasure(SUB_PORTFOLIO_2_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(15));
+
+    underTest.visit(PORTFOLIO);
+
+    assertThat(measureRepository.getAddedRawMeasure(SUB_PORTFOLIO_1_REF, SECURITY_REVIEW_RATING_KEY).get().getIntValue()).isEqualTo(B.getIndex());
+    assertThat(measureRepository.getAddedRawMeasure(SUB_PORTFOLIO_2_REF, SECURITY_REVIEW_RATING_KEY).get().getIntValue()).isEqualTo(C.getIndex());
+    assertThat(measureRepository.getAddedRawMeasure(PORTFOLIO_REF, SECURITY_REVIEW_RATING_KEY).get().getIntValue()).isEqualTo(B.getIndex());
+  }
+
+  @Test
+  public void compute_nothing_when_no_ncloc() {
+    treeRootHolder.setRoot(PORTFOLIO);
+    measureRepository.addRawMeasure(PORTFOLIO_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(2));
+
+    underTest.visit(PORTFOLIO);
+
+    assertThat(measureRepository.getAddedRawMeasure(PORTFOLIO_REF, SECURITY_REVIEW_RATING_KEY)).isEmpty();
+  }
+
+  @Test
+  public void compute_nothing_when_no_security_hotspot() {
+    treeRootHolder.setRoot(PORTFOLIO);
+    measureRepository.addRawMeasure(PORTFOLIO_REF, NCLOC_KEY, newMeasureBuilder().create(1000));
+
+    underTest.visit(PORTFOLIO);
+
+    assertThat(measureRepository.getAddedRawMeasure(PORTFOLIO_REF, SECURITY_REVIEW_RATING_KEY)).isEmpty();
+  }
+}
index 6a5d2673eb3111cacad5027c496b571709bbc2fd..f96839250fc726384fbee7db38687f10bc262201 100644 (file)
@@ -55,7 +55,7 @@ public class SecurityReviewRating {
     }
   }
 
-  public static Double computePercent(long hotspotsToReview, long hotspotsReviewed) {
+  public static double computePercent(long hotspotsToReview, long hotspotsReviewed) {
     long total = hotspotsToReview + hotspotsReviewed;
     if (total == 0) {
       return 100.0;
index f65b6bad7f04dabfef2dd8dfc5cd09fc1ff27d63..d4a63507f338466895bfe704d12206851f229149 100644 (file)
@@ -117,6 +117,12 @@ public class IssueMetricFormulaFactoryImpl implements IssueMetricFormulaFactory
       (context, issues) -> context
         .setValue(computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false)))),
 
+    new IssueMetricFormula(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, false,
+      (context, issues) -> context.setValue(issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false))),
+
+    new IssueMetricFormula(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, false,
+      (context, issues) -> context.setValue(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false))),
+
     new IssueMetricFormula(CoreMetrics.NEW_CODE_SMELLS, true,
       (context, issues) -> context.setLeakValue(issues.countUnresolvedByType(RuleType.CODE_SMELL, true))),
 
@@ -180,6 +186,12 @@ public class IssueMetricFormulaFactoryImpl implements IssueMetricFormulaFactory
         context.setLeakValue(percent);
       }),
 
+    new IssueMetricFormula(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, true,
+      (context, issues) -> context.setLeakValue(issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, true))),
+
+    new IssueMetricFormula(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, true,
+      (context, issues) -> context.setLeakValue(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, true))),
+
     new IssueMetricFormula(CoreMetrics.NEW_SQALE_DEBT_RATIO, true,
       (context, issues) -> context.setLeakValue(100.0 * newDebtDensity(context)),
       asList(CoreMetrics.NEW_TECHNICAL_DEBT, CoreMetrics.NEW_DEVELOPMENT_COST)),
index f23aa1c8cc12d34c28795dca871b8cb13aa0f6f4..7f92edb76e087c1c12967f0b89e9fa9ab12b8f81 100644 (file)
@@ -139,12 +139,34 @@ public class IssueMetricFormulaFactoryImplTest {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
-        .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, 75.0);
+      .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, 75.0);
 
     withNoIssues()
       .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, 100.0);
   }
 
+  @Test
+  public void test_security_hotspots_reviewed_status() {
+    with(
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
+      .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);
+
+    withNoIssues()
+      .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, 0.0);
+  }
+
+  @Test
+  public void test_security_hotspots_to_review_status() {
+    with(
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
+      .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);
+
+    withNoIssues()
+      .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 0.0);
+  }
+
   @Test
   public void count_unresolved_by_severity() {
     withNoIssues()
@@ -678,6 +700,32 @@ public class IssueMetricFormulaFactoryImplTest {
       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED, 100.0);
   }
 
+  @Test
+  public void test_new_security_hotspots_reviewed_status() {
+    with(
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
+      // not in leak
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);
+
+    withNoIssues()
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 0.0);
+  }
+
+  @Test
+  public void test_new_security_hotspots_to_review_status() {
+    with(
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
+      // not in leak
+      newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);
+
+    withNoIssues()
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 0.0);
+  }
+
   @Test
   public void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
     withNoIssues()
index 45e6844cc53c040a3f6a172cd9e8c99c85577328..a45aafd632149e4a55b48293a4f19a4dfa30829b 100644 (file)
@@ -2115,11 +2115,13 @@ metric.new_security_hotspots.short_name=Security Hotspots
 metric.new_security_hotspots_reviewed.description=Percentage of Security Hotspots Reviewed on New Code
 metric.new_security_hotspots_reviewed.name=Security Hotspots Reviewed on New Code
 metric.new_security_hotspots_reviewed.short_name=Security Hotspots Reviewed
+metric.new_security_hotspots_reviewed_status.description=Security Review Reviewed Status on New Code
+metric.new_security_hotspots_reviewed_status.name=Security Review Reviewed Status on New Code
+metric.new_security_hotspots_to_review_status.description=Security Review To Review Status on New Code
+metric.new_security_hotspots_to_review_status.name=Security Review To Review Status on New Code
 metric.new_security_rating.description=Security rating on new code
 metric.new_security_rating.name=Security Rating on New Code
 metric.new_security_rating.extra_short_name=Rating
-metric.new_security_review_rating.description=Security Review rating on new code
-metric.new_security_review_rating.name=Security Review Rating on New Code
 metric.new_security_remediation_effort.description=Security remediation effort on new code
 metric.new_security_remediation_effort.name=Security Remediation Effort on New Code
 metric.new_security_remediation_effort.extra_short_name=Remediation Effort
index 2f2e58de4c3fb76890437dfd634f593458035bab..4921e34a853c1d4bc80a9da7b329ba202b413a31 100644 (file)
@@ -1590,6 +1590,94 @@ public final class CoreMetrics {
       .setBestValue(100.0)
       .create();
 
+  /**
+   * @since 8.2
+   */
+  public static final String SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY = "security_hotspots_reviewed_status";
+
+  /**
+   * @since 8.2
+   *
+   * This hidden metric is only needed to compute 'security_review_rating' and 'security_hotspots_reviewed' on Applications.
+   */
+  public static final Metric<Integer> SECURITY_HOTSPOTS_REVIEWED_STATUS = new Metric.Builder(SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY, "Security Review Reviewed Status",
+    Metric.ValueType.INT)
+      .setDescription("Security Review Reviewed Status")
+      .setDirection(Metric.DIRECTION_WORST)
+      .setQualitative(false)
+      .setDomain(DOMAIN_SECURITY_REVIEW)
+      .setBestValue(0.0)
+      .setOptimizedBestValue(true)
+      .setDeleteHistoricalData(true)
+      .setHidden(true)
+      .create();
+
+  /**
+   * @since 8.2
+   */
+  public static final String SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY = "security_hotspots_to_review_status";
+
+  /**
+   * @since 8.2
+   *
+   * This hidden metric is only needed to compute 'security_review_rating' and 'security_hotspots_reviewed' on Applications.
+   */
+  public static final Metric<Integer> SECURITY_HOTSPOTS_TO_REVIEW_STATUS = new Metric.Builder(SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY, "Security Review To Review Status",
+    Metric.ValueType.INT)
+      .setDescription("Security Review To Review Status")
+      .setDirection(Metric.DIRECTION_WORST)
+      .setQualitative(false)
+      .setDomain(DOMAIN_SECURITY_REVIEW)
+      .setBestValue(0.0)
+      .setOptimizedBestValue(true)
+      .setDeleteHistoricalData(true)
+      .setHidden(true)
+      .create();
+
+  /**
+   * @since 8.2
+   */
+  public static final String NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY = "new_security_hotspots_reviewed_status";
+
+  /**
+   * @since 8.2
+   *
+   * This hidden metric is only needed to compute 'new_security_review_rating' and 'new_security_hotspots_reviewed' on Applications.
+   */
+  public static final Metric<Integer> NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS = new Metric.Builder(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY,
+    "Security Review Reviewed Status on New Code", Metric.ValueType.INT)
+      .setDescription("Security Review Reviewed Status on New Code")
+      .setDirection(Metric.DIRECTION_WORST)
+      .setQualitative(false)
+      .setDomain(DOMAIN_SECURITY_REVIEW)
+      .setBestValue(0.0)
+      .setOptimizedBestValue(true)
+      .setDeleteHistoricalData(true)
+      .setHidden(true)
+      .create();
+
+  /**
+   * @since 8.2
+   */
+  public static final String NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY = "new_security_hotspots_to_review_status";
+
+  /**
+   * @since 8.2
+   *
+   * This hidden metric is only needed to compute 'new_security_review_rating' and 'new_security_hotspots_reviewed' on Applications.
+   */
+  public static final Metric<Integer> NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS = new Metric.Builder(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY,
+    "Security Review To Review Status on New Code", Metric.ValueType.INT)
+      .setDescription("Security Review To Review Status on New Code")
+      .setDirection(Metric.DIRECTION_WORST)
+      .setQualitative(false)
+      .setDomain(DOMAIN_SECURITY_REVIEW)
+      .setBestValue(0.0)
+      .setOptimizedBestValue(true)
+      .setDeleteHistoricalData(true)
+      .setHidden(true)
+      .create();
+
   // --------------------------------------------------------------------------------------------------------------------
   //
   // FILE DATA