From: Julien Lancelot Date: Mon, 3 Feb 2020 13:10:24 +0000 (+0100) Subject: SONAR-12962 Compute Security Review measures on Applications X-Git-Tag: 8.2.0.32929~85 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c51fa2468cfe46d85b86d8121de84cd0a06fed89;p=sonarqube.git SONAR-12962 Compute Security Review measures on Applications --- 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/SecurityReviewRatingVisitorForPortfolios.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfolios.java new file mode 100644 index 00000000000..a88e3d07c43 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfolios.java @@ -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 nclocMeasure = measureRepository.getRawMeasure(component, nclocMetric); + Optional 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 index 382606a81e6..00000000000 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplications.java +++ /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 nclocMeasure = measureRepository.getRawMeasure(component, nclocMetric); - Optional 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/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/SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest.java deleted file mode 100644 index a55e2db19be..00000000000 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest.java +++ /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 index 00000000000..099113d525e --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosTest.java @@ -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(); + } +} 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