From 128a778ef1ba90423b0c2ffb23fe4a606a0c8e63 Mon Sep 17 00:00:00 2001 From: DDMili <130993898+dejan-milisavljevic-sonarsource@users.noreply.github.com> Date: Mon, 12 Aug 2024 13:42:10 +0200 Subject: [PATCH] SONAR-22727 Introduce software quality security review rating metrics --- .../NewSecurityReviewMeasuresVisitor.java | 8 +- .../SecurityReviewMeasuresVisitor.java | 6 + .../NewSecurityReviewMeasuresVisitorTest.java | 123 ++++++++--------- .../SecurityReviewMeasuresVisitorTest.java | 125 +++++++++--------- .../server/security/SecurityReviewRating.java | 11 ++ .../security/SecurityReviewRatingTest.java | 47 +++++-- 6 files changed, 188 insertions(+), 132 deletions(-) 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 c5eb63dc341..1b6c3309427 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 @@ -23,11 +23,11 @@ import java.util.Optional; import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.PathAwareVisitorAdapter; import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepository; +import org.sonar.ce.task.projectanalysis.issue.NewIssueClassifier; import org.sonar.ce.task.projectanalysis.measure.Measure; import org.sonar.ce.task.projectanalysis.measure.MeasureRepository; import org.sonar.ce.task.projectanalysis.metric.Metric; import org.sonar.ce.task.projectanalysis.metric.MetricRepository; -import org.sonar.ce.task.projectanalysis.issue.NewIssueClassifier; 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; @@ -36,6 +36,8 @@ 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; import static org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit.FILE; +import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY; +import static org.sonar.server.security.SecurityReviewRating.computeAToDRating; import static org.sonar.server.security.SecurityReviewRating.computePercent; import static org.sonar.server.security.SecurityReviewRating.computeRating; @@ -44,6 +46,7 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed()); measureRepository.add(component, newSecurityReviewRatingMetric, Measure.newMeasureBuilder().create(computeRating(percent.orElse(null)).getIndex())); + measureRepository.add(component, newSoftwareQualitySecurityReviewRatingMetric, + Measure.newMeasureBuilder().create(computeAToDRating(percent.orElse(null)).getIndex())); percent.ifPresent(p -> measureRepository.add(component, newSecurityHotspotsReviewedMetric, Measure.newMeasureBuilder().create(p))); if (!path.isRoot()) { 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 c8e8ec02fc0..34f3949b1e6 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 @@ -37,6 +37,8 @@ import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER; import static org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit.FILE; import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder; +import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY; +import static org.sonar.server.security.SecurityReviewRating.computeAToDRating; import static org.sonar.server.security.SecurityReviewRating.computePercent; import static org.sonar.server.security.SecurityReviewRating.computeRating; @@ -45,6 +47,7 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed()); measureRepository.add(component, securityReviewRatingMetric, RatingMeasures.get(computeRating(percent.orElse(null)))); + measureRepository.add(component, softwareQualitySecurityReviewRatingMetric, + RatingMeasures.get(computeAToDRating(percent.orElse(null)))); percent.ifPresent(p -> measureRepository.add(component, securityHotspotsReviewedMetric, newMeasureBuilder().create(p, securityHotspotsReviewedMetric.getDecimalScale()))); if (!path.isRoot()) { 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 b00a73f5dc9..e13d1590a9f 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 @@ -23,9 +23,9 @@ import java.util.Arrays; import javax.annotation.Nullable; import org.assertj.core.api.Assertions; import org.assertj.core.data.Offset; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.sonar.api.rules.RuleType; import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.FileAttributes; @@ -33,9 +33,9 @@ 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.issue.NewIssueClassifier; import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule; import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule; -import org.sonar.ce.task.projectanalysis.issue.NewIssueClassifier; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.util.UuidFactoryFast; import org.sonar.core.util.Uuids; @@ -68,8 +68,10 @@ import static org.sonar.server.measure.Rating.B; import static org.sonar.server.measure.Rating.C; import static org.sonar.server.measure.Rating.D; import static org.sonar.server.measure.Rating.E; +import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING; +import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY; -public class NewSecurityReviewMeasuresVisitorTest { +class NewSecurityReviewMeasuresVisitorTest { private static final Offset VALUE_COMPARISON_OFFSET = Offset.offset(0.01); private static final String LANGUAGE_KEY_1 = "lKey1"; @@ -91,31 +93,32 @@ public class NewSecurityReviewMeasuresVisitorTest { .build()) .build(); - @Rule - public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); - @Rule - public MetricRepositoryRule metricRepository = new MetricRepositoryRule() + @RegisterExtension + private final TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @RegisterExtension + private final MetricRepositoryRule metricRepository = new MetricRepositoryRule() .add(NEW_SECURITY_REVIEW_RATING) + .add(NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING) .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 - public ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder); - @Rule - public FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule = new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder); + @RegisterExtension + private final MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); + private final ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder); + @RegisterExtension + private final FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule = + new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder); private final NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class); private final VisitorsCrawler underTest = new VisitorsCrawler(Arrays.asList(fillComponentIssuesVisitorRule, new NewSecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, metricRepository, newIssueClassifier))); - @Before - public void setup() { + @BeforeEach + void setup() { when(newIssueClassifier.isEnabled()).thenReturn(true); } @Test - public void compute_measures_when_100_percent_hotspots_reviewed() { + void compute_measures_when_100_percent_hotspots_reviewed() { treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED), @@ -131,15 +134,15 @@ public class NewSecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(FILE_2_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(PROJECT_REF, A, A, 100.0); } @Test - public void compute_measures_when_more_than_80_percent_hotspots_reviewed() { + void compute_measures_when_more_than_80_percent_hotspots_reviewed() { treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED), @@ -160,15 +163,15 @@ public class NewSecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(FILE_2_REF, A, B, 80.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, B, 87.5); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, B, 87.5); + verifyRatingAndReviewedMeasures(PROJECT_REF, A, B, 87.5); } @Test - public void compute_measures_when_more_than_70_percent_hotspots_reviewed() { + void compute_measures_when_more_than_70_percent_hotspots_reviewed() { treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED), @@ -189,15 +192,15 @@ public class NewSecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(FILE_2_REF, B, B, 71.42); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, B, 75.0); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, B, 75.0); + verifyRatingAndReviewedMeasures(PROJECT_REF, B, B, 75.0); } @Test - public void compute_measures_when_more_than_50_percent_hotspots_reviewed() { + void compute_measures_when_more_than_50_percent_hotspots_reviewed() { treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newHotspot(STATUS_TO_REVIEW, null), @@ -217,15 +220,15 @@ public class NewSecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, C, C, 50.0); + verifyRatingAndReviewedMeasures(FILE_2_REF, C, C, 60.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, C, 57.14); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, C, 57.14); + verifyRatingAndReviewedMeasures(PROJECT_REF, C, C, 57.14); } @Test - public void compute_measures_when_more_30_than_percent_hotspots_reviewed() { + void compute_measures_when_more_30_than_percent_hotspots_reviewed() { treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newHotspot(STATUS_TO_REVIEW, null), @@ -246,15 +249,15 @@ public class NewSecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, D, D, 33.33); + verifyRatingAndReviewedMeasures(FILE_2_REF, D, D, 40.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, D, 37.5); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, D, 37.5); + verifyRatingAndReviewedMeasures(PROJECT_REF, D, D, 37.5); } @Test - public void compute_measures_when_less_than_30_percent_hotspots_reviewed() { + void compute_measures_when_less_than_30_percent_hotspots_reviewed() { treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newHotspot(STATUS_TO_REVIEW, null), @@ -273,15 +276,15 @@ public class NewSecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, D, D, 33.33); + verifyRatingAndReviewedMeasures(FILE_2_REF, E, D, 0.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, D, 16.66); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, D, 16.66); + verifyRatingAndReviewedMeasures(PROJECT_REF, E, D, 16.66); } @Test - public void compute_A_rating_and_no_percent_when_no_new_hotspot_on_new_code() { + void compute_A_rating_and_no_percent_when_no_new_hotspot_on_new_code() { treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, oldHotspot(STATUS_TO_REVIEW, null), @@ -290,11 +293,11 @@ public class NewSecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - verifyRatingAndReviewedMeasures(PROJECT_REF, A, null); + verifyRatingAndReviewedMeasures(PROJECT_REF, A, A, null); } @Test - public void compute_status_related_measures() { + void compute_status_related_measures() { treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newHotspot(STATUS_TO_REVIEW, null), @@ -319,7 +322,7 @@ public class NewSecurityReviewMeasuresVisitorTest { } @Test - public void compute_0_status_related_measures_when_no_hotspot() { + void compute_0_status_related_measures_when_no_hotspot() { treeRootHolder.setRoot(ROOT_PROJECT); underTest.visit(ROOT_PROJECT); @@ -328,7 +331,7 @@ public class NewSecurityReviewMeasuresVisitorTest { } @Test - public void no_measure_if_there_is_no_period() { + void no_measure_if_there_is_no_period() { when(newIssueClassifier.isEnabled()).thenReturn(false); treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, @@ -340,8 +343,10 @@ public class NewSecurityReviewMeasuresVisitorTest { assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).values()).isEmpty(); } - private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, @Nullable Double expectedHotspotsReviewed) { + private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, + Rating expectedSoftwareQualitySecurityReviewRating, @Nullable Double expectedHotspotsReviewed) { assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_REVIEW_RATING_KEY)).hasValue(expectedReviewRating.getIndex()); + assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY)).hasValue(expectedSoftwareQualitySecurityReviewRating.getIndex()); if (expectedHotspotsReviewed != null) { assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY)).hasValue(expectedHotspotsReviewed, VALUE_COMPARISON_OFFSET); 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 7d6d267f752..eb931585aa9 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 @@ -20,8 +20,8 @@ package org.sonar.ce.task.projectanalysis.qualitymodel; import javax.annotation.Nullable; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.sonar.api.rules.RuleType; import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; @@ -59,8 +59,10 @@ import static org.sonar.server.measure.Rating.B; import static org.sonar.server.measure.Rating.C; import static org.sonar.server.measure.Rating.D; import static org.sonar.server.measure.Rating.E; +import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING; +import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY; -public class SecurityReviewMeasuresVisitorTest { +class SecurityReviewMeasuresVisitorTest { private static final int PROJECT_REF = 1; private static final int ROOT_DIR_REF = 12; @@ -68,7 +70,7 @@ public class SecurityReviewMeasuresVisitorTest { private static final int FILE_1_REF = 1231; private static final int FILE_2_REF = 1232; - static final Component ROOT_PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project") + private static final Component ROOT_PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project") .addChildren( builder(DIRECTORY, ROOT_DIR_REF).setKey("dir") .addChildren( @@ -80,26 +82,27 @@ public class SecurityReviewMeasuresVisitorTest { .build()) .build(); - @Rule - public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); - @Rule - public MetricRepositoryRule metricRepository = new MetricRepositoryRule() + @RegisterExtension + private final TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @RegisterExtension + private final MetricRepositoryRule metricRepository = new MetricRepositoryRule() .add(SECURITY_REVIEW_RATING) + .add(SOFTWARE_QUALITY_SECURITY_REVIEW_RATING) .add(SECURITY_HOTSPOTS_REVIEWED) .add(SECURITY_HOTSPOTS_REVIEWED_STATUS) .add(SECURITY_HOTSPOTS_TO_REVIEW_STATUS); - @Rule - public ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder); - @Rule - public FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule = new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder); - @Rule - public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); - - private VisitorsCrawler underTest = new VisitorsCrawler(asList(fillComponentIssuesVisitorRule, + private final ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder); + @RegisterExtension + private final FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule = + new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder); + @RegisterExtension + private final MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); + + private final VisitorsCrawler underTest = new VisitorsCrawler(asList(fillComponentIssuesVisitorRule, new SecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, metricRepository))); @Test - public void compute_rating_and_reviewed_measures_when_100_percent_hotspots_reviewed() { + 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), @@ -112,15 +115,15 @@ public class SecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(FILE_2_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(PROJECT_REF, A, A, 100.0); } @Test - public void compute_rating_and_reviewed__measures_when_more_than_80_percent_hotspots_reviewed() { + 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), @@ -138,15 +141,15 @@ public class SecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(FILE_2_REF, A, B, 80.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, B, 87.5); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, B, 87.5); + verifyRatingAndReviewedMeasures(PROJECT_REF, A, B, 87.5); } @Test - public void compute_rating_and_reviewed__measures_when_more_than_70_percent_hotspots_reviewed() { + 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), @@ -164,15 +167,15 @@ public class SecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0); + verifyRatingAndReviewedMeasures(FILE_2_REF, B, B, 71.4); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, B, 75.0); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, B, 75.0); + verifyRatingAndReviewedMeasures(PROJECT_REF, B, B, 75.0); } @Test - public void compute_rating_and_reviewed__measures_when_more_than_50_percent_hotspots_reviewed() { + 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), @@ -189,15 +192,15 @@ public class SecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, C, C,50.0); + verifyRatingAndReviewedMeasures(FILE_2_REF, C, C,60.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, C,C, 57.1); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, C,57.1); + verifyRatingAndReviewedMeasures(PROJECT_REF, C, C,57.1); } @Test - public void compute_rating_and_reviewed__measures_when_more_30_than_percent_hotspots_reviewed() { + 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), @@ -215,15 +218,15 @@ public class SecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, D, D,33.3); + verifyRatingAndReviewedMeasures(FILE_2_REF, D, D,40.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, D,D, 37.5); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, D,37.5); + verifyRatingAndReviewedMeasures(PROJECT_REF, D, D,37.5); } @Test - public void compute_rating_and_reviewed__measures_when_less_than_30_percent_hotspots_reviewed() { + 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), @@ -239,24 +242,24 @@ public class SecurityReviewMeasuresVisitorTest { underTest.visit(ROOT_PROJECT); - 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); + verifyRatingAndReviewedMeasures(FILE_1_REF, D, D,33.3); + verifyRatingAndReviewedMeasures(FILE_2_REF, E, D,0.0); + verifyRatingAndReviewedMeasures(DIRECTORY_REF, E,D, 16.7); + verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, D,16.7); + verifyRatingAndReviewedMeasures(PROJECT_REF, E, D,16.7); } @Test - public void compute_A_rating_and_no_reviewed_when_no_hotspot() { + void compute_A_rating_and_no_reviewed_when_no_hotspot() { treeRootHolder.setRoot(ROOT_PROJECT); underTest.visit(ROOT_PROJECT); - verifyRatingAndReviewedMeasures(PROJECT_REF, A, null); + verifyRatingAndReviewedMeasures(PROJECT_REF, A, A,null); } @Test - public void compute_status_related_measures() { + void compute_status_related_measures() { treeRootHolder.setRoot(ROOT_PROJECT); fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newHotspot(STATUS_TO_REVIEW, null), @@ -281,7 +284,7 @@ public class SecurityReviewMeasuresVisitorTest { } @Test - public void compute_0_status_related_measures_when_no_hotspot() { + void compute_0_status_related_measures_when_no_hotspot() { treeRootHolder.setRoot(ROOT_PROJECT); underTest.visit(ROOT_PROJECT); @@ -289,8 +292,9 @@ public class SecurityReviewMeasuresVisitorTest { verifyHotspotStatusMeasures(PROJECT_REF, 0, 0); } - private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, @Nullable Double expectedHotspotsReviewed) { - verifySecurityReviewRating(componentRef, expectedReviewRating); + private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, Rating expectedSoftwareQualityReviewRating, + @Nullable Double expectedHotspotsReviewed) { + verifySecurityReviewRating(componentRef, expectedReviewRating, expectedSoftwareQualityReviewRating); if (expectedHotspotsReviewed != null) { verifySecurityHotspotsReviewed(componentRef, expectedHotspotsReviewed); } else { @@ -298,10 +302,13 @@ public class SecurityReviewMeasuresVisitorTest { } } - private void verifySecurityReviewRating(int componentRef, Rating rating) { + private void verifySecurityReviewRating(int componentRef, Rating rating, Rating softwareQualityRating) { Measure measure = measureRepository.getAddedRawMeasure(componentRef, SECURITY_REVIEW_RATING_KEY).get(); + Measure softwareQualityMeasure = measureRepository.getAddedRawMeasure(componentRef, SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY).get(); assertThat(measure.getIntValue()).isEqualTo(rating.getIndex()); assertThat(measure.getData()).isEqualTo(rating.name()); + assertThat(softwareQualityMeasure.getIntValue()).isEqualTo(softwareQualityRating.getIndex()); + assertThat(softwareQualityMeasure.getData()).isEqualTo(softwareQualityRating.name()); } private void verifySecurityHotspotsReviewed(int componentRef, double percent) { 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 03e5c44b6cd..69791800a7b 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,4 +55,15 @@ public class SecurityReviewRating { } return E; } + + public static Rating computeAToDRating(@Nullable Double percent) { + if (percent == null || Math.abs(percent - 100.0D) < 10e-6) { + return A; + } else if (percent >= 70.0D) { + return B; + } else if (percent >= 50.0D) { + return C; + } + return D; + } } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/security/SecurityReviewRatingTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/security/SecurityReviewRatingTest.java index b21b41daf50..1d586900a1b 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/security/SecurityReviewRatingTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/security/SecurityReviewRatingTest.java @@ -19,14 +19,12 @@ */ package org.sonar.server.security; -import com.tngtech.java.junit.dataprovider.DataProvider; -import com.tngtech.java.junit.dataprovider.DataProviderRunner; -import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.util.ArrayList; import java.util.List; import org.assertj.core.data.Offset; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.sonar.server.measure.Rating; import static org.assertj.core.api.Assertions.assertThat; @@ -35,16 +33,15 @@ import static org.sonar.server.measure.Rating.B; import static org.sonar.server.measure.Rating.C; import static org.sonar.server.measure.Rating.D; import static org.sonar.server.measure.Rating.E; +import static org.sonar.server.security.SecurityReviewRating.computeAToDRating; import static org.sonar.server.security.SecurityReviewRating.computePercent; import static org.sonar.server.security.SecurityReviewRating.computeRating; -@RunWith(DataProviderRunner.class) -public class SecurityReviewRatingTest { +class SecurityReviewRatingTest { private static final Offset DOUBLE_OFFSET = Offset.offset(0.01d); - @DataProvider - public static Object[][] values() { + private static Object[][] values() { List res = new ArrayList<>(); res.add(new Object[] {100.0, A}); res.add(new Object[] {90.0, A}); @@ -59,14 +56,38 @@ public class SecurityReviewRatingTest { return res.toArray(new Object[res.size()][2]); } - @Test - @UseDataProvider("values") - public void compute_rating(double percent, Rating expectedRating) { + private static Object[][] valuesForSoftwareQualityRatings() { + List res = new ArrayList<>(); + res.add(new Object[] {100.0, A}); + res.add(new Object[] {99.999999, A}); + res.add(new Object[] {99.99999, B}); + res.add(new Object[] {99.9, B}); + res.add(new Object[] {90.0, B}); + res.add(new Object[] {80.0, B}); + res.add(new Object[] {75.0, B}); + res.add(new Object[] {70.0, B}); + res.add(new Object[] {60, C}); + res.add(new Object[] {50.0, C}); + res.add(new Object[] {40.0, D}); + res.add(new Object[] {30.0, D}); + res.add(new Object[] {29.9, D}); + return res.toArray(new Object[res.size()][2]); + } + + @ParameterizedTest + @MethodSource("values") + void compute_rating(double percent, Rating expectedRating) { assertThat(computeRating(percent)).isEqualTo(expectedRating); } + @ParameterizedTest + @MethodSource("valuesForSoftwareQualityRatings") + void compute_ratingForSoftwareQuality(double percent, Rating expectedRating) { + assertThat(computeAToDRating(percent)).isEqualTo(expectedRating); + } + @Test - public void compute_percent() { + void compute_percent() { assertThat(computePercent(0, 0)).isEmpty(); assertThat(computePercent(0, 10)).contains(100.0); assertThat(computePercent(1, 3)).contains(75.0); -- 2.39.5