]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7338 SONAR-7339 SONAR-7340 new metrics to count issues by type
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Mon, 29 Feb 2016 13:32:25 +0000 (14:32 +0100)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Mon, 29 Feb 2016 14:34:50 +0000 (15:34 +0100)
it/it-tests/src/test/java/it/dbCleaner/PurgeTest.java
server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCounter.java
server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueCounterTest.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java

index 936d17517cfe42ce902397768e064cca28fccb0b..91edeef92db5e63fc4d0788abac62fed6954ee0b 100644 (file)
@@ -76,10 +76,10 @@ public class PurgeTest {
     collector.checkThat("Wrong number of files", count("projects where qualifier in ('FIL')"), equalTo(4));
     collector.checkThat("Wrong number of unit test files", count("projects where qualifier in ('UTS')"), equalTo(0));
 
-    int measuresOnTrk = 37;
-    int measuresOnBrc = 174;
-    int measuresOnDir = 109;
-    int measuresOnFil = 61;
+    int measuresOnTrk = 40;
+    int measuresOnBrc = 192;
+    int measuresOnDir = 121;
+    int measuresOnFil = 65;
 
     // count measures 
     assertMeasuresCountForQualifier("TRK", measuresOnTrk);
@@ -105,9 +105,9 @@ public class PurgeTest {
     // must be a different date, else a single snapshot is kept per day
     scan(PROJECT_SAMPLE_PATH, DateFormatUtils.ISO_DATE_FORMAT.format(today));
 
-    int newMeasuresOnTrk = 43;
-    int newMeasuresOnBrc = 214;
-    int newMeasuresOnDir = 32;
+    int newMeasuresOnTrk = 49;
+    int newMeasuresOnBrc = 250;
+    int newMeasuresOnDir = 44;
     int newMeasuresOnFil = 0;
 
     assertMeasuresCountForQualifier("TRK", measuresOnTrk + newMeasuresOnTrk);
@@ -119,7 +119,7 @@ public class PurgeTest {
     collector.checkThat(
       "Wrong number of measure of new_ metrics",
       count("project_measures, metrics where metrics.id = project_measures.metric_id and metrics.name like 'new_%'"),
-      equalTo(88));
+      equalTo(121));
 
     // added measures relate to project and new_* metrics
     expectedMeasures += newMeasuresOnTrk + newMeasuresOnBrc + newMeasuresOnDir + newMeasuresOnFil;
index 494525867796bde5a224e31e11ec197979f60967..75925943942f98bf719116cdf96f561d06aedf9d 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.computation.issue;
 
+import com.google.common.collect.EnumMultiset;
 import com.google.common.collect.HashMultiset;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Multiset;
@@ -26,7 +27,9 @@ import java.util.HashMap;
 import java.util.Map;
 import javax.annotation.Nullable;
 import org.sonar.api.issue.Issue;
+import org.sonar.api.measures.CoreMetrics;
 import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueType;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.measure.Measure;
 import org.sonar.server.computation.measure.MeasureRepository;
@@ -64,11 +67,12 @@ import static org.sonar.api.rule.Severity.MINOR;
 /**
  * For each component, computes the measures related to number of issues:
  * <ul>
- *   <li>unresolved issues</li>
- *   <li>false-positives</li>
- *   <li>open issues</li>
- *   <li>issues per status (open, reopen, confirmed)</li>
- *   <li>issues per severity (from info to blocker)</li>
+ * <li>unresolved issues</li>
+ * <li>false-positives</li>
+ * <li>open issues</li>
+ * <li>issues per status (open, reopen, confirmed)</li>
+ * <li>issues per severity (from info to blocker)</li>
+ * <li>issues per type (code smell, bug, vulnerability)</li>
  * </ul>
  * For each value, the variation on configured periods is also computed.
  */
@@ -90,6 +94,17 @@ public class IssueCounter extends IssueVisitor {
     INFO, NEW_INFO_VIOLATIONS_KEY
     );
 
+  private static final Map<IssueType, String> TYPE_TO_METRIC_KEY = ImmutableMap.<IssueType, String>builder()
+    .put(IssueType.CODE_SMELL, CoreMetrics.CODE_SMELLS_KEY)
+    .put(IssueType.BUG, CoreMetrics.BUGS_KEY)
+    .put(IssueType.VULNERABILITY, CoreMetrics.VULNERABILITIES_KEY)
+    .build();
+  private static final Map<IssueType, String> TYPE_TO_NEW_METRIC_KEY = ImmutableMap.<IssueType, String>builder()
+    .put(IssueType.CODE_SMELL, CoreMetrics.NEW_CODE_SMELLS_KEY)
+    .put(IssueType.BUG, CoreMetrics.NEW_BUGS_KEY)
+    .put(IssueType.VULNERABILITY, CoreMetrics.NEW_VULNERABILITIES_KEY)
+    .build();
+
   private final PeriodsHolder periodsHolder;
   private final MetricRepository metricRepository;
   private final MeasureRepository measureRepository;
@@ -131,8 +146,9 @@ public class IssueCounter extends IssueVisitor {
 
   @Override
   public void afterComponent(Component component) {
-    addMeasuresByStatus(component);
     addMeasuresBySeverity(component);
+    addMeasuresByStatus(component);
+    addMeasuresByType(component);
     addMeasuresByPeriod(component);
     currentCounters = null;
   }
@@ -153,6 +169,12 @@ public class IssueCounter extends IssueVisitor {
     addMeasure(component, FALSE_POSITIVE_ISSUES_KEY, currentCounters.counter().falsePositives);
   }
 
+  private void addMeasuresByType(Component component) {
+    for (Map.Entry<IssueType, String> entry : TYPE_TO_METRIC_KEY.entrySet()) {
+      addMeasure(component, entry.getValue(), currentCounters.counter().typeBag.count(entry.getKey()));
+    }
+  }
+
   private void addMeasure(Component component, String metricKey, int value) {
     Metric metric = metricRepository.getByKey(metricKey);
     measureRepository.add(component, metric, Measure.newMeasureBuilder().create(value));
@@ -174,7 +196,23 @@ public class IssueCounter extends IssueVisitor {
         Double[] variations = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS];
         for (Period period : periodsHolder.getPeriods()) {
           Multiset<String> bag = currentCounters.counterForPeriod(period.getIndex()).severityBag;
-          variations[period.getIndex() - 1] = new Double(bag.count(severity));
+          variations[period.getIndex() - 1] = (double) bag.count(severity);
+        }
+        Metric metric = metricRepository.getByKey(metricKey);
+        measureRepository.add(component, metric, Measure.newMeasureBuilder()
+          .setVariations(new MeasureVariations(variations))
+          .createNoValue());
+      }
+
+      // waiting for Java 8 lambda in order to factor this loop with the previous one
+      // (see call currentCounters.counterForPeriod(period.getIndex()).xxx with xxx as severityBag or typeBag)
+      for (Map.Entry<IssueType, String> entry : TYPE_TO_NEW_METRIC_KEY.entrySet()) {
+        IssueType type = entry.getKey();
+        String metricKey = entry.getValue();
+        Double[] variations = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS];
+        for (Period period : periodsHolder.getPeriods()) {
+          Multiset<IssueType> bag = currentCounters.counterForPeriod(period.getIndex()).typeBag;
+          variations[period.getIndex() - 1] = (double) bag.count(type);
         }
         Metric metric = metricRepository.getByKey(metricKey);
         measureRepository.add(component, metric, Measure.newMeasureBuilder()
@@ -193,7 +231,8 @@ public class IssueCounter extends IssueVisitor {
     private int reopened = 0;
     private int confirmed = 0;
     private int falsePositives = 0;
-    private Multiset<String> severityBag = HashMultiset.create();
+    private final Multiset<String> severityBag = HashMultiset.create();
+    private final EnumMultiset<IssueType> typeBag = EnumMultiset.create(IssueType.class);
 
     void add(Counter counter) {
       unresolved += counter.unresolved;
@@ -202,11 +241,13 @@ public class IssueCounter extends IssueVisitor {
       confirmed += counter.confirmed;
       falsePositives += counter.falsePositives;
       severityBag.addAll(counter.severityBag);
+      typeBag.addAll(counter.typeBag);
     }
 
-    void add(Issue issue) {
+    void add(DefaultIssue issue) {
       if (issue.resolution() == null) {
         unresolved++;
+        typeBag.add(issue.type());
         severityBag.add(issue.severity());
       } else if (Issue.RESOLUTION_FALSE_POSITIVE.equals(issue.resolution())) {
         falsePositives++;
@@ -248,11 +289,11 @@ public class IssueCounter extends IssueVisitor {
       }
     }
 
-    void addOnPeriod(Issue issue, int periodIndex) {
+    void addOnPeriod(DefaultIssue issue, int periodIndex) {
       array[periodIndex].add(issue);
     }
 
-    void add(Issue issue) {
+    void add(DefaultIssue issue) {
       array[0].add(issue);
     }
 
index 19086ea06b307315403b448dc6b0e0d545822872..776499638ef34a7da211ea83da277018f42bacf4 100644 (file)
@@ -25,7 +25,9 @@ import org.assertj.core.data.Offset;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
 import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueType;
 import org.sonar.db.rule.RuleTesting;
 import org.sonar.server.computation.batch.BatchReportReaderRule;
 import org.sonar.server.computation.batch.TreeRootHolderRule;
@@ -90,6 +92,12 @@ public class IssueCounterTest {
   static final Metric NEW_MINOR_ISSUES_METRIC = new MetricImpl(14, NEW_MINOR_VIOLATIONS_KEY, NEW_MINOR_VIOLATIONS_KEY, INT);
   static final Metric NEW_INFO_ISSUES_METRIC = new MetricImpl(15, NEW_INFO_VIOLATIONS_KEY, NEW_INFO_VIOLATIONS_KEY, INT);
   static final Metric FALSE_POSITIVE_ISSUES_METRIC = new MetricImpl(16, FALSE_POSITIVE_ISSUES_KEY, FALSE_POSITIVE_ISSUES_KEY, INT);
+  static final Metric CODE_SMELLS_METRIC = new MetricImpl(17, CoreMetrics.CODE_SMELLS_KEY, CoreMetrics.CODE_SMELLS_KEY, INT);
+  static final Metric BUGS_METRIC = new MetricImpl(18, CoreMetrics.BUGS_KEY, CoreMetrics.BUGS_KEY, INT);
+  static final Metric VULNERABILITIES_METRIC = new MetricImpl(19, CoreMetrics.VULNERABILITIES_KEY, CoreMetrics.VULNERABILITIES_KEY, INT);
+  static final Metric NEW_CODE_SMELLS_METRIC = new MetricImpl(20, CoreMetrics.NEW_CODE_SMELLS_KEY, CoreMetrics.NEW_CODE_SMELLS_KEY, INT);
+  static final Metric NEW_BUGS_METRIC = new MetricImpl(21, CoreMetrics.NEW_BUGS_KEY, CoreMetrics.NEW_BUGS_KEY, INT);
+  static final Metric NEW_VULNERABILITIES_METRIC = new MetricImpl(22, CoreMetrics.NEW_VULNERABILITIES_KEY, CoreMetrics.NEW_VULNERABILITIES_KEY, INT);
 
   @Rule
   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
@@ -117,7 +125,13 @@ public class IssueCounterTest {
     .add(NEW_MAJOR_ISSUES_METRIC)
     .add(NEW_MINOR_ISSUES_METRIC)
     .add(NEW_INFO_ISSUES_METRIC)
-    .add(FALSE_POSITIVE_ISSUES_METRIC);
+    .add(FALSE_POSITIVE_ISSUES_METRIC)
+    .add(CODE_SMELLS_METRIC)
+    .add(BUGS_METRIC)
+    .add(VULNERABILITIES_METRIC)
+    .add(NEW_CODE_SMELLS_METRIC)
+    .add(NEW_BUGS_METRIC)
+    .add(NEW_VULNERABILITIES_METRIC);
 
   @Rule
   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
@@ -202,19 +216,52 @@ public class IssueCounterTest {
     assertThat(measureRepository.getRawMeasure(PROJECT, MAJOR_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
   }
 
+  @Test
+  public void count_unresolved_issues_by_type() {
+    periodsHolder.setPeriods();
+
+    // bottom-up traversal -> from files to project
+    // file1 : one open code smell, one closed code smell (which will be excluded from metric)
+    underTest.beforeComponent(FILE1);
+    underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(IssueType.CODE_SMELL));
+    underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR).setType(IssueType.CODE_SMELL));
+    underTest.afterComponent(FILE1);
+
+    // file2 : one bug
+    underTest.beforeComponent(FILE2);
+    underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER).setType(IssueType.BUG));
+    underTest.afterComponent(FILE2);
+
+    underTest.beforeComponent(PROJECT);
+    underTest.afterComponent(PROJECT);
+
+    assertThat(measureRepository.getRawMeasure(FILE1, CODE_SMELLS_METRIC).get().getIntValue()).isEqualTo(1);
+    assertThat(measureRepository.getRawMeasure(FILE1, BUGS_METRIC).get().getIntValue()).isEqualTo(0);
+    assertThat(measureRepository.getRawMeasure(FILE1, VULNERABILITIES_METRIC).get().getIntValue()).isEqualTo(0);
+
+    assertThat(measureRepository.getRawMeasure(FILE2, CODE_SMELLS_METRIC).get().getIntValue()).isEqualTo(0);
+    assertThat(measureRepository.getRawMeasure(FILE2, BUGS_METRIC).get().getIntValue()).isEqualTo(1);
+    assertThat(measureRepository.getRawMeasure(FILE2, VULNERABILITIES_METRIC).get().getIntValue()).isEqualTo(0);
+
+    assertThat(measureRepository.getRawMeasure(PROJECT, CODE_SMELLS_METRIC).get().getIntValue()).isEqualTo(1);
+    assertThat(measureRepository.getRawMeasure(PROJECT, BUGS_METRIC).get().getIntValue()).isEqualTo(1);
+    assertThat(measureRepository.getRawMeasure(PROJECT, VULNERABILITIES_METRIC).get().getIntValue()).isEqualTo(0);
+  }
+
   @Test
   public void count_new_issues() {
     Period period = newPeriod(3, 1500000000000L);
     periodsHolder.setPeriods(period);
 
     underTest.beforeComponent(FILE1);
-    // created before -> existing issues
-    underTest.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, BLOCKER, period.getSnapshotDate() - 1000000L));
-    // created during the first analysis starting the period -> existing issues
-    underTest.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, BLOCKER, period.getSnapshotDate()));
-    // created after -> new issues
-    underTest.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, CRITICAL, period.getSnapshotDate() + 100000L));
-    underTest.onIssue(FILE1, createIssueAt(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR, period.getSnapshotDate() + 200000L));
+    // created before -> existing issues (so ignored)
+    underTest.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, BLOCKER, period.getSnapshotDate() - 1000000L).setType(IssueType.CODE_SMELL));
+    // created during the first analysis starting the period -> existing issues (so ignored)
+    underTest.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, BLOCKER, period.getSnapshotDate()).setType(IssueType.BUG));
+    // created after -> 3 new issues but 1 is closed
+    underTest.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, CRITICAL, period.getSnapshotDate() + 100000L).setType(IssueType.CODE_SMELL));
+    underTest.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, CRITICAL, period.getSnapshotDate() + 100000L).setType(IssueType.BUG));
+    underTest.onIssue(FILE1, createIssueAt(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR, period.getSnapshotDate() + 200000L).setType(IssueType.BUG));
     underTest.afterComponent(FILE1);
 
     underTest.beforeComponent(FILE2);
@@ -223,15 +270,21 @@ public class IssueCounterTest {
     underTest.beforeComponent(PROJECT);
     underTest.afterComponent(PROJECT);
 
-    assertVariation(FILE1, NEW_ISSUES_METRIC, period.getIndex(), 1);
-    assertVariation(FILE1, NEW_CRITICAL_ISSUES_METRIC, period.getIndex(), 1);
+    assertVariation(FILE1, NEW_ISSUES_METRIC, period.getIndex(), 2);
+    assertVariation(FILE1, NEW_CRITICAL_ISSUES_METRIC, period.getIndex(), 2);
     assertVariation(FILE1, NEW_BLOCKER_ISSUES_METRIC, period.getIndex(), 0);
     assertVariation(FILE1, NEW_MAJOR_ISSUES_METRIC, period.getIndex(), 0);
+    assertVariation(FILE1, NEW_CODE_SMELLS_METRIC, period.getIndex(), 1);
+    assertVariation(FILE1, NEW_BUGS_METRIC, period.getIndex(), 1);
+    assertVariation(FILE1, NEW_VULNERABILITIES_METRIC, period.getIndex(), 0);
 
-    assertVariation(PROJECT, NEW_ISSUES_METRIC, period.getIndex(), 1);
-    assertVariation(PROJECT, NEW_CRITICAL_ISSUES_METRIC, period.getIndex(), 1);
+    assertVariation(PROJECT, NEW_ISSUES_METRIC, period.getIndex(), 2);
+    assertVariation(PROJECT, NEW_CRITICAL_ISSUES_METRIC, period.getIndex(), 2);
     assertVariation(PROJECT, NEW_BLOCKER_ISSUES_METRIC, period.getIndex(), 0);
     assertVariation(PROJECT, NEW_MAJOR_ISSUES_METRIC, period.getIndex(), 0);
+    assertVariation(PROJECT, NEW_CODE_SMELLS_METRIC, period.getIndex(), 1);
+    assertVariation(PROJECT, NEW_BUGS_METRIC, period.getIndex(), 1);
+    assertVariation(PROJECT, NEW_VULNERABILITIES_METRIC, period.getIndex(), 0);
   }
 
   private void assertVariation(Component component, Metric metric, int periodIndex, int expectedVariation) {
@@ -243,6 +296,7 @@ public class IssueCounterTest {
     return new DefaultIssue()
       .setResolution(resolution).setStatus(status)
       .setSeverity(severity).setRuleKey(RuleTesting.XOO_X1)
+      .setType(IssueType.CODE_SMELL)
       .setCreationDate(new Date());
   }
 
@@ -250,6 +304,7 @@ public class IssueCounterTest {
     return new DefaultIssue()
       .setResolution(resolution).setStatus(status)
       .setSeverity(severity).setRuleKey(RuleTesting.XOO_X1)
+      .setType(IssueType.CODE_SMELL)
       .setCreationDate(new Date(creationDate));
   }
 
index 3f1fc6a41af2f46b756f57d880d4e09c8e06c431..562b3c9026f3a7285ecfa59c05efa9bc52e01645 100644 (file)
@@ -2268,8 +2268,9 @@ metric_domain.General=General
 metric_domain.Duplication=Duplication
 metric_domain.Design=Design
 metric_domain.SCM=SCM
-metric_domain.Management=Management
-metric_domain.Reviews=Reviews
+metric_domain.Maintainability=Maintainability
+metric_domain.Reliability=Reliability
+metric_domain.Security=Security
 
 
 #------------------------------------------------------------------------------
@@ -2665,6 +2666,25 @@ metric.new_minor_violations.description=New Minor issues
 metric.new_info_violations.name=New Info issues
 metric.new_info_violations.description=New Info issues
 
+metric.code_smells.name=Code Smells
+metric.code_smells.description=Code Smells
+
+metric.new_code_smells.name=New Code Smells
+metric.new_code_smells.description=New Code Smells
+
+metric.bugs.name=Bugs
+metric.bugs.description=Bugs
+
+metric.new_bugs.name=New Bugs
+metric.new_bugs.description=New Bugs
+
+metric.vulnerabilities.name=Vulnerabilities
+metric.vulnerabilities.description=Vulnerabilities
+
+metric.new_vulnerabilities.name=New Vulnerabilities
+metric.new_vulnerabilities.description=New Vulnerabilities
+
+
 #--------------------------------------------------------------------------------------------------------------------
 #
 # DESIGN
index 796c66d1a1593d5f32b6f074e088e7e07181c66c..4a93c6bdae3d4b0ff0d5f6ed6c6a4a54ae1c8726 100644 (file)
@@ -42,12 +42,29 @@ public final class CoreMetrics {
   public static String DOMAIN_COMPLEXITY = "Complexity";
   public static String DOMAIN_DOCUMENTATION = "Documentation";
   public static String DOMAIN_SCM = "SCM";
-
   public static String DOMAIN_ISSUES = "Issues";
   public static String DOMAIN_GENERAL = "General";
   public static String DOMAIN_DUPLICATION = "Duplication";
   public static String DOMAIN_DESIGN = "Design";
 
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static String DOMAIN_MAINTAINABILITY = "Maintainability";
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static String DOMAIN_RELIABILITY = "Reliability";
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static String DOMAIN_SECURITY = "Security";
+
   /**
    * @since 4.0
    */
@@ -365,21 +382,21 @@ public final class CoreMetrics {
   @Deprecated
   public static final Metric<String> CLASS_COMPLEXITY_DISTRIBUTION = new Metric.Builder(CLASS_COMPLEXITY_DISTRIBUTION_KEY, "Classes distribution /complexity",
     Metric.ValueType.DISTRIB)
-      .setDescription("Classes distribution /complexity")
-      .setDirection(Metric.DIRECTION_NONE)
-      .setQualitative(true)
-      .setDomain(DOMAIN_COMPLEXITY)
-      .setHidden(true)
-      .create();
+    .setDescription("Classes distribution /complexity")
+    .setDirection(Metric.DIRECTION_NONE)
+    .setQualitative(true)
+    .setDomain(DOMAIN_COMPLEXITY)
+    .setHidden(true)
+    .create();
 
   public static final String FUNCTION_COMPLEXITY_DISTRIBUTION_KEY = "function_complexity_distribution";
   public static final Metric<String> FUNCTION_COMPLEXITY_DISTRIBUTION = new Metric.Builder(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, "Functions distribution /complexity",
     Metric.ValueType.DISTRIB)
-      .setDescription("Functions distribution /complexity")
-      .setDirection(Metric.DIRECTION_NONE)
-      .setQualitative(true)
-      .setDomain(DOMAIN_COMPLEXITY)
-      .create();
+    .setDescription("Functions distribution /complexity")
+    .setDirection(Metric.DIRECTION_NONE)
+    .setQualitative(true)
+    .setDomain(DOMAIN_COMPLEXITY)
+    .create();
 
   public static final String FILE_COMPLEXITY_DISTRIBUTION_KEY = "file_complexity_distribution";
   public static final Metric<String> FILE_COMPLEXITY_DISTRIBUTION = new Metric.Builder(FILE_COMPLEXITY_DISTRIBUTION_KEY, "Files distribution /complexity", Metric.ValueType.DISTRIB)
@@ -886,12 +903,12 @@ public final class CoreMetrics {
    */
   public static final Metric<Integer> NEW_IT_UNCOVERED_CONDITIONS = new Metric.Builder(NEW_IT_UNCOVERED_CONDITIONS_KEY, "Uncovered branches by IT on new code",
     Metric.ValueType.INT)
-      .setDescription("Uncovered branches by Integration Tests on new code")
-      .setDirection(Metric.DIRECTION_WORST)
-      .setDomain(DOMAIN_INTEGRATION_TESTS)
-      .setBestValue(0.0)
-      .setDeleteHistoricalData(true)
-      .create();
+    .setDescription("Uncovered branches by Integration Tests on new code")
+    .setDirection(Metric.DIRECTION_WORST)
+    .setDomain(DOMAIN_INTEGRATION_TESTS)
+    .setBestValue(0.0)
+    .setDeleteHistoricalData(true)
+    .create();
 
   /**
    * @since 2.12
@@ -1113,12 +1130,12 @@ public final class CoreMetrics {
   @Deprecated
   public static final Metric<String> OVERALL_COVERAGE_LINE_HITS_DATA = new Metric.Builder(OVERALL_COVERAGE_LINE_HITS_DATA_KEY, "Overall coverage hits by line",
     Metric.ValueType.DATA)
-      .setDescription("Coverage hits by all tests and by line")
-      .setDirection(Metric.DIRECTION_NONE)
-      .setQualitative(false)
-      .setDomain(DOMAIN_OVERALL_TESTS)
-      .setDeleteHistoricalData(true)
-      .create();
+    .setDescription("Coverage hits by all tests and by line")
+    .setDirection(Metric.DIRECTION_NONE)
+    .setQualitative(false)
+    .setDomain(DOMAIN_OVERALL_TESTS)
+    .setDeleteHistoricalData(true)
+    .create();
 
   /**
    * @since 3.3
@@ -1146,11 +1163,11 @@ public final class CoreMetrics {
    */
   public static final Metric<Integer> NEW_OVERALL_CONDITIONS_TO_COVER = new Metric.Builder(NEW_OVERALL_CONDITIONS_TO_COVER_KEY, "Overall branches to cover on new code",
     Metric.ValueType.INT)
-      .setDescription("New branches to cover by all tests")
-      .setDomain(DOMAIN_OVERALL_TESTS)
-      .setDeleteHistoricalData(true)
-      .setHidden(true)
-      .create();
+    .setDescription("New branches to cover by all tests")
+    .setDomain(DOMAIN_OVERALL_TESTS)
+    .setDeleteHistoricalData(true)
+    .setHidden(true)
+    .create();
 
   /**
    * @since 3.3
@@ -1176,12 +1193,12 @@ public final class CoreMetrics {
    */
   public static final Metric<Integer> NEW_OVERALL_UNCOVERED_CONDITIONS = new Metric.Builder(NEW_OVERALL_UNCOVERED_CONDITIONS_KEY, "Overall uncovered branches on new code",
     Metric.ValueType.INT)
-      .setDescription("New branches that are not covered by any test")
-      .setDirection(Metric.DIRECTION_WORST)
-      .setDomain(DOMAIN_OVERALL_TESTS)
-      .setBestValue(0.0)
-      .setDeleteHistoricalData(true)
-      .create();
+    .setDescription("New branches that are not covered by any test")
+    .setDirection(Metric.DIRECTION_WORST)
+    .setDomain(DOMAIN_OVERALL_TESTS)
+    .setBestValue(0.0)
+    .setDeleteHistoricalData(true)
+    .create();
 
   /**
    * @since 3.3
@@ -1210,14 +1227,14 @@ public final class CoreMetrics {
    */
   public static final Metric<Double> NEW_OVERALL_BRANCH_COVERAGE = new Metric.Builder(NEW_OVERALL_BRANCH_COVERAGE_KEY, "Overall condition coverage on new code",
     Metric.ValueType.PERCENT)
-      .setDescription("Condition coverage of new/changed code by all tests")
-      .setDirection(Metric.DIRECTION_BETTER)
-      .setQualitative(true)
-      .setDomain(DOMAIN_OVERALL_TESTS)
-      .setWorstValue(0.0)
-      .setBestValue(100.0)
-      .setDeleteHistoricalData(true)
-      .create();
+    .setDescription("Condition coverage of new/changed code by all tests")
+    .setDirection(Metric.DIRECTION_BETTER)
+    .setQualitative(true)
+    .setDomain(DOMAIN_OVERALL_TESTS)
+    .setWorstValue(0.0)
+    .setBestValue(100.0)
+    .setDeleteHistoricalData(true)
+    .create();
 
   /**
    * @since 3.3
@@ -1251,10 +1268,10 @@ public final class CoreMetrics {
   @Deprecated
   public static final Metric<String> OVERALL_COVERED_CONDITIONS_BY_LINE = new Metric.Builder(OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, "Overall covered branches by line",
     Metric.ValueType.DATA)
-      .setDescription("Overall covered branches by all tests and by line")
-      .setDomain(DOMAIN_OVERALL_TESTS)
-      .setDeleteHistoricalData(true)
-      .create();
+    .setDescription("Overall covered branches by all tests and by line")
+    .setDomain(DOMAIN_OVERALL_TESTS)
+    .setDeleteHistoricalData(true)
+    .create();
 
   // --------------------------------------------------------------------------------------------------------------------
   //
@@ -1539,6 +1556,123 @@ public final class CoreMetrics {
     .setOptimizedBestValue(true)
     .create();
 
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final String CODE_SMELLS_KEY = "code_smells";
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final Metric<Integer> CODE_SMELLS = new Metric.Builder(CODE_SMELLS_KEY, "Code Smells", Metric.ValueType.INT)
+    .setDescription("Code Smells")
+    .setDirection(Metric.DIRECTION_WORST)
+    .setQualitative(false)
+    .setDomain(DOMAIN_MAINTAINABILITY)
+    .setBestValue(0.0)
+    .setOptimizedBestValue(true)
+    .create();
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final String NEW_CODE_SMELLS_KEY = "new_code_smells";
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final Metric<Integer> NEW_CODE_SMELLS = new Metric.Builder(NEW_CODE_SMELLS_KEY, "New code smells", Metric.ValueType.INT)
+    .setDescription("New Code Smells")
+    .setDirection(Metric.DIRECTION_WORST)
+    .setQualitative(true)
+    .setDomain(DOMAIN_MAINTAINABILITY)
+    .setBestValue(0.0)
+    .setOptimizedBestValue(true)
+    .setDeleteHistoricalData(true)
+    .create();
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final String BUGS_KEY = "bugs";
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final Metric<Integer> BUGS = new Metric.Builder(BUGS_KEY, "Bugs", Metric.ValueType.INT)
+    .setDescription("Bugs")
+    .setDirection(Metric.DIRECTION_WORST)
+    .setQualitative(false)
+    .setDomain(DOMAIN_RELIABILITY)
+    .setBestValue(0.0)
+    .setOptimizedBestValue(true)
+    .create();
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final String NEW_BUGS_KEY = "new_bugs";
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final Metric<Integer> NEW_BUGS = new Metric.Builder(NEW_BUGS_KEY, "New Bugs", Metric.ValueType.INT)
+    .setDescription("New Bugs")
+    .setDirection(Metric.DIRECTION_WORST)
+    .setQualitative(true)
+    .setDomain(DOMAIN_RELIABILITY)
+    .setBestValue(0.0)
+    .setOptimizedBestValue(true)
+    .setDeleteHistoricalData(true)
+    .create();
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final String VULNERABILITIES_KEY = "vulnerabilities";
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final Metric<Integer> VULNERABILITIES = new Metric.Builder(VULNERABILITIES_KEY, "Vulnerabilities", Metric.ValueType.INT)
+    .setDescription("Vulnerabilities")
+    .setDirection(Metric.DIRECTION_WORST)
+    .setQualitative(false)
+    .setDomain(DOMAIN_SECURITY)
+    .setBestValue(0.0)
+    .setOptimizedBestValue(true)
+    .create();
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final String NEW_VULNERABILITIES_KEY = "new_vulnerabilities";
+
+  /**
+   * SonarQube Quality Model
+   * @since 5.5
+   */
+  public static final Metric<Integer> NEW_VULNERABILITIES = new Metric.Builder(NEW_VULNERABILITIES_KEY, "New Vulnerabilities", Metric.ValueType.INT)
+    .setDescription("New Vulnerabilities")
+    .setDirection(Metric.DIRECTION_WORST)
+    .setQualitative(true)
+    .setDomain(DOMAIN_SECURITY)
+    .setBestValue(0.0)
+    .setOptimizedBestValue(true)
+    .setDeleteHistoricalData(true)
+    .create();
+
   // --------------------------------------------------------------------------------------------------------------------
   //
   // DESIGN
@@ -2003,8 +2137,8 @@ public final class CoreMetrics {
   @Deprecated
   public static final transient Metric<String> SCM_LAST_COMMIT_DATETIMES_BY_LINE = new Metric.Builder(SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY, "Last commit dates by line",
     Metric.ValueType.DATA)
-      .setDomain(DOMAIN_SCM)
-      .create();
+    .setDomain(DOMAIN_SCM)
+    .create();
 
   // --------------------------------------------------------------------------------------------------------------------
   //