group=org.sonarsource.sonarqube
version=10.4
-pluginApiVersion=10.4.0.2040
+pluginApiVersion=10.4.0.2048
description=Open source platform for continuous inspection of code quality
projectTitle=SonarQube
org.gradle.jvmargs=-Xmx2048m
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
+import org.sonar.api.issue.impact.Severity;
import org.sonar.api.rules.RuleType;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.measure.Measure;
import static org.sonar.api.measures.CoreMetrics.CONFIRMED_ISSUES_KEY;
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.MAJOR_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_ACCEPTED_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_BUGS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY;
addMeasure(component, CONFIRMED_ISSUES_KEY, currentCounters.counter().confirmed);
addMeasure(component, FALSE_POSITIVE_ISSUES_KEY, currentCounters.counter().falsePositives);
addMeasure(component, ACCEPTED_ISSUES_KEY, currentCounters.counter().accepted);
+ addMeasure(component, HIGH_IMPACT_ACCEPTED_ISSUES_KEY, currentCounters.counter().highImpactAccepted);
}
private void addMeasuresByType(Component component) {
measureRepository.add(component, metric, Measure.newMeasureBuilder()
.create(bag.count(type)));
}
+
+ addMeasure(component, NEW_ACCEPTED_ISSUES_KEY, currentCounters.counterForPeriod().accepted);
}
/**
private int confirmed = 0;
private int falsePositives = 0;
private int accepted = 0;
+ private int highImpactAccepted = 0;
private final Multiset<String> severityBag = HashMultiset.create();
private final EnumMultiset<RuleType> typeBag = EnumMultiset.create(RuleType.class);
confirmed += counter.confirmed;
falsePositives += counter.falsePositives;
accepted += counter.accepted;
+ highImpactAccepted += counter.highImpactAccepted;
severityBag.addAll(counter.severityBag);
typeBag.addAll(counter.typeBag);
}
falsePositives++;
} else if (IssueStatus.ACCEPTED.equals(issue.getIssueStatus())) {
accepted++;
+ if (issue.impacts().values().stream().anyMatch(severity -> severity == Severity.HIGH)) {
+ highImpactAccepted++;
+ }
}
switch (issue.status()) {
case STATUS_OPEN:
import org.assertj.core.data.MapEntry;
import org.junit.Rule;
import org.junit.Test;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rules.RuleType;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
import org.sonar.ce.task.projectanalysis.component.Component;
import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
import static org.sonar.api.issue.Issue.STATUS_OPEN;
import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
+import static org.sonar.api.issue.impact.Severity.HIGH;
+import static org.sonar.api.issue.impact.Severity.MEDIUM;
import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES;
import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES;
import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES;
+import static org.sonar.api.measures.CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS;
+import static org.sonar.api.measures.CoreMetrics.NEW_ACCEPTED_ISSUES;
+import static org.sonar.api.measures.CoreMetrics.NEW_ACCEPTED_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_BUGS;
import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES;
import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
-import static org.sonar.api.measures.CoreMetrics.WONT_FIX_ISSUES;
-import static org.sonar.api.measures.CoreMetrics.WONT_FIX_ISSUES_KEY;
import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.api.rule.Severity.CRITICAL;
import static org.sonar.api.rule.Severity.MAJOR;
+import static org.sonar.api.rules.RuleType.BUG;
+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.measure.Measure.newMeasureBuilder;
import static org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry.entryOf;
.add(NEW_CODE_SMELLS)
.add(NEW_BUGS)
.add(NEW_VULNERABILITIES)
- .add(NEW_SECURITY_HOTSPOTS);
+ .add(NEW_SECURITY_HOTSPOTS)
+ .add(NEW_ACCEPTED_ISSUES)
+ .add(HIGH_IMPACT_ACCEPTED_ISSUES);
@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
// 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);
- underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(RuleType.CODE_SMELL));
- underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR).setType(RuleType.CODE_SMELL));
+ underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(CODE_SMELL));
+ underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR).setType(CODE_SMELL));
underTest.afterComponent(FILE1);
// file2 : one bug
underTest.beforeComponent(FILE2);
- underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER).setType(RuleType.BUG));
+ underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER).setType(BUG));
underTest.afterComponent(FILE2);
// file3 : one unresolved security hotspot
underTest.beforeComponent(FILE1);
// created before -> existing issues (so ignored)
- underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(RuleType.CODE_SMELL));
- underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(RuleType.BUG));
+ underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(CODE_SMELL));
+ underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(BUG));
// created after -> 4 new issues but 1 is closed
- underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL).setType(RuleType.CODE_SMELL));
- underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL).setType(RuleType.BUG));
- underTest.onIssue(FILE1, createNewIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR).setType(RuleType.BUG));
+ underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL).setType(CODE_SMELL));
+ underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL).setType(BUG));
+ underTest.onIssue(FILE1, createNewIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR).setType(BUG));
underTest.onIssue(FILE1, createNewSecurityHotspot());
underTest.onIssue(FILE1, createNewSecurityHotspot().setResolution(RESOLUTION_WONT_FIX).setStatus(STATUS_CLOSED));
underTest.afterComponent(FILE1);
entry(NEW_CODE_SMELLS_KEY, 1), entry(NEW_BUGS_KEY, 1), entry(NEW_VULNERABILITIES_KEY, 0), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
}
+ @Test
+ public void count_new_accepted_issues() {
+ when(newIssueClassifier.isEnabled()).thenReturn(true);
+
+ underTest.beforeComponent(FILE1);
+ // created before -> existing issues (so ignored)
+ underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, CRITICAL));
+ underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, CRITICAL));
+
+ // created after -> 2 accepted, 1 open, 1 hotspot
+ underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL));
+ underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, CRITICAL));
+ underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, CRITICAL));
+ underTest.onIssue(FILE1, createNewSecurityHotspot());
+ underTest.afterComponent(FILE1);
+
+ underTest.beforeComponent(PROJECT);
+ underTest.afterComponent(PROJECT);
+
+ assertValues(FILE1, entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 2), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
+ assertValues(PROJECT, entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 2), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
+ }
+
+ @Test
+ public void count_high_impact_accepted_issues() {
+ when(newIssueClassifier.isEnabled()).thenReturn(true);
+
+ underTest.beforeComponent(FILE1);
+ // created before -> existing issues with 1 high impact accepted
+ underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, HIGH));
+ underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, HIGH));
+ underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MEDIUM));
+
+ // created after -> 2 high impact accepted
+ underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, HIGH));
+ underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, HIGH));
+ underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, HIGH));
+ underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MEDIUM));
+ underTest.onIssue(FILE1, createNewSecurityHotspot());
+ underTest.afterComponent(FILE1);
+
+ underTest.beforeComponent(PROJECT);
+ underTest.afterComponent(PROJECT);
+
+ assertValues(FILE1, entry(VIOLATIONS_KEY, 2), entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 3),
+ entry(HIGH_IMPACT_ACCEPTED_ISSUES_KEY, 3));
+ assertValues(PROJECT, entry(VIOLATIONS_KEY, 2), entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 3),
+ entry(HIGH_IMPACT_ACCEPTED_ISSUES_KEY, 3));
+ }
+
@Test
public void exclude_hotspots_from_issue_counts() {
// bottom-up traversal -> from files to project
}
private DefaultIssue createNewIssue(@Nullable String resolution, String status, String severity) {
- return createNewIssue(resolution, status, severity, RuleType.CODE_SMELL);
+ return createNewIssue(resolution, status, severity, CODE_SMELL);
+ }
+
+ private DefaultIssue createNewIssue(@Nullable String resolution, String status, Severity impactSeverity) {
+ DefaultIssue issue = createNewIssue(resolution, status, MAJOR, CODE_SMELL);
+ issue.addImpact(SoftwareQuality.MAINTAINABILITY, impactSeverity);
+ return issue;
}
private DefaultIssue createNewIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) {
}
private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity) {
- return createIssue(resolution, status, severity, RuleType.CODE_SMELL);
+ return createIssue(resolution, status, severity, CODE_SMELL);
+ }
+
+ private static DefaultIssue createIssue(@Nullable String resolution, String status, Severity impactSeverity) {
+ DefaultIssue issue = createIssue(resolution, status, MAJOR, CODE_SMELL);
+ issue.addImpact(SoftwareQuality.MAINTAINABILITY, impactSeverity);
+ return issue;
}
private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) {
}
private static DefaultIssue createSecurityHotspot() {
- return createIssue(null, STATUS_OPEN, "MAJOR", RuleType.SECURITY_HOTSPOT);
+ return createIssue(null, STATUS_OPEN, "MAJOR", SECURITY_HOTSPOT);
}
private DefaultIssue createNewSecurityHotspot() {
- return createNewIssue(null, STATUS_OPEN, "MAJOR", RuleType.SECURITY_HOTSPOT);
+ return createNewIssue(null, STATUS_OPEN, "MAJOR", SECURITY_HOTSPOT);
}
}
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.apache.ibatis.cursor.Cursor;
+import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactoryFast;
-import org.sonar.core.util.Uuids;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.Pagination;
ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
RuleDto rule = db.rules().insert();
db.issues().insert(rule, project, file,
- i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE").setSeverity("MAJOR").setType(RuleType.BUG).setIssueCreationTime(1_500L));
+ i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE").setSeverity("MAJOR").setType(RuleType.BUG).setIssueCreationTime(1_500L)
+ .replaceAllImpacts(List.of(createImpact(SECURITY, HIGH), createImpact(MAINTAINABILITY, LOW))));
db.issues().insert(rule, project, file,
- i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG).setIssueCreationTime(1_600L));
+ i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG).setIssueCreationTime(1_600L)
+ .replaceAllImpacts(List.of(createImpact(SECURITY, HIGH), createImpact(MAINTAINABILITY, HIGH))));
IssueDto criticalBug2 = db.issues().insert(rule, project, file,
- i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG).setIssueCreationTime(1_700L));
+ i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG).setIssueCreationTime(1_700L)
+ .replaceAllImpacts(List.of(createImpact(SECURITY, MEDIUM), createImpact(MAINTAINABILITY, LOW))));
// closed issues are ignored
db.issues().insert(rule, project, file,
- i -> i.setStatus("CLOSED").setResolution("REMOVED").setSeverity("CRITICAL").setType(RuleType.BUG).setIssueCreationTime(1_700L));
+ i -> i.setStatus("CLOSED").setResolution("REMOVED").setSeverity("CRITICAL").setType(RuleType.BUG).setIssueCreationTime(1_700L)
+ .replaceAllImpacts(List.of(createImpact(SECURITY, HIGH))));
Collection<IssueGroupDto> result = underTest.selectIssueGroupsByComponent(db.getSession(), file, 1_000L);
+ assertThat(result).hasSize(3);
assertThat(result.stream().mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.BUG.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
assertThat(result.stream().filter(g -> g.getSeverity().equals("CRITICAL")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(2);
assertThat(result.stream().filter(g -> g.getSeverity().equals("MAJOR")).mapToLong(IssueGroupDto::getCount).sum()).isOne();
assertThat(result.stream().filter(g -> g.getSeverity().equals("MINOR")).mapToLong(IssueGroupDto::getCount).sum()).isZero();
+ assertThat(result.stream().filter(IssueGroupDto::hasHighImpactSeverity).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(2);
assertThat(result.stream().filter(g -> g.getStatus().equals("OPEN")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(2);
assertThat(result.stream().filter(g -> g.getStatus().equals("RESOLVED")).mapToLong(IssueGroupDto::getCount).sum()).isOne();
assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
}
+ @NotNull
+ private static ImpactDto createImpact(SoftwareQuality softwareQuality, Severity high) {
+ return new ImpactDto().setUuid(UuidFactoryFast.getInstance().create()).setSoftwareQuality(softwareQuality).setSeverity(high);
+ }
+
@Test
public void selectGroupsOfComponentTreeOnLeak_on_file_new_code_reference_branch() {
ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
RuleDto rule = db.rules().insert();
IssueDto fpBug = db.issues().insert(rule, project, file,
i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE").setSeverity("MAJOR").setType(RuleType.BUG));
+ IssueDto acceptedBug = db.issues().insert(rule, project, file,
+ i -> i.setStatus("RESOLVED").setResolution("WONTFIX").setSeverity("MAJOR").setType(RuleType.BUG));
IssueDto criticalBug1 = db.issues().insert(rule, project, file,
i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG));
IssueDto criticalBug2 = db.issues().insert(rule, project, file,
// two issues part of new code period on reference branch
db.issues().insertNewCodeReferenceIssue(fpBug);
+ db.issues().insertNewCodeReferenceIssue(acceptedBug);
db.issues().insertNewCodeReferenceIssue(criticalBug1);
db.issues().insertNewCodeReferenceIssue(criticalBug2);
Collection<IssueGroupDto> result = underTest.selectIssueGroupsByComponent(db.getSession(), file, -1);
- assertThat(result.stream().mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(4);
+ assertThat(result.stream().mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(5);
- assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.BUG.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(4);
+ assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.BUG.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(5);
assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.CODE_SMELL.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isZero();
assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.VULNERABILITY.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isZero();
assertThat(result.stream().filter(g -> g.getSeverity().equals("CRITICAL")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
- assertThat(result.stream().filter(g -> g.getSeverity().equals("MAJOR")).mapToLong(IssueGroupDto::getCount).sum()).isOne();
+ assertThat(result.stream().filter(g -> g.getSeverity().equals("MAJOR")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(2);
assertThat(result.stream().filter(g -> g.getSeverity().equals("MINOR")).mapToLong(IssueGroupDto::getCount).sum()).isZero();
assertThat(result.stream().filter(g -> g.getStatus().equals("OPEN")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
- assertThat(result.stream().filter(g -> g.getStatus().equals("RESOLVED")).mapToLong(IssueGroupDto::getCount).sum()).isOne();
+ assertThat(result.stream().filter(g -> g.getStatus().equals("RESOLVED")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(2);
assertThat(result.stream().filter(g -> g.getStatus().equals("CLOSED")).mapToLong(IssueGroupDto::getCount).sum()).isZero();
assertThat(result.stream().filter(g -> "FALSE-POSITIVE".equals(g.getResolution())).mapToLong(IssueGroupDto::getCount).sum()).isOne();
+ assertThat(result.stream().filter(g -> "WONTFIX".equals(g.getResolution())).mapToLong(IssueGroupDto::getCount).sum()).isOne();
assertThat(result.stream().filter(g -> g.getResolution() == null).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
- assertThat(result.stream().filter(IssueGroupDto::isInLeak).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
+ assertThat(result.stream().filter(IssueGroupDto::isInLeak).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(4);
assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne();
}
prepareTables();
IssueDto issueDto = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1)
.setSelectedAt(1_400_000_000_000L)
- .replaceAllImpacts(List.of(new ImpactDto().setUuid(Uuids.createFast()).setSoftwareQuality(RELIABILITY).setSeverity(LOW)));
+ .replaceAllImpacts(List.of(createImpact(RELIABILITY, LOW)));
underTest.updateIfBeforeSelectedDate(db.getSession(), issueDto);
public class IssueGroupDto {
private int ruleType;
private String severity;
+ private boolean hasHighImpactSeverity;
@Nullable
private String resolution;
private String status;
private long count;
private boolean inLeak;
+ public IssueGroupDto() {
+ // nothing to do
+ }
+
public int getRuleType() {
return ruleType;
}
return severity;
}
+ public boolean hasHighImpactSeverity() {
+ return hasHighImpactSeverity;
+ }
+
@CheckForNull
public String getResolution() {
return resolution;
return this;
}
+ public IssueGroupDto setHasHighImpactSeverity(boolean hasHighImpactSeverity) {
+ this.hasHighImpactSeverity = hasHighImpactSeverity;
+ return this;
+ }
+
public IssueGroupDto setResolution(@Nullable String resolution) {
this.resolution = resolution;
return this;
</select>
<select id="selectIssueGroupsByComponent" resultType="org.sonar.db.issue.IssueGroupDto" parameterType="map">
- select i.issue_type as ruleType, i.severity as severity, i.resolution as resolution, i.status as status, sum(i.effort) as effort, count(i.issue_type) as "count",
- <if test="leakPeriodBeginningDate >= 0">
- (i.issue_creation_date > #{leakPeriodBeginningDate,jdbcType=BIGINT}) as inLeak
- </if>
- <if test="leakPeriodBeginningDate < 0">
- CASE WHEN n.uuid is null THEN false ELSE true END as inLeak
- </if>
+ <include refid="withHighImpactSeverityIssues"/>
+ select
+ i.issue_type as ruleType,
+ i.severity as severity,
+ exists(select 1 from high_impact_severity_issues hisi where hisi.kee = i.kee) as hasHighImpactSeverity,
+ i.resolution as resolution,
+ i.status as status, sum(i.effort) as effort,
+ count(i.issue_type) as "count",
+ <if test="leakPeriodBeginningDate >= 0">
+ (i.issue_creation_date > #{leakPeriodBeginningDate,jdbcType=BIGINT}) as inLeak
+ </if>
+ <if test="leakPeriodBeginningDate < 0">
+ CASE WHEN n.uuid is null THEN false ELSE true END as inLeak
+ </if>
from issues i
<if test="leakPeriodBeginningDate < 0">
left join new_code_reference_issues n on n.issue_key = i.kee
</if>
- where i.status !='CLOSED'
+ where i.status <> 'CLOSED'
and i.component_uuid = #{component.uuid,jdbcType=VARCHAR}
- group by i.issue_type, i.severity, i.resolution, i.status, inLeak
+ group by i.issue_type, i.severity, hasHighImpactSeverity, i.resolution, i.status, inLeak
</select>
- <select id="selectIssueGroupsByComponent" resultType="org.sonar.db.issue.IssueGroupDto" parameterType="map" databaseId="oracle">
- select i2.issue_type as ruleType, i2.severity as severity, i2.resolution as resolution, i2.status as status, sum(i2.effort) as effort, count(i2.issue_type) as "count", i2.inLeak as inLeak
- from (
- select i.issue_type, i.severity, i.resolution, i.status, i.effort,
- <if test="leakPeriodBeginningDate >= 0">
- case when i.issue_creation_date > #{leakPeriodBeginningDate,jdbcType=BIGINT} then 1 else 0 end as inLeak
- </if>
- <if test="leakPeriodBeginningDate < 0">
- case when n.uuid is null then 0 else 1 end as inLeak
- </if>
+ <sql id="withHighImpactSeverityIssues">
+ with high_impact_severity_issues as (
+ select distinct kee
from issues i
- <if test="leakPeriodBeginningDate < 0">
- left join new_code_reference_issues n on n.issue_key = i.kee
- </if>
- where i.status !='CLOSED'
+ inner join issues_impacts ii on ii.issue_key = i.kee
+ where i.status <> 'CLOSED'
and i.component_uuid = #{component.uuid,jdbcType=VARCHAR}
- ) i2
- group by i2.issue_type, i2.severity, i2.resolution, i2.status, i2.inLeak
+ and ii.severity = 'HIGH'
+ )
+ </sql>
+
+ <select id="selectIssueGroupsByComponent" resultType="org.sonar.db.issue.IssueGroupDto" parameterType="map" databaseId="oracle">
+ <include refid="selectIssueGroupsByComponentVendorSpecific"/>
</select>
<select id="selectIssueGroupsByComponent" resultType="org.sonar.db.issue.IssueGroupDto" parameterType="map" databaseId="mssql">
- select i2.issue_type as ruleType, i2.severity as severity, i2.resolution as resolution, i2.status as status, sum(i2.effort) as effort, count(i2.issue_type) as "count", i2.inLeak as inLeak
+ <include refid="selectIssueGroupsByComponentVendorSpecific"/>
+ </select>
+
+ <sql id="selectIssueGroupsByComponentVendorSpecific">
+ <include refid="withHighImpactSeverityIssues"/>
+ select
+ i2.issue_type as ruleType,
+ i2.severity as severity,
+ i2.hasHighImpactSeverity as hasHighImpactSeverity,
+ i2.resolution as resolution,
+ i2.status as status,
+ sum(i2.effort) as effort,
+ count(i2.issue_type) as "count",
+ i2.inLeak as inLeak
from (
- select i.issue_type, i.severity, i.resolution, i.status, i.effort,
- <if test="leakPeriodBeginningDate >= 0">
- case when i.issue_creation_date > #{leakPeriodBeginningDate,jdbcType=BIGINT} then 1 else 0 end as inLeak
- </if>
- <if test="leakPeriodBeginningDate < 0">
- case when n.uuid is null then 0 else 1 end as inLeak
- </if>
+ select
+ i.issue_type,
+ i.severity,
+ case when exists(select 1 from high_impact_severity_issues hisi where hisi.kee = i.kee) then 1 else 0 end as hasHighImpactSeverity,
+ i.resolution,
+ i.status,
+ i.effort,
+ <if test="leakPeriodBeginningDate >= 0">
+ case when i.issue_creation_date > #{leakPeriodBeginningDate,jdbcType=BIGINT} then 1 else 0 end as inLeak
+ </if>
+ <if test="leakPeriodBeginningDate < 0">
+ case when n.uuid is null then 0 else 1 end as inLeak
+ </if>
from issues i
<if test="leakPeriodBeginningDate < 0">
left join new_code_reference_issues n on n.issue_key = i.kee
</if>
- where i.status !='CLOSED'
+ where i.status <> 'CLOSED'
and i.component_uuid = #{component.uuid,jdbcType=VARCHAR}
) i2
- group by i2.issue_type, i2.severity, i2.resolution, i2.status, i2.inLeak
- </select>
+ group by i2.issue_type, i2.severity, i2.hasHighImpactSeverity, i2.resolution, i2.status, i2.inLeak
+ </sql>
<select id="selectIssueKeysByComponentUuid" parameterType="string" resultType="string">
select
import javax.annotation.Nullable;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.RuleType;
+import org.sonar.core.issue.status.IssueStatus;
import org.sonar.db.issue.IssueGroupDto;
import org.sonar.db.rule.SeverityUtil;
private final Map<String, Count> byStatus = new HashMap<>();
private final Map<String, Count> hotspotsByStatus = new HashMap<>();
private final Count unresolved = new Count();
+ private final Count highImpactAccepted = new Count();
IssueCounter(Collection<IssueGroupDto> groups) {
for (IssueGroupDto group : groups) {
- RuleType ruleType = RuleType.valueOf(group.getRuleType());
- if (ruleType.equals(SECURITY_HOTSPOT)) {
- if (group.getResolution() == null) {
- unresolvedByType
- .computeIfAbsent(SECURITY_HOTSPOT, k -> new Count())
- .add(group);
- }
- if (group.getStatus() != null) {
- hotspotsByStatus
- .computeIfAbsent(group.getStatus(), k -> new Count())
- .add(group);
- }
- continue;
- }
- if (group.getResolution() == null) {
- highestSeverityOfUnresolved
- .computeIfAbsent(ruleType, k -> new HighestSeverity())
- .add(group);
- effortOfUnresolved
- .computeIfAbsent(ruleType, k -> new Effort())
- .add(group);
- unresolvedBySeverity
- .computeIfAbsent(group.getSeverity(), k -> new Count())
- .add(group);
- unresolvedByType
- .computeIfAbsent(ruleType, k -> new Count())
- .add(group);
- unresolved.add(group);
+ if (RuleType.valueOf(group.getRuleType()).equals(SECURITY_HOTSPOT)) {
+ processHotspotGroup(group);
} else {
- byResolution
- .computeIfAbsent(group.getResolution(), k -> new Count())
- .add(group);
+ processGroup(group);
}
- if (group.getStatus() != null) {
- byStatus
- .computeIfAbsent(group.getStatus(), k -> new Count())
- .add(group);
+ }
+ }
+
+ private void processHotspotGroup(IssueGroupDto group) {
+ if (group.getResolution() == null) {
+ unresolvedByType
+ .computeIfAbsent(SECURITY_HOTSPOT, k -> new Count())
+ .add(group);
+ }
+ if (group.getStatus() != null) {
+ hotspotsByStatus
+ .computeIfAbsent(group.getStatus(), k -> new Count())
+ .add(group);
+ }
+ }
+
+ private void processGroup(IssueGroupDto group) {
+ if (group.getResolution() == null) {
+ RuleType ruleType = RuleType.valueOf(group.getRuleType());
+ highestSeverityOfUnresolved
+ .computeIfAbsent(ruleType, k -> new HighestSeverity())
+ .add(group);
+ effortOfUnresolved
+ .computeIfAbsent(ruleType, k -> new Effort())
+ .add(group);
+ unresolvedBySeverity
+ .computeIfAbsent(group.getSeverity(), k -> new Count())
+ .add(group);
+ unresolvedByType
+ .computeIfAbsent(ruleType, k -> new Count())
+ .add(group);
+ unresolved.add(group);
+ } else {
+ byResolution
+ .computeIfAbsent(group.getResolution(), k -> new Count())
+ .add(group);
+ if (IssueStatus.ACCEPTED.equals(IssueStatus.of(group.getStatus(), group.getResolution())) && group.hasHighImpactSeverity()) {
+ highImpactAccepted.add(group);
}
}
+ if (group.getStatus() != null) {
+ byStatus
+ .computeIfAbsent(group.getStatus(), k -> new Count())
+ .add(group);
+ }
}
public Optional<String> getHighestSeverityOfUnresolved(RuleType ruleType, boolean onlyInLeak) {
return value(unresolved, onlyInLeak);
}
+ public long countHighImpactAccepted(boolean onlyInLeak) {
+ return value(highImpactAccepted, onlyInLeak);
+ }
+
public long countHotspotsByStatus(String status, boolean onlyInLeak) {
return value(hotspotsByStatus.get(status), onlyInLeak);
}
new MeasureUpdateFormula(CoreMetrics.ACCEPTED_ISSUES, false, new AddChildren(),
(context, issues) -> context.setValue(issues.countByResolution(Issue.RESOLUTION_WONT_FIX, false))),
+ new MeasureUpdateFormula(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, false, new AddChildren(),
+ (context, issues) -> context.setValue(issues.countHighImpactAccepted(false))),
+
new MeasureUpdateFormula(CoreMetrics.OPEN_ISSUES, false, new AddChildren(),
(context, issues) -> context.setValue(issues.countByStatus(Issue.STATUS_OPEN, false))),
new MeasureUpdateFormula(CoreMetrics.NEW_INFO_VIOLATIONS, true, new AddChildren(),
(context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.INFO, true))),
+ new MeasureUpdateFormula(CoreMetrics.NEW_ACCEPTED_ISSUES, true, new AddChildren(),
+ (context, issues) -> context.setValue(issues.countByResolution(Issue.RESOLUTION_WONT_FIX, true))),
+
new MeasureUpdateFormula(CoreMetrics.NEW_TECHNICAL_DEBT, true, new AddChildren(),
(context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.CODE_SMELL, true))),
.assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 7);
}
+ @Test
+ public void test_high_impact_accepted_issues() {
+ withNoIssues()
+ .assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 0);
+
+ with(
+ newGroup(RuleType.CODE_SMELL).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)
+ .setHasHighImpactSeverity(true).setCount(3),
+ newGroup(RuleType.CODE_SMELL).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)
+ .setHasHighImpactSeverity(true).setCount(4),
+ newGroup(RuleType.CODE_SMELL).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)
+ .setHasHighImpactSeverity(false).setCount(5),
+ newGroup(RuleType.BUG).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)
+ .setHasHighImpactSeverity(true).setCount(30),
+ newGroup(RuleType.BUG).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)
+ .setHasHighImpactSeverity(true).setCount(40),
+ newGroup(RuleType.BUG).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)
+ .setHasHighImpactSeverity(false).setCount(50),
+ // exclude security hotspot
+ newGroup(RuleType.SECURITY_HOTSPOT).setResolution(Issue.RESOLUTION_WONT_FIX).setHasHighImpactSeverity(true).setCount(40))
+ .assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 4 + 40);
+ }
+
@Test
public void test_technical_debt() {
withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0);
.assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 3 + 5 + 7);
}
+ @Test
+ public void test_new_accepted_issues() {
+ withNoIssues()
+ .assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 0);
+
+ with(
+ newGroup(RuleType.CODE_SMELL).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setInLeak(true).setCount(3),
+ newGroup(RuleType.CODE_SMELL).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(true).setCount(4),
+ newGroup(RuleType.BUG).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setInLeak(true).setCount(30),
+ newGroup(RuleType.BUG).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(true).setCount(40),
+ // not in leak
+ newGroup(RuleType.CODE_SMELL).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(false).setCount(5),
+ newGroup(RuleType.BUG).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(false).setCount(50))
+ .assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 4 + 40);
+ }
+
@Test
public void test_new_technical_debt() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0);
metric.wont_fix_issues.name=Won't Fix Issues
metric.accepted_issues.description=Accepted issues
metric.accepted_issues.name=Accepted Issues
-metric.pull_request_fixed_issues.name=Fixed issues
-metric.pull_request_fixed_issues.description=Fixed issues
-metric.new_accepted_issues.name=Accepted issues
-metric.new_accepted_issues.description=Accepted issues
-metric.high_impact_accepted_issues.name=High impact accepted issues
-metric.high_impact_accepted_issues.description=High impact accepted issues
+metric.pull_request_fixed_issues.name=Pull request fixed issues
+metric.pull_request_fixed_issues.description=Count of issues that would be fixed by the pull request.
+metric.new_accepted_issues.name=New Accepted Issues
+metric.new_accepted_issues.description=New accepted issues
+metric.high_impact_accepted_issues.name=High Impact Accepted Issues
+metric.high_impact_accepted_issues.description=Accepted issues with high impact
+
#------------------------------------------------------------------------------
#
# PERMISSIONS
guiding.issue_accept.3.content.2=Consider Accepting issues, assigning the issue or using comments and tags instead.
guiding.issue_accept.3.content.link=Learn more about issues statuses
guiding.step_x_of_y={0} of {1}
-