]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-23299 add fallback condition to live quality gate evaluation
authorLéo Geoffroy <leo.geoffroy@sonarsource.com>
Mon, 28 Oct 2024 10:33:05 +0000 (11:33 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 5 Nov 2024 20:03:01 +0000 (20:03 +0000)
17 files changed:
server/sonar-server-common/src/main/java/org/sonar/server/metric/StandardToMQRMetrics.java [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/ConditionEvaluator.java
server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/EvaluatedCondition.java
server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/EvaluatedQualityGate.java
server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateEvaluatorImpl.java
server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFallbackManager.java [new file with mode: 0644]
server/sonar-server-common/src/test/java/org/sonar/server/metric/StandardToMQRMetricsTest.java [new file with mode: 0644]
server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedConditionTest.java
server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedQualityGateTest.java
server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateEvaluatorImplTest.java
server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateFallbackManagerTest.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateConditionsUpdater.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateModeChecker.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/StandardToMQRMetrics.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/StandardToMQRMetricsTest.java [deleted file]

diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/metric/StandardToMQRMetrics.java b/server/sonar-server-common/src/main/java/org/sonar/server/metric/StandardToMQRMetrics.java
new file mode 100644 (file)
index 0000000..a08ec97
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.metric;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import java.util.Optional;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.core.metric.SoftwareQualitiesMetrics;
+
+import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
+import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY;
+import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_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_INFO_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.SQALE_RATING_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_BLOCKER_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_HIGH_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_INFO_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_LOW_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MEDIUM_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_BLOCKER_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_HIGH_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_INFO_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_LOW_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
+import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MEDIUM_ISSUES_KEY;
+
+/**
+ * Defines the metrics mapping between the standard mode and the MQR mode.
+ * This list all the metrics that are specific for each mode, and the equivalent metric in the other mode.
+ */
+public class StandardToMQRMetrics {
+  private static final BiMap<String, String> STANDARD_TO_MQR_MODE_METRICS;
+
+  private static final BiMap<String, String> MQR_TO_STANDARD_MODE_METRICS;
+
+  static {
+    STANDARD_TO_MQR_MODE_METRICS = HashBiMap.create();
+    // Severity related metrics
+    STANDARD_TO_MQR_MODE_METRICS.put(BLOCKER_VIOLATIONS_KEY, SOFTWARE_QUALITY_BLOCKER_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(NEW_BLOCKER_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_BLOCKER_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CRITICAL_VIOLATIONS_KEY, SOFTWARE_QUALITY_HIGH_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(NEW_CRITICAL_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_HIGH_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(MAJOR_VIOLATIONS_KEY, SOFTWARE_QUALITY_MEDIUM_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(NEW_MAJOR_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_MEDIUM_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(MINOR_VIOLATIONS_KEY, SOFTWARE_QUALITY_LOW_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(NEW_MINOR_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_LOW_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(INFO_VIOLATIONS_KEY, SOFTWARE_QUALITY_INFO_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(NEW_INFO_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_INFO_ISSUES_KEY);
+
+    // Maintainability related metrics
+    STANDARD_TO_MQR_MODE_METRICS.put(CODE_SMELLS_KEY, SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(NEW_CODE_SMELLS_KEY, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(SQALE_RATING_KEY, SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.TECHNICAL_DEBT_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_TECHNICAL_DEBT_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.SQALE_DEBT_RATIO_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO_KEY);
+
+    STANDARD_TO_MQR_MODE_METRICS.put(EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY, EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A_KEY);
+
+    // Security related metrics
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.SECURITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_SECURITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.SECURITY_REMEDIATION_EFFORT_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.VULNERABILITIES_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_VULNERABILITIES_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_ISSUES_KEY);
+
+    // Reliability related metrics
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.BUGS_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_ISSUES_KEY);
+    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_BUGS_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_ISSUES_KEY);
+
+    MQR_TO_STANDARD_MODE_METRICS = STANDARD_TO_MQR_MODE_METRICS.inverse();
+  }
+
+  private StandardToMQRMetrics() {
+  }
+
+  public static boolean isStandardMetric(String metricKey) {
+    return STANDARD_TO_MQR_MODE_METRICS.containsKey(metricKey);
+  }
+
+  public static boolean isMQRMetric(String metricKey) {
+    return MQR_TO_STANDARD_MODE_METRICS.containsKey(metricKey);
+  }
+
+  /**
+   * Retrieves equivalent metric in the other mode. Return empty if metric has no equivalence
+   */
+  public static Optional<String> getEquivalentMetric(String metricKey) {
+    return Optional.ofNullable(STANDARD_TO_MQR_MODE_METRICS.get(metricKey))
+      .or(() -> Optional.ofNullable(MQR_TO_STANDARD_MODE_METRICS.get(metricKey)));
+  }
+}
index 916e79626d94bd7fcf2361f9f419542cdddd4b43..4e8e2fd33385f2a1a39eedc810df216dae287eab 100644 (file)
@@ -50,7 +50,7 @@ class ConditionEvaluator {
   static EvaluatedCondition evaluate(Condition condition, QualityGateEvaluator.Measures measures) {
     Optional<QualityGateEvaluator.Measure> measure = measures.get(condition.getMetricKey());
     if (measure.isEmpty()) {
-      return new EvaluatedCondition(condition, EvaluationStatus.OK, null);
+      return new EvaluatedCondition(condition, EvaluationStatus.OK, null, true);
     }
 
     Optional<Comparable> value = getMeasureValue(condition, measure.get());
@@ -105,7 +105,7 @@ class ConditionEvaluator {
       return measure.getValue().isPresent() ? getNumericValue(measure.getType(), measure.getValue().getAsDouble()) : null;
     }
 
-    checkArgument(ValueType.LEVEL.equals(measure.getType()), "Condition is not allowed for type %s" , measure.getType());
+    checkArgument(ValueType.LEVEL.equals(measure.getType()), "Condition is not allowed for type %s", measure.getType());
     return measure.getStringValue().orElse(null);
 
   }
index db183632ec88d44ed4111bd9ce557d762a49af7b..930e124096d5b303545e3d2c4d38e5b5923fbba6 100644 (file)
@@ -29,20 +29,41 @@ import static java.util.Objects.requireNonNull;
 @Immutable
 public class EvaluatedCondition {
   private final Condition condition;
+  private Condition originalCondition;
   private final EvaluationStatus status;
   @Nullable
   private final String value;
+  private final boolean missingMeasure;
 
   public EvaluatedCondition(Condition condition, EvaluationStatus status, @Nullable String value) {
+    this(condition, status, value, false);
+  }
+
+  public EvaluatedCondition(Condition condition, EvaluationStatus status, @Nullable String value, boolean measureMissing) {
+    this(condition, condition, status, value, measureMissing);
+  }
+
+  public EvaluatedCondition(Condition condition, Condition originalCondition, EvaluationStatus status, @Nullable String value, boolean measureMissing) {
     this.condition = requireNonNull(condition, "condition can't be null");
+    this.originalCondition = originalCondition;
     this.status = requireNonNull(status, "status can't be null");
     this.value = value;
+    this.missingMeasure = measureMissing;
   }
 
   public Condition getCondition() {
     return condition;
   }
 
+  public Condition getOriginalCondition() {
+    return originalCondition;
+  }
+
+  public EvaluatedCondition setOriginalCondition(Condition originalCondition) {
+    this.originalCondition = originalCondition;
+    return this;
+  }
+
   public EvaluationStatus getStatus() {
     return status;
   }
@@ -74,11 +95,16 @@ public class EvaluatedCondition {
   public String toString() {
     return "EvaluatedCondition{" +
       "condition=" + condition +
+      ", originalCondition=" + originalCondition +
       ", status=" + status +
       ", value=" + (value == null ? null : ('\'' + value + '\'')) +
       '}';
   }
 
+  public boolean isMissingMeasure() {
+    return missingMeasure;
+  }
+
   /**
    * Quality gate condition evaluation status.
    */
@@ -86,7 +112,9 @@ public class EvaluatedCondition {
     /**
      * No measure found or measure had no value. The condition has not been evaluated and therefor ignored in
      * the computation of the Quality Gate status.
+     * @deprecated since 10.8, the value is never used in the code
      */
+    @Deprecated(since = "10.8")
     NO_VALUE,
     /**
      * Condition evaluated as OK, error thresholds hasn't been reached.
index 82d702223cf099caf19f635e5c0112263279be3c..eb83e0a7d06f8cc6f9704f946fa050fd001ca8fa 100644 (file)
@@ -152,14 +152,20 @@ public class EvaluatedQualityGate {
       Set<Condition> conditions = qualityGate.getConditions();
 
       Set<Condition> conditionsNotEvaluated = conditions.stream()
-        .filter(c -> !evaluatedConditions.containsKey(c))
+        .filter(c -> !evaluatedConditions.containsKey(c) && !hasOriginalCondition(evaluatedConditions.values(), c))
         .collect(Collectors.toSet());
       checkArgument(conditionsNotEvaluated.isEmpty(), "Evaluation missing for the following conditions: %s", conditionsNotEvaluated);
 
-      Set<Condition> unknownConditions = evaluatedConditions.keySet().stream()
-        .filter(c -> !conditions.contains(c))
+      Set<Condition> unknownConditions = evaluatedConditions.entrySet().stream()
+        .filter(c -> !conditions.contains(c.getValue().getCondition()) && !conditions.contains(c.getValue().getOriginalCondition()))
+        .map(Map.Entry::getKey)
         .collect(Collectors.toSet());
+
       checkArgument(unknownConditions.isEmpty(), "Evaluation provided for unknown conditions: %s", unknownConditions);
     }
+
+    private static boolean hasOriginalCondition(Collection<EvaluatedCondition> evaluatedConditions, Condition c) {
+      return evaluatedConditions.stream().anyMatch(ec -> ec.getOriginalCondition().equals(c));
+    }
   }
 }
index ed13f8c86dd5583e2f054bc44f482963cc8fa5d4..b9239d9419b6593a77582397b348ce846b95b6f8 100644 (file)
@@ -45,6 +45,12 @@ public class QualityGateEvaluatorImpl implements QualityGateEvaluator {
     CoreMetrics.NEW_DUPLICATED_LINES_KEY,
     CoreMetrics.NEW_BLOCKS_DUPLICATED_KEY);
 
+  private final QualityGateFallbackManager qualityGateFallbackManager;
+
+  public QualityGateEvaluatorImpl(QualityGateFallbackManager qualityGateFallbackManager) {
+    this.qualityGateFallbackManager = qualityGateFallbackManager;
+  }
+
   @Override
   public EvaluatedQualityGate evaluate(QualityGate gate, Measures measures, Configuration configuration) {
     EvaluatedQualityGate.Builder result = EvaluatedQualityGate.newBuilder()
@@ -57,8 +63,15 @@ public class QualityGateEvaluatorImpl implements QualityGateEvaluator {
       String metricKey = condition.getMetricKey();
       EvaluatedCondition evaluation = ConditionEvaluator.evaluate(condition, measures);
 
+      if (evaluation.isMissingMeasure()) {
+        evaluation = qualityGateFallbackManager.getFallbackCondition(evaluation.getCondition())
+          .map(c -> ConditionEvaluator.evaluate(c, measures).setOriginalCondition(condition))
+          .orElse(evaluation);
+      }
+
       if (isSmallChangeset && evaluation.getStatus() != EvaluationStatus.OK && METRICS_TO_IGNORE_ON_SMALL_CHANGESETS.contains(metricKey)) {
-        result.addEvaluatedCondition(new EvaluatedCondition(evaluation.getCondition(), EvaluationStatus.OK, evaluation.getValue().orElse(null)));
+        result.addEvaluatedCondition(new EvaluatedCondition(evaluation.getCondition(), evaluation.getOriginalCondition(),
+          EvaluationStatus.OK, evaluation.getValue().orElse(null), evaluation.isMissingMeasure()));
         result.setIgnoredConditionsOnSmallChangeset(true);
       } else {
         result.addEvaluatedCondition(evaluation);
@@ -76,6 +89,8 @@ public class QualityGateEvaluatorImpl implements QualityGateEvaluator {
     metricKeys.add(CoreMetrics.NEW_LINES_KEY);
     for (Condition condition : gate.getConditions()) {
       metricKeys.add(condition.getMetricKey());
+      qualityGateFallbackManager.getFallbackCondition(condition)
+        .ifPresent(c -> metricKeys.add(c.getMetricKey()));
     }
     return metricKeys;
   }
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFallbackManager.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFallbackManager.java
new file mode 100644 (file)
index 0000000..d811651
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.qualitygate;
+
+import java.util.Optional;
+import org.sonar.server.metric.StandardToMQRMetrics;
+
+/**
+ * This class is used to manage the fallback of the quality gate conditions, in case one of the measure has not been computed yet.
+ * This is to ensure continuity of the quality gate evaluation in the scenario of introduction of new measures, but the project has not been reanalyzed yet.
+ * The live quality gate evaluation will fallback to an equivalent metric until the project is reanalyzed
+ */
+public class QualityGateFallbackManager {
+
+  public Optional<Condition> getFallbackCondition(Condition condition) {
+    Optional<String> equivalentMetric = StandardToMQRMetrics.getEquivalentMetric(condition.getMetricKey());
+    if (StandardToMQRMetrics.isMQRMetric(condition.getMetricKey()) && equivalentMetric.isPresent()) {
+      return Optional.of(new Condition(equivalentMetric.get(), condition.getOperator(), condition.getErrorThreshold()));
+    } else {
+      return Optional.empty();
+    }
+  }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/metric/StandardToMQRMetricsTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/metric/StandardToMQRMetricsTest.java
new file mode 100644 (file)
index 0000000..66d98b9
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.metric;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.core.metric.SoftwareQualitiesMetrics;
+
+class StandardToMQRMetricsTest {
+  @Test
+  void isStandardMetric_shouldReturnExpectedResult() {
+    Assertions.assertThat(StandardToMQRMetrics.isStandardMetric(CoreMetrics.RELIABILITY_RATING_KEY)).isTrue();
+    Assertions.assertThat(StandardToMQRMetrics.isMQRMetric(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_HIGH_ISSUES_KEY)).isTrue();
+
+    Assertions.assertThat(StandardToMQRMetrics.isMQRMetric(CoreMetrics.SECURITY_RATING_KEY)).isFalse();
+    Assertions.assertThat(StandardToMQRMetrics.isStandardMetric(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY)).isFalse();
+  }
+
+  @Test
+  void getEquivalentMetric_shouldReturnExpectedResult() {
+    Assertions.assertThat(StandardToMQRMetrics.getEquivalentMetric(CoreMetrics.COMMENT_LINES_DENSITY_KEY)).isEmpty();
+    Assertions.assertThat(StandardToMQRMetrics.getEquivalentMetric(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY)).hasValue(CoreMetrics.RELIABILITY_RATING_KEY);
+    Assertions.assertThat(StandardToMQRMetrics.getEquivalentMetric(CoreMetrics.RELIABILITY_RATING_KEY)).hasValue(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY);
+  }
+}
index 33402ef0969f6f0e3eca44399e41a91cb77f816d..9215eb11886d50eceab35efa6374d30fb879107a 100644 (file)
@@ -64,17 +64,20 @@ public class EvaluatedConditionTest {
 
   @Test
   public void override_toString() {
-    assertThat(underTest).hasToString("EvaluatedCondition{condition=" +
-      "Condition{metricKey='metricKey', operator=GREATER_THAN, errorThreshold='2'}, " +
-      "status=ERROR, value='value'}");
+    assertThat(underTest).hasToString("EvaluatedCondition{condition=Condition{metricKey='metricKey', " +
+      "operator=GREATER_THAN, errorThreshold='2'}, " +
+      "originalCondition=Condition{metricKey='metricKey', " +
+      "operator=GREATER_THAN, errorThreshold='2'}, status=ERROR, value='value'}");
   }
 
   @Test
   public void toString_does_not_quote_null_value() {
     EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, ERROR, null);
 
-    assertThat(underTest).hasToString("EvaluatedCondition{condition=" +
-      "Condition{metricKey='metricKey', operator=GREATER_THAN, errorThreshold='2'}, " +
+    assertThat(underTest).hasToString("EvaluatedCondition{condition=Condition{metricKey='metricKey', " +
+      "operator=GREATER_THAN, errorThreshold='2'}, " +
+      "originalCondition=Condition{metricKey='metricKey', " +
+      "operator=GREATER_THAN, errorThreshold='2'}, " +
       "status=ERROR, value=null}");
   }
 
index 2b0ce22d34d5393d8e215ef81e530da29a6d1f91..f076e163f5355047cf79ae489dff1cf1bd1a33b2 100644 (file)
@@ -24,7 +24,8 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Random;
 import org.apache.commons.lang3.RandomStringUtils;
-import org.junit.Test;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Metric.Level;
 
@@ -34,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.sonar.server.qualitygate.EvaluatedQualityGate.newBuilder;
 
-public class EvaluatedQualityGateTest {
+class EvaluatedQualityGateTest {
   private static final String QUALITY_GATE_ID = "qg_id";
   private static final String QUALITY_GATE_NAME = "qg_name";
   private static final QualityGate NO_CONDITION_QUALITY_GATE = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, emptySet());
@@ -55,7 +56,7 @@ public class EvaluatedQualityGateTest {
   private EvaluatedQualityGate.Builder builder = newBuilder();
 
   @Test
-  public void build_fails_with_NPE_if_status_not_set() {
+  void build_fails_with_NPE_if_status_not_set() {
     builder.setQualityGate(NO_CONDITION_QUALITY_GATE);
 
     assertThatThrownBy(() -> builder.build())
@@ -64,21 +65,21 @@ public class EvaluatedQualityGateTest {
   }
 
   @Test
-  public void addCondition_fails_with_NPE_if_condition_is_null() {
+  void addCondition_fails_with_NPE_if_condition_is_null() {
     assertThatThrownBy(() -> builder.addEvaluatedCondition(null, EvaluatedCondition.EvaluationStatus.ERROR, "a_value"))
       .isInstanceOf(NullPointerException.class)
       .hasMessage("condition can't be null");
   }
 
   @Test
-  public void addCondition_fails_with_NPE_if_status_is_null() {
+  void addCondition_fails_with_NPE_if_status_is_null() {
     assertThatThrownBy(() -> builder.addEvaluatedCondition(new Condition("metric_key", Condition.Operator.LESS_THAN, "2"), null, "a_value"))
       .isInstanceOf(NullPointerException.class)
       .hasMessage("status can't be null");
   }
 
   @Test
-  public void addCondition_accepts_null_value() {
+  void addCondition_accepts_null_value() {
     builder.addEvaluatedCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.NO_VALUE, null);
 
     assertThat(builder.getEvaluatedConditions())
@@ -86,12 +87,12 @@ public class EvaluatedQualityGateTest {
   }
 
   @Test
-  public void getEvaluatedConditions_returns_empty_with_no_condition_added_to_builder() {
+  void getEvaluatedConditions_returns_empty_with_no_condition_added_to_builder() {
     assertThat(builder.getEvaluatedConditions()).isEmpty();
   }
 
   @Test
-  public void build_fails_with_IAE_if_condition_added_and_no_on_QualityGate() {
+  void build_fails_with_IAE_if_condition_added_and_no_on_QualityGate() {
     builder.setQualityGate(NO_CONDITION_QUALITY_GATE)
       .setStatus(randomStatus)
       .addEvaluatedCondition(CONDITION_1, randomEvaluationStatus, randomValue);
@@ -102,7 +103,7 @@ public class EvaluatedQualityGateTest {
   }
 
   @Test
-  public void build_fails_with_IAE_if_condition_is_missing_for_one_defined_in_QualityGate() {
+  void build_fails_with_IAE_if_condition_is_missing_for_one_defined_in_QualityGate() {
     builder.setQualityGate(ONE_CONDITION_QUALITY_GATE)
       .setStatus(randomStatus);
 
@@ -112,7 +113,16 @@ public class EvaluatedQualityGateTest {
   }
 
   @Test
-  public void getEvaluatedConditions_is_sorted() {
+  void build_whenConditionIsAFallback_shouldNotThrowException() {
+    builder.setQualityGate(ONE_CONDITION_QUALITY_GATE).setStatus(randomStatus)
+      .addEvaluatedCondition(new EvaluatedCondition(CONDITION_2, CONDITION_1, randomEvaluationStatus, randomValue, false));
+
+    Assertions.assertThat(builder.build()
+      .getEvaluatedConditions()).hasSize(1);
+  }
+
+  @Test
+  void getEvaluatedConditions_is_sorted() {
     EvaluatedQualityGate underTest = builder
       .setQualityGate(ALL_CONDITIONS_QUALITY_GATE)
       .setStatus(randomStatus)
@@ -128,7 +138,7 @@ public class EvaluatedQualityGateTest {
   }
 
   @Test
-  public void verify_getters() {
+  void verify_getters() {
     EvaluatedQualityGate underTest = builder
       .setQualityGate(ONE_CONDITION_QUALITY_GATE)
       .setStatus(randomStatus)
@@ -142,7 +152,7 @@ public class EvaluatedQualityGateTest {
   }
 
   @Test
-  public void verify_getters_when_no_condition() {
+  void verify_getters_when_no_condition() {
     EvaluatedQualityGate underTest = builder
       .setQualityGate(NO_CONDITION_QUALITY_GATE)
       .setStatus(randomStatus)
@@ -154,7 +164,7 @@ public class EvaluatedQualityGateTest {
   }
 
   @Test
-  public void verify_getters_when_multiple_conditions() {
+  void verify_getters_when_multiple_conditions() {
     QualityGate qualityGate = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2));
     EvaluatedQualityGate underTest = builder
       .setQualityGate(qualityGate)
@@ -171,7 +181,7 @@ public class EvaluatedQualityGateTest {
   }
 
   @Test
-  public void equals_is_based_on_all_fields() {
+  void equals_is_based_on_all_fields() {
     EvaluatedQualityGate.Builder builder = this.builder
       .setQualityGate(ONE_CONDITION_QUALITY_GATE)
       .setStatus(Level.ERROR)
@@ -193,7 +203,7 @@ public class EvaluatedQualityGateTest {
   }
 
   @Test
-  public void hashcode_is_based_on_all_fields() {
+  void hashcode_is_based_on_all_fields() {
     EvaluatedQualityGate.Builder builder = this.builder
       .setQualityGate(ONE_CONDITION_QUALITY_GATE)
       .setStatus(Level.ERROR)
index 31f22433cf3775d1d6f2026700ee1a181575c0e6..07af74eed75ac9a2c5c76d2e0a2bd0e07830dc08 100644 (file)
@@ -25,7 +25,7 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.config.internal.ConfigurationBridge;
@@ -34,25 +34,28 @@ import org.sonar.api.measures.Metric;
 
 import static java.util.Collections.singleton;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_LINES_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY;
 
-public class QualityGateEvaluatorImplTest {
+class QualityGateEvaluatorImplTest {
   private final MapSettings settings = new MapSettings();
   private final Configuration configuration = new ConfigurationBridge(settings);
-  private final QualityGateEvaluator underTest = new QualityGateEvaluatorImpl();
+  QualityGateFallbackManager qualityGateFallbackManager = mock(QualityGateFallbackManager.class);
+  private final QualityGateEvaluator underTest = new QualityGateEvaluatorImpl(qualityGateFallbackManager);
 
   @Test
-  public void getMetricKeys_includes_by_default_new_lines() {
+  void getMetricKeys_includes_by_default_new_lines() {
     QualityGate gate = mock(QualityGate.class);
     assertThat(underTest.getMetricKeys(gate)).containsExactly(NEW_LINES_KEY);
   }
 
   @Test
-  public void getMetricKeys_includes_metrics_from_qgate() {
+  void getMetricKeys_includes_metrics_from_qgate() {
     Set<String> metricKeys = ImmutableSet.of("foo", "bar", "baz");
     Set<Condition> conditions = metricKeys.stream().map(key -> {
       Condition condition = mock(Condition.class);
@@ -66,7 +69,40 @@ public class QualityGateEvaluatorImplTest {
   }
 
   @Test
-  public void evaluated_conditions_are_sorted() {
+  void getMetricKeys_shouldIncludeFallbackConditionsMetricKeys() {
+    Set<String> metricKeys = ImmutableSet.of("foo", "bar", "baz");
+    when(qualityGateFallbackManager.getFallbackCondition(any())).thenReturn(Optional.of(new Condition("fallback", Condition.Operator.GREATER_THAN, "0")));
+    Set<Condition> conditions = metricKeys.stream().map(key -> {
+      Condition condition = mock(Condition.class);
+      when(condition.getMetricKey()).thenReturn(key);
+      return condition;
+    }).collect(Collectors.toSet());
+
+    QualityGate gate = mock(QualityGate.class);
+    when(gate.getConditions()).thenReturn(conditions);
+    assertThat(underTest.getMetricKeys(gate)).containsAll(metricKeys);
+    assertThat(underTest.getMetricKeys(gate)).contains("fallback");
+  }
+
+  @Test
+  void evaluate_whenConditionHasNoDataAndHasFallBack_shouldEvaluateFallbackInstead() {
+    when(qualityGateFallbackManager.getFallbackCondition(any())).thenReturn(Optional.of(new Condition("fallback", Condition.Operator.GREATER_THAN, "0")));
+    Condition condition = mock(Condition.class);
+    when(condition.getMetricKey()).thenReturn("foo");
+
+    QualityGate gate = mock(QualityGate.class);
+    when(gate.getConditions()).thenReturn(Set.of(condition));
+    QualityGateEvaluator.Measures measures = mock(QualityGateEvaluator.Measures.class);
+    when(measures.get("foo")).thenReturn(Optional.empty());
+    when(measures.get("fallback")).thenReturn(Optional.of(new FakeMeasure(1)));
+
+    assertThat(underTest.evaluate(gate, measures, configuration).getEvaluatedConditions())
+      .extracting(x -> x.getCondition().getMetricKey(), x -> x.getOriginalCondition().getMetricKey())
+      .containsExactly(tuple("fallback", "foo"));
+  }
+
+  @Test
+  void evaluated_conditions_are_sorted() {
     Set<String> metricKeys = ImmutableSet.of("foo", "bar", NEW_MAINTAINABILITY_RATING_KEY);
     Set<Condition> conditions = metricKeys.stream().map(key -> {
       Condition condition = mock(Condition.class);
@@ -79,11 +115,11 @@ public class QualityGateEvaluatorImplTest {
     QualityGateEvaluator.Measures measures = mock(QualityGateEvaluator.Measures.class);
 
     assertThat(underTest.evaluate(gate, measures, configuration).getEvaluatedConditions()).extracting(x -> x.getCondition().getMetricKey())
-    .containsExactly(NEW_MAINTAINABILITY_RATING_KEY, "bar", "foo");
+      .containsExactly(NEW_MAINTAINABILITY_RATING_KEY, "bar", "foo");
   }
 
   @Test
-  public void evaluate_is_OK_for_empty_qgate() {
+  void evaluate_is_OK_for_empty_qgate() {
     QualityGate gate = mock(QualityGate.class);
     QualityGateEvaluator.Measures measures = mock(QualityGateEvaluator.Measures.class);
     EvaluatedQualityGate evaluatedQualityGate = underTest.evaluate(gate, measures, configuration);
@@ -91,7 +127,7 @@ public class QualityGateEvaluatorImplTest {
   }
 
   @Test
-  public void evaluate_is_ERROR() {
+  void evaluate_is_ERROR() {
     Condition condition = new Condition(NEW_MAINTAINABILITY_RATING_KEY, Condition.Operator.GREATER_THAN, "0");
 
     QualityGate gate = mock(QualityGate.class);
@@ -102,7 +138,7 @@ public class QualityGateEvaluatorImplTest {
   }
 
   @Test
-  public void evaluate_for_small_changes() {
+  void evaluate_for_small_changes() {
     Condition condition = new Condition(NEW_DUPLICATED_LINES_KEY, Condition.Operator.GREATER_THAN, "0");
 
     Map<String, QualityGateEvaluator.Measure> notSmallChange = new HashMap<>();
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateFallbackManagerTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateFallbackManagerTest.java
new file mode 100644 (file)
index 0000000..2552761
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.qualitygate;
+
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.core.metric.SoftwareQualitiesMetrics;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+class QualityGateFallbackManagerTest {
+
+  QualityGateFallbackManager underTest = new QualityGateFallbackManager();
+
+  @Test
+  void getFallbackCondition_whenConditionIsSoftwareQualityMetricWithEquivalence_shouldReturnExpectedCondition() {
+    Condition condition = new Condition(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, Condition.Operator.GREATER_THAN, "3");
+    Optional<Condition> fallbackCondition = underTest.getFallbackCondition(condition);
+    assertThat(fallbackCondition).isPresent()
+      .get()
+      .extracting(Condition::getMetricKey, Condition::getOperator, Condition::getErrorThreshold)
+      .containsExactly(CoreMetrics.SECURITY_RATING_KEY, Condition.Operator.GREATER_THAN, "3");
+  }
+
+  @Test
+  void getFallbackCondition_whenConditionIsStandardMetricWithEquivalence_shouldNotReturnCondition() {
+    Condition condition = new Condition(CoreMetrics.SECURITY_RATING_KEY, Condition.Operator.GREATER_THAN, "3");
+    Optional<Condition> fallbackCondition = underTest.getFallbackCondition(condition);
+    assertThat(fallbackCondition).isEmpty();
+  }
+
+  @Test
+  void getFallbackCondition_whenConditionIsAnyMetricWithoutEquivalence_shouldNotReturnCondition() {
+    Condition condition = new Condition(CoreMetrics.LINES_KEY, Condition.Operator.GREATER_THAN, "3");
+    Optional<Condition> fallbackCondition = underTest.getFallbackCondition(condition);
+    assertThat(fallbackCondition).isEmpty();
+  }
+
+}
index 41490914c5b154ee97dcac7e9a1836a56ef5bf62..42c23ed78edb83c5c937b3acbaa802d796ad1036 100644 (file)
@@ -39,7 +39,7 @@ import org.sonar.db.qualitygate.QualityGateConditionDto;
 import org.sonar.db.qualitygate.QualityGateDto;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.measure.Rating;
-import org.sonar.server.qualitygate.ws.StandardToMQRMetrics;
+import org.sonar.server.metric.StandardToMQRMetrics;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
 import static java.lang.Double.parseDouble;
index e664db04910569a560ca8d1db7de279740e5fbe0..413aabad2db6bee6fa478663d1f044401dacc4f2 100644 (file)
@@ -23,7 +23,7 @@ import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.sonar.db.metric.MetricDto;
-import org.sonar.server.qualitygate.ws.StandardToMQRMetrics;
+import org.sonar.server.metric.StandardToMQRMetrics;
 
 public class QualityGateModeChecker {
 
index 53b6a93cc3825afd8c024f4bf13d3c3911831f6f..24c4eb83a215f2ff84c11def512705cb6c79960d 100644 (file)
@@ -30,6 +30,7 @@ public class QualityGateModule extends Module {
       QualityGateModeChecker.class,
       QualityGateConditionsUpdater.class,
       QualityGateFinder.class,
-      QualityGateEvaluatorImpl.class);
+      QualityGateEvaluatorImpl.class,
+      QualityGateFallbackManager.class);
   }
 }
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/StandardToMQRMetrics.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/StandardToMQRMetrics.java
deleted file mode 100644 (file)
index e50a91c..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.qualitygate.ws;
-
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import java.util.Optional;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.core.metric.SoftwareQualitiesMetrics;
-
-import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
-import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY;
-import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_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_INFO_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.SQALE_RATING_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_BLOCKER_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_HIGH_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_INFO_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_LOW_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MEDIUM_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_BLOCKER_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_HIGH_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_INFO_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_LOW_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MEDIUM_ISSUES_KEY;
-
-/**
- * Defines the metrics mapping between the standard mode and the MQR mode.
- * This list all the metrics that are specific for each mode, and the equivalent metric in the other mode.
- */
-public class StandardToMQRMetrics {
-  private static final BiMap<String, String> STANDARD_TO_MQR_MODE_METRICS;
-
-  private static final BiMap<String, String> MQR_TO_STANDARD_MODE_METRICS;
-
-  static {
-    STANDARD_TO_MQR_MODE_METRICS = HashBiMap.create();
-    // Severity related metrics
-    STANDARD_TO_MQR_MODE_METRICS.put(BLOCKER_VIOLATIONS_KEY, SOFTWARE_QUALITY_BLOCKER_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(NEW_BLOCKER_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_BLOCKER_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CRITICAL_VIOLATIONS_KEY, SOFTWARE_QUALITY_HIGH_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(NEW_CRITICAL_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_HIGH_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(MAJOR_VIOLATIONS_KEY, SOFTWARE_QUALITY_MEDIUM_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(NEW_MAJOR_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_MEDIUM_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(MINOR_VIOLATIONS_KEY, SOFTWARE_QUALITY_LOW_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(NEW_MINOR_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_LOW_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(INFO_VIOLATIONS_KEY, SOFTWARE_QUALITY_INFO_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(NEW_INFO_VIOLATIONS_KEY, NEW_SOFTWARE_QUALITY_INFO_ISSUES_KEY);
-
-    // Maintainability related metrics
-    STANDARD_TO_MQR_MODE_METRICS.put(CODE_SMELLS_KEY, SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(NEW_CODE_SMELLS_KEY, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(SQALE_RATING_KEY, SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.TECHNICAL_DEBT_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_TECHNICAL_DEBT_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.SQALE_DEBT_RATIO_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO_KEY);
-
-    STANDARD_TO_MQR_MODE_METRICS.put(EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY, EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A_KEY);
-
-    // Security related metrics
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.SECURITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_SECURITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.SECURITY_REMEDIATION_EFFORT_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.VULNERABILITIES_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_VULNERABILITIES_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_ISSUES_KEY);
-
-    // Reliability related metrics
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.BUGS_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_ISSUES_KEY);
-    STANDARD_TO_MQR_MODE_METRICS.put(CoreMetrics.NEW_BUGS_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_ISSUES_KEY);
-
-    MQR_TO_STANDARD_MODE_METRICS = STANDARD_TO_MQR_MODE_METRICS.inverse();
-  }
-
-  private StandardToMQRMetrics() {
-  }
-
-  public static boolean isStandardMetric(String metricKey) {
-    return STANDARD_TO_MQR_MODE_METRICS.containsKey(metricKey);
-  }
-
-  public static boolean isMQRMetric(String metricKey) {
-    return MQR_TO_STANDARD_MODE_METRICS.containsKey(metricKey);
-  }
-
-  /**
-   * Retrieves equivalent metric in the other mode. Return empty if metric has no equivalence
-   */
-  public static Optional<String> getEquivalentMetric(String metricKey) {
-    return Optional.ofNullable(STANDARD_TO_MQR_MODE_METRICS.get(metricKey))
-      .or(() -> Optional.ofNullable(MQR_TO_STANDARD_MODE_METRICS.get(metricKey)));
-  }
-}
index 1c785a45dd1b3c619cb8f7fc7d3ac97f54aa95b3..4579de0b93664b83ebd2602885c72030b30956a9 100644 (file)
@@ -29,6 +29,6 @@ public class QualityGateModuleTest {
   public void verify_count_of_added_components() {
     ListContainer container = new ListContainer();
     new QualityGateModule().configure(container);
-    assertThat(container.getAddedObjects()).hasSize(6);
+    assertThat(container.getAddedObjects()).hasSize(7);
   }
 }
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/StandardToMQRMetricsTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/StandardToMQRMetricsTest.java
deleted file mode 100644 (file)
index c779311..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.qualitygate.ws;
-
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.core.metric.SoftwareQualitiesMetrics;
-
-class StandardToMQRMetricsTest {
-
-  @Test
-  void isStandardMetric_shouldReturnExpectedResult() {
-    Assertions.assertThat(StandardToMQRMetrics.isStandardMetric(CoreMetrics.RELIABILITY_RATING_KEY)).isTrue();
-    Assertions.assertThat(StandardToMQRMetrics.isMQRMetric(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_HIGH_ISSUES_KEY)).isTrue();
-
-    Assertions.assertThat(StandardToMQRMetrics.isMQRMetric(CoreMetrics.SECURITY_RATING_KEY)).isFalse();
-    Assertions.assertThat(StandardToMQRMetrics.isStandardMetric(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY)).isFalse();
-  }
-
-  @Test
-  void getEquivalentMetric_shouldReturnExpectedResult() {
-    Assertions.assertThat(StandardToMQRMetrics.getEquivalentMetric(CoreMetrics.COMMENT_LINES_DENSITY_KEY)).isEmpty();
-    Assertions.assertThat(StandardToMQRMetrics.getEquivalentMetric(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY)).hasValue(CoreMetrics.RELIABILITY_RATING_KEY);
-    Assertions.assertThat(StandardToMQRMetrics.getEquivalentMetric(CoreMetrics.RELIABILITY_RATING_KEY)).hasValue(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY);
-  }
-
-}