diff options
author | Dejan Milisavljevic <130993898+dejan-milisavljevic-sonarsource@users.noreply.github.com> | 2024-09-18 14:03:50 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-09-18 20:02:59 +0000 |
commit | e55e29f6e2632c1eef4db2d659e685a50caa10a6 (patch) | |
tree | c178331b8d79e523a9c3bdbbc82f611781413dbc /server/sonar-server-common | |
parent | 7fe4eae27f3b725ea08c79d5a0373596200a1627 (diff) | |
download | sonarqube-e55e29f6e2632c1eef4db2d659e685a50caa10a6.tar.gz sonarqube-e55e29f6e2632c1eef4db2d659e685a50caa10a6.zip |
SONAR-22951 Use 5 levels severities for Software Impact
Co-authored-by: Léo Geoffroy <leo.geoffroy@sonarsource.com>
Co-authored-by: Stanislav <31501873+stanislavhh@users.noreply.github.com>
Co-authored-by: Viktor Vorona <viktor.vorona@sonarsource.com>
Co-authored-by: OrlovAlexander <35396155+OrlovAlexander85@users.noreply.github.com>
Co-authored-by: stanislavh <stanislav.honcharov@sonarsource.com>
Diffstat (limited to 'server/sonar-server-common')
11 files changed, 153 insertions, 174 deletions
diff --git a/server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIndexerIT.java b/server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIndexerIT.java index 39b9e1cd6b0..76897cb5743 100644 --- a/server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIndexerIT.java +++ b/server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIndexerIT.java @@ -48,6 +48,7 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.db.component.ProjectData; import org.sonar.db.es.EsQueueDto; +import org.sonar.db.issue.ImpactDto; import org.sonar.db.issue.IssueDto; import org.sonar.db.issue.IssueTesting; import org.sonar.db.project.ProjectDto; @@ -174,7 +175,9 @@ public class IssueIndexerIT { ComponentDto project = projectData.getMainBranchComponent(); ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java/foo")); ComponentDto file = db.components().insertComponent(newFileDto(project, dir, "F1")); - IssueDto issue = db.issues().insert(rule, project, file); + IssueDto issue = db.issues().insert(rule, project, file, + i -> i.replaceAllImpacts(List.of(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.HIGH), + new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.INFO)))); underTest.indexAllIssues(); @@ -205,7 +208,10 @@ public class IssueIndexerIT { assertThat(doc.impacts()) .containsExactlyInAnyOrder(Map.of( SUB_FIELD_SOFTWARE_QUALITY, SoftwareQuality.MAINTAINABILITY.name(), - SUB_FIELD_SEVERITY, Severity.HIGH.name())); + SUB_FIELD_SEVERITY, Severity.HIGH.name()), + Map.of( + SUB_FIELD_SOFTWARE_QUALITY, SoftwareQuality.SECURITY.name(), + SUB_FIELD_SEVERITY, Severity.INFO.name())); assertThat(doc.issueStatus()).isEqualTo(issue.getIssueStatus().name()); } diff --git a/server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java b/server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java index a7be47d330e..87c8474ffe6 100644 --- a/server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java +++ b/server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java @@ -50,11 +50,11 @@ import org.sonar.server.es.SearchOptions; import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; import org.sonar.server.security.SecurityStandards; -import static com.google.common.collect.ImmutableSet.of; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static java.util.Collections.singletonList; +import static java.util.Set.of; import static java.util.stream.IntStream.rangeClosed; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -225,7 +225,8 @@ public class RuleIndexIT { RuleDto rule1 = insertJavaRule("My great rule CWE-123 which makes your code 1000 times better!", "123", "rule 123"); RuleDto rule2 = insertJavaRule("Another great and shiny rule CWE-124", "124", "rule 124"); RuleDto rule3 = insertJavaRule("Another great rule CWE-1000", "1000", "rule 1000"); - RuleDto rule4 = insertJavaRule("<h1>HTML-Geeks</h1><p style=\"color:blue\">special formatting!</p><table><tr><td>inside</td><td>tables</td></tr></table>", "404", "rule 404"); + RuleDto rule4 = insertJavaRule("<h1>HTML-Geeks</h1><p style=\"color:blue\">special " + + "formatting!</p><table><tr><td>inside</td><td>tables</td></tr></table>", "404", "rule 404"); RuleDto rule5 = insertJavaRule("internationalization missunderstandings alsdkjfnadklsjfnadkdfnsksdjfn", "405", "rule 405"); index(); @@ -323,7 +324,7 @@ public class RuleIndexIT { @Test public void tags_facet_supports_selected_value_with_regexp_special_characters() { - createRule(r -> r.setTags(Set.of("misra++"))); + createRule(r -> r.setTags(of("misra++"))); index(); RuleQuery query = new RuleQuery() @@ -480,7 +481,8 @@ public class RuleIndexIT { @Test public void search_by_security_owaspTop10_2021_return_vulnerabilities_and_hotspots_only() { - RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10-2021:a1", "owaspTop10-2021:a10", "cwe:543")), r -> r.setType(VULNERABILITY)); + RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10-2021:a1", "owaspTop10-2021:a10", "cwe:543")), + r -> r.setType(VULNERABILITY)); RuleDto rule2 = createRule(setSecurityStandards(of("owaspTop10-2021:a10", "cwe:543")), r -> r.setType(SECURITY_HOTSPOT)); createRule(setSecurityStandards(of("cwe:543")), r -> r.setType(CODE_SMELL)); index(); @@ -521,7 +523,8 @@ public class RuleIndexIT { // Creation of one rule for each standard security category defined (except other) for (Map.Entry<SecurityStandards.SQCategory, Set<String>> sqCategorySetEntry : SecurityStandards.CWES_BY_SQ_CATEGORY.entrySet()) { - rules.add(createRule(setSecurityStandards(of("cwe:" + sqCategorySetEntry.getValue().iterator().next())), r -> r.setType(SECURITY_HOTSPOT))); + rules.add(createRule(setSecurityStandards(of("cwe:" + sqCategorySetEntry.getValue().iterator().next())), + r -> r.setType(SECURITY_HOTSPOT))); } index(); @@ -553,9 +556,11 @@ public class RuleIndexIT { db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule2); index(); - verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(anotherProfile), anotherProfileRule1, anotherProfileRule2); + verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(anotherProfile), anotherProfileRule1, + anotherProfileRule2); verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(anotherProfile), commonRule); - verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(profile), commonRule, profileRule1, profileRule2, profileRule3); + verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(profile), commonRule, profileRule1, + profileRule2, profileRule3); verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(profile)); } @@ -704,7 +709,8 @@ public class RuleIndexIT { verifyEmptySearch(newRuleQuery().setActivation(true).setQProfile(parent).setInheritance(of(INHERITED.name(), OVERRIDES.name()))); // inherited AND overridden on child - verifySearch(newRuleQuery().setActivation(true).setQProfile(child).setInheritance(of(INHERITED.name(), OVERRIDES.name())), rule1, rule2, rule3); + verifySearch(newRuleQuery().setActivation(true).setQProfile(child).setInheritance(of(INHERITED.name(), OVERRIDES.name())), rule1, + rule2, rule3); } @Test @@ -812,17 +818,19 @@ public class RuleIndexIT { index(); RuleQuery query = new RuleQuery(); - SearchIdResult result1 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())), new SearchOptions()); + SearchIdResult result1 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())), + new SearchOptions()); assertThat(result1.getUuids()).isEmpty(); query = new RuleQuery(); - SearchIdResult result2 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.SECURITY.name())), new SearchOptions()); + SearchIdResult result2 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.SECURITY.name())), + new SearchOptions()); assertThat(result2.getUuids()).containsOnly(phpRule.getUuid()); } @Test public void search_by_severity() { - ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH); + ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.BLOCKER); RuleDto phpRule = createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto))); index(); @@ -831,7 +839,7 @@ public class RuleIndexIT { assertThat(result1.getUuids()).isEmpty(); query = new RuleQuery(); - SearchIdResult result2 = underTest.search(query.setImpactSeverities(List.of(Severity.HIGH.name())), new SearchOptions()); + SearchIdResult result2 = underTest.search(query.setImpactSeverities(List.of(Severity.BLOCKER.name())), new SearchOptions()); assertThat(result2.getUuids()).containsOnly(phpRule.getUuid()); } @@ -846,7 +854,8 @@ public class RuleIndexIT { SearchIdResult result2 = underTest.search(query, new SearchOptions().addFacets(singletonList("cleanCodeAttributeCategories"))); assertThat(result2.getFacets().getAll()).hasSize(1); - assertThat(result2.getFacets().getAll().get("cleanCodeAttributeCategories")).containsOnly(entry("ADAPTABLE", 1L), entry("INTENTIONAL", 1L)); + assertThat(result2.getFacets().getAll().get("cleanCodeAttributeCategories")).containsOnly(entry("ADAPTABLE", 1L), entry("INTENTIONAL" + , 1L)); } @Test @@ -859,7 +868,8 @@ public class RuleIndexIT { RuleQuery query = new RuleQuery(); SearchIdResult result = underTest.search( - query.setCleanCodeAttributesCategories(List.of(CleanCodeAttributeCategory.CONSISTENT.name(), CleanCodeAttributeCategory.ADAPTABLE.name())), + query.setCleanCodeAttributesCategories(List.of(CleanCodeAttributeCategory.CONSISTENT.name(), + CleanCodeAttributeCategory.ADAPTABLE.name())), new SearchOptions().addFacets(singletonList("cleanCodeAttributeCategories"))); assertThat(result.getUuids()).containsExactlyInAnyOrder(php.getUuid(), java.getUuid()); @@ -895,7 +905,8 @@ public class RuleIndexIT { RuleQuery query = new RuleQuery(); - SearchIdResult result2 = underTest.search(query.setImpactSeverities(Set.of(Severity.HIGH.name())), new SearchOptions().addFacets(singletonList("impactSoftwareQualities"))); + SearchIdResult result2 = underTest.search(query.setImpactSeverities(of(Severity.HIGH.name())), + new SearchOptions().addFacets(singletonList("impactSoftwareQualities"))); assertThat(result2.getFacets().getAll()).hasSize(1); assertThat(result2.getFacets().getAll().get("impactSoftwareQualities")) @@ -910,14 +921,21 @@ public class RuleIndexIT { ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.HIGH); ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW); ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.LOW); + ImpactDto impactDto4 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.BLOCKER); + ImpactDto impactDto5 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.BLOCKER); + ImpactDto impactDto6 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.INFO); createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto))); - createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2, impactDto3))); + createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2))); + createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto3))); + createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto4))); + createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto5))); + createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto6))); index(); RuleQuery query = new RuleQuery(); SearchIdResult result = underTest.search( - query.setImpactSeverities(Set.of(Severity.LOW.name())).setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())), + query.setImpactSeverities(of(Severity.LOW.name())).setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())), new SearchOptions().addFacets(List.of("impactSoftwareQualities", "impactSeverities"))); assertThat(result.getFacets().getAll()).hasSize(2); @@ -931,15 +949,18 @@ public class RuleIndexIT { .containsOnly( entry("HIGH", 1L), entry("MEDIUM", 0L), - entry("LOW", 1L)); + entry("LOW", 1L), + entry("INFO", 0L), + entry("BLOCKER", 1L)); } @Test public void search_should_support_severity_facet() { ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH); ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW); + ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.BLOCKER); createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto))); - createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2))); + createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2, impactDto3))); index(); RuleQuery query = new RuleQuery(); @@ -947,40 +968,65 @@ public class RuleIndexIT { SearchIdResult result2 = underTest.search(query, new SearchOptions().addFacets(singletonList("impactSeverities"))); assertThat(result2.getFacets().getAll()).hasSize(1); - assertThat(result2.getFacets().getAll().get("impactSeverities")).containsOnly(entry("LOW", 1L), entry("MEDIUM", 0L), entry("HIGH", 1L)); + assertThat(result2.getFacets().getAll().get("impactSeverities")) + .containsOnly( + entry("LOW", 1L), + entry("MEDIUM", 0L), + entry("HIGH", 1L), + entry("INFO", 0L), + entry("BLOCKER", 1L)); } @Test public void search_should_support_severity_facet_with_filters() { ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH); ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW); + ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.INFO); + ImpactDto impactDto4 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.INFO); + ImpactDto impactDto5 = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.BLOCKER); createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto))); - createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2))); + createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2, impactDto3))); + createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto4, impactDto5))); index(); RuleQuery query = new RuleQuery(); - SearchIdResult result2 = underTest.search(query.setImpactSeverities(Set.of("LOW")), new SearchOptions().addFacets(singletonList("impactSeverities"))); + SearchIdResult result2 = underTest.search(query.setImpactSeverities(of("LOW")), new SearchOptions().addFacets(singletonList( + "impactSeverities"))); assertThat(result2.getFacets().getAll()).hasSize(1); - assertThat(result2.getFacets().getAll().get("impactSeverities")).containsOnly(entry("LOW", 1L), entry("MEDIUM", 0L), entry("HIGH", 1L)); + assertThat(result2.getFacets().getAll().get("impactSeverities")) + .containsOnly( + entry("LOW", 1L), + entry("MEDIUM", 0L), + entry("HIGH", 1L), + entry("INFO", 2L), + entry("BLOCKER", 1L)); } @Test public void search_should_support_software_quality_and_severity_facets_with_filtering() { ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH); ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW); + ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.BLOCKER); createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto))); createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2))); + createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto3))); index(); - RuleQuery query = new RuleQuery().setImpactSeverities(Set.of("LOW")) - .setImpactSoftwareQualities(Set.of(SoftwareQuality.MAINTAINABILITY.name())); + RuleQuery query = new RuleQuery().setImpactSeverities(of("LOW")) + .setImpactSoftwareQualities(of(SoftwareQuality.MAINTAINABILITY.name())); SearchOptions searchOptions = new SearchOptions().addFacets(List.of("impactSeverities", "impactSoftwareQualities")); SearchIdResult result2 = underTest.search(query, searchOptions); assertThat(result2.getFacets().getAll()).hasSize(2); - assertThat(result2.getFacets().getAll().get("impactSeverities")).containsOnly(entry("LOW", 1L), entry("MEDIUM", 0L), entry("HIGH", 0L)); + assertThat(result2.getFacets().getAll().get("impactSeverities")) + .containsOnly( + entry("LOW", 1L), + entry("MEDIUM", 0L), + entry("HIGH", 0L), + entry("INFO", 0L), + entry("BLOCKER", 1L)); assertThat(result2.getFacets().getAll().get("impactSoftwareQualities")).containsOnly( entry(SoftwareQuality.SECURITY.name(), 0L), entry(SoftwareQuality.MAINTAINABILITY.name(), 1L), @@ -1000,7 +1046,8 @@ public class RuleIndexIT { assertThat(result1.getFacets().getAll()).isEmpty(); // should not have any facet on non matching query! - SearchIdResult result2 = underTest.search(new RuleQuery().setQueryText("aeiou"), new SearchOptions().addFacets(singletonList("repositories"))); + SearchIdResult result2 = underTest.search(new RuleQuery().setQueryText("aeiou"), new SearchOptions().addFacets(singletonList( + "repositories"))); assertThat(result2.getFacets().getAll()).hasSize(1); assertThat(result2.getFacets().getAll().get("repositories")).isEmpty(); @@ -1070,7 +1117,8 @@ public class RuleIndexIT { setupStickyFacets(); RuleQuery query = new RuleQuery().setLanguages(ImmutableList.of("cpp")); - SearchIdResult<String> result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS))); + SearchIdResult<String> result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, + FACET_TAGS))); assertThat(result.getUuids()).hasSize(3); assertThat(result.getFacets().getAll()).hasSize(3); assertThat(result.getFacets().get(FACET_LANGUAGES)).containsOnlyKeys("cpp", "java", "cobol"); @@ -1194,7 +1242,8 @@ public class RuleIndexIT { .setTags(ImmutableList.of("T2")) .setTypes(asList(BUG, CODE_SMELL)); - SearchIdResult<String> result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS, + SearchIdResult<String> result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, + FACET_TAGS, FACET_TYPES))); assertThat(result.getUuids()).hasSize(2); assertThat(result.getFacets().getAll()).hasSize(4); diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/measure/DebtRatingGrid.java b/server/sonar-server-common/src/main/java/org/sonar/server/measure/DebtRatingGrid.java index 4bbc2b54ac7..247a1829f98 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/measure/DebtRatingGrid.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/measure/DebtRatingGrid.java @@ -78,17 +78,6 @@ public class DebtRatingGrid { .orElseThrow(() -> new IllegalArgumentException(format("Invalid value '%s'", value))); } - /** - * Computes a rating from A to D, where E is converted to D. - */ - public Rating getAToDRatingForDensity(double value) { - return ratingBounds.entrySet().stream() - .filter(e -> e.getValue().match(value)) - .map(e -> e.getKey() == E ? D : e.getKey()) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException(format("Invalid value '%s'", value))); - } - public double getGradeLowerBound(Rating rating) { if (rating.getIndex() > 1) { return gridValues[rating.getIndex() - 2]; diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/measure/ImpactMeasureBuilder.java b/server/sonar-server-common/src/main/java/org/sonar/server/measure/ImpactMeasureBuilder.java index 3167b5d03ee..eae57b9d482 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/measure/ImpactMeasureBuilder.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/measure/ImpactMeasureBuilder.java @@ -23,7 +23,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; -import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; import javax.annotation.CheckForNull; @@ -32,7 +31,8 @@ import org.sonar.api.issue.impact.Severity; import static org.sonar.api.utils.Preconditions.checkArgument; /** - * Builder class to help build measures based on impacts with payload such as @{link {@link org.sonar.api.measures.CoreMetrics#RELIABILITY_ISSUES}}. + * Builder class to help build measures based on impacts with payload such as @{link + * {@link org.sonar.api.measures.CoreMetrics#RELIABILITY_ISSUES}}. */ public class ImpactMeasureBuilder { @@ -65,9 +65,16 @@ public class ImpactMeasureBuilder { return new ImpactMeasureBuilder(map); } - private static void checkImpactMap(Map<String, Long> map) { - checkArgument(map.containsKey(TOTAL_KEY), "Map must contain a total key"); - Arrays.stream(Severity.values()).forEach(severity -> checkArgument(map.containsKey(severity.name()), "Map must contain a key for severity " + severity.name())); + /** + * As we moved from 3 to 5 severities, we need to be able to handle measures saved with missing severities. + */ + private static void checkImpactMap(Map<String, Long> impactMap) { + checkArgument(impactMap.containsKey(TOTAL_KEY), "Map must contain a total key"); + for (Severity severity : Severity.values()) { + if (!impactMap.containsKey(severity.name())) { + impactMap.put(severity.name(), 0L); + } + } } public static ImpactMeasureBuilder fromString(String value) { diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/measure/Rating.java b/server/sonar-server-common/src/main/java/org/sonar/server/measure/Rating.java index f00b8796dd8..3ba14d771a3 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/measure/Rating.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/measure/Rating.java @@ -45,9 +45,11 @@ public enum Rating { INFO, A); public static final Map<Severity, Rating> RATING_BY_SOFTWARE_QUALITY_SEVERITY = Map.of( + Severity.BLOCKER, Rating.E, Severity.HIGH, Rating.D, Severity.MEDIUM, Rating.C, - Severity.LOW, Rating.B); + Severity.LOW, Rating.B, + Severity.INFO, Rating.A); private final int index; diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializer.java b/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializer.java index fecce77fcd4..2c2a1d017b1 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializer.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializer.java @@ -26,7 +26,7 @@ import org.sonar.server.measure.Rating; /** * This class defines "default" measures values for the "Software Quality Rating Metrics" when they do not exist. - * The "default" value is the same as the equivalent "Rule Type Rating Metric", except for E Rating that is converted to D Rating + * The "default" value is the same as the equivalent "Rule Type Rating Metric" * If the "Software Quality Rating Metrics" exists, then no changes are made */ public class ProjectMeasuresSoftwareQualityRatingsInitializer { @@ -35,9 +35,7 @@ public class ProjectMeasuresSoftwareQualityRatingsInitializer { CoreMetrics.SQALE_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, CoreMetrics.RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, CoreMetrics.SECURITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, - CoreMetrics.SECURITY_REVIEW_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, CoreMetrics.NEW_SECURITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, - CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, CoreMetrics.NEW_RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY ); @@ -59,7 +57,7 @@ public class ProjectMeasuresSoftwareQualityRatingsInitializer { Double value = measures.get(ruleTypeMetric); if (value != null) { - measures.put(softwareQualityMetric, value > Rating.D.getIndex() ? Double.valueOf(Rating.D.getIndex()) : value); + measures.put(softwareQualityMetric, value); } } } 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 69791800a7b..1af9a5db8c8 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 @@ -56,14 +56,4 @@ 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/measure/DebtRatingGridTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/measure/DebtRatingGridTest.java index 6557e108815..8a312cba599 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/measure/DebtRatingGridTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/measure/DebtRatingGridTest.java @@ -55,21 +55,6 @@ public class DebtRatingGridTest { } @Test - public void getAToDRatingForDensity_returnsValueBetweenAAndD() { - assertThat(ratingGrid.getAToDRatingForDensity(0)).isEqualTo(A); - assertThat(ratingGrid.getAToDRatingForDensity(0.05)).isEqualTo(A); - assertThat(ratingGrid.getAToDRatingForDensity(0.09999999)).isEqualTo(A); - assertThat(ratingGrid.getAToDRatingForDensity(0.1)).isEqualTo(A); - assertThat(ratingGrid.getAToDRatingForDensity(0.15)).isEqualTo(B); - assertThat(ratingGrid.getAToDRatingForDensity(0.2)).isEqualTo(B); - assertThat(ratingGrid.getAToDRatingForDensity(0.25)).isEqualTo(C); - assertThat(ratingGrid.getAToDRatingForDensity(0.5)).isEqualTo(C); - assertThat(ratingGrid.getAToDRatingForDensity(0.65)).isEqualTo(D); - assertThat(ratingGrid.getAToDRatingForDensity(1)).isEqualTo(D); - assertThat(ratingGrid.getAToDRatingForDensity(1.01)).isEqualTo(D); - } - - @Test public void density_matching_exact_grid_values() { assertThat(ratingGrid.getRatingForDensity(0.1)).isEqualTo(A); assertThat(ratingGrid.getRatingForDensity(0.2)).isEqualTo(B); diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/measure/ImpactMeasureBuilderTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/measure/ImpactMeasureBuilderTest.java index 82f26c979c7..a7a393a6c62 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/measure/ImpactMeasureBuilderTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/measure/ImpactMeasureBuilderTest.java @@ -19,12 +19,17 @@ */ package org.sonar.server.measure; +import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; -import org.sonar.api.issue.impact.Severity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.sonar.api.issue.impact.Severity.BLOCKER; +import static org.sonar.api.issue.impact.Severity.HIGH; +import static org.sonar.api.issue.impact.Severity.INFO; +import static org.sonar.api.issue.impact.Severity.LOW; +import static org.sonar.api.issue.impact.Severity.MEDIUM; class ImpactMeasureBuilderTest { @@ -32,17 +37,17 @@ class ImpactMeasureBuilderTest { void createEmptyMeasure_shouldReturnMeasureWithAllFields() { ImpactMeasureBuilder builder = ImpactMeasureBuilder.createEmpty(); assertThat(builder.buildAsMap()) - .containsAllEntriesOf(getImpactMap(0L, 0L, 0L, 0L)); - } - - private static Map<String, Long> getImpactMap(Long total, Long high, Long medium, Long low) { - return Map.of("total", total, Severity.HIGH.name(), high, Severity.MEDIUM.name(), medium, Severity.LOW.name(), low); + .containsAllEntriesOf(getImpactMap(0L, 0L, 0L, 0L, 0L, 0L)); } @Test void fromMap_shouldInitializeCorrectlyTheBuilder() { Map<String, Long> map = getImpactMap(6L, 3L, 2L, 1L); ImpactMeasureBuilder builder = ImpactMeasureBuilder.fromMap(map); + + map.put(INFO.name(), 0L); + map.put(BLOCKER.name(), 0L); + assertThat(builder.buildAsMap()) .isEqualTo(map); } @@ -65,8 +70,9 @@ class ImpactMeasureBuilderTest { LOW: 1 } """); + Map<String, Long> expectedMap = getImpactMap(6L, 3L, 2L, 1L, 0L, 0L); assertThat(builder.buildAsMap()) - .isEqualTo(getImpactMap(6L, 3L, 2L, 1L)); + .isEqualTo(expectedMap); } @Test @@ -78,44 +84,30 @@ class ImpactMeasureBuilderTest { } @Test - void buildAsMap_whenMissingSeverity_shouldThrowException() { - ImpactMeasureBuilder impactMeasureBuilder = ImpactMeasureBuilder.newInstance() - .setTotal(1L) - .setSeverity(Severity.HIGH, 1L) - .setSeverity(Severity.MEDIUM, 1L); - assertThatThrownBy(impactMeasureBuilder::buildAsMap) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Map must contain a key for severity LOW"); - } - - @Test - void buildAsString_whenMissingSeverity_shouldThrowException() { - ImpactMeasureBuilder impactMeasureBuilder = ImpactMeasureBuilder.newInstance() - .setTotal(1L) - .setSeverity(Severity.HIGH, 1L) - .setSeverity(Severity.MEDIUM, 1L); - assertThatThrownBy(impactMeasureBuilder::buildAsString) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Map must contain a key for severity LOW"); - } - - @Test void setSeverity_shouldInitializeSeverityValues() { ImpactMeasureBuilder builder = ImpactMeasureBuilder.newInstance() - .setSeverity(Severity.HIGH, 3L) - .setSeverity(Severity.MEDIUM, 2L) - .setSeverity(Severity.LOW, 1L) - .setTotal(6L); + .setSeverity(HIGH, 3L) + .setSeverity(MEDIUM, 2L) + .setSeverity(LOW, 1L) + .setSeverity(INFO, 4L) + .setSeverity(BLOCKER, 5L) + .setTotal(15L); assertThat(builder.buildAsMap()) - .isEqualTo(getImpactMap(6L, 3L, 2L, 1L)); + .isEqualTo(getImpactMap(15L, 3L, 2L, 1L, 4L, 5L)); } @Test void add_shouldSumImpactsAndTotal() { - ImpactMeasureBuilder builder = ImpactMeasureBuilder.fromMap(getImpactMap(6L, 3L, 2L, 1L)) - .add(ImpactMeasureBuilder.newInstance().setTotal(6L).setSeverity(Severity.HIGH, 3L).setSeverity(Severity.MEDIUM, 2L).setSeverity(Severity.LOW, 1L)); + ImpactMeasureBuilder builder = ImpactMeasureBuilder.fromMap(getImpactMap(11L, 3L, 2L, 1L, 1L, 4L)) + .add(ImpactMeasureBuilder.newInstance() + .setTotal(14L) + .setSeverity(HIGH, 3L) + .setSeverity(MEDIUM, 2L) + .setSeverity(LOW, 1L) + .setSeverity(INFO, 5L) + .setSeverity(BLOCKER, 3L)); assertThat(builder.buildAsMap()) - .isEqualTo(getImpactMap(12L, 6L, 4L, 2L)); + .isEqualTo(getImpactMap(25L, 6L, 4L, 2L, 6L, 7L)); } @Test @@ -128,10 +120,28 @@ class ImpactMeasureBuilderTest { } @Test - void getTotal_shoudReturnExpectedTotal() { + void getTotal_shouldReturnExpectedTotal() { ImpactMeasureBuilder builder = ImpactMeasureBuilder.fromMap(getImpactMap(6L, 3L, 2L, 1L)); assertThat(builder.getTotal()).isEqualTo(6L); } + private static Map<String, Long> getImpactMap(Long total, Long high, Long medium, Long low) { + return new HashMap<>() { + { + put("total", total); + put(HIGH.name(), high); + put(MEDIUM.name(), medium); + put(LOW.name(), low); + } + }; + } + + private static Map<String, Long> getImpactMap(Long total, Long high, Long medium, Long low, Long info, Long blocker) { + Map<String, Long> impactMap = getImpactMap(total, high, medium, low); + impactMap.put(INFO.name(), info); + impactMap.put(BLOCKER.name(), blocker); + return impactMap; + } + } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializerTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializerTest.java index ed9119174ec..e1dddaaafc0 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializerTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializerTest.java @@ -49,10 +49,8 @@ class ProjectMeasuresSoftwareQualityRatingsInitializerTest { initialMeasures.put(CoreMetrics.SQALE_RATING_KEY, 1.0); initialMeasures.put(CoreMetrics.RELIABILITY_RATING_KEY, 2.0); initialMeasures.put(CoreMetrics.SECURITY_RATING_KEY, 3.0); - initialMeasures.put(CoreMetrics.SECURITY_REVIEW_RATING_KEY, 4.0); initialMeasures.put(CoreMetrics.NEW_SECURITY_RATING_KEY, 4.0); - initialMeasures.put(CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY, 3.0); - initialMeasures.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, 2.0); + initialMeasures.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, 5.0); initialMeasures.put(CoreMetrics.NEW_RELIABILITY_RATING_KEY, 1.0); Map<String, Double> measures = new HashMap<>(initialMeasures); @@ -64,38 +62,8 @@ class ProjectMeasuresSoftwareQualityRatingsInitializerTest { .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 1.0) .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 2.0) .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, 3.0) - .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 4.0) .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, 4.0) - .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 3.0) - .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 2.0) + .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 5.0) .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 1.0); } - - @Test - void initializeSoftwareQualityRatings_whenERating_thenSoftwareQualityRatingCreatedWithD() { - Map<String, Double> initialMeasures = new HashMap<>(); - initialMeasures.put(CoreMetrics.SQALE_RATING_KEY, (double) Rating.E.getIndex()); - initialMeasures.put(CoreMetrics.RELIABILITY_RATING_KEY, (double) Rating.E.getIndex()); - initialMeasures.put(CoreMetrics.SECURITY_RATING_KEY, (double) Rating.E.getIndex()); - initialMeasures.put(CoreMetrics.SECURITY_REVIEW_RATING_KEY, (double) Rating.E.getIndex()); - initialMeasures.put(CoreMetrics.NEW_SECURITY_RATING_KEY, (double) Rating.E.getIndex()); - initialMeasures.put(CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY, (double) Rating.E.getIndex()); - initialMeasures.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, (double) Rating.E.getIndex()); - initialMeasures.put(CoreMetrics.NEW_RELIABILITY_RATING_KEY, (double) Rating.E.getIndex()); - - Map<String, Double> measures = new HashMap<>(initialMeasures); - - ProjectMeasuresSoftwareQualityRatingsInitializer.initializeSoftwareQualityRatings(measures); - - assertThat(measures).hasSize(initialMeasures.size() * 2) - .containsAllEntriesOf(initialMeasures) - .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, (double) Rating.D.getIndex()) - .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, (double) Rating.D.getIndex()) - .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, (double) Rating.D.getIndex()) - .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, (double) Rating.D.getIndex()) - .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, (double) Rating.D.getIndex()) - .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, (double) Rating.D.getIndex()) - .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, (double) Rating.D.getIndex()) - .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, (double) Rating.D.getIndex()); - } } 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 1d586900a1b..ec7c91b03b2 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 @@ -33,7 +33,6 @@ 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; @@ -56,36 +55,12 @@ class SecurityReviewRatingTest { return res.toArray(new Object[res.size()][2]); } - private static Object[][] valuesForSoftwareQualityRatings() { - List<Object[]> 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 void compute_percent() { assertThat(computePercent(0, 0)).isEmpty(); |