aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch
diff options
context:
space:
mode:
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>2014-04-24 17:09:41 +0200
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>2014-04-24 17:21:30 +0200
commit631797e48ccb77632bc5e6f4f2256561b1aa59f9 (patch)
treef2e3a5b77dd32c7a97364e635e685fc99aed5305 /sonar-batch
parent722b816695bbddcdc454063b0fc9386d8d1a2c4f (diff)
downloadsonarqube-631797e48ccb77632bc5e6f4f2256561b1aa59f9.tar.gz
sonarqube-631797e48ccb77632bc5e6f4f2256561b1aa59f9.zip
SONAR-4927 Store quality gate details in dedicated measure
Diffstat (limited to 'sonar-batch')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java16
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateDetails.java93
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java18
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java35
4 files changed, 139 insertions, 23 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java
index 7515efc5b5a..59b3209dfef 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java
@@ -178,22 +178,24 @@ class ConditionUtils {
return metric.getType() == Metric.ValueType.WORK_DUR;
}
- private static Double getValue(ResolvedCondition condition, Measure measure) {
+ static Double getValue(ResolvedCondition condition, Measure measure) {
Integer period = condition.period();
+ Double value;
if (period == null) {
- return measure.getValue();
+ value = measure.getValue();
} else if (period == 1) {
- return measure.getVariation1();
+ value = measure.getVariation1();
} else if (period == 2) {
- return measure.getVariation2();
+ value = measure.getVariation2();
} else if (period == 3) {
- return measure.getVariation3();
+ value = measure.getVariation3();
} else if (period == 4) {
- return measure.getVariation4();
+ value = measure.getVariation4();
} else if (period == 5) {
- return measure.getVariation5();
+ value = measure.getVariation5();
} else {
throw new IllegalStateException("Following index period is not allowed : " + Double.toString(period));
}
+ return value;
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateDetails.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateDetails.java
new file mode 100644
index 00000000000..ca85bdc85d8
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateDetails.java
@@ -0,0 +1,93 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.qualitygate;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.inject.internal.util.Lists;
+import org.sonar.api.measures.Metric.Level;
+
+import java.util.List;
+
+/**
+ * Holds the details of a quality gate evaluation (status per condition)
+ *
+ * @since 4.4
+ */
+class QualityGateDetails {
+
+ private static final String FIELD_LEVEL = "level";
+
+ private Level level = Level.OK;
+
+ private List<EvaluatedCondition> conditions = Lists.newArrayList();
+
+ void setLevel(Level level) {
+ this.level = level;
+ }
+
+ void addCondition(ResolvedCondition condition, Level level, Double actualValue) {
+ conditions.add(new EvaluatedCondition(condition, level, actualValue));
+ }
+
+ String toJson() {
+ JsonObject details = new JsonObject();
+ details.addProperty(FIELD_LEVEL, level.toString());
+ JsonArray conditionResults = new JsonArray();
+ for (EvaluatedCondition condition: this.conditions) {
+ conditionResults.add(condition.toJson());
+ }
+ details.add("conditions", conditionResults);
+ return details.toString();
+ }
+
+ static class EvaluatedCondition {
+
+ private ResolvedCondition condition;
+
+ private Level level;
+
+ private String actualValue;
+
+ EvaluatedCondition(ResolvedCondition condition, Level level, Double actualValue) {
+ this.condition = condition;
+ this.level = level;
+ this.actualValue = actualValue == null ? "" : actualValue.toString();
+ }
+
+ JsonObject toJson() {
+ JsonObject result = new JsonObject();
+ result.addProperty("metric", condition.metricKey());
+ result.addProperty("op", condition.operator());
+ if (condition.period() != null) {
+ result.addProperty("period", condition.period());
+ }
+ if (condition.warningThreshold() != null) {
+ result.addProperty("warning", condition.warningThreshold());
+ }
+ if (condition.errorThreshold() != null) {
+ result.addProperty("error", condition.errorThreshold());
+ }
+ result.addProperty("actual", actualValue);
+ result.addProperty(FIELD_LEVEL, level.toString());
+ return result;
+ }
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java
index a0cc9b3e150..406ca87648c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java
@@ -108,6 +108,7 @@ public class QualityGateVerifier implements Decorator {
private void checkProjectConditions(Resource resource, DecoratorContext context) {
Metric.Level globalLevel = Metric.Level.OK;
+ QualityGateDetails details = new QualityGateDetails();
List<String> labels = Lists.newArrayList();
for (ResolvedCondition condition : qualityGate.conditions()) {
@@ -115,9 +116,6 @@ public class QualityGateVerifier implements Decorator {
if (measure != null) {
Metric.Level level = ConditionUtils.getLevel(condition, measure);
- /*
- * This should probably be done only after migration from alerts
- */
measure.setAlertStatus(level);
String text = getText(condition, level);
if (!StringUtils.isBlank(text)) {
@@ -133,6 +131,8 @@ public class QualityGateVerifier implements Decorator {
} else if (Metric.Level.ERROR == level) {
globalLevel = Metric.Level.ERROR;
}
+
+ details.addCondition(condition, level, ConditionUtils.getValue(condition, measure));
}
}
@@ -140,6 +140,12 @@ public class QualityGateVerifier implements Decorator {
globalMeasure.setAlertStatus(globalLevel);
globalMeasure.setAlertText(StringUtils.join(labels, ", "));
context.saveMeasure(globalMeasure);
+
+ details.setLevel(globalLevel);
+ Measure detailsMeasure = new Measure(CoreMetrics.QUALITY_GATE_DETAILS, details.toJson());
+ detailsMeasure.setAlertStatus(globalLevel);
+ context.saveMeasure(detailsMeasure);
+
}
private String getText(ResolvedCondition condition, Metric.Level level) {
@@ -175,12 +181,16 @@ public class QualityGateVerifier implements Decorator {
private String alertValue(ResolvedCondition condition, Metric.Level level) {
String value = level.equals(Metric.Level.ERROR) ? condition.errorThreshold() : condition.warningThreshold();
if (condition.metric().getType().equals(Metric.ValueType.WORK_DUR)) {
- return durations.format(Locale.ENGLISH, Duration.create(Long.parseLong(value)), Durations.DurationFormat.SHORT);
+ return formatDuration(value);
} else {
return value;
}
}
+ private String formatDuration(String value) {
+ return durations.format(Locale.ENGLISH, Duration.create(Long.parseLong(value)), Durations.DurationFormat.SHORT);
+ }
+
private String operatorLabel(String operator) {
return OPERATOR_LABELS.get(operator);
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
index f1671fe5ece..e79d27bc99d 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
@@ -19,6 +19,8 @@
*/
package org.sonar.batch.qualitygate;
+import org.sonar.api.measures.Metric.Level;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.commons.lang.NotImplementedException;
@@ -46,14 +48,8 @@ import java.util.ArrayList;
import java.util.Locale;
import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
public class QualityGateVerifierTest {
@@ -132,9 +128,16 @@ public class QualityGateVerifierTest {
verifier.decorate(project, context);
- verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.ALERT_STATUS, Metric.Level.OK.toString())));
verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.OK)));
verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.ALERT_STATUS, Metric.Level.OK.toString())));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_GATE_DETAILS, "{\"level\":\"OK\","
+ + "\"conditions\":"
+ + "["
+ + "{\"metric\":\"classes\",\"op\":\"GT\",\"warning\":\"20\",\"actual\":\"20.0\",\"level\":\"OK\"},"
+ + "{\"metric\":\"coverage\",\"op\":\"GT\",\"warning\":\"35.0\",\"actual\":\"35.0\",\"level\":\"OK\"}"
+ + "]"
+ + "}")));
}
@Test
@@ -394,12 +397,20 @@ public class QualityGateVerifierTest {
when(i18n.message(any(Locale.class), eq("metric.tech_debt.name"), anyString())).thenReturn("The Debt");
when(durations.format(any(Locale.class), eq(Duration.create(3600L)), eq(Durations.DurationFormat.SHORT))).thenReturn("1h");
- when(context.getMeasure(metric)).thenReturn(new Measure(metric, 1800d));
- ArrayList<ResolvedCondition> conditions = Lists.newArrayList(mockCondition(metric, QualityGateConditionDto.OPERATOR_LESS_THAN, "3600", null));
+ when(context.getMeasure(metric)).thenReturn(new Measure(metric, 7200d));
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(mockCondition(metric, QualityGateConditionDto.OPERATOR_GREATER_THAN, "3600", null));
when(qualityGate.conditions()).thenReturn(conditions);
verifier.decorate(project, context);
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "The Debt < 1h")));
+ // First call to saveMeasure is for the update of debt
+ verify(index).updateMeasure(eq(project), argThat(matchesMetric(metric, Level.ERROR, "The Debt > 1h")));
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "The Debt > 1h")));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_GATE_DETAILS, "{\"level\":\"ERROR\","
+ + "\"conditions\":"
+ + "["
+ + "{\"metric\":\"tech_debt\",\"op\":\"GT\",\"error\":\"3600\",\"actual\":\"7200.0\",\"level\":\"ERROR\"}"
+ + "]"
+ + "}")));
}
private ArgumentMatcher<Measure> matchesMetric(final Metric metric, final Metric.Level alertStatus, final String alertText) {