]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22727 Add new software qualities remediation efforts measures
authorLéo Geoffroy <leo.geoffroy@sonarsource.com>
Fri, 9 Aug 2024 09:43:28 +0000 (11:43 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 26 Aug 2024 20:03:05 +0000 (20:03 +0000)
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/EffortAggregator.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregator.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/MaintainabilityMeasuresVisitor.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/EffortAggregatorTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregatorTest.java
server/sonar-server-common/src/main/java/org/sonar/server/metric/SoftwareQualitiesMetrics.java
server/sonar-server-common/src/test/java/org/sonar/server/metric/SoftwareQualitiesMetricsTest.java

index 22efd4cd1bc65d0a94509c69ee69bd5a62ad5334..434e328b35b1f7cef01b46ef8182b245f45b5dc2 100644 (file)
@@ -28,16 +28,23 @@ import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
 import org.sonar.ce.task.projectanalysis.metric.Metric;
 import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
 import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.metric.SoftwareQualitiesMetrics;
 
 import static org.sonar.api.measures.CoreMetrics.RELIABILITY_REMEDIATION_EFFORT_KEY;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_REMEDIATION_EFFORT_KEY;
 import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY;
 
 /**
  * Compute effort related measures :
  * {@link CoreMetrics#TECHNICAL_DEBT_KEY}
  * {@link CoreMetrics#RELIABILITY_REMEDIATION_EFFORT_KEY}
  * {@link CoreMetrics#SECURITY_REMEDIATION_EFFORT_KEY}
+ * {@link SoftwareQualitiesMetrics#SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY}
+ * {@link SoftwareQualitiesMetrics#SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY}
+ * {@link SoftwareQualitiesMetrics#SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY}
  */
 public class EffortAggregator extends IssueVisitor {
 
@@ -48,13 +55,24 @@ public class EffortAggregator extends IssueVisitor {
   private final Metric reliabilityEffortMetric;
   private final Metric securityEffortMetric;
 
+  private final Metric softwareQualityMaintainabilityEffortMetric;
+  private final Metric softwareQualityReliabilityEffortMetric;
+  private final Metric softwareQualitySecurityEffortMetric;
+
   private EffortCounter effortCounter;
 
   public EffortAggregator(MetricRepository metricRepository, MeasureRepository measureRepository) {
     this.measureRepository = measureRepository;
+
+    // Based on issue Type and Severity
     this.maintainabilityEffortMetric = metricRepository.getByKey(TECHNICAL_DEBT_KEY);
     this.reliabilityEffortMetric = metricRepository.getByKey(RELIABILITY_REMEDIATION_EFFORT_KEY);
     this.securityEffortMetric = metricRepository.getByKey(SECURITY_REMEDIATION_EFFORT_KEY);
+
+    // Based on software qualities
+    this.softwareQualityMaintainabilityEffortMetric = metricRepository.getByKey(SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY);
+    this.softwareQualityReliabilityEffortMetric = metricRepository.getByKey(SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY);
+    this.softwareQualitySecurityEffortMetric = metricRepository.getByKey(SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY);
   }
 
   @Override
@@ -90,14 +108,17 @@ public class EffortAggregator extends IssueVisitor {
 
   private void computeMaintainabilityEffortMeasure(Component component) {
     measureRepository.add(component, maintainabilityEffortMetric, Measure.newMeasureBuilder().create(effortCounter.maintainabilityEffort));
+    measureRepository.add(component, softwareQualityMaintainabilityEffortMetric, Measure.newMeasureBuilder().create(effortCounter.softwareQualityMaintainabilityEffort));
   }
 
   private void computeReliabilityEffortMeasure(Component component) {
     measureRepository.add(component, reliabilityEffortMetric, Measure.newMeasureBuilder().create(effortCounter.reliabilityEffort));
+    measureRepository.add(component, softwareQualityReliabilityEffortMetric, Measure.newMeasureBuilder().create(effortCounter.softwareQualityReliabilityEffort));
   }
 
   private void computeSecurityEffortMeasure(Component component) {
     measureRepository.add(component, securityEffortMetric, Measure.newMeasureBuilder().create(effortCounter.securityEffort));
+    measureRepository.add(component, softwareQualitySecurityEffortMetric, Measure.newMeasureBuilder().create(effortCounter.softwareQualitySecurityEffort));
   }
 
   private static class EffortCounter {
@@ -105,25 +126,52 @@ public class EffortAggregator extends IssueVisitor {
     private long reliabilityEffort = 0L;
     private long securityEffort = 0L;
 
+    private long softwareQualityMaintainabilityEffort = 0L;
+    private long softwareQualityReliabilityEffort = 0L;
+    private long softwareQualitySecurityEffort = 0L;
+
     void add(DefaultIssue issue) {
       Long issueEffort = issue.effortInMinutes();
       if (issueEffort != null && issueEffort != 0L) {
-        switch (issue.type()) {
-          case CODE_SMELL:
-            maintainabilityEffort += issueEffort;
-            break;
-          case BUG:
-            reliabilityEffort += issueEffort;
+        computeTypeEffort(issue, issueEffort);
+        computeSoftwareQualityEffort(issue, issueEffort);
+      }
+    }
+
+    private void computeSoftwareQualityEffort(DefaultIssue issue, Long issueEffort) {
+      issue.impacts().forEach((sq, severity) -> {
+        switch (sq) {
+          case MAINTAINABILITY:
+            softwareQualityMaintainabilityEffort += issueEffort;
             break;
-          case VULNERABILITY:
-            securityEffort += issueEffort;
+          case RELIABILITY:
+            softwareQualityReliabilityEffort += issueEffort;
             break;
-          case SECURITY_HOTSPOT:
-            // Not counted
+          case SECURITY:
+            softwareQualitySecurityEffort += issueEffort;
             break;
           default:
-            throw new IllegalStateException(String.format("Unknown type '%s'", issue.type()));
+            throw new IllegalStateException(String.format("Unknown software quality '%s'", sq));
         }
+      });
+    }
+
+    private void computeTypeEffort(DefaultIssue issue, Long issueEffort) {
+      switch (issue.type()) {
+        case CODE_SMELL:
+          maintainabilityEffort += issueEffort;
+          break;
+        case BUG:
+          reliabilityEffort += issueEffort;
+          break;
+        case VULNERABILITY:
+          securityEffort += issueEffort;
+          break;
+        case SECURITY_HOTSPOT:
+          // Not counted
+          break;
+        default:
+          throw new IllegalStateException(String.format("Unknown type '%s'", issue.type()));
       }
     }
 
@@ -131,6 +179,10 @@ public class EffortAggregator extends IssueVisitor {
       maintainabilityEffort += effortCounter.maintainabilityEffort;
       reliabilityEffort += effortCounter.reliabilityEffort;
       securityEffort += effortCounter.securityEffort;
+
+      softwareQualityMaintainabilityEffort += effortCounter.softwareQualityMaintainabilityEffort;
+      softwareQualityReliabilityEffort += effortCounter.softwareQualityReliabilityEffort;
+      softwareQualitySecurityEffort += effortCounter.softwareQualitySecurityEffort;
     }
   }
 }
index f080b95ad2b125b8b8a8c2c4780a370457ec5bc4..5006fe2fe1637ac6453ee1114b021523020b57ca 100644 (file)
@@ -29,16 +29,23 @@ import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
 import org.sonar.ce.task.projectanalysis.metric.Metric;
 import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
 import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.metric.SoftwareQualitiesMetrics;
 
 import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY;
 
 /**
  * Compute new effort related measures :
  * {@link CoreMetrics#NEW_TECHNICAL_DEBT_KEY}
  * {@link CoreMetrics#NEW_RELIABILITY_REMEDIATION_EFFORT_KEY}
  * {@link CoreMetrics#NEW_SECURITY_REMEDIATION_EFFORT_KEY}
+ * {@link SoftwareQualitiesMetrics#NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY}
+ * {@link SoftwareQualitiesMetrics#NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY}
+ * {@link SoftwareQualitiesMetrics#NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY}
  */
 public class NewEffortAggregator extends IssueVisitor {
   private final Map<String, NewEffortCounter> counterByComponentUuid = new HashMap<>();
@@ -48,15 +55,25 @@ public class NewEffortAggregator extends IssueVisitor {
   private final Metric newReliabilityEffortMetric;
   private final Metric newSecurityEffortMetric;
   private final NewIssueClassifier newIssueClassifier;
+  private final Metric newSoftwareQualityMaintainabilityEffortMetric;
+  private final Metric newSoftwareQualityReliabilityEffortMetric;
+  private final Metric newSoftwareQualitySecurityEffortMetric;
 
   private NewEffortCounter counter = null;
 
   public NewEffortAggregator(MetricRepository metricRepository, MeasureRepository measureRepository, NewIssueClassifier newIssueClassifier) {
     this.measureRepository = measureRepository;
 
+    // Based on issue Type and Severity
     this.newMaintainabilityEffortMetric = metricRepository.getByKey(NEW_TECHNICAL_DEBT_KEY);
     this.newReliabilityEffortMetric = metricRepository.getByKey(NEW_RELIABILITY_REMEDIATION_EFFORT_KEY);
     this.newSecurityEffortMetric = metricRepository.getByKey(NEW_SECURITY_REMEDIATION_EFFORT_KEY);
+
+    // Based on software qualities
+    this.newSoftwareQualityMaintainabilityEffortMetric = metricRepository.getByKey(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY);
+    this.newSoftwareQualityReliabilityEffortMetric = metricRepository.getByKey(NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY);
+    this.newSoftwareQualitySecurityEffortMetric = metricRepository.getByKey(NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY);
+
     this.newIssueClassifier = newIssueClassifier;
   }
 
@@ -85,6 +102,10 @@ public class NewEffortAggregator extends IssueVisitor {
       computeMeasure(component, newMaintainabilityEffortMetric, counter.maintainabilitySum);
       computeMeasure(component, newReliabilityEffortMetric, counter.reliabilitySum);
       computeMeasure(component, newSecurityEffortMetric, counter.securitySum);
+
+      computeMeasure(component, newSoftwareQualityMaintainabilityEffortMetric, counter.softwareQualityMaintainabilitySum);
+      computeMeasure(component, newSoftwareQualityReliabilityEffortMetric, counter.softwareQualityReliabilitySum);
+      computeMeasure(component, newSoftwareQualitySecurityEffortMetric, counter.softwareQualitySecuritySum);
     }
     counter = null;
   }
@@ -99,14 +120,45 @@ public class NewEffortAggregator extends IssueVisitor {
     private final EffortSum reliabilitySum = new EffortSum();
     private final EffortSum securitySum = new EffortSum();
 
+    private final EffortSum softwareQualityMaintainabilitySum = new EffortSum();
+    private final EffortSum softwareQualityReliabilitySum = new EffortSum();
+    private final EffortSum softwareQualitySecuritySum = new EffortSum();
+
     void add(NewEffortCounter otherCounter) {
       maintainabilitySum.add(otherCounter.maintainabilitySum);
       reliabilitySum.add(otherCounter.reliabilitySum);
       securitySum.add(otherCounter.securitySum);
+
+      softwareQualityMaintainabilitySum.add(otherCounter.softwareQualityMaintainabilitySum);
+      softwareQualityReliabilitySum.add(otherCounter.softwareQualityReliabilitySum);
+      softwareQualitySecuritySum.add(otherCounter.softwareQualitySecuritySum);
     }
 
     void add(Component component, DefaultIssue issue) {
       long newEffort = calculate(component, issue);
+      computeTypeEffort(issue, newEffort);
+      computeSoftwareQualityEffort(issue, newEffort);
+    }
+
+    private void computeSoftwareQualityEffort(DefaultIssue issue, long newEffort) {
+      issue.impacts().forEach((sq, severity) -> {
+        switch (sq) {
+          case MAINTAINABILITY:
+            softwareQualityMaintainabilitySum.add(newEffort);
+            break;
+          case RELIABILITY:
+            softwareQualityReliabilitySum.add(newEffort);
+            break;
+          case SECURITY:
+            softwareQualitySecuritySum.add(newEffort);
+            break;
+          default:
+            throw new IllegalStateException(String.format("Unknown software quality '%s'", sq));
+        }
+      });
+    }
+
+    private void computeTypeEffort(DefaultIssue issue, long newEffort) {
       switch (issue.type()) {
         case CODE_SMELL:
           maintainabilitySum.add(newEffort);
index 94cb910e2c22d4618fea018816cf5e34438ae280..8301ead2685240bb8b583428c46cf6fed21fdc50 100644 (file)
@@ -24,7 +24,6 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.ce.task.projectanalysis.component.Component;
 import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
 import org.sonar.ce.task.projectanalysis.component.PathAwareVisitorAdapter;
-import org.sonar.ce.task.projectanalysis.formula.counter.RatingValue;
 import org.sonar.ce.task.projectanalysis.measure.Measure;
 import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
 import org.sonar.ce.task.projectanalysis.measure.RatingMeasures;
@@ -148,8 +147,6 @@ public class MaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter<Main
 
   public static final class Counter {
     private long devCosts = 0;
-    private RatingValue reliabilityRating = new RatingValue();
-    private RatingValue securityRating = new RatingValue();
 
     private Counter() {
       // prevents instantiation
@@ -157,8 +154,6 @@ public class MaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter<Main
 
     void add(Counter otherCounter) {
       addDevCosts(otherCounter.devCosts);
-      reliabilityRating.increment(otherCounter.reliabilityRating);
-      securityRating.increment(otherCounter.securityRating);
     }
 
     void addDevCosts(long developmentCosts) {
index 33e1c7340074fd7db975e229dedf111a4b81797a..720b2eb3dea117d79a7be98ec6b7a247afa05b8f 100644 (file)
  */
 package org.sonar.ce.task.projectanalysis.issue;
 
-import org.junit.Test;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.utils.Duration;
 import org.sonar.ce.task.projectanalysis.component.Component;
 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
@@ -29,6 +33,10 @@ import org.sonar.core.issue.DefaultIssue;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.issue.impact.Severity.HIGH;
+import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
+import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY;
+import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY;
 import static org.sonar.api.measures.CoreMetrics.RELIABILITY_REMEDIATION_EFFORT;
 import static org.sonar.api.measures.CoreMetrics.RELIABILITY_REMEDIATION_EFFORT_KEY;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_REMEDIATION_EFFORT;
@@ -38,29 +46,38 @@ import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
 import static org.sonar.api.rules.RuleType.BUG;
 import static org.sonar.api.rules.RuleType.CODE_SMELL;
 import static org.sonar.api.rules.RuleType.VULNERABILITY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY;
 
-public class EffortAggregatorTest {
+class EffortAggregatorTest {
 
   static final Component FILE = ReportComponent.builder(Component.Type.FILE, 1).build();
   static final Component PROJECT = ReportComponent.builder(Component.Type.PROJECT, 2).addChildren(FILE).build();
 
-  @org.junit.Rule
+  @RegisterExtension
   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
     .add(TECHNICAL_DEBT)
     .add(RELIABILITY_REMEDIATION_EFFORT)
-    .add(SECURITY_REMEDIATION_EFFORT);
+    .add(SECURITY_REMEDIATION_EFFORT)
+    .add(SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT)
+    .add(SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT)
+    .add(SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT);
 
-  @org.junit.Rule
+  @RegisterExtension
   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(PROJECT, metricRepository);
 
   EffortAggregator underTest = new EffortAggregator(metricRepository, measureRepository);
 
   @Test
-  public void sum_maintainability_effort_of_unresolved_issues() {
-    DefaultIssue unresolved1 = newCodeSmellIssue(10);
-    DefaultIssue unresolved2 = newCodeSmellIssue(30);
-    DefaultIssue unresolvedWithoutEffort = newCodeSmellIssueWithoutEffort();
-    DefaultIssue resolved = newCodeSmellIssue(50).setResolution(RESOLUTION_FIXED);
+  void sum_maintainability_effort_of_unresolved_issues() {
+    DefaultIssue unresolved1 = newMaintainabilityIssue(10);
+    DefaultIssue unresolved2 = newMaintainabilityIssue(30);
+    DefaultIssue unresolvedWithoutEffort = newMaintainabilityIssueWithoutEffort();
+    DefaultIssue resolved = newMaintainabilityIssue(50).setResolution(RESOLUTION_FIXED);
 
     underTest.beforeComponent(FILE);
     underTest.onIssue(FILE, unresolved1);
@@ -71,14 +88,31 @@ public class EffortAggregatorTest {
 
     // total maintainability effort
     assertMeasure(FILE, TECHNICAL_DEBT_KEY, 10L + 30L);
+    assertMeasure(FILE, SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 10L + 30L);
   }
 
   @Test
-  public void maintainability_effort_is_only_computed_using_code_smell_issues() {
-    DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
+  void sum_effort_when_multiple_impacts() {
+    DefaultIssue unresolved1 = newIssue(10, Map.of(MAINTAINABILITY, HIGH, SECURITY, HIGH, RELIABILITY, HIGH));
+    DefaultIssue unresolved2 = newIssue(30, Map.of(MAINTAINABILITY, HIGH, SECURITY, HIGH, RELIABILITY, HIGH));
+
+    underTest.beforeComponent(FILE);
+    underTest.onIssue(FILE, unresolved1);
+    underTest.onIssue(FILE, unresolved2);
+    underTest.afterComponent(FILE);
+
+    // total maintainability effort
+    assertMeasure(FILE, SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 10L + 30L);
+    assertMeasure(FILE, SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 10L + 30L);
+    assertMeasure(FILE, SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 10L + 30L);
+  }
+
+  @Test
+  void maintainability_effort_is_only_computed_using_maintainability_issues() {
+    DefaultIssue codeSmellIssue = newMaintainabilityIssue(10);
     // Issues of type BUG and VULNERABILITY should be ignored
-    DefaultIssue bugIssue = newBugIssue(15);
-    DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
+    DefaultIssue bugIssue = newReliabilityIssue(15);
+    DefaultIssue vulnerabilityIssue = newSecurityIssue(12);
 
     underTest.beforeComponent(FILE);
     underTest.onIssue(FILE, codeSmellIssue);
@@ -88,14 +122,16 @@ public class EffortAggregatorTest {
 
     // Only effort of CODE SMELL issue is used
     assertMeasure(FILE, TECHNICAL_DEBT_KEY, 10L);
+    assertMeasure(FILE, SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 10L);
+
   }
 
   @Test
-  public void sum_reliability_effort_of_unresolved_issues() {
-    DefaultIssue unresolved1 = newBugIssue(10);
-    DefaultIssue unresolved2 = newBugIssue(30);
-    DefaultIssue unresolvedWithoutEffort = newBugIssueWithoutEffort();
-    DefaultIssue resolved = newBugIssue(50).setResolution(RESOLUTION_FIXED);
+  void sum_reliability_effort_of_unresolved_issues() {
+    DefaultIssue unresolved1 = newReliabilityIssue(10);
+    DefaultIssue unresolved2 = newReliabilityIssue(30);
+    DefaultIssue unresolvedWithoutEffort = newReliabilityIssueWithoutEffort();
+    DefaultIssue resolved = newReliabilityIssue(50).setResolution(RESOLUTION_FIXED);
 
     underTest.beforeComponent(FILE);
     underTest.onIssue(FILE, unresolved1);
@@ -105,14 +141,15 @@ public class EffortAggregatorTest {
     underTest.afterComponent(FILE);
 
     assertMeasure(FILE, RELIABILITY_REMEDIATION_EFFORT_KEY, 10L + 30L);
+    assertMeasure(FILE, SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 10L + 30L);
   }
 
   @Test
-  public void reliability_effort_is_only_computed_using_bug_issues() {
-    DefaultIssue bugIssue = newBugIssue(10);
+  void reliability_effort_is_only_computed_using_reliability_issues() {
+    DefaultIssue bugIssue = newReliabilityIssue(10);
     // Issues of type CODE SMELL and VULNERABILITY should be ignored
-    DefaultIssue codeSmellIssue = newCodeSmellIssue(15);
-    DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
+    DefaultIssue codeSmellIssue = newMaintainabilityIssue(15);
+    DefaultIssue vulnerabilityIssue = newSecurityIssue(12);
 
     underTest.beforeComponent(FILE);
     underTest.onIssue(FILE, bugIssue);
@@ -122,14 +159,15 @@ public class EffortAggregatorTest {
 
     // Only effort of BUG issue is used
     assertMeasure(FILE, RELIABILITY_REMEDIATION_EFFORT_KEY, 10L);
+    assertMeasure(FILE, SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 10L);
   }
 
   @Test
-  public void sum_security_effort_of_unresolved_issues() {
-    DefaultIssue unresolved1 = newVulnerabilityIssue(10);
-    DefaultIssue unresolved2 = newVulnerabilityIssue(30);
-    DefaultIssue unresolvedWithoutEffort = newVulnerabilityIssueWithoutEffort();
-    DefaultIssue resolved = newVulnerabilityIssue(50).setResolution(RESOLUTION_FIXED);
+  void sum_security_effort_of_unresolved_issues() {
+    DefaultIssue unresolved1 = newSecurityIssue(10);
+    DefaultIssue unresolved2 = newSecurityIssue(30);
+    DefaultIssue unresolvedWithoutEffort = newSecurityIssueWithoutEffort();
+    DefaultIssue resolved = newSecurityIssue(50).setResolution(RESOLUTION_FIXED);
 
     underTest.beforeComponent(FILE);
     underTest.onIssue(FILE, unresolved1);
@@ -139,14 +177,15 @@ public class EffortAggregatorTest {
     underTest.afterComponent(FILE);
 
     assertMeasure(FILE, SECURITY_REMEDIATION_EFFORT_KEY, 10L + 30L);
+    assertMeasure(FILE, SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 10L + 30L);
   }
 
   @Test
-  public void security_effort_is_only_computed_using_code_smell_issues() {
-    DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(10);
+  void security_effort_is_only_computed_using_maintainability_issues() {
+    DefaultIssue vulnerabilityIssue = newSecurityIssue(10);
     // Issues of type BUG and CODE SMELL should be ignored
-    DefaultIssue bugIssue = newBugIssue(15);
-    DefaultIssue codeSmellIssue = newCodeSmellIssue(12);
+    DefaultIssue bugIssue = newReliabilityIssue(15);
+    DefaultIssue codeSmellIssue = newMaintainabilityIssue(12);
 
     underTest.beforeComponent(FILE);
     underTest.onIssue(FILE, vulnerabilityIssue);
@@ -156,68 +195,81 @@ public class EffortAggregatorTest {
 
     // Only effort of VULNERABILITY issue is used
     assertMeasure(FILE, SECURITY_REMEDIATION_EFFORT_KEY, 10L);
+    assertMeasure(FILE, SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 10L);
   }
 
   @Test
-  public void aggregate_maintainability_measures_of_children() {
+  void aggregate_maintainability_measures_of_children() {
     underTest.beforeComponent(FILE);
-    underTest.onIssue(FILE, newCodeSmellIssue(10));
-    underTest.onIssue(FILE, newBugIssue(8));
-    underTest.onIssue(FILE, newVulnerabilityIssue(12));
+    underTest.onIssue(FILE, newMaintainabilityIssue(10));
+    underTest.onIssue(FILE, newReliabilityIssue(8));
+    underTest.onIssue(FILE, newSecurityIssue(12));
     underTest.afterComponent(FILE);
     underTest.beforeComponent(PROJECT);
-    underTest.onIssue(PROJECT, newCodeSmellIssue(30));
-    underTest.onIssue(PROJECT, newBugIssue(38));
-    underTest.onIssue(PROJECT, newVulnerabilityIssue(42));
+    underTest.onIssue(PROJECT, newMaintainabilityIssue(30));
+    underTest.onIssue(PROJECT, newReliabilityIssue(38));
+    underTest.onIssue(PROJECT, newSecurityIssue(42));
     underTest.afterComponent(PROJECT);
 
     assertMeasure(PROJECT, TECHNICAL_DEBT_KEY, 10L + 30L);
     assertMeasure(PROJECT, RELIABILITY_REMEDIATION_EFFORT_KEY, 8L + 38L);
     assertMeasure(PROJECT, SECURITY_REMEDIATION_EFFORT_KEY, 12L + 42L);
+
+    assertMeasure(PROJECT, SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 10L + 30L);
+    assertMeasure(PROJECT, SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 8L + 38L);
+    assertMeasure(PROJECT, SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 12L + 42L);
   }
 
   @Test
-  public void sum_characteristic_measures_of_issues_without_effort() {
+  void sum_characteristic_measures_of_issues_without_effort() {
     underTest.beforeComponent(FILE);
-    underTest.onIssue(FILE, newCodeSmellIssueWithoutEffort());
-    underTest.onIssue(FILE, newBugIssueWithoutEffort());
-    underTest.onIssue(FILE, newVulnerabilityIssueWithoutEffort());
+    underTest.onIssue(FILE, newMaintainabilityIssueWithoutEffort());
+    underTest.onIssue(FILE, newReliabilityIssueWithoutEffort());
+    underTest.onIssue(FILE, newSecurityIssueWithoutEffort());
     underTest.afterComponent(FILE);
     underTest.beforeComponent(PROJECT);
-    underTest.onIssue(PROJECT, newCodeSmellIssueWithoutEffort());
+    underTest.onIssue(PROJECT, newMaintainabilityIssueWithoutEffort());
     underTest.afterComponent(PROJECT);
 
     assertMeasure(PROJECT, TECHNICAL_DEBT_KEY, 0L);
     assertMeasure(PROJECT, RELIABILITY_REMEDIATION_EFFORT_KEY, 0L);
     assertMeasure(PROJECT, SECURITY_REMEDIATION_EFFORT_KEY, 0L);
+
+    assertMeasure(PROJECT, SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 0L);
+    assertMeasure(PROJECT, SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 0L);
+    assertMeasure(PROJECT, SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 0L);
   }
 
   private void assertMeasure(Component component, String metricKey, long expectedValue) {
     assertThat(measureRepository.getAddedRawMeasure(component, metricKey).get().getLongValue()).isEqualTo(expectedValue);
   }
 
-  private static DefaultIssue newCodeSmellIssue(long effort) {
-    return newCodeSmellIssueWithoutEffort().setEffort(Duration.create(effort)).setType(CODE_SMELL);
+  private static DefaultIssue newIssue(long effort, Map<SoftwareQuality, Severity> impacts) {
+    return newMaintainabilityIssueWithoutEffort().setEffort(Duration.create(effort)).replaceImpacts(impacts);
+  }
+
+  private static DefaultIssue newMaintainabilityIssue(long effort) {
+    return newMaintainabilityIssueWithoutEffort().setEffort(Duration.create(effort)).setType(CODE_SMELL).replaceImpacts(Map.of(MAINTAINABILITY, HIGH));
   }
 
-  private static DefaultIssue newBugIssue(long effort) {
-    return newCodeSmellIssueWithoutEffort().setEffort(Duration.create(effort)).setType(BUG);
+  private static DefaultIssue newReliabilityIssue(long effort) {
+    return newMaintainabilityIssueWithoutEffort().setEffort(Duration.create(effort)).setType(BUG).replaceImpacts(Map.of(RELIABILITY, HIGH));
   }
 
-  private static DefaultIssue newVulnerabilityIssue(long effort) {
-    return newCodeSmellIssueWithoutEffort().setEffort(Duration.create(effort)).setType(VULNERABILITY);
+  private static DefaultIssue newSecurityIssue(long effort) {
+    return newMaintainabilityIssueWithoutEffort().setEffort(Duration.create(effort)).setType(VULNERABILITY).replaceImpacts(Map.of(SECURITY, HIGH));
   }
 
-  private static DefaultIssue newCodeSmellIssueWithoutEffort() {
-    return new DefaultIssue().setType(CODE_SMELL);
+  private static DefaultIssue newMaintainabilityIssueWithoutEffort() {
+    return new DefaultIssue().setType(CODE_SMELL).replaceImpacts(Map.of(MAINTAINABILITY, HIGH));
   }
 
-  private static DefaultIssue newBugIssueWithoutEffort() {
-    return new DefaultIssue().setType(BUG);
+  private static DefaultIssue newReliabilityIssueWithoutEffort() {
+    return new DefaultIssue().setType(BUG).replaceImpacts(Map.of(RELIABILITY, HIGH));
   }
 
-  private static DefaultIssue newVulnerabilityIssueWithoutEffort() {
-    return new DefaultIssue().setType(VULNERABILITY);
+  private static DefaultIssue newSecurityIssueWithoutEffort() {
+    return new DefaultIssue().setType(VULNERABILITY).replaceImpacts(Map.of(SECURITY, HIGH));
   }
 
 }
index 914c8185a90be97b0297aef4834181871a10b17d..9aef4e37ac58e01a55b83b59052f869c6e9a31a9 100644 (file)
  */
 package org.sonar.ce.task.projectanalysis.issue;
 
-import org.junit.Test;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.Duration;
 import org.sonar.ce.task.projectanalysis.analysis.Branch;
@@ -39,6 +45,10 @@ import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.issue.impact.Severity.HIGH;
+import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
+import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY;
+import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY;
 import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT;
 import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT;
@@ -48,33 +58,42 @@ import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT_KEY;
 import static org.sonar.api.rules.RuleType.BUG;
 import static org.sonar.api.rules.RuleType.CODE_SMELL;
 import static org.sonar.api.rules.RuleType.VULNERABILITY;
-
-public class NewEffortAggregatorTest {
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT;
+import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY;
+
+class NewEffortAggregatorTest {
   private static final Component FILE = ReportComponent.builder(Component.Type.FILE, 1).setUuid("FILE").build();
   private static final Component PROJECT = ReportComponent.builder(Component.Type.PROJECT, 2).addChildren(FILE).build();
 
-  @org.junit.Rule
+  @RegisterExtension
   public PeriodHolderRule periodsHolder = new PeriodHolderRule();
-  @org.junit.Rule
+  @RegisterExtension
   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
     .add(NEW_TECHNICAL_DEBT)
     .add(NEW_RELIABILITY_REMEDIATION_EFFORT)
-    .add(NEW_SECURITY_REMEDIATION_EFFORT);
-  @org.junit.Rule
+    .add(NEW_SECURITY_REMEDIATION_EFFORT)
+    .add(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT)
+    .add(NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT)
+    .add(NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT);
+  @RegisterExtension
   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create();
   private final NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
   private final NewEffortAggregator underTest = new NewEffortAggregator(metricRepository, measureRepository, newIssueClassifier);
 
   @Test
-  public void sum_new_maintainability_effort_of_issues() {
+  void sum_new_maintainability_effort_of_issues() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
-    DefaultIssue unresolved1 = newCodeSmellIssue(10L);
+    DefaultIssue unresolved1 = newMaintainabilityIssue(10L);
     DefaultIssue old1 = oldCodeSmellIssue(100L);
-    DefaultIssue unresolved2 = newCodeSmellIssue(30L);
+    DefaultIssue unresolved2 = newMaintainabilityIssue(30L);
     DefaultIssue old2 = oldCodeSmellIssue(300L);
-    DefaultIssue unresolvedWithoutDebt = newCodeSmellIssueWithoutEffort();
-    DefaultIssue resolved = newCodeSmellIssue(50L).setResolution(RESOLUTION_FIXED);
+    DefaultIssue unresolvedWithoutDebt = newMaintainabilityIssueWithoutEffort();
+    DefaultIssue resolved = newMaintainabilityIssue(50L).setResolution(RESOLUTION_FIXED);
 
     underTest.beforeComponent(FILE);
     underTest.onIssue(FILE, unresolved1);
@@ -86,18 +105,38 @@ public class NewEffortAggregatorTest {
     underTest.afterComponent(FILE);
 
     assertValue(FILE, NEW_TECHNICAL_DEBT_KEY, 10 + 30);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 10 + 30);
   }
 
   @Test
-  public void new_maintainability_effort_is_only_computed_using_code_smell_issues() {
+  void sum_effort_when_multiple_impacts() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
-    DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
+
+    DefaultIssue unresolved1 = createIssue(CODE_SMELL, List.of(MAINTAINABILITY, RELIABILITY, SECURITY), 10, true);
+    DefaultIssue unresolved2 = createIssue(CODE_SMELL, List.of(MAINTAINABILITY, RELIABILITY, SECURITY), 10, true);
+
+    underTest.beforeComponent(FILE);
+    underTest.onIssue(FILE, unresolved1);
+    underTest.onIssue(FILE, unresolved2);
+    underTest.afterComponent(FILE);
+
+    // total maintainability effort
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 20);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 20);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 20);
+  }
+
+  @Test
+  void new_maintainability_effort_is_only_computed_using_maintainability_issues() {
+    when(newIssueClassifier.isEnabled()).thenReturn(true);
+    when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
+    DefaultIssue codeSmellIssue = newMaintainabilityIssue(10);
     DefaultIssue oldSmellIssue = oldCodeSmellIssue(100);
     // Issues of type BUG and VULNERABILITY should be ignored
-    DefaultIssue bugIssue = newBugIssue(15);
+    DefaultIssue bugIssue = newReliabilityIssue(15);
     DefaultIssue oldBugIssue = oldBugIssue(150);
-    DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
+    DefaultIssue vulnerabilityIssue = newSecurityIssue(12);
     DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
 
     underTest.beforeComponent(FILE);
@@ -111,19 +150,20 @@ public class NewEffortAggregatorTest {
 
     // Only effort of CODE SMELL issue is used
     assertValue(FILE, NEW_TECHNICAL_DEBT_KEY, 10);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 10);
   }
 
   @Test
-  public void sum_new_reliability_effort_of_issues() {
+  void sum_new_reliability_effort_of_issues() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
-    DefaultIssue unresolved1 = newBugIssue(10L);
+    DefaultIssue unresolved1 = newReliabilityIssue(10L);
     DefaultIssue old1 = oldBugIssue(100L);
-    DefaultIssue unresolved2 = newBugIssue(30L);
+    DefaultIssue unresolved2 = newReliabilityIssue(30L);
 
     DefaultIssue old2 = oldBugIssue(300L);
-    DefaultIssue unresolvedWithoutDebt = newBugIssueWithoutEffort();
-    DefaultIssue resolved = newBugIssue(50L).setResolution(RESOLUTION_FIXED);
+    DefaultIssue unresolvedWithoutDebt = newReliabilityIssueWithoutEffort();
+    DefaultIssue resolved = newReliabilityIssue(50L).setResolution(RESOLUTION_FIXED);
 
     underTest.beforeComponent(FILE);
     underTest.onIssue(FILE, unresolved1);
@@ -135,18 +175,19 @@ public class NewEffortAggregatorTest {
     underTest.afterComponent(FILE);
 
     assertValue(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 10 + 30);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 10 + 30);
   }
 
   @Test
-  public void new_reliability_effort_is_only_computed_using_bug_issues() {
+  void new_reliability_effort_is_only_computed_using_bug_issues() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
-    DefaultIssue bugIssue = newBugIssue(15);
+    DefaultIssue bugIssue = newReliabilityIssue(15);
     DefaultIssue oldBugIssue = oldBugIssue(150);
     // Issues of type CODE SMELL and VULNERABILITY should be ignored
-    DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
+    DefaultIssue codeSmellIssue = newMaintainabilityIssue(10);
     DefaultIssue oldCodeSmellIssue = oldCodeSmellIssue(100);
-    DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
+    DefaultIssue vulnerabilityIssue = newSecurityIssue(12);
     DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
 
     underTest.beforeComponent(FILE);
@@ -160,17 +201,18 @@ public class NewEffortAggregatorTest {
 
     // Only effort of BUG issue is used
     assertValue(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 15);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 15);
   }
 
   @Test
-  public void sum_new_vulnerability_effort_of_issues() {
+  void sum_new_vulnerability_effort_of_issues() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
-    DefaultIssue unresolved1 = newVulnerabilityIssue(10L);
+    DefaultIssue unresolved1 = newSecurityIssue(10L);
     DefaultIssue old1 = oldVulnerabilityIssue(100L);
-    DefaultIssue unresolved2 = newVulnerabilityIssue(30L);
+    DefaultIssue unresolved2 = newSecurityIssue(30L);
     DefaultIssue old2 = oldVulnerabilityIssue(300L);
-    DefaultIssue unresolvedWithoutDebt = newVulnerabilityIssueWithoutEffort();
-    DefaultIssue resolved = newVulnerabilityIssue(50L).setResolution(RESOLUTION_FIXED);
+    DefaultIssue unresolvedWithoutDebt = newSecurityIssueWithoutEffort();
+    DefaultIssue resolved = newSecurityIssue(50L).setResolution(RESOLUTION_FIXED);
     DefaultIssue oldResolved = oldVulnerabilityIssue(500L).setResolution(RESOLUTION_FIXED);
 
     underTest.beforeComponent(FILE);
@@ -184,18 +226,19 @@ public class NewEffortAggregatorTest {
     underTest.afterComponent(FILE);
 
     assertValue(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 10 + 30);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 10 + 30);
   }
 
   @Test
-  public void new_security_effort_is_only_computed_using_vulnerability_issues() {
+  void new_security_effort_is_only_computed_using_vulnerability_issues() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
-    DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
+    DefaultIssue vulnerabilityIssue = newSecurityIssue(12);
     DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
     // Issues of type CODE SMELL and BUG should be ignored
-    DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
+    DefaultIssue codeSmellIssue = newMaintainabilityIssue(10);
     DefaultIssue oldCodeSmellIssue = oldCodeSmellIssue(100);
-    DefaultIssue bugIssue = newBugIssue(15);
+    DefaultIssue bugIssue = newReliabilityIssue(15);
     DefaultIssue oldBugIssue = oldBugIssue(150);
 
     underTest.beforeComponent(FILE);
@@ -209,25 +252,26 @@ public class NewEffortAggregatorTest {
 
     // Only effort of VULNERABILITY issue is used
     assertValue(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 12);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 12);
   }
 
   @Test
-  public void aggregate_new_characteristic_measures_of_children() {
+  void aggregate_new_characteristic_measures_of_children() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
 
-    DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
+    DefaultIssue codeSmellIssue = newMaintainabilityIssue(10);
     DefaultIssue oldCodeSmellIssue = oldCodeSmellIssue(100);
-    DefaultIssue bugIssue = newBugIssue(8);
+    DefaultIssue bugIssue = newReliabilityIssue(8);
     DefaultIssue oldBugIssue = oldBugIssue(80);
-    DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
+    DefaultIssue vulnerabilityIssue = newSecurityIssue(12);
     DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
 
-    DefaultIssue codeSmellProjectIssue = newCodeSmellIssue(30);
+    DefaultIssue codeSmellProjectIssue = newMaintainabilityIssue(30);
     DefaultIssue oldCodeSmellProjectIssue = oldCodeSmellIssue(300);
-    DefaultIssue bugProjectIssue = newBugIssue(28);
+    DefaultIssue bugProjectIssue = newReliabilityIssue(28);
     DefaultIssue oldBugProjectIssue = oldBugIssue(280);
-    DefaultIssue vulnerabilityProjectIssue = newVulnerabilityIssue(32);
+    DefaultIssue vulnerabilityProjectIssue = newSecurityIssue(32);
     DefaultIssue oldVulnerabilityProjectIssue = oldVulnerabilityIssue(320);
 
     underTest.beforeComponent(FILE);
@@ -250,15 +294,19 @@ public class NewEffortAggregatorTest {
     assertValue(PROJECT, NEW_TECHNICAL_DEBT_KEY, 10 + 30);
     assertValue(PROJECT, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 8 + 28);
     assertValue(PROJECT, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 12 + 32);
+
+    assertValue(PROJECT, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 10 + 30);
+    assertValue(PROJECT, NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 8 + 28);
+    assertValue(PROJECT, NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 12 + 32);
   }
 
   @Test
-  public void no_measures_if_no_periods() {
+  void no_measures_if_no_periods() {
     when(newIssueClassifier.isEnabled()).thenReturn(false);
     Branch branch = mock(Branch.class);
     when(branch.getType()).thenReturn(BranchType.BRANCH);
     periodsHolder.setPeriod(null);
-    DefaultIssue unresolved = newCodeSmellIssue(10);
+    DefaultIssue unresolved = newMaintainabilityIssue(10);
 
     underTest.beforeComponent(FILE);
     underTest.onIssue(FILE, unresolved);
@@ -268,7 +316,7 @@ public class NewEffortAggregatorTest {
   }
 
   @Test
-  public void should_have_empty_measures_if_no_issues() {
+  void should_have_empty_measures_if_no_issues() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
 
@@ -278,6 +326,10 @@ public class NewEffortAggregatorTest {
     assertValue(FILE, NEW_TECHNICAL_DEBT_KEY, 0);
     assertValue(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 0);
     assertValue(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 0);
+
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY, 0);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, 0);
+    assertValue(FILE, NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY, 0);
   }
 
   private void assertValue(Component component, String metricKey, int value) {
@@ -285,52 +337,58 @@ public class NewEffortAggregatorTest {
     assertThat(newMeasure.getLongValue()).isEqualTo(value);
   }
 
-  private DefaultIssue newCodeSmellIssue(long effort) {
-    return createIssue(CODE_SMELL, effort, true);
+  private DefaultIssue newMaintainabilityIssue(long effort) {
+    return createIssue(CODE_SMELL, MAINTAINABILITY, effort, true);
   }
 
   private DefaultIssue oldCodeSmellIssue(long effort) {
-    return createIssue(CODE_SMELL, effort, false);
+    return createIssue(CODE_SMELL, MAINTAINABILITY, effort, false);
   }
 
-  private DefaultIssue newBugIssue(long effort) {
-    return createIssue(BUG, effort, true);
+  private DefaultIssue newReliabilityIssue(long effort) {
+    return createIssue(BUG, RELIABILITY, effort, true);
   }
 
   private DefaultIssue oldBugIssue(long effort) {
-    return createIssue(BUG, effort, false);
+    return createIssue(BUG, RELIABILITY, effort, false);
   }
 
-  private DefaultIssue newVulnerabilityIssue(long effort) {
-    return createIssue(VULNERABILITY, effort, true);
+  private DefaultIssue newSecurityIssue(long effort) {
+    return createIssue(VULNERABILITY, SECURITY, effort, true);
   }
 
   private DefaultIssue oldVulnerabilityIssue(long effort) {
-    return createIssue(VULNERABILITY, effort, false);
+    return createIssue(VULNERABILITY, SECURITY, effort, false);
   }
 
-  private DefaultIssue newCodeSmellIssueWithoutEffort() {
+  private DefaultIssue newMaintainabilityIssueWithoutEffort() {
     DefaultIssue defaultIssue = new DefaultIssue()
       .setKey(UuidFactoryFast.getInstance().create())
+      .replaceImpacts(Map.of(MAINTAINABILITY, Severity.HIGH))
       .setType(CODE_SMELL);
     when(newIssueClassifier.isNew(any(), eq(defaultIssue))).thenReturn(true);
     return defaultIssue;
   }
 
-  private DefaultIssue createIssue(RuleType type, long effort, boolean isNew) {
+  private DefaultIssue createIssue(RuleType type, SoftwareQuality softwareQuality, long effort, boolean isNew) {
+    return createIssue(type, List.of(softwareQuality), effort, isNew);
+  }
+
+  private DefaultIssue createIssue(RuleType type, List<SoftwareQuality> softwareQualities, long effort, boolean isNew) {
     DefaultIssue defaultIssue = new DefaultIssue()
       .setKey(UuidFactoryFast.getInstance().create())
       .setEffort(Duration.create(effort))
-      .setType(type);
+      .setType(type)
+      .replaceImpacts(softwareQualities.stream().collect(Collectors.toMap(e -> e, e -> HIGH)));
     when(newIssueClassifier.isNew(any(), eq(defaultIssue))).thenReturn(isNew);
     return defaultIssue;
   }
 
-  private static DefaultIssue newBugIssueWithoutEffort() {
-    return new DefaultIssue().setType(BUG);
+  private static DefaultIssue newReliabilityIssueWithoutEffort() {
+    return new DefaultIssue().setType(BUG).replaceImpacts(Map.of(RELIABILITY, Severity.HIGH));
   }
 
-  private static DefaultIssue newVulnerabilityIssueWithoutEffort() {
-    return new DefaultIssue().setType(VULNERABILITY);
+  private static DefaultIssue newSecurityIssueWithoutEffort() {
+    return new DefaultIssue().setType(VULNERABILITY).replaceImpacts(Map.of(SECURITY, Severity.HIGH));
   }
 }
index b5d037ddd04772e041657f52c0e53e22757a00bc..229e9ebb76fc23fbbead1b3764124d077f6d2cb3 100644 (file)
@@ -208,7 +208,7 @@ public class SoftwareQualitiesMetrics implements Metrics {
 
   public static final String SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY = "software_quality_reliability_remediation_effort";
 
-  public static final Metric<Long> RELIABILITY_REMEDIATION_EFFORT =
+  public static final Metric<Long> SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT =
     new Metric.Builder(SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY, "Software Quality Reliability Remediation Effort",
       Metric.ValueType.WORK_DUR)
       .setDescription("Software quality reliability remediation effort")
@@ -275,7 +275,7 @@ public class SoftwareQualitiesMetrics implements Metrics {
       NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
       SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT,
       NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT,
-      RELIABILITY_REMEDIATION_EFFORT,
+      SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT,
       NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT,
       SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO,
       NEW_SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO
index 717c88af58a1bcb46cb833b6ca61148c31ed9dc5..b4315ffaa51d1d136ad3013907a1570a15bf8651 100644 (file)
@@ -41,7 +41,7 @@ class SoftwareQualitiesMetricsTest {
         SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
         SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT,
         SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT,
-        SoftwareQualitiesMetrics.RELIABILITY_REMEDIATION_EFFORT,
+        SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT,
         SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT,
         SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO,
         SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO);