Browse Source

SONAR-21770 Accept new metrics 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'

tags/10.5.0.89998
Dejan Milisavljevic 1 month ago
parent
commit
494316d9f2
18 changed files with 344 additions and 127 deletions
  1. 1
    1
      gradle.properties
  2. 21
    12
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java
  3. 83
    30
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java
  4. 47
    4
      server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java
  5. 2
    2
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java
  6. 9
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueImpactGroupDto.java
  7. 1
    1
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java
  8. 29
    11
      server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
  9. 47
    6
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java
  10. 6
    3
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueCounter.java
  11. 1
    1
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveMeasureTreeUpdaterImpl.java
  12. 12
    3
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java
  13. 1
    0
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java
  14. 12
    1
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
  15. 1
    0
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java
  16. 1
    0
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java
  17. 61
    52
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java
  18. 9
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 1
- 1
gradle.properties View File

group=org.sonarsource.sonarqube group=org.sonarsource.sonarqube
version=10.5 version=10.5
pluginApiVersion=10.6.0.2114
pluginApiVersion=10.7.0.2175
description=Open source platform for continuous inspection of code quality description=Open source platform for continuous inspection of code quality
projectTitle=SonarQube projectTitle=SonarQube
org.gradle.jvmargs=-Xmx2048m org.gradle.jvmargs=-Xmx2048m

+ 21
- 12
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java View File

import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_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.HIGH_IMPACT_ACCEPTED_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES;
import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.MINOR_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_ACCEPTED_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES_KEY;
import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES;
import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES;
import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY; import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
import static org.sonar.api.rule.Severity.BLOCKER; import static org.sonar.api.rule.Severity.BLOCKER;
INFO, NEW_INFO_VIOLATIONS_KEY); INFO, NEW_INFO_VIOLATIONS_KEY);


static final Map<String, String> IMPACT_TO_METRIC_KEY = Map.of( static final Map<String, String> IMPACT_TO_METRIC_KEY = Map.of(
SoftwareQuality.SECURITY.name(), SECURITY_ISSUES.key(),
SoftwareQuality.RELIABILITY.name(), RELIABILITY_ISSUES.key(),
SoftwareQuality.MAINTAINABILITY.name(), MAINTAINABILITY_ISSUES.key());
SoftwareQuality.SECURITY.name(), SECURITY_ISSUES_KEY,
SoftwareQuality.RELIABILITY.name(), RELIABILITY_ISSUES_KEY,
SoftwareQuality.MAINTAINABILITY.name(), MAINTAINABILITY_ISSUES_KEY);

static final Map<String, String> IMPACT_TO_NEW_METRIC_KEY = Map.of(
SoftwareQuality.SECURITY.name(), NEW_SECURITY_ISSUES_KEY,
SoftwareQuality.RELIABILITY.name(), NEW_RELIABILITY_ISSUES_KEY,
SoftwareQuality.MAINTAINABILITY.name(), NEW_MAINTAINABILITY_ISSUES_KEY);


private static final Map<RuleType, String> TYPE_TO_METRIC_KEY = ImmutableMap.<RuleType, String>builder() private static final Map<RuleType, String> TYPE_TO_METRIC_KEY = ImmutableMap.<RuleType, String>builder()
.put(CODE_SMELL, CODE_SMELLS_KEY) .put(CODE_SMELL, CODE_SMELLS_KEY)
String severity = entry.getKey(); String severity = entry.getKey();
String metricKey = entry.getValue(); String metricKey = entry.getValue();
Multiset<String> bag = currentCounters.counterForPeriod().severityBag; Multiset<String> bag = currentCounters.counterForPeriod().severityBag;
Metric metric = metricRepository.getByKey(metricKey);
measureRepository.add(component, metric, Measure.newMeasureBuilder()
.create(bag.count(severity)));
addMeasure(component, metricKey, bag.count(severity));
} }


// waiting for Java 8 lambda in order to factor this loop with the previous one // waiting for Java 8 lambda in order to factor this loop with the previous one
RuleType type = entry.getKey(); RuleType type = entry.getKey();
String metricKey = entry.getValue(); String metricKey = entry.getValue();
Multiset<RuleType> bag = currentCounters.counterForPeriod().typeBag; Multiset<RuleType> bag = currentCounters.counterForPeriod().typeBag;
Metric metric = metricRepository.getByKey(metricKey);
measureRepository.add(component, metric, Measure.newMeasureBuilder()
.create(bag.count(type)));
addMeasure(component, metricKey, bag.count(type));
}

for (Map.Entry<String, Map<String, Long>> impactEntry : currentCounters.counterForPeriod().impactsBag.entrySet()) {
String json = ImpactMeasureBuilder.fromMap(impactEntry.getValue()).buildAsString();
addMeasure(component, IMPACT_TO_NEW_METRIC_KEY.get(impactEntry.getKey()), json);
} }


addMeasure(component, NEW_ACCEPTED_ISSUES_KEY, currentCounters.counterForPeriod().accepted); addMeasure(component, NEW_ACCEPTED_ISSUES_KEY, currentCounters.counterForPeriod().accepted);

+ 83
- 30
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java View File

import java.util.Set; import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.assertj.core.data.MapEntry; import org.assertj.core.data.MapEntry;
import org.junit.Rule;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.sonar.api.issue.impact.Severity; import org.sonar.api.issue.impact.Severity;
import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rules.RuleType; import org.sonar.api.rules.RuleType;
import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES;
import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES;
import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES; import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; 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.component.ReportComponent.builder;
import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_METRIC_KEY; import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_METRIC_KEY;
import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_NEW_METRIC_KEY;
import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder; import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
import static org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry.entryOf; import static org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry.entryOf;


public class IssueCounterTest {
class IssueCounterTest {


private static final Component FILE1 = builder(Component.Type.FILE, 1).build(); private static final Component FILE1 = builder(Component.Type.FILE, 1).build();
private static final Component FILE2 = builder(Component.Type.FILE, 2).build(); private static final Component FILE2 = builder(Component.Type.FILE, 2).build();
private static final Component FILE3 = builder(Component.Type.FILE, 3).build(); private static final Component FILE3 = builder(Component.Type.FILE, 3).build();
private static final Component PROJECT = builder(Component.Type.PROJECT, 4).addChildren(FILE1, FILE2, FILE3).build(); private static final Component PROJECT = builder(Component.Type.PROJECT, 4).addChildren(FILE1, FILE2, FILE3).build();


@Rule
public BatchReportReaderRule reportReader = new BatchReportReaderRule();
@RegisterExtension
private final BatchReportReaderRule reportReader = new BatchReportReaderRule();


@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
@RegisterExtension
private final TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();


@Rule
public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
@RegisterExtension
private final MetricRepositoryRule metricRepository = new MetricRepositoryRule()
.add(VIOLATIONS) .add(VIOLATIONS)
.add(OPEN_ISSUES) .add(OPEN_ISSUES)
.add(REOPENED_ISSUES) .add(REOPENED_ISSUES)
.add(HIGH_IMPACT_ACCEPTED_ISSUES) .add(HIGH_IMPACT_ACCEPTED_ISSUES)
.add(RELIABILITY_ISSUES) .add(RELIABILITY_ISSUES)
.add(MAINTAINABILITY_ISSUES) .add(MAINTAINABILITY_ISSUES)
.add(SECURITY_ISSUES);
.add(SECURITY_ISSUES)
.add(NEW_RELIABILITY_ISSUES)
.add(NEW_MAINTAINABILITY_ISSUES)
.add(NEW_SECURITY_ISSUES);


@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
private NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
private IssueCounter underTest = new IssueCounter(metricRepository, measureRepository, newIssueClassifier);
@RegisterExtension
private final MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
private final NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
private final IssueCounter underTest = new IssueCounter(metricRepository, measureRepository, newIssueClassifier);
private static int issueCounter;


@Test @Test
public void count_issues_by_status() {
void count_issues_by_status() {
// bottom-up traversal -> from files to project // bottom-up traversal -> from files to project
underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);
underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER)); underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
} }


@Test @Test
public void count_issues_by_resolution() {
void count_issues_by_resolution() {
// bottom-up traversal -> from files to project // bottom-up traversal -> from files to project
underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);
underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER)); underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
} }


@Test @Test
public void count_unresolved_issues_by_severity() {
void count_unresolved_issues_by_severity() {
// bottom-up traversal -> from files to project // bottom-up traversal -> from files to project
underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);
underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER)); underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
} }


@Test @Test
public void count_unresolved_issues_by_type() {
void count_unresolved_issues_by_type() {
// bottom-up traversal -> from files to project // bottom-up traversal -> from files to project
// file1 : one open code smell, one closed code smell (which will be excluded from metric) // file1 : one open code smell, one closed code smell (which will be excluded from metric)
underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);
} }


@Test @Test
public void count_new_issues() {
void count_new_issues() {
when(newIssueClassifier.isEnabled()).thenReturn(true); when(newIssueClassifier.isEnabled()).thenReturn(true);


underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);
} }


@Test @Test
public void count_new_accepted_issues() {
void count_new_accepted_issues() {
when(newIssueClassifier.isEnabled()).thenReturn(true); when(newIssueClassifier.isEnabled()).thenReturn(true);


underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);
} }


@Test @Test
public void count_impacts() {
void onIssue_shouldCountOverallSoftwareQualitiesMeasures() {
when(newIssueClassifier.isEnabled()).thenReturn(true); when(newIssueClassifier.isEnabled()).thenReturn(true);


underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);


Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet(); Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet();


assertSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(4, 2, 2, 0), entries);
assertSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(2, 1, 1, 0), entries);
assertSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(0, 0, 0, 0), entries);
assertOverallSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(4, 2, 2, 0), entries);
assertOverallSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(2, 1, 1, 0), entries);
assertOverallSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(0, 0, 0, 0), entries);
}

@Test
void onIssue_shouldCountNewSoftwareQualitiesMeasures() {
when(newIssueClassifier.isEnabled()).thenReturn(true);

underTest.beforeComponent(FILE1);
underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.MAINTAINABILITY, HIGH));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, MEDIUM));

underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, HIGH));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, LOW));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.RELIABILITY, HIGH));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, MEDIUM));

underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, MEDIUM));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, LOW));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.SECURITY, HIGH));
underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, MEDIUM));

underTest.afterComponent(FILE1);

underTest.beforeComponent(PROJECT);
underTest.afterComponent(PROJECT);

Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet();

assertNewSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(2, 1, 1, 0), entries);
assertNewSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(2, 0, 1, 1), entries);
assertNewSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(4, 2, 1, 1), entries);
} }


private static Map<String, Long> getImpactMeasure(long total, long high, long medium, long low) { private static Map<String, Long> getImpactMeasure(long total, long high, long medium, long low) {
return map; return map;
} }


private void assertSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
private void assertOverallSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
Set<Map.Entry<String, Measure>> actualRaw) { Set<Map.Entry<String, Measure>> actualRaw) {
assertSoftwareQualityMeasures(softwareQuality, expectedMap, actualRaw, IMPACT_TO_METRIC_KEY);
}

private void assertNewSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
Set<Map.Entry<String, Measure>> actualRaw) {
assertSoftwareQualityMeasures(softwareQuality, expectedMap, actualRaw, IMPACT_TO_NEW_METRIC_KEY);
}

private void assertSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
Set<Map.Entry<String, Measure>> actualRaw, Map<String, String> impactToMetricMap) {


Map.Entry<String, Measure> softwareQualityMap = actualRaw.stream() Map.Entry<String, Measure> softwareQualityMap = actualRaw.stream()
.filter(e -> e.getKey().equals(IMPACT_TO_METRIC_KEY.get(softwareQuality.name())))
.filter(e -> e.getKey().equals(impactToMetricMap.get(softwareQuality.name())))
.findFirst() .findFirst()
.get(); .get();


} }


@Test @Test
public void count_high_impact_accepted_issues() {
void count_high_impact_accepted_issues() {
when(newIssueClassifier.isEnabled()).thenReturn(true); when(newIssueClassifier.isEnabled()).thenReturn(true);


underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);
} }


@Test @Test
public void exclude_hotspots_from_issue_counts() {
void exclude_hotspots_from_issue_counts() {
// bottom-up traversal -> from files to project // bottom-up traversal -> from files to project
underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);
underTest.onIssue(FILE1, createSecurityHotspot()); underTest.onIssue(FILE1, createSecurityHotspot());
} }


@Test @Test
public void exclude_new_hotspots_from_issue_counts() {
void exclude_new_hotspots_from_issue_counts() {
when(newIssueClassifier.isEnabled()).thenReturn(true); when(newIssueClassifier.isEnabled()).thenReturn(true);


underTest.beforeComponent(FILE1); underTest.beforeComponent(FILE1);


private DefaultIssue createNewIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality, Severity impactSeverity) { private DefaultIssue createNewIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality, Severity impactSeverity) {
DefaultIssue issue = createNewIssue(resolution, status, MAJOR, CODE_SMELL); DefaultIssue issue = createNewIssue(resolution, status, MAJOR, CODE_SMELL);
issue.addImpact(SoftwareQuality.MAINTAINABILITY, impactSeverity);
issue.addImpact(softwareQuality, impactSeverity);
return issue; return issue;
} }




private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) { private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) {
return new DefaultIssue() return new DefaultIssue()
.setKey(String.valueOf(++issueCounter))
.setResolution(resolution).setStatus(status) .setResolution(resolution).setStatus(status)
.setSeverity(severity).setRuleKey(RuleTesting.XOO_X1) .setSeverity(severity).setRuleKey(RuleTesting.XOO_X1)
.setType(ruleType); .setType(ruleType);

+ 47
- 4
server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java View File

import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.sonar.api.issue.Issue; import org.sonar.api.issue.Issue;
import org.sonar.api.issue.impact.Severity; import org.sonar.api.issue.impact.Severity;
import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.issue.impact.SoftwareQuality;
assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne(); assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne();
} }


@Test
void selectIssueImpactGroupsByComponent_shouldReturnImpactGroups() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void selectIssueImpactGroupsByComponent_shouldReturnImpactGroups(boolean inLeak) {

ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project)); ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
RuleDto rule = db.rules().insert(); RuleDto rule = db.rules().insert();
db.issues().insert(rule, project, file, db.issues().insert(rule, project, file,
i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_FALSE_POSITIVE).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH)))); i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_FALSE_POSITIVE).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH))));


Collection<IssueImpactGroupDto> result = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file);
Collection<IssueImpactGroupDto> result = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file, inLeak ? 1L : Long.MAX_VALUE);


assertThat(result).hasSize(5); assertThat(result).hasSize(5);
assertThat(result.stream().filter(IssueImpactGroupDto::isInLeak)).hasSize(inLeak ? 5 : 0);
assertThat(result.stream().mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(6); assertThat(result.stream().mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(6);


assertThat(result.stream().filter(g -> g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(4); assertThat(result.stream().filter(g -> g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(4);
assertThat(result.stream().noneMatch(g -> RESOLUTION_FALSE_POSITIVE.equals(g.getResolution()))).isTrue(); assertThat(result.stream().noneMatch(g -> RESOLUTION_FALSE_POSITIVE.equals(g.getResolution()))).isTrue();
assertThat(result.stream().noneMatch(g -> RELIABILITY == g.getSoftwareQuality())).isTrue(); assertThat(result.stream().noneMatch(g -> RELIABILITY == g.getSoftwareQuality())).isTrue();
assertThat(result.stream().noneMatch(g -> MEDIUM == g.getSeverity())).isTrue(); assertThat(result.stream().noneMatch(g -> MEDIUM == g.getSeverity())).isTrue();

}

@Test
void selectIssueImpactGroupsByComponent_whenNewCodeFromReferenceBranch_shouldReturnImpactGroups() {

ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
RuleDto rule = db.rules().insert();
IssueDto issueInNewCodePeriod = db.issues().insert(rule, project, file,
i -> i.setStatus(STATUS_OPEN).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH), createImpact(MAINTAINABILITY, LOW))));
db.issues().insert(rule, project, file,
i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH))));

db.issues().insertNewCodeReferenceIssue(issueInNewCodePeriod);

Collection<IssueImpactGroupDto> result = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file, -1L);

assertThat(result).hasSize(3);
assertThat(result.stream().filter(IssueImpactGroupDto::isInLeak)).hasSize(2);
assertThat(result.stream().mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(3);

assertThat(result.stream().filter(g -> g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2);
assertThat(result.stream().filter(g -> g.getSoftwareQuality() == MAINTAINABILITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(1);

assertThat(result.stream().filter(g -> g.getSeverity() == HIGH).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2);
assertThat(result.stream().filter(g -> g.getSeverity() == LOW).mapToLong(IssueImpactGroupDto::getCount).sum()).isOne();

assertThat(result.stream().filter(g -> STATUS_OPEN.equals(g.getStatus())).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2);
assertThat(result.stream().filter(g -> STATUS_REOPENED.equals(g.getStatus())).mapToLong(IssueImpactGroupDto::getCount).sum()).isZero();
assertThat(result.stream().filter(g -> STATUS_RESOLVED.equals(g.getStatus())).mapToLong(IssueImpactGroupDto::getCount).sum()).isOne();

assertThat(result.stream().filter(g -> RESOLUTION_WONT_FIX.equals(g.getResolution())).mapToLong(IssueImpactGroupDto::getCount).sum()).isOne();


assertThat(result.stream().filter(IssueImpactGroupDto::isInLeak).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2);
assertThat(result.stream().filter(g -> g.isInLeak() && g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(1);
assertThat(result.stream().filter(g -> g.isInLeak() && g.getSoftwareQuality() == MAINTAINABILITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(1);
} }


@NotNull @NotNull
ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project)); ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));


Collection<IssueImpactGroupDto> groups = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file);
Collection<IssueImpactGroupDto> groups = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file, 1_000L);


assertThat(groups).isEmpty(); assertThat(groups).isEmpty();
} }

+ 2
- 2
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java View File

return mapper(dbSession).selectIssueGroupsByComponent(component, leakPeriodBeginningDate); return mapper(dbSession).selectIssueGroupsByComponent(component, leakPeriodBeginningDate);
} }


public Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(DbSession dbSession, ComponentDto component) {
return mapper(dbSession).selectIssueImpactGroupsByComponent(component);
public Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(DbSession dbSession, ComponentDto component, long leakPeriodBeginningDate) {
return mapper(dbSession).selectIssueImpactGroupsByComponent(component, leakPeriodBeginningDate);
} }


public Cursor<IndexedIssueDto> scrollIssuesForIndexation(DbSession dbSession, @Nullable @Param("branchUuid") String branchUuid, public Cursor<IndexedIssueDto> scrollIssuesForIndexation(DbSession dbSession, @Nullable @Param("branchUuid") String branchUuid,

+ 9
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueImpactGroupDto.java View File

private SoftwareQuality softwareQuality; private SoftwareQuality softwareQuality;
private Severity severity; private Severity severity;
private long count; private long count;
private boolean inLeak;


public IssueImpactGroupDto() { public IssueImpactGroupDto() {
// nothing to do // nothing to do
public void setCount(long count) { public void setCount(long count) {
this.count = count; this.count = count;
} }

public boolean isInLeak() {
return inLeak;
}

public void setInLeak(boolean inLeak) {
this.inLeak = inLeak;
}
} }

+ 1
- 1
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java View File



Collection<IssueGroupDto> selectIssueGroupsByComponent(@Param("component") ComponentDto component, @Param("leakPeriodBeginningDate") long leakPeriodBeginningDate); Collection<IssueGroupDto> selectIssueGroupsByComponent(@Param("component") ComponentDto component, @Param("leakPeriodBeginningDate") long leakPeriodBeginningDate);


Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(@Param("component") ComponentDto component);
Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(@Param("component") ComponentDto component, @Param("leakPeriodBeginningDate") long leakPeriodBeginningDate);


List<IssueDto> selectByBranch(@Param("keys") Set<String> keys, @Nullable @Param("changedSince") Long changedSince); List<IssueDto> selectByBranch(@Param("keys") Set<String> keys, @Nullable @Param("changedSince") Long changedSince);



+ 29
- 11
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml View File



<select id="selectIssueImpactGroupsByComponent" resultType="org.sonar.db.issue.IssueImpactGroupDto" parameterType="map"> <select id="selectIssueImpactGroupsByComponent" resultType="org.sonar.db.issue.IssueImpactGroupDto" parameterType="map">
select select
i.status as status,
i.resolution as resolution,
ii.software_quality as softwareQuality,
ii.severity as severity,
count(i.kee) as "count"
from issues i
inner join issues_impacts ii on i.kee = ii.issue_key
where i.status &lt;&gt; 'CLOSED'
and (i.resolution is null or i.resolution = 'WONTFIX')
and i.component_uuid = #{component.uuid,jdbcType=VARCHAR}
group by i.status, i.resolution, ii.software_quality, ii.severity
i2.status as status,
i2.resolution as resolution,
i2.software_quality as softwareQuality,
i2.severity as severity,
count(i2.kee) as "count",
i2.inLeak as inLeak
from (
select
i.status,
i.resolution,
i.kee,
ii.software_quality,
ii.severity,
<if test="leakPeriodBeginningDate &gt;= 0">
case when i.issue_creation_date &gt; #{leakPeriodBeginningDate,jdbcType=BIGINT} then ${_true} else ${_false} end as inLeak
</if>
<if test="leakPeriodBeginningDate &lt; 0">
case when n.uuid is null then ${_false} else ${_true} end as inLeak
</if>
from issues i
inner join issues_impacts ii on i.kee = ii.issue_key
<if test="leakPeriodBeginningDate &lt; 0">
left join new_code_reference_issues n on n.issue_key = i.kee
</if>
where i.status &lt;&gt; 'CLOSED'
and (i.resolution is null or i.resolution = 'WONTFIX')
and i.component_uuid = #{component.uuid,jdbcType=VARCHAR}
) i2
group by i2.status, i2.resolution, i2.software_quality, i2.severity, i2.inLeak
</select> </select>


<select id="selectIssueKeysByComponentUuid" parameterType="string" resultType="string"> <select id="selectIssueKeysByComponentUuid" parameterType="string" resultType="string">

+ 47
- 6
server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java View File

import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple; import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY;
import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY;
ComponentDto mainBranch = projectData.getMainBranchComponent(); ComponentDto mainBranch = projectData.getMainBranchComponent();
addProjectPermission(projectData); addProjectPermission(projectData);
db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(mainBranch)); db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(mainBranch));
MetricDto dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(SECURITY_ISSUES_KEY));
db.measures().insertLiveMeasure(mainBranch, dataMetric, c -> c.setData(SECURITY_ISSUES_KEY + "_data"));
dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(MAINTAINABILITY_ISSUES_KEY));
db.measures().insertLiveMeasure(mainBranch, dataMetric, c -> c.setData(MAINTAINABILITY_ISSUES_KEY + "_data"));
dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(RELIABILITY_ISSUES_KEY));
db.measures().insertLiveMeasure(mainBranch, dataMetric, c -> c.setData(RELIABILITY_ISSUES_KEY + "_data"));

insertMetricAndLiveMeasure(mainBranch, SECURITY_ISSUES_KEY, "_data");
insertMetricAndLiveMeasure(mainBranch, MAINTAINABILITY_ISSUES_KEY, "_data");
insertMetricAndLiveMeasure(mainBranch, RELIABILITY_ISSUES_KEY, "_data");


ComponentTreeWsResponse response = ws.newRequest() ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, mainBranch.getKey()) .setParam(PARAM_COMPONENT, mainBranch.getKey())
tuple(RELIABILITY_ISSUES_KEY, RELIABILITY_ISSUES_KEY + "_data")); tuple(RELIABILITY_ISSUES_KEY, RELIABILITY_ISSUES_KEY + "_data"));
} }


@Test
void execute_whenUsingSupportedNewDATAMetrics_shouldReturnMetrics() {
ProjectData projectData = db.components().insertPrivateProject();
ComponentDto mainBranch = projectData.getMainBranchComponent();
addProjectPermission(projectData);
db.components().insertSnapshot(mainBranch);
ComponentDto file1 = db.components().insertComponent(newFileDto(mainBranch));

insertMetricAndLiveMeasure(file1, NEW_SECURITY_ISSUES_KEY, "_data");
insertMetricAndLiveMeasure(file1, NEW_MAINTAINABILITY_ISSUES_KEY, "_data");
insertMetricAndLiveMeasure(file1, NEW_RELIABILITY_ISSUES_KEY, "_data");

ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, mainBranch.getKey())
.setParam(PARAM_METRIC_KEYS, format("%s,%s,%s",
NEW_SECURITY_ISSUES_KEY,
NEW_MAINTAINABILITY_ISSUES_KEY,
NEW_RELIABILITY_ISSUES_KEY
))
.setParam(PARAM_ADDITIONAL_FIELDS, "metrics,period")
.executeProtobuf(ComponentTreeWsResponse.class);

assertThat(response.getComponents(0).getMeasuresCount()).isEqualTo(3);

List<Measure> dataMeasures = response.getComponents(0).getMeasuresList();

assertThat(dataMeasures)
.extracting(Measure::getMetric, m-> m.getPeriod().getValue())
.containsExactlyInAnyOrder(tuple(NEW_SECURITY_ISSUES_KEY, NEW_SECURITY_ISSUES_KEY + "_data"),
tuple(NEW_MAINTAINABILITY_ISSUES_KEY, NEW_MAINTAINABILITY_ISSUES_KEY + "_data"),
tuple(NEW_RELIABILITY_ISSUES_KEY, NEW_RELIABILITY_ISSUES_KEY + "_data")
);
}

@Test @Test
void fail_when_setting_more_than_15_metric_keys() { void fail_when_setting_more_than_15_metric_keys() {
ComponentDto mainBranch = db.components().insertPrivateProject().getMainBranchComponent(); ComponentDto mainBranch = db.components().insertPrivateProject().getMainBranchComponent();
.setOptimizedBestValue(false); .setOptimizedBestValue(false);
} }


private void insertMetricAndLiveMeasure(ComponentDto dto, String key, String additionalData) {
MetricDto dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(key));
db.measures().insertLiveMeasure(dto, dataMetric, c -> c.setData(key + additionalData));

}

private static MetricDto newDataMetricDto(String key) { private static MetricDto newDataMetricDto(String key) {
return newMetricDto().setValueType(DATA.name()).setKey(key); return newMetricDto().setValueType(DATA.name()).setKey(key);
} }

+ 6
- 3
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueCounter.java View File

return onlyInLeak ? count.leak : count.absolute; return onlyInLeak ? count.leak : count.absolute;
} }


public String getBySoftwareQuality(SoftwareQuality softwareQuality) {
public String getBySoftwareQuality(SoftwareQuality softwareQuality, boolean onlyInLeak) {
Map<Severity, Count> severityToCount = bySoftwareQualityAndSeverity.get(softwareQuality); Map<Severity, Count> severityToCount = bySoftwareQualityAndSeverity.get(softwareQuality);


ImpactMeasureBuilder impactMeasureBuilder; ImpactMeasureBuilder impactMeasureBuilder;
if (severityToCount != null) { if (severityToCount != null) {
impactMeasureBuilder = ImpactMeasureBuilder.newInstance(); impactMeasureBuilder = ImpactMeasureBuilder.newInstance();
for (Severity severity : Severity.values()) { for (Severity severity : Severity.values()) {
impactMeasureBuilder = impactMeasureBuilder.setSeverity(severity, Optional.ofNullable(severityToCount.get(severity)).map(count -> count.absolute).orElse(0L));
impactMeasureBuilder = impactMeasureBuilder.setSeverity(severity, value(severityToCount.get(severity), onlyInLeak));
} }
impactMeasureBuilder = impactMeasureBuilder.setTotal(severityToCount.values().stream().mapToLong(count -> count.absolute).sum());
impactMeasureBuilder = impactMeasureBuilder.setTotal(severityToCount.values().stream().mapToLong(count -> value(count, onlyInLeak)).sum());
} else { } else {
impactMeasureBuilder = ImpactMeasureBuilder.createEmpty(); impactMeasureBuilder = ImpactMeasureBuilder.createEmpty();
} }


public void add(IssueImpactGroupDto group) { public void add(IssueImpactGroupDto group) {
absolute += group.getCount(); absolute += group.getCount();
if (group.isInLeak()) {
leak += group.getCount();
}
} }
} }



+ 1
- 1
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveMeasureTreeUpdaterImpl.java View File



components.getSortedTree().forEach(c -> { components.getSortedTree().forEach(c -> {
IssueCounter issueCounter = new IssueCounter(dbClient.issueDao().selectIssueGroupsByComponent(dbSession, c, beginningOfLeak), IssueCounter issueCounter = new IssueCounter(dbClient.issueDao().selectIssueGroupsByComponent(dbSession, c, beginningOfLeak),
dbClient.issueDao().selectIssueImpactGroupsByComponent(dbSession, c));
dbClient.issueDao().selectIssueImpactGroupsByComponent(dbSession, c, beginningOfLeak));
for (MeasureUpdateFormula formula : formulaFactory.getFormulas()) { for (MeasureUpdateFormula formula : formulaFactory.getFormulas()) {
if (shouldComputeMetric(formula, useLeakFormulas, components.getBranch(), matrix)) { if (shouldComputeMetric(formula, useLeakFormulas, components.getBranch(), matrix)) {
context.change(c, formula); context.change(c, formula);

+ 12
- 3
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java View File

(context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.SECURITY_HOTSPOT, false))), (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.SECURITY_HOTSPOT, false))),


new MeasureUpdateFormula(CoreMetrics.RELIABILITY_ISSUES, false, true, new ImpactAddChildren(), new MeasureUpdateFormula(CoreMetrics.RELIABILITY_ISSUES, false, true, new ImpactAddChildren(),
(context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.RELIABILITY))),
(context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.RELIABILITY, false))),


new MeasureUpdateFormula(CoreMetrics.MAINTAINABILITY_ISSUES, false, true, new ImpactAddChildren(), new MeasureUpdateFormula(CoreMetrics.MAINTAINABILITY_ISSUES, false, true, new ImpactAddChildren(),
(context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.MAINTAINABILITY))),
(context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.MAINTAINABILITY, false))),


new MeasureUpdateFormula(CoreMetrics.SECURITY_ISSUES, false, true, new ImpactAddChildren(), new MeasureUpdateFormula(CoreMetrics.SECURITY_ISSUES, false, true, new ImpactAddChildren(),
(context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.SECURITY))),
(context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.SECURITY, false))),

new MeasureUpdateFormula(CoreMetrics.NEW_RELIABILITY_ISSUES, true, true, new ImpactAddChildren(),
(context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.RELIABILITY, true))),

new MeasureUpdateFormula(CoreMetrics.NEW_MAINTAINABILITY_ISSUES, true, true, new ImpactAddChildren(),
(context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.MAINTAINABILITY, true))),

new MeasureUpdateFormula(CoreMetrics.NEW_SECURITY_ISSUES, true, true, new ImpactAddChildren(),
(context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.SECURITY, true))),


new MeasureUpdateFormula(CoreMetrics.VIOLATIONS, false, new AddChildren(), new MeasureUpdateFormula(CoreMetrics.VIOLATIONS, false, new AddChildren(),
(context, issues) -> context.setValue(issues.countUnresolved(false))), (context, issues) -> context.setValue(issues.countUnresolved(false))),

+ 1
- 0
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java View File

new Change("10.5", String.format("The metrics %s are now deprecated " + new Change("10.5", String.format("The metrics %s are now deprecated " +
"without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
MeasuresWsModule.getDeprecatedMetricsInSonarQube105())), MeasuresWsModule.getDeprecatedMetricsInSonarQube105())),
new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"),
new Change("10.4", String.format("The metrics %s are now deprecated " + new Change("10.4", String.format("The metrics %s are now deprecated " +
"without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
MeasuresWsModule.getDeprecatedMetricsInSonarQube104())), MeasuresWsModule.getDeprecatedMetricsInSonarQube104())),

+ 12
- 1
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java View File

import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY;
import static org.sonar.api.measures.Metric.ValueType.DATA; import static org.sonar.api.measures.Metric.ValueType.DATA;
.setHandler(this) .setHandler(this)
.addPagingParams(100, MAX_SIZE) .addPagingParams(100, MAX_SIZE)
.setChangelog( .setChangelog(
new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"),
new Change("10.5", String.format("The metrics %s are now deprecated " + new Change("10.5", String.format("The metrics %s are now deprecated " +
"without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
MeasuresWsModule.getDeprecatedMetricsInSonarQube105())), MeasuresWsModule.getDeprecatedMetricsInSonarQube105())),


static final Set<String> FORBIDDEN_METRIC_TYPES = Set.of(DISTRIB.name()); static final Set<String> FORBIDDEN_METRIC_TYPES = Set.of(DISTRIB.name());
static final Map<String, Set<String>> PARTIALLY_SUPPORTED_METRICS= Map. of( static final Map<String, Set<String>> PARTIALLY_SUPPORTED_METRICS= Map. of(
DATA.name(), Set.of(SECURITY_ISSUES_KEY, MAINTAINABILITY_ISSUES_KEY, RELIABILITY_ISSUES_KEY));
DATA.name(),
Set.of(
SECURITY_ISSUES_KEY,
MAINTAINABILITY_ISSUES_KEY,
RELIABILITY_ISSUES_KEY,
NEW_SECURITY_ISSUES_KEY,
NEW_MAINTAINABILITY_ISSUES_KEY,
NEW_RELIABILITY_ISSUES_KEY));


@Override @Override
public boolean test(@Nonnull MetricDto input) { public boolean test(@Nonnull MetricDto input) {

+ 1
- 0
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java View File

new Change("10.5", String.format("The metrics %s are now deprecated " + new Change("10.5", String.format("The metrics %s are now deprecated " +
"without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
MeasuresWsModule.getDeprecatedMetricsInSonarQube105())), MeasuresWsModule.getDeprecatedMetricsInSonarQube105())),
new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"),
new Change("10.4", String.format("The metrics %s are now deprecated " + new Change("10.4", String.format("The metrics %s are now deprecated " +
"without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
MeasuresWsModule.getDeprecatedMetricsInSonarQube104())), MeasuresWsModule.getDeprecatedMetricsInSonarQube104())),

+ 1
- 0
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java View File

new Change("10.5", String.format("The metrics %s are now deprecated " + new Change("10.5", String.format("The metrics %s are now deprecated " +
"without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
MeasuresWsModule.getDeprecatedMetricsInSonarQube105())), MeasuresWsModule.getDeprecatedMetricsInSonarQube105())),
new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"),
new Change("10.4", String.format("The metrics %s are now deprecated " + new Change("10.4", String.format("The metrics %s are now deprecated " +
"without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.", "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
MeasuresWsModule.getDeprecatedMetricsInSonarQube104())), MeasuresWsModule.getDeprecatedMetricsInSonarQube104())),

+ 61
- 52
server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java View File

import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.sonar.api.issue.Issue; import org.sonar.api.issue.Issue;
import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.CoreMetrics;
import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING; import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING;
import static org.sonar.test.JsonAssert.assertJson; import static org.sonar.test.JsonAssert.assertJson;


public class MeasureUpdateFormulaFactoryImplTest {
class MeasureUpdateFormulaFactoryImplTest {


public static final Gson GSON = new GsonBuilder().create();
private static final Gson GSON = new GsonBuilder().create();
private final MeasureUpdateFormulaFactoryImpl underTest = new MeasureUpdateFormulaFactoryImpl(); private final MeasureUpdateFormulaFactoryImpl underTest = new MeasureUpdateFormulaFactoryImpl();


@Test @Test
public void getFormulaMetrics_include_the_dependent_metrics() {
void getFormulaMetrics_include_the_dependent_metrics() {
for (MeasureUpdateFormula formula : underTest.getFormulas()) { for (MeasureUpdateFormula formula : underTest.getFormulas()) {
assertThat(underTest.getFormulaMetrics()).contains(formula.getMetric()); assertThat(underTest.getFormulaMetrics()).contains(formula.getMetric());
for (Metric<?> dependentMetric : formula.getDependentMetrics()) { for (Metric<?> dependentMetric : formula.getDependentMetrics()) {
} }


@Test @Test
public void hierarchy_adding_numbers() {
void hierarchy_adding_numbers() {
new HierarchyTester(CoreMetrics.VIOLATIONS) new HierarchyTester(CoreMetrics.VIOLATIONS)
.withValue(1d) .withValue(1d)
.withChildrenValues(2d, 3d) .withChildrenValues(2d, 3d)
} }


@Test @Test
public void hierarchy_highest_rating() {
void hierarchy_highest_rating() {
new HierarchyTester(CoreMetrics.RELIABILITY_RATING) new HierarchyTester(CoreMetrics.RELIABILITY_RATING)
.withValue(1d) .withValue(1d)
.withChildrenValues(2d, 3d) .withChildrenValues(2d, 3d)
} }


@Test @Test
public void hierarchy_combining_other_metrics() {
void hierarchy_combining_other_metrics() {
new HierarchyTester(SECURITY_HOTSPOTS_TO_REVIEW_STATUS) new HierarchyTester(SECURITY_HOTSPOTS_TO_REVIEW_STATUS)
.withValue(SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1d) .withValue(SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1d)
.withChildrenHotspotsCounts(10, 10, 2, 10) .withChildrenHotspotsCounts(10, 10, 2, 10)
} }


@Test @Test
public void test_violations() {
void test_violations() {
withNoIssues().assertThatValueIs(CoreMetrics.VIOLATIONS, 0); withNoIssues().assertThatValueIs(CoreMetrics.VIOLATIONS, 0);
with(newGroup(), newGroup().setCount(4)).assertThatValueIs(CoreMetrics.VIOLATIONS, 5); with(newGroup(), newGroup().setCount(4)).assertThatValueIs(CoreMetrics.VIOLATIONS, 5);


} }


@Test @Test
public void test_bugs() {
void test_bugs() {
withNoIssues().assertThatValueIs(CoreMetrics.BUGS, 0); withNoIssues().assertThatValueIs(CoreMetrics.BUGS, 0);
with( with(
newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(3), newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(3),
} }


@Test @Test
public void test_code_smells() {
void test_code_smells() {
withNoIssues().assertThatValueIs(CoreMetrics.CODE_SMELLS, 0); withNoIssues().assertThatValueIs(CoreMetrics.CODE_SMELLS, 0);
with( with(
newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3), newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
} }


@Test @Test
public void test_vulnerabilities() {
void test_vulnerabilities() {
withNoIssues().assertThatValueIs(CoreMetrics.VULNERABILITIES, 0); withNoIssues().assertThatValueIs(CoreMetrics.VULNERABILITIES, 0);
with( with(
newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3), newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3),
} }


@Test @Test
public void test_security_hotspots() {
void test_security_hotspots() {
withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 0); withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 0);
with( with(
newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR).setCount(3), newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR).setCount(3),
} }


@Test @Test
public void test_security_review_rating() {
void test_security_review_rating() {
with( with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1)) newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
} }


@Test @Test
public void test_security_hotspots_reviewed() {
void test_security_hotspots_reviewed() {
with( with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1)) newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
} }


@Test @Test
public void test_security_hotspots_reviewed_status() {
void test_security_hotspots_reviewed_status() {
with( with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1)) newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
} }


@Test @Test
public void test_security_hotspots_to_review_status() {
void test_security_hotspots_to_review_status() {
with( with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1)) newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
} }


@Test @Test
public void count_unresolved_by_severity() {
void count_unresolved_by_severity() {
withNoIssues() withNoIssues()
.assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 0) .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 0)
.assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 0) .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 0)
} }


@Test @Test
public void count_resolved() {
void count_resolved() {
withNoIssues() withNoIssues()
.assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 0) .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 0)
.assertThatValueIs(CoreMetrics.ACCEPTED_ISSUES, 0); .assertThatValueIs(CoreMetrics.ACCEPTED_ISSUES, 0);
} }


@Test @Test
public void count_by_status() {
void count_by_status() {
withNoIssues() withNoIssues()
.assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 0) .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 0)
.assertThatValueIs(CoreMetrics.OPEN_ISSUES, 0) .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 0)
} }


@Test @Test
public void test_technical_debt() {
void test_technical_debt() {
withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0); withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0);


with( with(
} }


@Test @Test
public void test_reliability_remediation_effort() {
void test_reliability_remediation_effort() {
withNoIssues().assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 0); withNoIssues().assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 0);


with( with(
} }


@Test @Test
public void test_security_remediation_effort() {
void test_security_remediation_effort() {
withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 0); withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 0);


with( with(
} }


@Test @Test
public void test_sqale_debt_ratio_and_sqale_rating() {
void test_sqale_debt_ratio_and_sqale_rating() {
withNoIssues() withNoIssues()
.assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0) .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
.assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A); .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
} }


@Test @Test
public void test_effort_to_reach_maintainability_rating_A() {
void test_effort_to_reach_maintainability_rating_A() {
withNoIssues() withNoIssues()
.assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0); .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);


} }


@Test @Test
public void test_reliability_rating() {
void test_reliability_rating() {
withNoIssues() withNoIssues()
.assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A); .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);


} }


@Test @Test
public void test_security_rating() {
void test_security_rating() {
withNoIssues() withNoIssues()
.assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A); .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);


} }


@Test @Test
public void test_new_bugs() {
void test_new_bugs() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 0.0); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 0.0);


with( with(
} }


@Test @Test
public void test_new_code_smells() {
void test_new_code_smells() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 0.0); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 0.0);


with( with(
} }


@Test @Test
public void test_new_vulnerabilities() {
void test_new_vulnerabilities() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 0.0); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 0.0);


with( with(
} }


@Test @Test
public void test_new_security_hotspots() {
void test_new_security_hotspots() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 0); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 0);


with( with(
} }


@Test @Test
public void test_new_violations() {
void test_new_violations() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 0.0); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 0.0);


with( with(
} }


@Test @Test
public void test_new_blocker_violations() {
void test_new_blocker_violations() {
withNoIssues() withNoIssues()
.assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0); .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0);


} }


@Test @Test
public void test_new_critical_violations() {
void test_new_critical_violations() {
withNoIssues() withNoIssues()
.assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 0.0); .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 0.0);


} }


@Test @Test
public void test_new_major_violations() {
void test_new_major_violations() {
withNoIssues() withNoIssues()
.assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0); .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0);


} }


@Test @Test
public void test_new_minor_violations() {
void test_new_minor_violations() {
withNoIssues() withNoIssues()
.assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0); .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0);


} }


@Test @Test
public void test_new_info_violations() {
void test_new_info_violations() {
withNoIssues() withNoIssues()
.assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0); .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0);


} }


@Test @Test
public void test_new_accepted_issues() {
void test_new_accepted_issues() {
withNoIssues() withNoIssues()
.assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 0); .assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 0);


} }


@Test @Test
public void test_new_technical_debt() {
void test_new_technical_debt() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0);


with( with(
} }


@Test @Test
public void test_new_reliability_remediation_effort() {
void test_new_reliability_remediation_effort() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 0.0); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 0.0);


with( with(
} }


@Test @Test
public void test_new_security_remediation_effort() {
void test_new_security_remediation_effort() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 0.0); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 0.0);


with( with(
} }


@Test @Test
public void test_new_reliability_rating() {
void test_new_reliability_rating() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.A); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.A);


with( with(
} }


@Test @Test
public void test_new_security_rating() {
void test_new_security_rating() {
withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.A); withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.A);


with( with(
} }


@Test @Test
public void test_new_security_review_rating() {
void test_new_security_review_rating() {
with( with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
} }


@Test @Test
public void test_new_security_hotspots_reviewed() {
void test_new_security_hotspots_reviewed() {
with( with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
} }


@Test @Test
public void test_new_security_hotspots_reviewed_status() {
void test_new_security_hotspots_reviewed_status() {
with( with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
} }


@Test @Test
public void test_new_security_hotspots_to_review_status() {
void test_new_security_hotspots_to_review_status() {
with( with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true), newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
} }


@Test @Test
public void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
withNoIssues() withNoIssues()
.assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0) .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
.assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A); .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
} }


@Test @Test
public void compute_shouldComputeHighImpactAcceptedIssues() {
void compute_shouldComputeHighImpactAcceptedIssues() {
withNoIssues() withNoIssues()
.assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 0); .assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 0);


} }


@Test @Test
public void computeHierarchy_shouldComputeImpactMeasures() {
void computeHierarchy_shouldComputeImpactMeasures() {
new HierarchyTester(CoreMetrics.RELIABILITY_ISSUES) new HierarchyTester(CoreMetrics.RELIABILITY_ISSUES)
.withValue(impactMeasureToJson(6, 1, 2, 3)) .withValue(impactMeasureToJson(6, 1, 2, 3))
.withChildrenValues(impactMeasureToJson(6, 1, 2, 3), impactMeasureToJson(10, 5, 3, 2)) .withChildrenValues(impactMeasureToJson(6, 1, 2, 3), impactMeasureToJson(10, 5, 3, 2))
} }


@Test @Test
public void compute_shouldComputeImpactMeasures() {
void compute_shouldComputeImpactMeasures() {
with( with(
newImpactGroup(RELIABILITY, HIGH, 3), newImpactGroup(RELIABILITY, HIGH, 3),
newImpactGroup(RELIABILITY, MEDIUM, 4), newImpactGroup(RELIABILITY, MEDIUM, 4),
} }


@Test @Test
public void compute_whenNoIssues_shouldComputeImpactMeasures() {
void compute_whenNoIssues_shouldComputeImpactMeasures() {
withNoIssues() withNoIssues()
.assertThatJsonValueIs(CoreMetrics.RELIABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0)) .assertThatJsonValueIs(CoreMetrics.RELIABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
.assertThatJsonValueIs(CoreMetrics.MAINTAINABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0)) .assertThatJsonValueIs(CoreMetrics.MAINTAINABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
.assertThatJsonValueIs(CoreMetrics.SECURITY_ISSUES, impactMeasureToJson(0, 0, 0, 0));
.assertThatJsonValueIs(CoreMetrics.SECURITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
.assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
.assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
.assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_ISSUES, impactMeasureToJson(0, 0, 0, 0));
} }


private static String impactMeasureToJson(long total, long high, long medium, long low) { private static String impactMeasureToJson(long total, long high, long medium, long low) {
return this; return this;
} }


Verifier assertThatLeakValueIs(Metric metric, String expectedValue) {
TestContext context = run(metric, true);
assertJson(context.stringValue).isSimilarTo(expectedValue);
return this;
}

Verifier assertNoLeakValue(Metric metric) { Verifier assertNoLeakValue(Metric metric) {
TestContext context = run(metric, true); TestContext context = run(metric, true);
assertThat(context.ratingValue).isNull(); assertThat(context.ratingValue).isNull();

+ 9
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

metric.security_issues.name=Security Issues metric.security_issues.name=Security Issues
metric.security_issues.description=Security issues metric.security_issues.description=Security issues
metric.security_issues.short_name=Security metric.security_issues.short_name=Security
metric.new_maintainability_issues.name=New Maintainability Issues
metric.new_maintainability_issues.description=New maintainability issues
metric.new_maintainability_issues.short_name=New Maintainability
metric.new_reliability_issues.name=New Reliability Issues
metric.new_reliability_issues.description=New reliability issues
metric.new_reliability_issues.short_name=New Reliability
metric.new_security_issues.name=New Security Issues
metric.new_security_issues.description=New security issues
metric.new_security_issues.short_name=New Security


#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# #

Loading…
Cancel
Save