From c51fa2468cfe46d85b86d8121de84cd0a06fed89 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 3 Feb 2020 14:10:24 +0100 Subject: [PATCH] SONAR-12962 Compute Security Review measures on Applications --- .../NewSecurityReviewMeasuresVisitor.java | 16 ++- .../qualitymodel/SecurityReviewCounter.java | 8 +- .../SecurityReviewMeasuresVisitor.java | 11 +- ...rityReviewRatingVisitorForPortfolios.java} | 13 +- .../NewSecurityReviewMeasuresVisitorTest.java | 136 ++++++++++++------ .../SecurityReviewMeasuresVisitorTest.java | 131 ++++++++++++----- ...ReviewRatingVisitorForPortfoliosTest.java} | 24 +--- .../server/security/SecurityReviewRating.java | 2 +- .../live/IssueMetricFormulaFactoryImpl.java | 12 ++ .../IssueMetricFormulaFactoryImplTest.java | 50 ++++++- .../resources/org/sonar/l10n/core.properties | 6 +- .../org/sonar/api/measures/CoreMetrics.java | 88 ++++++++++++ 12 files changed, 378 insertions(+), 119 deletions(-) rename server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/{SecurityReviewRatingVisitorForPortfoliosAndApplications.java => SecurityReviewRatingVisitorForPortfolios.java} (88%) rename server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/{SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest.java => SecurityReviewRatingVisitorForPortfoliosTest.java} (81%) diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java index 5c48a164805..806915dded5 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java @@ -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 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 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()); diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewCounter.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewCounter.java index 07d35e3a98c..d09f039b98d 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewCounter.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewCounter.java @@ -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; } } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitor.java index 2bb1fdc3d3f..574d53a74e3 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitor.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitor.java @@ -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 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 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/SecurityReviewRatingVisitorForPortfoliosAndApplications.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfolios.java similarity index 88% rename from server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplications.java rename to server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfolios.java index 382606a81e6..a88e3d07c43 100644 --- 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/SecurityReviewRatingVisitorForPortfolios.java @@ -34,15 +34,16 @@ 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 SecurityReviewRatingVisitorForPortfoliosAndApplications extends TypeAwareVisitorAdapter { +public class SecurityReviewRatingVisitorForPortfolios extends TypeAwareVisitorAdapter { private final MeasureRepository measureRepository; private final Metric nclocMetric; private final Metric securityHostspotsMetric; private final Metric securityReviewRatingMetric; - public SecurityReviewRatingVisitorForPortfoliosAndApplications(MeasureRepository measureRepository, MetricRepository metricRepository) { + public SecurityReviewRatingVisitorForPortfolios(MeasureRepository measureRepository, MetricRepository metricRepository) { super(CrawlerDepthLimit.SUBVIEW, Order.POST_ORDER); this.measureRepository = measureRepository; this.nclocMetric = metricRepository.getByKey(NCLOC_KEY); @@ -50,13 +51,11 @@ public class SecurityReviewRatingVisitorForPortfoliosAndApplications extends Typ this.securityReviewRatingMetric = metricRepository.getByKey(SECURITY_REVIEW_RATING_KEY); } - @Override - public void visitProject(Component project) { - // Do nothing - } - @Override public void visitView(Component view) { + if (view.getViewAttributes().getType().equals(APPLICATION)) { + return; + } computeMeasure(view); } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitorTest.java index 08b85755a8d..c39752eb775 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitorTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitorTest.java @@ -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()) diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitorTest.java index 7e259ea5998..9780f4e72f3 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitorTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitorTest.java @@ -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/SecurityReviewRatingVisitorForPortfoliosTest.java similarity index 81% rename from server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest.java rename to server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosTest.java index a55e2db19be..099113d525e 100644 --- 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/SecurityReviewRatingVisitorForPortfoliosTest.java @@ -26,7 +26,6 @@ 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; @@ -42,22 +41,18 @@ import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilde import static org.sonar.server.measure.Rating.B; import static org.sonar.server.measure.Rating.C; -public class SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest { +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(); - 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(); @@ -70,7 +65,7 @@ public class SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest { @Rule public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); - private VisitorsCrawler underTest = new VisitorsCrawler(singletonList(new SecurityReviewRatingVisitorForPortfoliosAndApplications(measureRepository, metricRepository))); + private VisitorsCrawler underTest = new VisitorsCrawler(singletonList(new SecurityReviewRatingVisitorForPortfolios(measureRepository, metricRepository))); @Test public void compute_security_review_rating_on_portfolio() { @@ -89,19 +84,6 @@ public class SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest { 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); diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityReviewRating.java b/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityReviewRating.java index 6a5d2673eb3..f96839250fc 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityReviewRating.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityReviewRating.java @@ -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; diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImpl.java index f65b6bad7f0..d4a63507f33 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImpl.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImpl.java @@ -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)), diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImplTest.java index f23aa1c8cc1..7f92edb76e0 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImplTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImplTest.java @@ -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() 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 45e6844cc53..a45aafd6321 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -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 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 2f2e58de4c3..4921e34a853 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 @@ -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 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 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 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 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 -- 2.39.5