aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2024-12-18 11:00:05 +0100
committerSteve Marion <steve.marion@sonarsource.com>2024-12-18 11:13:25 +0100
commitfb42f37f9e0f25420d67211690aa9bd4273faa08 (patch)
tree7a3a614cb7fb22f9f2f2dd77987b461ce09bd462
parentd3bba946fe854e126d713fce6dcfbcaa61b6eedd (diff)
downloadsonarqube-fb42f37f9e0f25420d67211690aa9bd4273faa08.tar.gz
sonarqube-fb42f37f9e0f25420d67211690aa9bd4273faa08.zip
SONAR-23974 Optimize the scanner report
-rw-r--r--server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorIT.java8
-rw-r--r--server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorIT.java33
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ImpactMapper.java45
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRule.java13
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java112
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRule.java11
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStep.java10
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ImpactMapperTest.java46
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRuleTest.java7
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java34
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRulesHolderImplTest.java9
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/AlwaysActiveRulesHolderImpl.java3
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStepTest.java9
-rw-r--r--sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java6
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ImpactMapper.java38
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java22
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ActiveRulesPublisher.java8
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java13
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java6
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ImpactMapperTest.java39
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java81
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ActiveRulesPublisherTest.java9
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java8
-rw-r--r--sonar-scanner-protocol/src/main/protobuf/scanner_report.proto31
26 files changed, 401 insertions, 206 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorIT.java b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorIT.java
index dcd42febcb9..41ea7e82137 100644
--- a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorIT.java
+++ b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorIT.java
@@ -86,8 +86,8 @@ public class AdHocRuleCreatorIT {
.setSeverity(Constants.Severity.BLOCKER)
.setType(ScannerReport.IssueType.BUG)
.setCleanCodeAttribute(CleanCodeAttribute.DISTINCT.name())
- .addDefaultImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(SoftwareQuality.RELIABILITY.name())
- .setSeverity(org.sonar.api.issue.impact.Severity.LOW.name()).build())
+ .addDefaultImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(ScannerReport.SoftwareQuality.RELIABILITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_LOW).build())
.build());
RuleDto rule = underTest.persistAndIndex(dbSession, addHocRule);
@@ -178,8 +178,8 @@ public class AdHocRuleCreatorIT {
.setDescription(rule.getAdHocDescription())
.setSeverity(Constants.Severity.valueOf(rule.getAdHocSeverity()))
.setType(ScannerReport.IssueType.forNumber(rule.getAdHocType()))
- .addDefaultImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY.name())
- .setSeverity(org.sonar.api.issue.impact.Severity.HIGH.name()).build())
+ .addDefaultImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(ScannerReport.SoftwareQuality.MAINTAINABILITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_HIGH).build())
.setCleanCodeAttribute(CleanCodeAttribute.CLEAR.name())
.build()));
diff --git a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorIT.java b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorIT.java
index 7f87fe8baa7..5a78470a1ee 100644
--- a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorIT.java
+++ b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorIT.java
@@ -170,6 +170,7 @@ public class IntegrateIssuesVisitorIT {
@Test
public void process_new_issue() {
+ // Active rule has severity major
ruleRepositoryRule.add(RuleTesting.XOO_X1);
when(analysisMetadataHolder.isBranch()).thenReturn(true);
ScannerReport.Issue reportIssue = getReportIssue(RuleTesting.XOO_X1);
@@ -180,32 +181,32 @@ public class IntegrateIssuesVisitorIT {
List<DefaultIssue> issues = newArrayList(protoIssueCache.traverse());
assertThat(issues).hasSize(1);
assertThat(issues.get(0).codeVariants()).containsExactlyInAnyOrder("foo", "bar");
+ assertThat(issues.get(0).severity()).isEqualTo(Severity.MAJOR);
}
@Test
- public void process_existing_issue() {
- RuleKey ruleKey = RuleTesting.XOO_X1;
- // Issue from db has severity major
- addBaseIssue(ruleKey);
-
- // Issue from report has severity blocker
- ScannerReport.Issue reportIssue = getReportIssue(ruleKey);
+ public void process_new_issue_with_overridden_severity() {
+ // Active rule has severity major
+ ruleRepositoryRule.add(RuleTesting.XOO_X1);
+ when(analysisMetadataHolder.isBranch()).thenReturn(true);
+ ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder(getReportIssue(RuleTesting.XOO_X1)).setOverriddenSeverity(Constants.Severity.BLOCKER).build();
reportReader.putIssues(FILE_REF, singletonList(reportIssue));
underTest.visitAny(FILE);
List<DefaultIssue> issues = newArrayList(protoIssueCache.traverse());
assertThat(issues).hasSize(1);
+ assertThat(issues.get(0).codeVariants()).containsExactlyInAnyOrder("foo", "bar");
assertThat(issues.get(0).severity()).isEqualTo(Severity.BLOCKER);
}
@Test
- public void dont_cache_existing_issue_if_unmodified() {
+ public void process_existing_issue() {
+ // Active rule has severity major
RuleKey ruleKey = RuleTesting.XOO_X1;
- // Issue from db has severity major
+ // Issue from db has severity minor
addBaseIssue(ruleKey);
- // Issue from report has severity blocker
ScannerReport.Issue reportIssue = getReportIssue(ruleKey);
reportReader.putIssues(FILE_REF, singletonList(reportIssue));
@@ -213,7 +214,7 @@ public class IntegrateIssuesVisitorIT {
List<DefaultIssue> issues = newArrayList(protoIssueCache.traverse());
assertThat(issues).hasSize(1);
- assertThat(issues.get(0).severity()).isEqualTo(Severity.BLOCKER);
+ assertThat(issues.get(0).severity()).isEqualTo(Severity.MAJOR);
}
@Test
@@ -267,7 +268,6 @@ public class IntegrateIssuesVisitorIT {
@Test
public void reuse_issues_when_data_unchanged() {
RuleKey ruleKey = RuleTesting.XOO_X1;
- // Issue from db has severity major
addBaseIssue(ruleKey);
// Issue from report has severity blocker
@@ -298,11 +298,11 @@ public class IntegrateIssuesVisitorIT {
when(branch.getType()).thenReturn(BranchType.BRANCH);
when(analysisMetadataHolder.getBranch()).thenReturn(branch);
+ // Active rule has severity major
RuleKey ruleKey = RuleTesting.XOO_X1;
- // Issue from main branch has severity major
+ // Issue from main branch has severity minor
addBaseIssueOnBranch(ruleKey);
- // Issue from report has severity blocker
ScannerReport.Issue reportIssue = getReportIssue(ruleKey);
reportReader.putIssues(FILE_REF, singletonList(reportIssue));
@@ -310,7 +310,7 @@ public class IntegrateIssuesVisitorIT {
List<DefaultIssue> issues = newArrayList(protoIssueCache.traverse());
assertThat(issues).hasSize(1);
- assertThat(issues.get(0).severity()).isEqualTo(Severity.BLOCKER);
+ assertThat(issues.get(0).severity()).isEqualTo(Severity.MAJOR);
assertThat(issues.get(0).isNew()).isFalse();
assertThat(issues.get(0).isCopied()).isTrue();
assertThat(issues.get(0).changes()).hasSize(1);
@@ -352,7 +352,7 @@ public class IntegrateIssuesVisitorIT {
IssueDto issue = IssueTesting.newIssue(ruleDto, project, file)
.setKee("ISSUE")
- .setSeverity(Severity.MAJOR);
+ .setSeverity(Severity.MINOR);
dbTester.getDbClient().issueDao().insert(dbTester.getSession(), issue);
dbTester.getSession().commit();
}
@@ -382,7 +382,6 @@ public class IntegrateIssuesVisitorIT {
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
.addAllCodeVariants(Set.of("foo", "bar"))
- .setSeverity(Constants.Severity.BLOCKER)
.build();
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ImpactMapper.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ImpactMapper.java
new file mode 100644
index 00000000000..5f5ad83adbb
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ImpactMapper.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+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;
+
+public class ImpactMapper {
+ private ImpactMapper() {
+ }
+
+ public static Severity mapImpactSeverity(ScannerReport.ImpactSeverity severity) {
+ return switch (severity) {
+ case ImpactSeverity_BLOCKER -> BLOCKER;
+ case ImpactSeverity_HIGH -> HIGH;
+ case ImpactSeverity_MEDIUM -> MEDIUM;
+ case ImpactSeverity_LOW -> LOW;
+ case ImpactSeverity_INFO -> INFO;
+ case UNRECOGNIZED, ImpactSeverity_UNKNOWN_IMPACT_SEVERITY -> throw new IllegalArgumentException("Unknown impact severity: " + severity);
+ };
+ }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRule.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRule.java
index 8ca6d401cdf..eabdef556dc 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRule.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRule.java
@@ -134,20 +134,12 @@ public class NewAdHocRule {
private static Map<SoftwareQuality, Severity> mapImpacts(List<ScannerReport.Impact> impacts) {
if (!impacts.isEmpty()) {
- return impacts.stream()
- .collect(Collectors.toMap(i -> mapSoftwareQuality(i.getSoftwareQuality()), i -> mapImpactSeverity(i.getSeverity())));
+ return impacts.stream().collect(Collectors.toMap(impact -> SoftwareQuality.valueOf(
+ impact.getSoftwareQuality().name()), impact -> org.sonar.ce.task.projectanalysis.issue.ImpactMapper.mapImpactSeverity(impact.getSeverity())));
}
return Collections.emptyMap();
}
- private static Severity mapImpactSeverity(String severity) {
- return Severity.valueOf(severity);
- }
-
- private static SoftwareQuality mapSoftwareQuality(String softwareQuality) {
- return SoftwareQuality.valueOf(softwareQuality);
- }
-
public RuleKey getKey() {
return key;
}
@@ -210,5 +202,4 @@ public class NewAdHocRule {
return key.hashCode();
}
-
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
index 3100a1368b6..6a86e5648be 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
@@ -21,13 +21,16 @@ package org.sonar.ce.task.projectanalysis.issue;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory;
+import org.sonar.api.issue.impact.Severity;
import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType;
@@ -37,6 +40,7 @@ import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
import org.sonar.ce.task.projectanalysis.issue.filter.IssueFilter;
+import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRule;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder;
import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
import org.sonar.core.issue.DefaultIssue;
@@ -47,13 +51,11 @@ import org.sonar.core.util.CloseableIterator;
import org.sonar.db.protobuf.DbCommons;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.scanner.protocol.Constants;
-import org.sonar.scanner.protocol.Constants.Severity;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReport.Impact;
import org.sonar.scanner.protocol.output.ScannerReport.IssueType;
import org.sonar.server.rule.CommonRuleKeys;
-import static java.util.stream.Collectors.toMap;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
import static org.sonar.api.issue.Issue.STATUS_OPEN;
import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
@@ -146,7 +148,7 @@ public class TrackerRawInputFactory {
private boolean isOnInactiveRule(ScannerReport.Issue reportIssue) {
RuleKey ruleKey = RuleKey.of(reportIssue.getRuleRepository(), reportIssue.getRuleKey());
- return !activeRulesHolder.get(ruleKey).isPresent();
+ return activeRulesHolder.get(ruleKey).isEmpty();
}
private boolean isIssueOnUnsupportedCommonRule(ScannerReport.Issue issue) {
@@ -176,9 +178,7 @@ public class TrackerRawInputFactory {
Rule rule = ruleRepository.getByKey(ruleKey);
issue.setMessage(rule.getName());
}
- if (reportIssue.getSeverity() != Severity.UNSET_SEVERITY) {
- issue.setSeverity(reportIssue.getSeverity().name());
- }
+ issue.setSeverity(replaceDefaultWithOverriddenSeverity(issue.ruleKey(), reportIssue));
if (Double.compare(reportIssue.getGap(), 0D) != 0) {
issue.setGap(reportIssue.getGap());
}
@@ -198,24 +198,56 @@ public class TrackerRawInputFactory {
issue.setRuleDescriptionContextKey(reportIssue.hasRuleDescriptionContextKey() ? reportIssue.getRuleDescriptionContextKey() : null);
issue.setCodeVariants(reportIssue.getCodeVariantsList());
- issue.replaceImpacts(replaceDefaultWithOverridenImpacts(issue.ruleKey(), reportIssue.getOverridenImpactsList()));
+ issue.replaceImpacts(replaceDefaultWithOverriddenImpactsForIssue(issue.ruleKey(), reportIssue.getOverriddenImpactsList()));
return issue;
}
- private Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> replaceDefaultWithOverridenImpacts(RuleKey ruleKey, List<Impact> overridenImpactsList) {
- Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> overridenImpactMap = getOverridenImpactMap(overridenImpactsList);
- return ruleRepository.getByKey(ruleKey).getDefaultImpacts().entrySet()
- .stream()
- .collect(toMap(
- Map.Entry::getKey,
- entry -> overridenImpactMap.containsKey(entry.getKey()) ? overridenImpactMap.get(entry.getKey()) : entry.getValue()));
+ private String replaceDefaultWithOverriddenSeverity(RuleKey ruleKey, ScannerReport.Issue reportIssue) {
+ if (reportIssue.hasOverriddenSeverity()) {
+ return reportIssue.getOverriddenSeverity().name();
+ } else {
+ // Rule can't be inactive (see contract of IssueVisitor)
+ ActiveRule activeRule = activeRulesHolder.get(ruleKey).orElseThrow(() -> new IllegalStateException("Rule " + ruleKey + " is not active"));
+ return activeRule.getSeverity();
+ }
+ }
+
+ private Map<SoftwareQuality, Severity> replaceDefaultWithOverriddenImpactsForIssue(RuleKey ruleKey, List<Impact> overriddenImpactsList) {
+ // Rule can't be inactive (see contract of IssueVisitor)
+ ActiveRule activeRule = activeRulesHolder.get(ruleKey).orElseThrow(() -> new IllegalStateException("Rule " + ruleKey + " is not active"));
+ return replaceDefaultWithOverriddenImpacts(ruleKey, activeRule, overriddenImpactsList);
+ }
+
+ private Map<SoftwareQuality, Severity> replaceDefaultWithOverriddenImpactsForExternalIssue(RuleKey ruleKey, List<Impact> overriddenImpactsList) {
+ return replaceDefaultWithOverriddenImpacts(ruleKey, null, overriddenImpactsList);
+ }
+
+ private Map<SoftwareQuality, Severity> replaceDefaultWithOverriddenImpacts(RuleKey ruleKey, @Nullable ActiveRule activeRule,
+ List<Impact> overriddenImpactsList) {
+ EnumMap<SoftwareQuality, Severity> impacts = new EnumMap<>(SoftwareQuality.class);
+ impacts.putAll(ruleRepository.getByKey(ruleKey).getDefaultImpacts());
+ if (activeRule != null) {
+ // Override default impacts with the ones possibly defined in the quality profile
+ overrideImpacts(impacts, activeRule.getImpacts());
+ }
+ // Override default impacts with the ones possibly defined at the issue level
+ overrideImpacts(impacts, toMap(overriddenImpactsList));
+ return impacts;
}
- private static Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> getOverridenImpactMap(List<Impact> overridenImpactsList) {
- return overridenImpactsList.stream()
- .collect(toMap(
- impact -> SoftwareQuality.valueOf(impact.getSoftwareQuality()),
- impact -> org.sonar.api.issue.impact.Severity.valueOf(impact.getSeverity())));
+ private void overrideImpacts(EnumMap<SoftwareQuality, Severity> impacts, Map<SoftwareQuality, Severity> overridenImpactMap) {
+ overridenImpactMap.forEach((softwareQuality, severity) -> {
+ if (impacts.containsKey(softwareQuality)) {
+ impacts.put(softwareQuality, severity);
+ }
+ });
+ }
+
+ private static Map<SoftwareQuality, Severity> toMap(List<Impact> overriddenImpactsList) {
+ return overriddenImpactsList.stream()
+ .collect(Collectors.toMap(
+ impact -> SoftwareQuality.valueOf(impact.getSoftwareQuality().name()),
+ impact -> org.sonar.ce.task.projectanalysis.issue.ImpactMapper.mapImpactSeverity(impact.getSeverity())));
}
private DbIssues.Flow.Builder convertLocations(ScannerReport.Flow flow) {
@@ -230,7 +262,6 @@ public class TrackerRawInputFactory {
return dbFlowBuilder;
}
-
private DefaultIssue toExternalIssue(LineHashSequence lineHashSeq, ScannerReport.ExternalIssue reportExternalIssue, Map<RuleKey, ScannerReport.AdHocRule> adHocRuleMap) {
DefaultIssue issue = new DefaultIssue();
RuleKey ruleKey = RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + reportExternalIssue.getEngineId(), reportExternalIssue.getRuleId());
@@ -240,7 +271,7 @@ public class TrackerRawInputFactory {
Rule existingRule = ruleRepository.getByKey(ruleKey);
issue.setSeverity(determineDeprecatedSeverity(reportExternalIssue, existingRule));
issue.setType(determineDeprecatedType(reportExternalIssue, existingRule));
- issue.replaceImpacts(replaceDefaultWithOverridenImpacts(issue.ruleKey(), reportExternalIssue.getImpactsList()));
+ issue.replaceImpacts(replaceDefaultWithOverriddenImpactsForExternalIssue(issue.ruleKey(), reportExternalIssue.getImpactsList()));
init(issue, issue.type() == RuleType.SECURITY_HOTSPOT ? STATUS_TO_REVIEW : STATUS_OPEN);
@@ -282,32 +313,22 @@ public class TrackerRawInputFactory {
}
private Optional<DbIssues.FlowType> toFlowType(ScannerReport.FlowType flowType) {
- switch (flowType) {
- case DATA:
- return Optional.of(DbIssues.FlowType.DATA);
- case EXECUTION:
- return Optional.of(DbIssues.FlowType.EXECUTION);
- case UNDEFINED:
- return Optional.empty();
- default:
- throw new IllegalArgumentException("Unrecognized type: " + flowType);
- }
+ return switch (flowType) {
+ case DATA -> Optional.of(DbIssues.FlowType.DATA);
+ case EXECUTION -> Optional.of(DbIssues.FlowType.EXECUTION);
+ case UNDEFINED -> Optional.empty();
+ default -> throw new IllegalArgumentException("Unrecognized type: " + flowType);
+ };
}
private RuleType toRuleType(IssueType type) {
- switch (type) {
- case BUG:
- return RuleType.BUG;
- case CODE_SMELL:
- return RuleType.CODE_SMELL;
- case VULNERABILITY:
- return RuleType.VULNERABILITY;
- case SECURITY_HOTSPOT:
- return RuleType.SECURITY_HOTSPOT;
- case UNRECOGNIZED:
- default:
- throw new IllegalStateException("Invalid issue type: " + type);
- }
+ return switch (type) {
+ case BUG -> RuleType.BUG;
+ case CODE_SMELL -> RuleType.CODE_SMELL;
+ case VULNERABILITY -> RuleType.VULNERABILITY;
+ case SECURITY_HOTSPOT -> RuleType.SECURITY_HOTSPOT;
+ default -> throw new IllegalStateException("Invalid issue type: " + type);
+ };
}
private DefaultIssue init(DefaultIssue issue, String initialStatus) {
@@ -325,7 +346,7 @@ public class TrackerRawInputFactory {
if (source.getComponentRef() != 0 && source.getComponentRef() != component.getReportAttributes().getRef()) {
// SONAR-10781 Component might not exist because on PR, only changed components are included in the report
Optional<Component> optionalComponent = treeRootHolder.getOptionalComponentByRef(source.getComponentRef());
- if (!optionalComponent.isPresent()) {
+ if (optionalComponent.isEmpty()) {
return Optional.empty();
}
target.setComponentId(optionalComponent.get().getUuid());
@@ -380,7 +401,6 @@ public class TrackerRawInputFactory {
}
-
private static DbIssues.MessageFormattings convertMessageFormattings(List<ScannerReport.MessageFormatting> msgFormattings) {
DbIssues.MessageFormattings.Builder builder = DbIssues.MessageFormattings.newBuilder();
msgFormattings.stream()
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRule.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRule.java
index 1af38d22367..94e7293ad9b 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRule.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRule.java
@@ -24,6 +24,8 @@ import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
@Immutable
@@ -34,14 +36,17 @@ public class ActiveRule {
private final String pluginKey;
private final long updatedAt;
private final String qProfileKey;
+ private final Map<SoftwareQuality, Severity> impacts;
- public ActiveRule(RuleKey ruleKey, String severity, Map<String, String> params, long updatedAt, @Nullable String pluginKey, String qProfileKey) {
+ public ActiveRule(RuleKey ruleKey, String severity, Map<String, String> params, long updatedAt, @Nullable String pluginKey, String qProfileKey,
+ Map<SoftwareQuality, Severity> impacts) {
this.ruleKey = ruleKey;
this.severity = severity;
this.pluginKey = pluginKey;
this.params = ImmutableMap.copyOf(params);
this.updatedAt = updatedAt;
this.qProfileKey = qProfileKey;
+ this.impacts = impacts;
}
public RuleKey getRuleKey() {
@@ -68,4 +73,8 @@ public class ActiveRule {
public String getQProfileKey() {
return qProfileKey;
}
+
+ public Map<SoftwareQuality, Severity> getImpacts() {
+ return impacts;
+ }
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStep.java
index 4fb403138e9..faa5a55cc44 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStep.java
@@ -24,9 +24,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.stream.Collectors;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
+import org.sonar.ce.task.projectanalysis.issue.ImpactMapper;
import org.sonar.ce.task.projectanalysis.issue.Rule;
import org.sonar.ce.task.projectanalysis.issue.RuleRepository;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRule;
@@ -71,6 +75,10 @@ public class LoadQualityProfilesStep implements ComputationStep {
private static ActiveRule convert(ScannerReport.ActiveRule input, Rule rule) {
RuleKey key = RuleKey.of(input.getRuleRepository(), input.getRuleKey());
Map<String, String> params = new HashMap<>(input.getParamsByKeyMap());
- return new ActiveRule(key, input.getSeverity().name(), params, input.getUpdatedAt(), rule.getPluginKey(), input.getQProfileKey());
+ Map<SoftwareQuality, Severity> impacts = input.getImpactsList().stream()
+ .collect(Collectors.toMap(
+ i -> SoftwareQuality.valueOf(i.getSoftwareQuality().name()),
+ i -> ImpactMapper.mapImpactSeverity(i.getSeverity())));
+ return new ActiveRule(key, input.getSeverity().name(), params, input.getUpdatedAt(), rule.getPluginKey(), input.getQProfileKey(), impacts);
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ImpactMapperTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ImpactMapperTest.java
new file mode 100644
index 00000000000..b6bfdac50c9
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ImpactMapperTest.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import org.junit.jupiter.api.Test;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class ImpactMapperTest {
+
+ @Test
+ void mapImpactSeverity_shouldReturnExpectedValue() {
+ assertThat(ImpactMapper.mapImpactSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_BLOCKER)).isEqualTo(Severity.BLOCKER);
+ assertThat(ImpactMapper.mapImpactSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_HIGH)).isEqualTo(Severity.HIGH);
+ assertThat(ImpactMapper.mapImpactSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_MEDIUM)).isEqualTo(Severity.MEDIUM);
+ assertThat(ImpactMapper.mapImpactSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_LOW)).isEqualTo(Severity.LOW);
+ assertThat(ImpactMapper.mapImpactSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_INFO)).isEqualTo(Severity.INFO);
+ }
+
+ @Test
+ void mapImpactSeverity_whenUnknownValue_shouldThrowException() {
+ assertThatThrownBy(() -> ImpactMapper.mapImpactSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_UNKNOWN_IMPACT_SEVERITY)).isInstanceOf(IllegalArgumentException.class);
+ assertThatThrownBy(() -> ImpactMapper.mapImpactSeverity(ScannerReport.ImpactSeverity.UNRECOGNIZED)).isInstanceOf(IllegalArgumentException.class);
+ }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRuleTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRuleTest.java
index aef0e1b632e..738c4d986f7 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRuleTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRuleTest.java
@@ -59,7 +59,8 @@ public class NewAdHocRuleTest {
public void constructor_whenAdhocRuleHasProvidedImpact_shouldMapTypeAndSeverityAccordingly() {
NewAdHocRule adHocRule = new NewAdHocRule(ScannerReport.AdHocRule.newBuilder()
.setEngineId("eslint").setRuleId("no-cond-assign").setName("name")
- .addDefaultImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY.name()).setSeverity(Severity.LOW.name()).build())
+ .addDefaultImpacts(
+ ScannerReport.Impact.newBuilder().setSoftwareQuality(ScannerReport.SoftwareQuality.MAINTAINABILITY).setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_LOW).build())
.build());
assertThat(adHocRule.getRuleType()).isEqualTo(RuleType.CODE_SMELL);
@@ -84,8 +85,8 @@ public class NewAdHocRuleTest {
.setEngineId("eslint").setRuleId("no-cond-assign").setName("name")
.setType(ScannerReport.IssueType.CODE_SMELL)
.setSeverity(Constants.Severity.MINOR)
- .addDefaultImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(SoftwareQuality.RELIABILITY.name())
- .setSeverity(Severity.HIGH.name()).build())
+ .addDefaultImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(ScannerReport.SoftwareQuality.RELIABILITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_HIGH).build())
.build());
assertThat(adHocRule.getDefaultImpacts())
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java
index 646061b3525..3051e9198ab 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java
@@ -66,8 +66,8 @@ import static org.mockito.Mockito.when;
import static org.sonar.api.issue.Issue.STATUS_OPEN;
import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
import static org.sonar.api.issue.impact.Severity.LOW;
+import static org.sonar.api.issue.impact.Severity.MEDIUM;
import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
-import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY;
import static org.sonar.scanner.protocol.output.ScannerReport.MessageFormattingType.CODE;
class TrackerRawInputFactoryTest {
@@ -126,21 +126,23 @@ class TrackerRawInputFactoryTest {
}
@Test
- void load_issues_from_report() {
+ void load_issues_from_report_with_overridden_severity() {
RuleKey ruleKey = RuleKey.of("java", "S001");
+ // Default severity is CRITICAL
markRuleAsActive(ruleKey);
registerRule(ruleKey, "name", r -> r.addDefaultImpact(MAINTAINABILITY, LOW));
ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
.setTextRange(newTextRange(2))
.setMsg("the message")
.addMsgFormatting(ScannerReport.MessageFormatting.newBuilder().setStart(0).setEnd(3).setType(CODE).build())
- .addOverridenImpacts(ScannerReport.Impact.newBuilder()
- .setSoftwareQuality(MAINTAINABILITY.name())
- .setSeverity(org.sonar.api.issue.impact.Severity.HIGH.name())
+ .addOverriddenImpacts(ScannerReport.Impact.newBuilder()
+ .setSoftwareQuality(ScannerReport.SoftwareQuality.MAINTAINABILITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_HIGH)
.build())
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
- .setSeverity(Constants.Severity.BLOCKER)
+ // Override to BLOCKER
+ .setOverriddenSeverity(Constants.Severity.BLOCKER)
.setGap(3.14)
.setQuickFixAvailable(true)
.build();
@@ -248,6 +250,7 @@ class TrackerRawInputFactoryTest {
@Test
void create_whenImpactIsNotDefinedAtRuleLevel_shouldDiscardImpacts() {
RuleKey ruleKey = RuleKey.of("java", "S001");
+ // Maintainability impact is overridden to MEDIUM in the quality profile
markRuleAsActive(ruleKey);
registerRule(ruleKey, "name", r -> r.addDefaultImpact(MAINTAINABILITY, LOW));
ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
@@ -255,9 +258,10 @@ class TrackerRawInputFactoryTest {
.setMsg("the message")
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
- .addOverridenImpacts(ScannerReport.Impact.newBuilder()
- .setSoftwareQuality(SECURITY.name())
- .setSeverity(LOW.name()).build())
+ .addOverriddenImpacts(ScannerReport.Impact.newBuilder()
+ // Security impact is not defined at rule level, so it should be ignored
+ .setSoftwareQuality(ScannerReport.SoftwareQuality.SECURITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_LOW).build())
.build();
reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
@@ -265,7 +269,7 @@ class TrackerRawInputFactoryTest {
Collection<DefaultIssue> issues = input.getIssues();
assertThat(issues).hasSize(1);
- assertThat(issues.iterator().next().impacts()).hasSize(1).containsEntry(MAINTAINABILITY, LOW);
+ assertThat(issues.iterator().next().impacts()).hasSize(1).containsEntry(MAINTAINABILITY, MEDIUM);
}
@Test
@@ -305,7 +309,6 @@ class TrackerRawInputFactoryTest {
.setMsg("the message")
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
- .setSeverity(Constants.Severity.BLOCKER)
.setGap(3.14)
.addFlow(ScannerReport.Flow.newBuilder()
.addLocation(ScannerReport.IssueLocation.newBuilder()
@@ -352,8 +355,8 @@ class TrackerRawInputFactoryTest {
.setEffort(20L)
.setType(issueType)
.addFlow(ScannerReport.Flow.newBuilder().setType(FlowType.DATA).addLocation(ScannerReport.IssueLocation.newBuilder().build()).build())
- .addImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(MAINTAINABILITY.name())
- .setSeverity(org.sonar.api.issue.impact.Severity.MEDIUM.name()).build())
+ .addImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(ScannerReport.SoftwareQuality.MAINTAINABILITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_MEDIUM).build())
.build();
reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
Input<DefaultIssue> input = underTest.create(FILE);
@@ -492,7 +495,6 @@ class TrackerRawInputFactoryTest {
.setMsg("the message")
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
- .setSeverity(Constants.Severity.BLOCKER)
.setGap(3.14)
.build();
reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
@@ -513,7 +515,6 @@ class TrackerRawInputFactoryTest {
.setMsg("the message")
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
- .setSeverity(Constants.Severity.BLOCKER)
.setGap(3.14)
.build();
reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
@@ -531,7 +532,6 @@ class TrackerRawInputFactoryTest {
.setMsg("the message")
.setRuleRepository(ruleKey.repository())
.setRuleKey(ruleKey.rule())
- .setSeverity(Constants.Severity.BLOCKER)
.build();
reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
@@ -566,7 +566,7 @@ class TrackerRawInputFactoryTest {
}
private void markRuleAsActive(RuleKey ruleKey) {
- activeRulesHolder.put(new ActiveRule(ruleKey, Severity.CRITICAL, emptyMap(), 1_000L, null, "qp1"));
+ activeRulesHolder.put(new ActiveRule(ruleKey, Severity.CRITICAL, emptyMap(), 1_000L, null, "qp1", Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)));
}
private void registerRule(RuleKey ruleKey, String name) {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRulesHolderImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRulesHolderImplTest.java
index 5ea0af56f0f..bcdc79efbbf 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRulesHolderImplTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRulesHolderImplTest.java
@@ -20,6 +20,7 @@
package org.sonar.ce.task.projectanalysis.qualityprofile;
import java.util.Collections;
+import java.util.Map;
import java.util.Optional;
import org.junit.Test;
import org.sonar.api.rule.RuleKey;
@@ -50,7 +51,7 @@ public class ActiveRulesHolderImplTest {
@Test
public void get_active_rule() {
- underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY)));
+ underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY, Map.of())));
Optional<ActiveRule> activeRule = underTest.get(RULE_KEY);
assertThat(activeRule).isPresent();
@@ -61,7 +62,7 @@ public class ActiveRulesHolderImplTest {
@Test
public void can_not_set_twice() {
assertThatThrownBy(() -> {
- underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY)));
+ underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY, Map.of())));
underTest.set(Collections.emptyList());
})
.isInstanceOf(IllegalStateException.class)
@@ -79,8 +80,8 @@ public class ActiveRulesHolderImplTest {
public void can_not_set_duplicated_rules() {
assertThatThrownBy(() -> {
underTest.set(asList(
- new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY),
- new ActiveRule(RULE_KEY, Severity.MAJOR, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY)));
+ new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY, Map.of()),
+ new ActiveRule(RULE_KEY, Severity.MAJOR, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY, Map.of())));
})
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Active rule must not be declared multiple times: java:S001");
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/AlwaysActiveRulesHolderImpl.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/AlwaysActiveRulesHolderImpl.java
index d27cb06caab..10395dd16c2 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/AlwaysActiveRulesHolderImpl.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/AlwaysActiveRulesHolderImpl.java
@@ -19,6 +19,7 @@
*/
package org.sonar.ce.task.projectanalysis.qualityprofile;
+import java.util.Map;
import java.util.Optional;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
@@ -28,7 +29,7 @@ import static java.util.Collections.emptyMap;
public class AlwaysActiveRulesHolderImpl implements ActiveRulesHolder {
@Override
public Optional<ActiveRule> get(RuleKey ruleKey) {
- return Optional.of(new ActiveRule(ruleKey, Severity.MAJOR, emptyMap(), 1_000L, null, "qp1"));
+ return Optional.of(new ActiveRule(ruleKey, Severity.MAJOR, emptyMap(), 1_000L, null, "qp1", Map.of()));
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStepTest.java
index 70b74b91608..d86c748335a 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStepTest.java
@@ -60,6 +60,12 @@ class LoadQualityProfilesStepTest {
.setRuleRepository(XOO_X1.repository())
.setRuleKey(XOO_X1.rule())
.setSeverity(Constants.Severity.BLOCKER)
+ .addImpacts(ScannerReport.Impact.newBuilder()
+ .setSoftwareQuality(ScannerReport.SoftwareQuality.MAINTAINABILITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_LOW))
+ .addImpacts(ScannerReport.Impact.newBuilder()
+ .setSoftwareQuality(ScannerReport.SoftwareQuality.RELIABILITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_BLOCKER))
.setCreatedAt(1000L)
.setUpdatedAt(1200L);
batch1.getMutableParamsByKey().put("p1", "v1");
@@ -74,6 +80,9 @@ class LoadQualityProfilesStepTest {
ActiveRule ar1 = activeRulesHolder.get(XOO_X1).get();
assertThat(ar1.getSeverity()).isEqualTo(Severity.BLOCKER);
+ assertThat(ar1.getImpacts()).contains(
+ MapEntry.entry(org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW),
+ MapEntry.entry(org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.BLOCKER));
assertThat(ar1.getParams()).containsExactly(MapEntry.entry("p1", "v1"));
assertThat(ar1.getPluginKey()).isEqualTo("xoo");
assertThat(ar1.getUpdatedAt()).isEqualTo(1200L);
diff --git a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java
index 541a690a63c..2ae6379db66 100644
--- a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java
+++ b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java
@@ -142,7 +142,7 @@ public class IssuesMediumIT {
.execute();
List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
- assertThat(issues.get(0).getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.CRITICAL);
+ assertThat(issues.get(0).getOverriddenSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.CRITICAL);
}
@Test
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java
index ddb665c7965..d070a464cb4 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java
@@ -34,12 +34,14 @@ import org.sonar.scanner.protocol.output.ScannerReport.Issue;
@ThreadSafe
public class DefaultFilterableIssue implements FilterableIssue {
private final Issue rawIssue;
+ private final String severity;
private final InputComponent component;
private DefaultInputProject project;
- public DefaultFilterableIssue(DefaultInputProject project, Issue rawIssue, InputComponent component) {
+ public DefaultFilterableIssue(DefaultInputProject project, Issue rawIssue, String severity, InputComponent component) {
this.project = project;
this.rawIssue = rawIssue;
+ this.severity = severity;
this.component = component;
}
@@ -55,7 +57,7 @@ public class DefaultFilterableIssue implements FilterableIssue {
@Override
public String severity() {
- return rawIssue.getSeverity().name();
+ return severity;
}
@Override
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ImpactMapper.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ImpactMapper.java
new file mode 100644
index 00000000000..76111b56055
--- /dev/null
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ImpactMapper.java
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.scanner.issue;
+
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+public class ImpactMapper {
+ private ImpactMapper() {
+ }
+
+ public static ScannerReport.ImpactSeverity mapImpactSeverity(Severity severity) {
+ return switch (severity) {
+ case BLOCKER -> ScannerReport.ImpactSeverity.ImpactSeverity_BLOCKER;
+ case HIGH -> ScannerReport.ImpactSeverity.ImpactSeverity_HIGH;
+ case MEDIUM -> ScannerReport.ImpactSeverity.ImpactSeverity_MEDIUM;
+ case LOW -> ScannerReport.ImpactSeverity.ImpactSeverity_LOW;
+ case INFO -> ScannerReport.ImpactSeverity.ImpactSeverity_INFO;
+ };
+ }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java
index cce20df21d8..948e9552370 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java
@@ -41,11 +41,11 @@ public class IssueFilters {
this.project = project;
}
- public boolean accept(InputComponent component, ScannerReport.Issue rawIssue) {
+ public boolean accept(InputComponent component, ScannerReport.Issue rawIssue, String severity) {
if (filterChain == null) {
throw new IllegalStateException("Issue filters must be registered before this class can be used");
}
- FilterableIssue fIssue = new DefaultFilterableIssue(project, rawIssue, component);
+ FilterableIssue fIssue = new DefaultFilterableIssue(project, rawIssue, severity, component);
return filterChain.accept(fIssue);
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java
index 6de30db7829..da79ae46bd0 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java
@@ -33,7 +33,6 @@ import org.sonar.api.batch.fs.internal.DefaultInputComponent;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.rule.ActiveRules;
-import org.sonar.api.batch.rule.internal.DefaultActiveRule;
import org.sonar.api.batch.sensor.issue.ExternalIssue;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.Issue.Flow;
@@ -78,9 +77,9 @@ public class IssuePublisher {
return false;
}
- ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.scannerId(), activeRule.severity(), ((DefaultActiveRule) activeRule).impacts());
+ ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.scannerId());
- if (filters.accept(inputComponent, rawIssue)) {
+ if (filters.accept(inputComponent, rawIssue, activeRule.severity())) {
write(inputComponent.scannerId(), rawIssue);
return true;
}
@@ -108,29 +107,28 @@ public class IssuePublisher {
return str;
}
- private static ScannerReport.Issue createReportIssue(Issue issue, int componentRef, String activeRuleSeverity,
- Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> activeRuleImpacts) {
+ private static ScannerReport.Issue createReportIssue(Issue issue, int componentRef) {
String primaryMessage = nullToEmpty(issue.primaryLocation().message());
- org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity();
- Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRuleSeverity);
ScannerReport.Issue.Builder builder = ScannerReport.Issue.newBuilder();
ScannerReport.IssueLocation.Builder locationBuilder = IssueLocation.newBuilder();
ScannerReport.TextRange.Builder textRangeBuilder = ScannerReport.TextRange.newBuilder();
// non-null fields
- builder.setSeverity(severity);
builder.setRuleRepository(issue.ruleKey().repository());
builder.setRuleKey(issue.ruleKey().rule());
builder.setMsg(primaryMessage);
builder.addAllMsgFormatting(toProtobufMessageFormattings(issue.primaryLocation().messageFormattings()));
Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> overriddenImpacts = new EnumMap<>(issue.overridenImpacts());
- activeRuleImpacts.entrySet().forEach(e -> overriddenImpacts.putIfAbsent(e.getKey(), e.getValue()));
- builder.addAllOverridenImpacts(toProtobufImpacts(overriddenImpacts));
+ builder.addAllOverriddenImpacts(toProtobufImpacts(overriddenImpacts));
locationBuilder.setMsg(primaryMessage);
locationBuilder.addAllMsgFormatting(toProtobufMessageFormattings(issue.primaryLocation().messageFormattings()));
locationBuilder.setComponentRef(componentRef);
+ org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity();
+ if (overriddenSeverity != null) {
+ builder.setOverriddenSeverity(Severity.valueOf(overriddenSeverity.name()));
+ }
TextRange primaryTextRange = issue.primaryLocation().textRange();
if (primaryTextRange != null) {
builder.setTextRange(toProtobufTextRange(textRangeBuilder, primaryTextRange));
@@ -151,7 +149,9 @@ public class IssuePublisher {
private static List<ScannerReport.Impact> toProtobufImpacts(Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> softwareQualitySeverityMap) {
List<ScannerReport.Impact> impacts = new ArrayList<>();
- softwareQualitySeverityMap.forEach((q, s) -> impacts.add(ScannerReport.Impact.newBuilder().setSoftwareQuality(q.name()).setSeverity(s.name()).build()));
+ softwareQualitySeverityMap.forEach((q, s) -> impacts.add(ScannerReport.Impact.newBuilder()
+ .setSoftwareQuality(ScannerReport.SoftwareQuality.valueOf(q.name()))
+ .setSeverity(ImpactMapper.mapImpactSeverity(s)).build()));
return impacts;
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ActiveRulesPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ActiveRulesPublisher.java
index 63672225c9e..dcad7da095b 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ActiveRulesPublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ActiveRulesPublisher.java
@@ -21,6 +21,7 @@ package org.sonar.scanner.report;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.internal.DefaultActiveRule;
+import org.sonar.scanner.issue.ImpactMapper;
import org.sonar.scanner.protocol.Constants;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
@@ -46,7 +47,12 @@ public class ActiveRulesPublisher implements ReportPublisherStep {
builder.setCreatedAt(input.createdAt());
builder.setUpdatedAt(input.updatedAt());
builder.setQProfileKey(input.qpKey());
- builder.getMutableParamsByKey().putAll(input.params());
+ builder.putAllParamsByKey(input.params());
+ builder.addAllImpacts(input.impacts().entrySet().stream()
+ .map(entry -> ScannerReport.Impact.newBuilder()
+ .setSoftwareQuality(ScannerReport.SoftwareQuality.valueOf(entry.getKey().name()))
+ .setSeverity(ImpactMapper.mapImpactSeverity(entry.getValue())).build())
+ .toList());
return builder.build();
}).toList());
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
index e0930fc5372..9ee364cf916 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
@@ -68,6 +68,7 @@ import org.sonar.core.util.CloseableIterator;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.internal.pmd.PmdBlockChunker;
import org.sonar.scanner.cpd.index.SonarCpdBlockIndex;
+import org.sonar.scanner.issue.ImpactMapper;
import org.sonar.scanner.issue.IssuePublisher;
import org.sonar.scanner.protocol.Constants;
import org.sonar.scanner.protocol.output.FileStructure;
@@ -263,7 +264,6 @@ public class DefaultSensorStorage implements SensorStorage {
builder.setDescription(description);
}
-
org.sonar.api.batch.rule.Severity severity = adHocRule.severity();
if (severity != null) {
builder.setSeverity(Constants.Severity.valueOf(severity.name()));
@@ -285,8 +285,8 @@ public class DefaultSensorStorage implements SensorStorage {
private static List<ScannerReport.Impact> mapImpacts(Map<SoftwareQuality, Severity> impactsMap) {
return impactsMap.entrySet().stream()
.map(e -> ScannerReport.Impact.newBuilder()
- .setSoftwareQuality(e.getKey().name())
- .setSeverity(e.getValue().name()).build())
+ .setSoftwareQuality(ScannerReport.SoftwareQuality.valueOf(e.getKey().name()))
+ .setSeverity(ImpactMapper.mapImpactSeverity(e.getValue())).build())
.toList();
}
@@ -379,8 +379,7 @@ public class DefaultSensorStorage implements SensorStorage {
private SortedMap<Integer, ScannerReport.LineCoverage.Builder> reloadExistingCoverage(DefaultInputFile inputFile) {
SortedMap<Integer, ScannerReport.LineCoverage.Builder> coveragePerLine = new TreeMap<>();
- try (CloseableIterator<ScannerReport.LineCoverage> lineCoverageCloseableIterator =
- reportPublisher.getReader().readComponentCoverage(inputFile.scannerId())) {
+ try (CloseableIterator<ScannerReport.LineCoverage> lineCoverageCloseableIterator = reportPublisher.getReader().readComponentCoverage(inputFile.scannerId())) {
while (lineCoverageCloseableIterator.hasNext()) {
final ScannerReport.LineCoverage lineCoverage = lineCoverageCloseableIterator.next();
coveragePerLine.put(lineCoverage.getLine(), ScannerReport.LineCoverage.newBuilder(lineCoverage));
@@ -393,8 +392,8 @@ public class DefaultSensorStorage implements SensorStorage {
void apply(Integer value, ScannerReport.LineCoverage.Builder builder);
}
- private static void mergeLineCoverageValues(int lineCount, SortedMap<Integer, Integer> valueByLine, SortedMap<Integer,
- ScannerReport.LineCoverage.Builder> coveragePerLine, LineCoverageOperation op) {
+ private static void mergeLineCoverageValues(int lineCount, SortedMap<Integer, Integer> valueByLine, SortedMap<Integer, ScannerReport.LineCoverage.Builder> coveragePerLine,
+ LineCoverageOperation op) {
for (Map.Entry<Integer, Integer> lineMeasure : valueByLine.entrySet()) {
int lineIdx = lineMeasure.getKey();
if (lineIdx <= lineCount) {
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java
index 1f8c28a2768..6145aec1c11 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java
@@ -53,20 +53,18 @@ public class DefaultFilterableIssueTest {
.setStartOffset(10)
.setEndLine(31)
.setEndOffset(3));
- builder.setSeverity(Severity.MAJOR);
return builder.build();
}
private Issue createIssueWithoutFields() {
Issue.Builder builder = Issue.newBuilder();
- builder.setSeverity(Severity.MAJOR);
return builder.build();
}
@Test
public void testRoundTrip() {
rawIssue = createIssue();
- issue = new DefaultFilterableIssue(mockedProject, rawIssue, component);
+ issue = new DefaultFilterableIssue(mockedProject, rawIssue, "MAJOR", component);
when(mockedProject.key()).thenReturn("projectKey");
@@ -84,7 +82,7 @@ public class DefaultFilterableIssueTest {
@Test
public void nullValues() {
rawIssue = createIssueWithoutFields();
- issue = new DefaultFilterableIssue(mockedProject, rawIssue, component);
+ issue = new DefaultFilterableIssue(mockedProject, rawIssue, "MAJOR", component);
assertThat(issue.line()).isNull();
assertThat(issue.gap()).isNull();
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ImpactMapperTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ImpactMapperTest.java
new file mode 100644
index 00000000000..f543b023c75
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ImpactMapperTest.java
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.scanner.issue;
+
+import org.junit.jupiter.api.Test;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ImpactMapperTest {
+
+ @Test
+ void mapImpactSeverity_shouldReturnExpectedValue() {
+ assertEquals(ScannerReport.ImpactSeverity.ImpactSeverity_BLOCKER, ImpactMapper.mapImpactSeverity(Severity.BLOCKER));
+ assertEquals(ScannerReport.ImpactSeverity.ImpactSeverity_HIGH, ImpactMapper.mapImpactSeverity(Severity.HIGH));
+ assertEquals(ScannerReport.ImpactSeverity.ImpactSeverity_MEDIUM, ImpactMapper.mapImpactSeverity(Severity.MEDIUM));
+ assertEquals(ScannerReport.ImpactSeverity.ImpactSeverity_LOW, ImpactMapper.mapImpactSeverity(Severity.LOW));
+ assertEquals(ScannerReport.ImpactSeverity.ImpactSeverity_INFO, ImpactMapper.mapImpactSeverity(Severity.INFO));
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java
index aa59812f63c..0132e1d7ecc 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java
@@ -53,6 +53,7 @@ import org.sonar.scanner.report.ReportPublisher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
@@ -62,7 +63,6 @@ import static org.mockito.Mockito.when;
import static org.sonar.api.batch.sensor.issue.MessageFormatting.Type.CODE;
import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY;
-import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY;
@RunWith(MockitoJUnitRunner.class)
public class IssuePublisherTest {
@@ -136,21 +136,23 @@ public class IssuePublisherTest {
.overrideImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH)
.overrideImpact(RELIABILITY, org.sonar.api.issue.impact.Severity.LOW);
- when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true);
+ when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class), anyString())).thenReturn(true);
boolean added = moduleIssues.initAndAddIssue(issue);
assertThat(added).isTrue();
ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture());
- assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.CRITICAL);
+ assertThat(argument.getValue().getOverriddenSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.CRITICAL);
assertThat(argument.getValue().getQuickFixAvailable()).isTrue();
assertThat(argument.getValue().getRuleDescriptionContextKey()).isEqualTo(ruleDescriptionContextKey);
assertThat(argument.getValue().getCodeVariantsList()).containsExactly("variant1", "variant2");
- ScannerReport.Impact impact1 = ScannerReport.Impact.newBuilder().setSoftwareQuality(MAINTAINABILITY.name()).setSeverity("HIGH").build();
- ScannerReport.Impact impact2 = ScannerReport.Impact.newBuilder().setSoftwareQuality(RELIABILITY.name()).setSeverity("LOW").build();
- assertThat(argument.getValue().getOverridenImpactsList()).containsExactlyInAnyOrder(impact1, impact2);
+ ScannerReport.Impact impact1 = ScannerReport.Impact.newBuilder().setSoftwareQuality(ScannerReport.SoftwareQuality.MAINTAINABILITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_HIGH).build();
+ ScannerReport.Impact impact2 = ScannerReport.Impact.newBuilder().setSoftwareQuality(ScannerReport.SoftwareQuality.RELIABILITY)
+ .setSeverity(ScannerReport.ImpactSeverity.ImpactSeverity_LOW).build();
+ assertThat(argument.getValue().getOverriddenImpactsList()).containsExactlyInAnyOrder(impact1, impact2);
}
@Test
@@ -169,7 +171,7 @@ public class IssuePublisherTest {
.addFlow(List.of(new DefaultIssueLocation().on(file)), NewIssue.FlowType.EXECUTION, null)
.forRule(JAVA_RULE_KEY);
- when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true);
+ when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class), anyString())).thenReturn(true);
moduleIssues.initAndAddIssue(issue);
ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
@@ -227,75 +229,24 @@ public class IssuePublisherTest {
ArgumentCaptor<ScannerReport.ExternalIssue> argument = ArgumentCaptor.forClass(ScannerReport.ExternalIssue.class);
verify(reportPublisher.getWriter()).appendComponentExternalIssue(eq(file.scannerId()), argument.capture());
assertThat(argument.getValue().getImpactsList()).extracting(ScannerReport.Impact::getSoftwareQuality, ScannerReport.Impact::getSeverity)
- .containsExactly(tuple(MAINTAINABILITY.name(), org.sonar.api.issue.impact.Severity.LOW.name()));
+ .containsExactly(tuple(ScannerReport.SoftwareQuality.MAINTAINABILITY, ScannerReport.ImpactSeverity.ImpactSeverity_LOW));
assertThat(argument.getValue().getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR.name());
}
@Test
- public void use_severity_from_active_rule_if_no_severity_on_issue() {
+ public void dont_store_severity_if_no_severity_override_on_issue() {
initModuleIssues();
DefaultIssue issue = new DefaultIssue(project)
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
.forRule(JAVA_RULE_KEY);
- when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true);
- moduleIssues.initAndAddIssue(issue);
-
- ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
- verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture());
- assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO);
- assertThat(argument.getValue().getOverridenImpactsList()).isEmpty();
- }
-
- @Test
- public void initAndAddIssue_whenImpactsOverriddenOnActiveRule_shouldOverrideIssue() {
- activeRulesBuilder.addRule(new NewActiveRule.Builder()
- .setRuleKey(NOSONAR_RULE_KEY)
- .setSeverity(Severity.INFO)
- .setImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH)
- .setQProfileKey("qp-1")
- .build());
- initModuleIssues();
-
- DefaultIssue issue = new DefaultIssue(project)
- .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
- .forRule(NOSONAR_RULE_KEY);
- when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true);
- moduleIssues.initAndAddIssue(issue);
-
- ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
- verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture());
- assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO);
- assertThat(argument.getValue().getOverridenImpactsList()).extracting(ScannerReport.Impact::getSoftwareQuality, ScannerReport.Impact::getSeverity)
- .containsExactly(tuple(MAINTAINABILITY.name(), org.sonar.api.issue.impact.Severity.HIGH.name()));
- }
-
- @Test
- public void initAndAddIssue_whenImpactsOverriddenOnActiveRuleAndInIssue_shouldCombineOverriddenImpacts() {
- activeRulesBuilder.addRule(new NewActiveRule.Builder()
- .setRuleKey(NOSONAR_RULE_KEY)
- .setSeverity(Severity.INFO)
- .setImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH)
- .setImpact(SECURITY, org.sonar.api.issue.impact.Severity.INFO)
- .setQProfileKey("qp-1")
- .build());
- initModuleIssues();
-
- DefaultIssue issue = new DefaultIssue(project)
- .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
- .overrideImpact(RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)
- .overrideImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)
- .forRule(NOSONAR_RULE_KEY);
- when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true);
+ when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class), eq("INFO"))).thenReturn(true);
moduleIssues.initAndAddIssue(issue);
ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture());
- assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO);
- assertThat(argument.getValue().getOverridenImpactsList()).extracting(ScannerReport.Impact::getSoftwareQuality, ScannerReport.Impact::getSeverity)
- .containsExactlyInAnyOrder(tuple(MAINTAINABILITY.name(), org.sonar.api.issue.impact.Severity.LOW.name()),
- tuple(RELIABILITY.name(), org.sonar.api.issue.impact.Severity.MEDIUM.name()),
- tuple(SECURITY.name(), org.sonar.api.issue.impact.Severity.INFO.name()));
+ assertThat(argument.getValue().hasOverriddenSeverity()).isFalse();
+ assertThat(argument.getValue().getOverriddenImpactsList()).isEmpty();
}
@Test
@@ -304,7 +255,7 @@ public class IssuePublisherTest {
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
.forRule(JAVA_RULE_KEY);
- when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(false);
+ when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class), anyString())).thenReturn(false);
boolean added = moduleIssues.initAndAddIssue(issue);
@@ -344,7 +295,7 @@ public class IssuePublisherTest {
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
.forRule(NOSONAR_RULE_KEY);
- when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true);
+ when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class), anyString())).thenReturn(true);
boolean added = moduleIssues.initAndAddIssue(issue);
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ActiveRulesPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ActiveRulesPublisherTest.java
index 772040e1759..8c4db588240 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ActiveRulesPublisherTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ActiveRulesPublisherTest.java
@@ -26,6 +26,8 @@ import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.internal.DefaultActiveRules;
import org.sonar.api.batch.rule.internal.NewActiveRule;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.core.util.CloseableIterator;
import org.sonar.scanner.protocol.Constants;
@@ -36,6 +38,7 @@ import org.sonar.scanner.protocol.output.ScannerReportWriter;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.groups.Tuple.tuple;
public class ActiveRulesPublisherTest {
@@ -55,6 +58,8 @@ public class ActiveRulesPublisherTest {
.setCreatedAt(1_000L)
.setUpdatedAt(2_000L)
.setQProfileKey("qp1")
+ .setImpact(SoftwareQuality.MAINTAINABILITY, Severity.BLOCKER)
+ .setImpact(SoftwareQuality.RELIABILITY, Severity.LOW)
.build();
ActiveRules activeRules = new DefaultActiveRules(singletonList(ar));
@@ -73,6 +78,10 @@ public class ActiveRulesPublisherTest {
assertThat(reportAr.getParamsByKeyMap()).hasSize(1);
assertThat(reportAr.getParamsByKeyMap().entrySet().iterator().next().getKey()).isEqualTo("p1");
assertThat(reportAr.getParamsByKeyMap().entrySet().iterator().next().getValue()).isEqualTo("v1");
+ assertThat(reportAr.getImpactsList()).extracting(ScannerReport.Impact::getSoftwareQuality, ScannerReport.Impact::getSeverity)
+ .containsExactlyInAnyOrder(
+ tuple(ScannerReport.SoftwareQuality.MAINTAINABILITY, ScannerReport.ImpactSeverity.ImpactSeverity_BLOCKER),
+ tuple(ScannerReport.SoftwareQuality.RELIABILITY, ScannerReport.ImpactSeverity.ImpactSeverity_LOW));
assertThat(readIt.hasNext()).isFalse();
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
index a0d785bd163..5a6f8f66a1f 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
@@ -151,8 +151,8 @@ class DefaultSensorStorageTest {
.forMetric(CoreMetrics.LINES)
.withValue(10);
assertThatThrownBy(() -> underTest.store(defaultMeasure))
- .isInstanceOf(UnsupportedOperationException.class)
- .hasMessage("Unknown metric: lines");
+ .isInstanceOf(UnsupportedOperationException.class)
+ .hasMessage("Unknown metric: lines");
}
@Test
@@ -392,8 +392,8 @@ class DefaultSensorStorageTest {
.containsExactlyInAnyOrder("ruleId", "name", Constants.Severity.MAJOR, ScannerReport.IssueType.CODE_SMELL, "description");
assertThat(adhocRule.getDefaultImpactsList()).hasSize(2).extracting(ScannerReport.Impact::getSoftwareQuality, ScannerReport.Impact::getSeverity)
.containsExactlyInAnyOrder(
- Tuple.tuple(SoftwareQuality.MAINTAINABILITY.name(), Severity.HIGH.name()),
- Tuple.tuple(SoftwareQuality.RELIABILITY.name(), Severity.MEDIUM.name()));
+ Tuple.tuple(ScannerReport.SoftwareQuality.MAINTAINABILITY, ScannerReport.ImpactSeverity.ImpactSeverity_HIGH),
+ Tuple.tuple(ScannerReport.SoftwareQuality.RELIABILITY, ScannerReport.ImpactSeverity.ImpactSeverity_MEDIUM));
assertThat(adhocRule.getCleanCodeAttribute())
.isEqualTo(CleanCodeAttribute.CLEAR.name());
}
diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
index 74ff2bc0404..c86f48c91d9 100644
--- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
+++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
@@ -100,6 +100,7 @@ message ActiveRule {
int64 createdAt = 5;
int64 updatedAt = 6;
string q_profile_key = 7;
+ repeated Impact impacts = 8;
}
message ComponentLink {
@@ -196,7 +197,7 @@ message Issue {
string rule_key = 2;
// Only when issue component is a file. Can also be empty for a file if this is an issue global to the file.
string msg = 3;
- Severity severity = 4;
+ optional Severity overriddenSeverity = 4;
double gap = 5;
// Only when issue component is a file. Can also be empty for a file if this is an issue global to the file.
// Will be identical to the first location of the first flow
@@ -206,7 +207,7 @@ message Issue {
optional string ruleDescriptionContextKey = 9;
repeated MessageFormatting msgFormatting = 10;
repeated string codeVariants = 11;
- repeated Impact overridenImpacts = 12;
+ repeated Impact overriddenImpacts = 12;
}
message ExternalIssue {
@@ -373,8 +374,30 @@ message AnalysisWarning {
}
message Impact {
- string software_quality = 1;
- string severity = 2;
+ SoftwareQuality software_quality = 1;
+ ImpactSeverity severity = 2;
+}
+
+enum ImpactSeverity {
+ // Zero is required in order to not get first entry as a default value
+ // See http://androiddevblog.com/protocol-buffers-pitfall-adding-enum-values/
+ ImpactSeverity_UNKNOWN_IMPACT_SEVERITY = 0;
+ ImpactSeverity_LOW = 1;
+ ImpactSeverity_MEDIUM = 2;
+ ImpactSeverity_HIGH = 3;
+ // INFO and BLOCKER conflicts with Severity enum, so we use different values prefixed with enum name
+ // See https://github.com/protocolbuffers/protobuf/issues/5425
+ ImpactSeverity_INFO = 4;
+ ImpactSeverity_BLOCKER = 5;
+}
+
+enum SoftwareQuality {
+ // Zero is required in order to not get first entry as a default value
+ // See http://androiddevblog.com/protocol-buffers-pitfall-adding-enum-values/
+ UNKNOWN_IMPACT_QUALITY = 0;
+ MAINTAINABILITY = 1;
+ RELIABILITY = 2;
+ SECURITY = 3;
}
message Dependency {