diff options
author | Dejan Milisavljevic <dejan.milisavljevic@sonarsource.com> | 2024-03-18 11:09:18 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-03-25 20:02:42 +0000 |
commit | 494316d9f27f7a64b762e37d4f8bc34e4b4370d7 (patch) | |
tree | 99614caafba41b900439722a96483c1b7a9b0421 | |
parent | d4518aca0c74114be84a81b9958027255453628d (diff) | |
download | sonarqube-494316d9f27f7a64b762e37d4f8bc34e4b4370d7.tar.gz sonarqube-494316d9f27f7a64b762e37d4f8bc34e4b4370d7.zip |
SONAR-21770 Accept new metrics 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'
18 files changed, 344 insertions, 127 deletions
diff --git a/gradle.properties b/gradle.properties index f4c8ae87ca5..73cf0543060 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group=org.sonarsource.sonarqube version=10.5 -pluginApiVersion=10.6.0.2114 +pluginApiVersion=10.7.0.2175 description=Open source platform for continuous inspection of code quality projectTitle=SonarQube org.gradle.jvmargs=-Xmx2048m diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java index b2b8f2cd0e7..f2e4de123ad 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java @@ -50,7 +50,7 @@ import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY; -import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES; +import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_ACCEPTED_ISSUES_KEY; @@ -59,16 +59,19 @@ import static org.sonar.api.measures.CoreMetrics.NEW_BUGS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES_KEY; import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY; -import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES; +import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY; -import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES; +import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY; import static org.sonar.api.rule.Severity.BLOCKER; @@ -109,9 +112,14 @@ public class IssueCounter extends IssueVisitor { INFO, NEW_INFO_VIOLATIONS_KEY); static final Map<String, String> IMPACT_TO_METRIC_KEY = Map.of( - SoftwareQuality.SECURITY.name(), SECURITY_ISSUES.key(), - SoftwareQuality.RELIABILITY.name(), RELIABILITY_ISSUES.key(), - SoftwareQuality.MAINTAINABILITY.name(), MAINTAINABILITY_ISSUES.key()); + SoftwareQuality.SECURITY.name(), SECURITY_ISSUES_KEY, + SoftwareQuality.RELIABILITY.name(), RELIABILITY_ISSUES_KEY, + SoftwareQuality.MAINTAINABILITY.name(), MAINTAINABILITY_ISSUES_KEY); + + static final Map<String, String> IMPACT_TO_NEW_METRIC_KEY = Map.of( + SoftwareQuality.SECURITY.name(), NEW_SECURITY_ISSUES_KEY, + SoftwareQuality.RELIABILITY.name(), NEW_RELIABILITY_ISSUES_KEY, + SoftwareQuality.MAINTAINABILITY.name(), NEW_MAINTAINABILITY_ISSUES_KEY); private static final Map<RuleType, String> TYPE_TO_METRIC_KEY = ImmutableMap.<RuleType, String>builder() .put(CODE_SMELL, CODE_SMELLS_KEY) @@ -222,9 +230,7 @@ public class IssueCounter extends IssueVisitor { String severity = entry.getKey(); String metricKey = entry.getValue(); Multiset<String> bag = currentCounters.counterForPeriod().severityBag; - Metric metric = metricRepository.getByKey(metricKey); - measureRepository.add(component, metric, Measure.newMeasureBuilder() - .create(bag.count(severity))); + addMeasure(component, metricKey, bag.count(severity)); } // waiting for Java 8 lambda in order to factor this loop with the previous one @@ -233,9 +239,12 @@ public class IssueCounter extends IssueVisitor { RuleType type = entry.getKey(); String metricKey = entry.getValue(); Multiset<RuleType> bag = currentCounters.counterForPeriod().typeBag; - Metric metric = metricRepository.getByKey(metricKey); - measureRepository.add(component, metric, Measure.newMeasureBuilder() - .create(bag.count(type))); + addMeasure(component, metricKey, bag.count(type)); + } + + for (Map.Entry<String, Map<String, Long>> impactEntry : currentCounters.counterForPeriod().impactsBag.entrySet()) { + String json = ImpactMeasureBuilder.fromMap(impactEntry.getValue()).buildAsString(); + addMeasure(component, IMPACT_TO_NEW_METRIC_KEY.get(impactEntry.getKey()), json); } addMeasure(component, NEW_ACCEPTED_ISSUES_KEY, currentCounters.counterForPeriod().accepted); diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java index 49d8ca5bd2f..7ad66ed9b40 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java @@ -27,8 +27,8 @@ import java.util.Map; import java.util.Set; import javax.annotation.Nullable; import org.assertj.core.data.MapEntry; -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.issue.impact.Severity; import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rules.RuleType; @@ -92,11 +92,14 @@ import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS; +import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES; import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS; +import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES; import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES; @@ -120,24 +123,25 @@ import static org.sonar.api.rules.RuleType.CODE_SMELL; import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder; import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_METRIC_KEY; +import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_NEW_METRIC_KEY; import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder; import static org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry.entryOf; -public class IssueCounterTest { +class IssueCounterTest { private static final Component FILE1 = builder(Component.Type.FILE, 1).build(); private static final Component FILE2 = builder(Component.Type.FILE, 2).build(); private static final Component FILE3 = builder(Component.Type.FILE, 3).build(); private static final Component PROJECT = builder(Component.Type.PROJECT, 4).addChildren(FILE1, FILE2, FILE3).build(); - @Rule - public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + @RegisterExtension + private final BatchReportReaderRule reportReader = new BatchReportReaderRule(); - @Rule - public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @RegisterExtension + private final TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); - @Rule - public MetricRepositoryRule metricRepository = new MetricRepositoryRule() + @RegisterExtension + private final MetricRepositoryRule metricRepository = new MetricRepositoryRule() .add(VIOLATIONS) .add(OPEN_ISSUES) .add(REOPENED_ISSUES) @@ -167,15 +171,19 @@ public class IssueCounterTest { .add(HIGH_IMPACT_ACCEPTED_ISSUES) .add(RELIABILITY_ISSUES) .add(MAINTAINABILITY_ISSUES) - .add(SECURITY_ISSUES); + .add(SECURITY_ISSUES) + .add(NEW_RELIABILITY_ISSUES) + .add(NEW_MAINTAINABILITY_ISSUES) + .add(NEW_SECURITY_ISSUES); - @Rule - public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); - private NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class); - private IssueCounter underTest = new IssueCounter(metricRepository, measureRepository, newIssueClassifier); + @RegisterExtension + private final MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); + private final NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class); + private final IssueCounter underTest = new IssueCounter(metricRepository, measureRepository, newIssueClassifier); + private static int issueCounter; @Test - public void count_issues_by_status() { + void count_issues_by_status() { // bottom-up traversal -> from files to project underTest.beforeComponent(FILE1); underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER)); @@ -203,7 +211,7 @@ public class IssueCounterTest { } @Test - public void count_issues_by_resolution() { + void count_issues_by_resolution() { // bottom-up traversal -> from files to project underTest.beforeComponent(FILE1); underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER)); @@ -233,7 +241,7 @@ public class IssueCounterTest { } @Test - public void count_unresolved_issues_by_severity() { + void count_unresolved_issues_by_severity() { // bottom-up traversal -> from files to project underTest.beforeComponent(FILE1); underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER)); @@ -257,7 +265,7 @@ public class IssueCounterTest { } @Test - public void count_unresolved_issues_by_type() { + void count_unresolved_issues_by_type() { // bottom-up traversal -> from files to project // file1 : one open code smell, one closed code smell (which will be excluded from metric) underTest.beforeComponent(FILE1); @@ -286,7 +294,7 @@ public class IssueCounterTest { } @Test - public void count_new_issues() { + void count_new_issues() { when(newIssueClassifier.isEnabled()).thenReturn(true); underTest.beforeComponent(FILE1); @@ -315,7 +323,7 @@ public class IssueCounterTest { } @Test - public void count_new_accepted_issues() { + void count_new_accepted_issues() { when(newIssueClassifier.isEnabled()).thenReturn(true); underTest.beforeComponent(FILE1); @@ -338,7 +346,7 @@ public class IssueCounterTest { } @Test - public void count_impacts() { + void onIssue_shouldCountOverallSoftwareQualitiesMeasures() { when(newIssueClassifier.isEnabled()).thenReturn(true); underTest.beforeComponent(FILE1); @@ -360,9 +368,43 @@ public class IssueCounterTest { Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet(); - assertSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(4, 2, 2, 0), entries); - assertSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(2, 1, 1, 0), entries); - assertSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(0, 0, 0, 0), entries); + assertOverallSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(4, 2, 2, 0), entries); + assertOverallSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(2, 1, 1, 0), entries); + assertOverallSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(0, 0, 0, 0), entries); + } + + @Test + void onIssue_shouldCountNewSoftwareQualitiesMeasures() { + when(newIssueClassifier.isEnabled()).thenReturn(true); + + underTest.beforeComponent(FILE1); + underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.MAINTAINABILITY, HIGH)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, MEDIUM)); + + underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, HIGH)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, LOW)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.RELIABILITY, HIGH)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, MEDIUM)); + + underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, MEDIUM)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, LOW)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.SECURITY, HIGH)); + underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, MEDIUM)); + + underTest.afterComponent(FILE1); + + underTest.beforeComponent(PROJECT); + underTest.afterComponent(PROJECT); + + Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet(); + + assertNewSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(2, 1, 1, 0), entries); + assertNewSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(2, 0, 1, 1), entries); + assertNewSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(4, 2, 1, 1), entries); } private static Map<String, Long> getImpactMeasure(long total, long high, long medium, long low) { @@ -374,11 +416,21 @@ public class IssueCounterTest { return map; } - private void assertSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap, + private void assertOverallSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap, Set<Map.Entry<String, Measure>> actualRaw) { + assertSoftwareQualityMeasures(softwareQuality, expectedMap, actualRaw, IMPACT_TO_METRIC_KEY); + } + + private void assertNewSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap, + Set<Map.Entry<String, Measure>> actualRaw) { + assertSoftwareQualityMeasures(softwareQuality, expectedMap, actualRaw, IMPACT_TO_NEW_METRIC_KEY); + } + + private void assertSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap, + Set<Map.Entry<String, Measure>> actualRaw, Map<String, String> impactToMetricMap) { Map.Entry<String, Measure> softwareQualityMap = actualRaw.stream() - .filter(e -> e.getKey().equals(IMPACT_TO_METRIC_KEY.get(softwareQuality.name()))) + .filter(e -> e.getKey().equals(impactToMetricMap.get(softwareQuality.name()))) .findFirst() .get(); @@ -386,7 +438,7 @@ public class IssueCounterTest { } @Test - public void count_high_impact_accepted_issues() { + void count_high_impact_accepted_issues() { when(newIssueClassifier.isEnabled()).thenReturn(true); underTest.beforeComponent(FILE1); @@ -413,7 +465,7 @@ public class IssueCounterTest { } @Test - public void exclude_hotspots_from_issue_counts() { + void exclude_hotspots_from_issue_counts() { // bottom-up traversal -> from files to project underTest.beforeComponent(FILE1); underTest.onIssue(FILE1, createSecurityHotspot()); @@ -437,7 +489,7 @@ public class IssueCounterTest { } @Test - public void exclude_new_hotspots_from_issue_counts() { + void exclude_new_hotspots_from_issue_counts() { when(newIssueClassifier.isEnabled()).thenReturn(true); underTest.beforeComponent(FILE1); @@ -496,7 +548,7 @@ public class IssueCounterTest { private DefaultIssue createNewIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality, Severity impactSeverity) { DefaultIssue issue = createNewIssue(resolution, status, MAJOR, CODE_SMELL); - issue.addImpact(SoftwareQuality.MAINTAINABILITY, impactSeverity); + issue.addImpact(softwareQuality, impactSeverity); return issue; } @@ -522,6 +574,7 @@ public class IssueCounterTest { private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) { return new DefaultIssue() + .setKey(String.valueOf(++issueCounter)) .setResolution(resolution).setStatus(status) .setSeverity(severity).setRuleKey(RuleTesting.XOO_X1) .setType(ruleType); diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java index 015f87d4271..29c6932e53f 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java @@ -37,6 +37,8 @@ import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.sonar.api.issue.Issue; import org.sonar.api.issue.impact.Severity; import org.sonar.api.issue.impact.SoftwareQuality; @@ -562,8 +564,10 @@ class IssueDaoIT { assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne(); } - @Test - void selectIssueImpactGroupsByComponent_shouldReturnImpactGroups() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void selectIssueImpactGroupsByComponent_shouldReturnImpactGroups(boolean inLeak) { + ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project)); RuleDto rule = db.rules().insert(); @@ -581,9 +585,10 @@ class IssueDaoIT { db.issues().insert(rule, project, file, i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_FALSE_POSITIVE).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH)))); - Collection<IssueImpactGroupDto> result = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file); + Collection<IssueImpactGroupDto> result = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file, inLeak ? 1L : Long.MAX_VALUE); assertThat(result).hasSize(5); + assertThat(result.stream().filter(IssueImpactGroupDto::isInLeak)).hasSize(inLeak ? 5 : 0); assertThat(result.stream().mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(6); assertThat(result.stream().filter(g -> g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(4); @@ -603,6 +608,44 @@ class IssueDaoIT { assertThat(result.stream().noneMatch(g -> RESOLUTION_FALSE_POSITIVE.equals(g.getResolution()))).isTrue(); assertThat(result.stream().noneMatch(g -> RELIABILITY == g.getSoftwareQuality())).isTrue(); assertThat(result.stream().noneMatch(g -> MEDIUM == g.getSeverity())).isTrue(); + + } + + @Test + void selectIssueImpactGroupsByComponent_whenNewCodeFromReferenceBranch_shouldReturnImpactGroups() { + + ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); + ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project)); + RuleDto rule = db.rules().insert(); + IssueDto issueInNewCodePeriod = db.issues().insert(rule, project, file, + i -> i.setStatus(STATUS_OPEN).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH), createImpact(MAINTAINABILITY, LOW)))); + db.issues().insert(rule, project, file, + i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH)))); + + db.issues().insertNewCodeReferenceIssue(issueInNewCodePeriod); + + Collection<IssueImpactGroupDto> result = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file, -1L); + + assertThat(result).hasSize(3); + assertThat(result.stream().filter(IssueImpactGroupDto::isInLeak)).hasSize(2); + assertThat(result.stream().mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(3); + + assertThat(result.stream().filter(g -> g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2); + assertThat(result.stream().filter(g -> g.getSoftwareQuality() == MAINTAINABILITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(1); + + assertThat(result.stream().filter(g -> g.getSeverity() == HIGH).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2); + assertThat(result.stream().filter(g -> g.getSeverity() == LOW).mapToLong(IssueImpactGroupDto::getCount).sum()).isOne(); + + assertThat(result.stream().filter(g -> STATUS_OPEN.equals(g.getStatus())).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2); + assertThat(result.stream().filter(g -> STATUS_REOPENED.equals(g.getStatus())).mapToLong(IssueImpactGroupDto::getCount).sum()).isZero(); + assertThat(result.stream().filter(g -> STATUS_RESOLVED.equals(g.getStatus())).mapToLong(IssueImpactGroupDto::getCount).sum()).isOne(); + + assertThat(result.stream().filter(g -> RESOLUTION_WONT_FIX.equals(g.getResolution())).mapToLong(IssueImpactGroupDto::getCount).sum()).isOne(); + + + assertThat(result.stream().filter(IssueImpactGroupDto::isInLeak).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2); + assertThat(result.stream().filter(g -> g.isInLeak() && g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(1); + assertThat(result.stream().filter(g -> g.isInLeak() && g.getSoftwareQuality() == MAINTAINABILITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(1); } @NotNull @@ -615,7 +658,7 @@ class IssueDaoIT { ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project)); - Collection<IssueImpactGroupDto> groups = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file); + Collection<IssueImpactGroupDto> groups = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file, 1_000L); assertThat(groups).isEmpty(); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java index d4708ab2fbd..e533bf62a1b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java @@ -90,8 +90,8 @@ public class IssueDao implements Dao { return mapper(dbSession).selectIssueGroupsByComponent(component, leakPeriodBeginningDate); } - public Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(DbSession dbSession, ComponentDto component) { - return mapper(dbSession).selectIssueImpactGroupsByComponent(component); + public Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(DbSession dbSession, ComponentDto component, long leakPeriodBeginningDate) { + return mapper(dbSession).selectIssueImpactGroupsByComponent(component, leakPeriodBeginningDate); } public Cursor<IndexedIssueDto> scrollIssuesForIndexation(DbSession dbSession, @Nullable @Param("branchUuid") String branchUuid, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueImpactGroupDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueImpactGroupDto.java index 9455e3a66ec..dbf05f35843 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueImpactGroupDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueImpactGroupDto.java @@ -31,6 +31,7 @@ public class IssueImpactGroupDto { private SoftwareQuality softwareQuality; private Severity severity; private long count; + private boolean inLeak; public IssueImpactGroupDto() { // nothing to do @@ -76,4 +77,12 @@ public class IssueImpactGroupDto { public void setCount(long count) { this.count = count; } + + public boolean isInLeak() { + return inLeak; + } + + public void setInLeak(boolean inLeak) { + this.inLeak = inLeak; + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java index 21f43b1773f..b1b3d7f760d 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java @@ -74,7 +74,7 @@ public interface IssueMapper { Collection<IssueGroupDto> selectIssueGroupsByComponent(@Param("component") ComponentDto component, @Param("leakPeriodBeginningDate") long leakPeriodBeginningDate); - Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(@Param("component") ComponentDto component); + Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(@Param("component") ComponentDto component, @Param("leakPeriodBeginningDate") long leakPeriodBeginningDate); List<IssueDto> selectByBranch(@Param("keys") Set<String> keys, @Nullable @Param("changedSince") Long changedSince); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml index 1b399c4a812..12aafee88dd 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml @@ -514,17 +514,35 @@ <select id="selectIssueImpactGroupsByComponent" resultType="org.sonar.db.issue.IssueImpactGroupDto" parameterType="map"> select - i.status as status, - i.resolution as resolution, - ii.software_quality as softwareQuality, - ii.severity as severity, - count(i.kee) as "count" - from issues i - inner join issues_impacts ii on i.kee = ii.issue_key - where i.status <> 'CLOSED' - and (i.resolution is null or i.resolution = 'WONTFIX') - and i.component_uuid = #{component.uuid,jdbcType=VARCHAR} - group by i.status, i.resolution, ii.software_quality, ii.severity + i2.status as status, + i2.resolution as resolution, + i2.software_quality as softwareQuality, + i2.severity as severity, + count(i2.kee) as "count", + i2.inLeak as inLeak + from ( + select + i.status, + i.resolution, + i.kee, + ii.software_quality, + ii.severity, + <if test="leakPeriodBeginningDate >= 0"> + case when i.issue_creation_date > #{leakPeriodBeginningDate,jdbcType=BIGINT} then ${_true} else ${_false} end as inLeak + </if> + <if test="leakPeriodBeginningDate < 0"> + case when n.uuid is null then ${_false} else ${_true} end as inLeak + </if> + from issues i + inner join issues_impacts ii on i.kee = ii.issue_key + <if test="leakPeriodBeginningDate < 0"> + left join new_code_reference_issues n on n.issue_key = i.kee + </if> + where i.status <> 'CLOSED' + and (i.resolution is null or i.resolution = 'WONTFIX') + and i.component_uuid = #{component.uuid,jdbcType=VARCHAR} + ) i2 + group by i2.status, i2.resolution, i2.software_quality, i2.severity, i2.inLeak </select> <select id="selectIssueKeysByComponentUuid" parameterType="string" resultType="string"> diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java index 00535396b66..62165b7ab7a 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java @@ -60,6 +60,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.tuple; import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY; @@ -867,12 +870,10 @@ class ComponentTreeActionIT { ComponentDto mainBranch = projectData.getMainBranchComponent(); addProjectPermission(projectData); db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(mainBranch)); - MetricDto dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(SECURITY_ISSUES_KEY)); - db.measures().insertLiveMeasure(mainBranch, dataMetric, c -> c.setData(SECURITY_ISSUES_KEY + "_data")); - dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(MAINTAINABILITY_ISSUES_KEY)); - db.measures().insertLiveMeasure(mainBranch, dataMetric, c -> c.setData(MAINTAINABILITY_ISSUES_KEY + "_data")); - dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(RELIABILITY_ISSUES_KEY)); - db.measures().insertLiveMeasure(mainBranch, dataMetric, c -> c.setData(RELIABILITY_ISSUES_KEY + "_data")); + + insertMetricAndLiveMeasure(mainBranch, SECURITY_ISSUES_KEY, "_data"); + insertMetricAndLiveMeasure(mainBranch, MAINTAINABILITY_ISSUES_KEY, "_data"); + insertMetricAndLiveMeasure(mainBranch, RELIABILITY_ISSUES_KEY, "_data"); ComponentTreeWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, mainBranch.getKey()) @@ -892,6 +893,40 @@ class ComponentTreeActionIT { } @Test + void execute_whenUsingSupportedNewDATAMetrics_shouldReturnMetrics() { + ProjectData projectData = db.components().insertPrivateProject(); + ComponentDto mainBranch = projectData.getMainBranchComponent(); + addProjectPermission(projectData); + db.components().insertSnapshot(mainBranch); + ComponentDto file1 = db.components().insertComponent(newFileDto(mainBranch)); + + insertMetricAndLiveMeasure(file1, NEW_SECURITY_ISSUES_KEY, "_data"); + insertMetricAndLiveMeasure(file1, NEW_MAINTAINABILITY_ISSUES_KEY, "_data"); + insertMetricAndLiveMeasure(file1, NEW_RELIABILITY_ISSUES_KEY, "_data"); + + ComponentTreeWsResponse response = ws.newRequest() + .setParam(PARAM_COMPONENT, mainBranch.getKey()) + .setParam(PARAM_METRIC_KEYS, format("%s,%s,%s", + NEW_SECURITY_ISSUES_KEY, + NEW_MAINTAINABILITY_ISSUES_KEY, + NEW_RELIABILITY_ISSUES_KEY + )) + .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,period") + .executeProtobuf(ComponentTreeWsResponse.class); + + assertThat(response.getComponents(0).getMeasuresCount()).isEqualTo(3); + + List<Measure> dataMeasures = response.getComponents(0).getMeasuresList(); + + assertThat(dataMeasures) + .extracting(Measure::getMetric, m-> m.getPeriod().getValue()) + .containsExactlyInAnyOrder(tuple(NEW_SECURITY_ISSUES_KEY, NEW_SECURITY_ISSUES_KEY + "_data"), + tuple(NEW_MAINTAINABILITY_ISSUES_KEY, NEW_MAINTAINABILITY_ISSUES_KEY + "_data"), + tuple(NEW_RELIABILITY_ISSUES_KEY, NEW_RELIABILITY_ISSUES_KEY + "_data") + ); + } + + @Test void fail_when_setting_more_than_15_metric_keys() { ComponentDto mainBranch = db.components().insertPrivateProject().getMainBranchComponent(); db.components().insertSnapshot(mainBranch); @@ -1125,6 +1160,12 @@ class ComponentTreeActionIT { .setOptimizedBestValue(false); } + private void insertMetricAndLiveMeasure(ComponentDto dto, String key, String additionalData) { + MetricDto dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(key)); + db.measures().insertLiveMeasure(dto, dataMetric, c -> c.setData(key + additionalData)); + + } + private static MetricDto newDataMetricDto(String key) { return newMetricDto().setValueType(DATA.name()).setKey(key); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueCounter.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueCounter.java index 11cc56ac454..e2f3840e667 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueCounter.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueCounter.java @@ -167,16 +167,16 @@ class IssueCounter { return onlyInLeak ? count.leak : count.absolute; } - public String getBySoftwareQuality(SoftwareQuality softwareQuality) { + public String getBySoftwareQuality(SoftwareQuality softwareQuality, boolean onlyInLeak) { Map<Severity, Count> severityToCount = bySoftwareQualityAndSeverity.get(softwareQuality); ImpactMeasureBuilder impactMeasureBuilder; if (severityToCount != null) { impactMeasureBuilder = ImpactMeasureBuilder.newInstance(); for (Severity severity : Severity.values()) { - impactMeasureBuilder = impactMeasureBuilder.setSeverity(severity, Optional.ofNullable(severityToCount.get(severity)).map(count -> count.absolute).orElse(0L)); + impactMeasureBuilder = impactMeasureBuilder.setSeverity(severity, value(severityToCount.get(severity), onlyInLeak)); } - impactMeasureBuilder = impactMeasureBuilder.setTotal(severityToCount.values().stream().mapToLong(count -> count.absolute).sum()); + impactMeasureBuilder = impactMeasureBuilder.setTotal(severityToCount.values().stream().mapToLong(count -> value(count, onlyInLeak)).sum()); } else { impactMeasureBuilder = ImpactMeasureBuilder.createEmpty(); } @@ -197,6 +197,9 @@ class IssueCounter { public void add(IssueImpactGroupDto group) { absolute += group.getCount(); + if (group.isInLeak()) { + leak += group.getCount(); + } } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveMeasureTreeUpdaterImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveMeasureTreeUpdaterImpl.java index 71eb40833c8..7cff81bb3d9 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveMeasureTreeUpdaterImpl.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveMeasureTreeUpdaterImpl.java @@ -89,7 +89,7 @@ public class LiveMeasureTreeUpdaterImpl implements LiveMeasureTreeUpdater { components.getSortedTree().forEach(c -> { IssueCounter issueCounter = new IssueCounter(dbClient.issueDao().selectIssueGroupsByComponent(dbSession, c, beginningOfLeak), - dbClient.issueDao().selectIssueImpactGroupsByComponent(dbSession, c)); + dbClient.issueDao().selectIssueImpactGroupsByComponent(dbSession, c, beginningOfLeak)); for (MeasureUpdateFormula formula : formulaFactory.getFormulas()) { if (shouldComputeMetric(formula, useLeakFormulas, components.getBranch(), matrix)) { context.change(c, formula); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java index f70214658ae..24c02872a79 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java @@ -60,13 +60,22 @@ public class MeasureUpdateFormulaFactoryImpl implements MeasureUpdateFormulaFact (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.SECURITY_HOTSPOT, false))), new MeasureUpdateFormula(CoreMetrics.RELIABILITY_ISSUES, false, true, new ImpactAddChildren(), - (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.RELIABILITY))), + (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.RELIABILITY, false))), new MeasureUpdateFormula(CoreMetrics.MAINTAINABILITY_ISSUES, false, true, new ImpactAddChildren(), - (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.MAINTAINABILITY))), + (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.MAINTAINABILITY, false))), new MeasureUpdateFormula(CoreMetrics.SECURITY_ISSUES, false, true, new ImpactAddChildren(), - (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.SECURITY))), + (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.SECURITY, false))), + + new MeasureUpdateFormula(CoreMetrics.NEW_RELIABILITY_ISSUES, true, true, new ImpactAddChildren(), + (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.RELIABILITY, true))), + + new MeasureUpdateFormula(CoreMetrics.NEW_MAINTAINABILITY_ISSUES, true, true, new ImpactAddChildren(), + (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.MAINTAINABILITY, true))), + + new MeasureUpdateFormula(CoreMetrics.NEW_SECURITY_ISSUES, true, true, new ImpactAddChildren(), + (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.SECURITY, true))), new MeasureUpdateFormula(CoreMetrics.VIOLATIONS, false, new AddChildren(), (context, issues) -> context.setValue(issues.countUnresolved(false))), diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java index 3356cd598e8..98f87ca1bfb 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java @@ -99,6 +99,7 @@ public class ComponentAction implements MeasuresWsAction { new Change("10.5", String.format("The metrics %s are now deprecated " + "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", MeasuresWsModule.getDeprecatedMetricsInSonarQube105())), + new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"), new Change("10.4", String.format("The metrics %s are now deprecated " + "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", MeasuresWsModule.getDeprecatedMetricsInSonarQube104())), diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java index 3e65aee8c97..845cf6ccc2c 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java @@ -79,6 +79,9 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Optional.ofNullable; import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY; import static org.sonar.api.measures.Metric.ValueType.DATA; @@ -184,6 +187,7 @@ public class ComponentTreeAction implements MeasuresWsAction { .setHandler(this) .addPagingParams(100, MAX_SIZE) .setChangelog( + new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"), new Change("10.5", String.format("The metrics %s are now deprecated " + "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", MeasuresWsModule.getDeprecatedMetricsInSonarQube105())), @@ -683,7 +687,14 @@ public class ComponentTreeAction implements MeasuresWsAction { static final Set<String> FORBIDDEN_METRIC_TYPES = Set.of(DISTRIB.name()); static final Map<String, Set<String>> PARTIALLY_SUPPORTED_METRICS= Map. of( - DATA.name(), Set.of(SECURITY_ISSUES_KEY, MAINTAINABILITY_ISSUES_KEY, RELIABILITY_ISSUES_KEY)); + DATA.name(), + Set.of( + SECURITY_ISSUES_KEY, + MAINTAINABILITY_ISSUES_KEY, + RELIABILITY_ISSUES_KEY, + NEW_SECURITY_ISSUES_KEY, + NEW_MAINTAINABILITY_ISSUES_KEY, + NEW_RELIABILITY_ISSUES_KEY)); @Override public boolean test(@Nonnull MetricDto input) { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java index fc9743db4ea..6acd198f964 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java @@ -90,6 +90,7 @@ public class SearchAction implements MeasuresWsAction { new Change("10.5", String.format("The metrics %s are now deprecated " + "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", MeasuresWsModule.getDeprecatedMetricsInSonarQube105())), + new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"), new Change("10.4", String.format("The metrics %s are now deprecated " + "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", MeasuresWsModule.getDeprecatedMetricsInSonarQube104())), diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java index 1c63a887e3f..2f47507fd31 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java @@ -98,6 +98,7 @@ public class SearchHistoryAction implements MeasuresWsAction { new Change("10.5", String.format("The metrics %s are now deprecated " + "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", MeasuresWsModule.getDeprecatedMetricsInSonarQube105())), + new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"), new Change("10.4", String.format("The metrics %s are now deprecated " + "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", MeasuresWsModule.getDeprecatedMetricsInSonarQube104())), diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java index b8e35b8882b..b3ef284a41d 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java @@ -31,7 +31,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.sonar.api.issue.Issue; import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.measures.CoreMetrics; @@ -62,13 +62,13 @@ import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STA import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING; import static org.sonar.test.JsonAssert.assertJson; -public class MeasureUpdateFormulaFactoryImplTest { +class MeasureUpdateFormulaFactoryImplTest { - public static final Gson GSON = new GsonBuilder().create(); + private static final Gson GSON = new GsonBuilder().create(); private final MeasureUpdateFormulaFactoryImpl underTest = new MeasureUpdateFormulaFactoryImpl(); @Test - public void getFormulaMetrics_include_the_dependent_metrics() { + void getFormulaMetrics_include_the_dependent_metrics() { for (MeasureUpdateFormula formula : underTest.getFormulas()) { assertThat(underTest.getFormulaMetrics()).contains(formula.getMetric()); for (Metric<?> dependentMetric : formula.getDependentMetrics()) { @@ -78,7 +78,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void hierarchy_adding_numbers() { + void hierarchy_adding_numbers() { new HierarchyTester(CoreMetrics.VIOLATIONS) .withValue(1d) .withChildrenValues(2d, 3d) @@ -95,7 +95,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void hierarchy_highest_rating() { + void hierarchy_highest_rating() { new HierarchyTester(CoreMetrics.RELIABILITY_RATING) .withValue(1d) .withChildrenValues(2d, 3d) @@ -113,7 +113,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void hierarchy_combining_other_metrics() { + void hierarchy_combining_other_metrics() { new HierarchyTester(SECURITY_HOTSPOTS_TO_REVIEW_STATUS) .withValue(SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1d) .withChildrenHotspotsCounts(10, 10, 2, 10) @@ -152,7 +152,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_violations() { + void test_violations() { withNoIssues().assertThatValueIs(CoreMetrics.VIOLATIONS, 0); with(newGroup(), newGroup().setCount(4)).assertThatValueIs(CoreMetrics.VIOLATIONS, 5); @@ -166,7 +166,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_bugs() { + void test_bugs() { withNoIssues().assertThatValueIs(CoreMetrics.BUGS, 0); with( newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(3), @@ -179,7 +179,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_code_smells() { + void test_code_smells() { withNoIssues().assertThatValueIs(CoreMetrics.CODE_SMELLS, 0); with( newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3), @@ -192,7 +192,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_vulnerabilities() { + void test_vulnerabilities() { withNoIssues().assertThatValueIs(CoreMetrics.VULNERABILITIES, 0); with( newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3), @@ -205,7 +205,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_security_hotspots() { + void test_security_hotspots() { withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 0); with( newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR).setCount(3), @@ -218,7 +218,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_security_review_rating() { + void test_security_review_rating() { with( newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1)) @@ -229,7 +229,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_security_hotspots_reviewed() { + void test_security_hotspots_reviewed() { with( newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1)) @@ -240,7 +240,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_security_hotspots_reviewed_status() { + 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)) @@ -251,7 +251,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_security_hotspots_to_review_status() { + 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)) @@ -262,7 +262,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void count_unresolved_by_severity() { + void count_unresolved_by_severity() { withNoIssues() .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 0) .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 0) @@ -291,7 +291,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void count_resolved() { + void count_resolved() { withNoIssues() .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 0) .assertThatValueIs(CoreMetrics.ACCEPTED_ISSUES, 0); @@ -312,7 +312,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void count_by_status() { + void count_by_status() { withNoIssues() .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 0) .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 0) @@ -333,7 +333,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_technical_debt() { + void test_technical_debt() { withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0); with( @@ -350,7 +350,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_reliability_remediation_effort() { + void test_reliability_remediation_effort() { withNoIssues().assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 0); with( @@ -364,7 +364,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_security_remediation_effort() { + void test_security_remediation_effort() { withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 0); with( @@ -378,7 +378,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_sqale_debt_ratio_and_sqale_rating() { + void test_sqale_debt_ratio_and_sqale_rating() { withNoIssues() .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0) .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A); @@ -449,7 +449,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_effort_to_reach_maintainability_rating_A() { + void test_effort_to_reach_maintainability_rating_A() { withNoIssues() .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0); @@ -493,7 +493,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_reliability_rating() { + void test_reliability_rating() { withNoIssues() .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A); @@ -513,7 +513,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_security_rating() { + void test_security_rating() { withNoIssues() .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A); @@ -533,7 +533,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_bugs() { + void test_new_bugs() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 0.0); with( @@ -548,7 +548,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_code_smells() { + void test_new_code_smells() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 0.0); with( @@ -562,7 +562,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_vulnerabilities() { + void test_new_vulnerabilities() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 0.0); with( @@ -576,7 +576,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_security_hotspots() { + void test_new_security_hotspots() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 0); with( @@ -590,7 +590,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_violations() { + void test_new_violations() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 0.0); with( @@ -605,7 +605,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_blocker_violations() { + void test_new_blocker_violations() { withNoIssues() .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0); @@ -622,7 +622,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_critical_violations() { + void test_new_critical_violations() { withNoIssues() .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 0.0); @@ -639,7 +639,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_major_violations() { + void test_new_major_violations() { withNoIssues() .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0); @@ -656,7 +656,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_minor_violations() { + void test_new_minor_violations() { withNoIssues() .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0); @@ -673,7 +673,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_info_violations() { + void test_new_info_violations() { withNoIssues() .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0); @@ -690,7 +690,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_accepted_issues() { + void test_new_accepted_issues() { withNoIssues() .assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 0); @@ -706,7 +706,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_technical_debt() { + void test_new_technical_debt() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0); with( @@ -722,7 +722,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_reliability_remediation_effort() { + void test_new_reliability_remediation_effort() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 0.0); with( @@ -737,7 +737,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_security_remediation_effort() { + void test_new_security_remediation_effort() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 0.0); with( @@ -752,7 +752,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_reliability_rating() { + void test_new_reliability_rating() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.A); with( @@ -769,7 +769,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_security_rating() { + void test_new_security_rating() { withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.A); with( @@ -786,7 +786,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_security_review_rating() { + void test_new_security_review_rating() { 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), @@ -799,7 +799,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_security_hotspots_reviewed() { + void test_new_security_hotspots_reviewed() { 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), @@ -812,7 +812,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_security_hotspots_reviewed_status() { + 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), @@ -825,7 +825,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_security_hotspots_to_review_status() { + 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), @@ -838,7 +838,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void test_new_sqale_debt_ratio_and_new_maintainability_rating() { + void test_new_sqale_debt_ratio_and_new_maintainability_rating() { withNoIssues() .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0) .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A); @@ -909,7 +909,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void compute_shouldComputeHighImpactAcceptedIssues() { + void compute_shouldComputeHighImpactAcceptedIssues() { withNoIssues() .assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 0); @@ -927,7 +927,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void computeHierarchy_shouldComputeImpactMeasures() { + void computeHierarchy_shouldComputeImpactMeasures() { new HierarchyTester(CoreMetrics.RELIABILITY_ISSUES) .withValue(impactMeasureToJson(6, 1, 2, 3)) .withChildrenValues(impactMeasureToJson(6, 1, 2, 3), impactMeasureToJson(10, 5, 3, 2)) @@ -939,7 +939,7 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void compute_shouldComputeImpactMeasures() { + void compute_shouldComputeImpactMeasures() { with( newImpactGroup(RELIABILITY, HIGH, 3), newImpactGroup(RELIABILITY, MEDIUM, 4), @@ -953,11 +953,14 @@ public class MeasureUpdateFormulaFactoryImplTest { } @Test - public void compute_whenNoIssues_shouldComputeImpactMeasures() { + void compute_whenNoIssues_shouldComputeImpactMeasures() { withNoIssues() .assertThatJsonValueIs(CoreMetrics.RELIABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0)) .assertThatJsonValueIs(CoreMetrics.MAINTAINABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0)) - .assertThatJsonValueIs(CoreMetrics.SECURITY_ISSUES, impactMeasureToJson(0, 0, 0, 0)); + .assertThatJsonValueIs(CoreMetrics.SECURITY_ISSUES, impactMeasureToJson(0, 0, 0, 0)) + .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0)) + .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0)) + .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_ISSUES, impactMeasureToJson(0, 0, 0, 0)); } private static String impactMeasureToJson(long total, long high, long medium, long low) { @@ -1031,6 +1034,12 @@ public class MeasureUpdateFormulaFactoryImplTest { return this; } + Verifier assertThatLeakValueIs(Metric metric, String expectedValue) { + TestContext context = run(metric, true); + assertJson(context.stringValue).isSimilarTo(expectedValue); + return this; + } + Verifier assertNoLeakValue(Metric metric) { TestContext context = run(metric, true); assertThat(context.ratingValue).isNull(); 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 7d302471139..b92c78759ad 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -3295,6 +3295,15 @@ metric.reliability_issues.short_name=Reliability metric.security_issues.name=Security Issues metric.security_issues.description=Security issues metric.security_issues.short_name=Security +metric.new_maintainability_issues.name=New Maintainability Issues +metric.new_maintainability_issues.description=New maintainability issues +metric.new_maintainability_issues.short_name=New Maintainability +metric.new_reliability_issues.name=New Reliability Issues +metric.new_reliability_issues.description=New reliability issues +metric.new_reliability_issues.short_name=New Reliability +metric.new_security_issues.name=New Security Issues +metric.new_security_issues.description=New security issues +metric.new_security_issues.short_name=New Security #------------------------------------------------------------------------------ # |