]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9352 add "ignoredConditions" to response example of api/qualitygates/project_status
authorDaniel Schwarz <daniel.schwarz@sonarsource.com>
Wed, 4 Oct 2017 07:42:38 +0000 (09:42 +0200)
committerDaniel Schwarz <bartfastiel@users.noreply.github.com>
Sat, 7 Oct 2017 07:06:25 +0000 (09:06 +0200)
13 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/qualitygatedetails/QualityGateDetailsData.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateMeasuresStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/SmallChangesetQualityGateSpecialCase.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java
server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/project_status-example.json
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/qualitygatedetails/QualityGateDetailsDataTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateMeasuresStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/SmallChangesetQualityGateSpecialCaseTest.java
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java
sonar-ws/src/main/protobuf/ws-qualitygates.proto
tests/projects/qualitygate/small-changesets/v2-1020-lines/src/sample/Sample.xoo.scm
tests/src/test/java/org/sonarqube/tests/qualityGate/QualityGateForSmallChangesetsTest.java

index 4c4af01b9772e771b09edf0ccc056cc17619cf59..e157d5aa6508b65c72dde26b40aab214ff0acdbd 100644 (file)
@@ -32,13 +32,16 @@ import static java.util.Objects.requireNonNull;
 @Immutable
 public class QualityGateDetailsData {
   private static final String FIELD_LEVEL = "level";
+  private static final String FIELD_IGNORED_CONDITIONS = "ignoredConditions";
 
   private final Measure.Level level;
   private final List<EvaluatedCondition> conditions;
+  private final boolean ignoredConditions;
 
-  public QualityGateDetailsData(Measure.Level level, Iterable<EvaluatedCondition> conditions) {
+  public QualityGateDetailsData(Measure.Level level, Iterable<EvaluatedCondition> conditions, boolean ignoredConditions) {
     this.level = requireNonNull(level);
     this.conditions = from(conditions).toList();
+    this.ignoredConditions = ignoredConditions;
   }
 
   public String toJson() {
@@ -49,6 +52,7 @@ public class QualityGateDetailsData {
       conditionResults.add(toJson(condition));
     }
     details.add("conditions", conditionResults);
+    details.addProperty(FIELD_IGNORED_CONDITIONS, ignoredConditions);
     return details.toString();
   }
 
index 22a0b04aa2f84ec414f5d26d36b7f9e5840dad2f..8bfffcac9318ec3ccfd59da09b47b5bc1afc19cd 100644 (file)
@@ -174,6 +174,7 @@ public class QualityGateMeasuresStep implements ComputationStep {
 
   private void updateMeasures(Component project, Set<Condition> conditions, QualityGateDetailsDataBuilder builder) {
     Multimap<Metric, Condition> conditionsPerMetric = conditions.stream().collect(MoreCollectors.index(Condition::getMetric, java.util.function.Function.identity()));
+    boolean ignoredConditions = false;
     for (Map.Entry<Metric, Collection<Condition>> entry : conditionsPerMetric.asMap().entrySet()) {
       Metric metric = entry.getKey();
       Optional<Measure> measure = measureRepository.getRawMeasure(project, metric);
@@ -182,7 +183,13 @@ public class QualityGateMeasuresStep implements ComputationStep {
       }
 
       final MetricEvaluationResult metricEvaluationResult = evaluateQualityGate(measure.get(), entry.getValue());
-      final MetricEvaluationResult finalMetricEvaluationResult = smallChangesetQualityGateSpecialCase.applyIfNeeded(project, metricEvaluationResult);
+      final MetricEvaluationResult finalMetricEvaluationResult;
+      if (smallChangesetQualityGateSpecialCase.appliesTo(project, metricEvaluationResult)) {
+        finalMetricEvaluationResult = smallChangesetQualityGateSpecialCase.apply(metricEvaluationResult);
+        ignoredConditions = true;
+      } else {
+        finalMetricEvaluationResult = metricEvaluationResult;
+      }
       String text = evaluationResultTextConverter.asText(finalMetricEvaluationResult.condition, finalMetricEvaluationResult.evaluationResult);
       builder.addLabel(text);
 
@@ -193,6 +200,7 @@ public class QualityGateMeasuresStep implements ComputationStep {
 
       builder.addEvaluatedCondition(finalMetricEvaluationResult);
     }
+    builder.setIgnoredConditions(ignoredConditions);
   }
 
   private static MetricEvaluationResult evaluateQualityGate(Measure measure, Collection<Condition> conditions) {
@@ -214,7 +222,7 @@ public class QualityGateMeasuresStep implements ComputationStep {
     Metric metric = metricRepository.getByKey(CoreMetrics.ALERT_STATUS_KEY);
     measureRepository.add(project, metric, globalMeasure);
 
-    String detailMeasureValue = new QualityGateDetailsData(builder.getGlobalLevel(), builder.getEvaluatedConditions()).toJson();
+    String detailMeasureValue = new QualityGateDetailsData(builder.getGlobalLevel(), builder.getEvaluatedConditions(), builder.isIgnoredConditions()).toJson();
     Measure detailsMeasure = Measure.newMeasureBuilder().create(detailMeasureValue);
     Metric qgDetailsMetric = metricRepository.getByKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY);
     measureRepository.add(project, qgDetailsMetric, detailsMeasure);
@@ -229,6 +237,7 @@ public class QualityGateMeasuresStep implements ComputationStep {
     private Measure.Level globalLevel = Measure.Level.OK;
     private List<String> labels = new ArrayList<>();
     private List<EvaluatedCondition> evaluatedConditions = new ArrayList<>();
+    private boolean ignoredConditions;
 
     public Measure.Level getGlobalLevel() {
       return globalLevel;
@@ -259,6 +268,15 @@ public class QualityGateMeasuresStep implements ComputationStep {
     public List<EvaluatedCondition> getEvaluatedConditions() {
       return evaluatedConditions;
     }
+
+    public boolean isIgnoredConditions() {
+      return ignoredConditions;
+    }
+
+    public QualityGateDetailsDataBuilder setIgnoredConditions(boolean ignoredConditions) {
+      this.ignoredConditions = ignoredConditions;
+      return this;
+    }
   }
 
   private enum EvaluatedConditionToCondition implements Function<EvaluatedCondition, Condition> {
index 6f4a1224cd839d306cd8998c66bde0f7ed59fa85..fc0a3af48514d334b87687f79a4993b84bbe996d 100644 (file)
@@ -27,15 +27,16 @@ import org.sonar.server.computation.task.projectanalysis.measure.Measure;
 import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository;
 import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
 import org.sonar.server.computation.task.projectanalysis.qualitygate.EvaluationResult;
+import org.sonar.server.computation.task.projectanalysis.step.QualityGateMeasuresStep.MetricEvaluationResult;
 
 import static java.util.Arrays.asList;
 
 public class SmallChangesetQualityGateSpecialCase {
 
   /**
-   * Some metrics will only produce warnings (never errors) on very small change sets.
+   * Some metrics will be ignored on very small change sets.
    */
-  private static final Collection<String> METRICS_THAT_CAN_ONLY_PRODUCE_WARNINGS_ON_SMALL_CHANGESETS = asList(
+  private static final Collection<String> METRICS_TO_IGNORE_ON_SMALL_CHANGESETS = asList(
     CoreMetrics.NEW_COVERAGE_KEY,
     CoreMetrics.NEW_LINE_COVERAGE_KEY,
     CoreMetrics.NEW_BRANCH_COVERAGE_KEY,
@@ -53,24 +54,15 @@ public class SmallChangesetQualityGateSpecialCase {
     this.metricRepository = metricRepository;
   }
 
-  QualityGateMeasuresStep.MetricEvaluationResult applyIfNeeded(Component project, @Nullable QualityGateMeasuresStep.MetricEvaluationResult metricEvaluationResult) {
-    if (metricEvaluationResult == null) {
-      return metricEvaluationResult;
-    }
-    if (metricEvaluationResult.evaluationResult.getLevel() == Measure.Level.OK) {
-      return metricEvaluationResult;
-    }
-    if (!METRICS_THAT_CAN_ONLY_PRODUCE_WARNINGS_ON_SMALL_CHANGESETS.contains(metricEvaluationResult.condition.getMetric().getKey())) {
-      return metricEvaluationResult;
-    }
-    if (!isSmallChangeset(project)) {
-      return metricEvaluationResult;
-    }
-    return calculateModifiedResult(metricEvaluationResult);
+  public boolean appliesTo(Component project, @Nullable MetricEvaluationResult metricEvaluationResult) {
+    return metricEvaluationResult != null
+      && metricEvaluationResult.evaluationResult.getLevel() != Measure.Level.OK
+      && METRICS_TO_IGNORE_ON_SMALL_CHANGESETS.contains(metricEvaluationResult.condition.getMetric().getKey())
+      && isSmallChangeset(project);
   }
 
-  private QualityGateMeasuresStep.MetricEvaluationResult calculateModifiedResult(@Nullable QualityGateMeasuresStep.MetricEvaluationResult metricEvaluationResult) {
-    return new QualityGateMeasuresStep.MetricEvaluationResult(
+  MetricEvaluationResult apply(MetricEvaluationResult metricEvaluationResult) {
+    return new MetricEvaluationResult(
       new EvaluationResult(Measure.Level.OK, metricEvaluationResult.evaluationResult.getValue()), metricEvaluationResult.condition);
   }
 
index d61184b3d9a634a969e2826b1d8660d5e8448553..37b98e830a62d6898af2d7322ce4fc7617cc0684 100644 (file)
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.server.ws.Change;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
@@ -87,7 +88,10 @@ public class ProjectStatusAction implements QualityGatesWsAction {
         "</ul>", QG_STATUSES_ONE_LINE, ProjectStatusWsResponse.Status.NONE))
       .setResponseExample(getClass().getResource("project_status-example.json"))
       .setSince("5.3")
-      .setHandler(this);
+      .setHandler(this)
+      .setChangelog(
+        new Change("6.4", "The field 'ignoredConditions' is added to the response")
+      );
 
     action.createParam(PARAM_ANALYSIS_ID)
       .setDescription("Analysis id")
index b45804f50e7c01122b2b3ecb0d73ab42e7a843af..6c6a9508142ed28dc7ff24b12ab50e86ab2c120e 100644 (file)
@@ -55,12 +55,22 @@ public class QualityGateDetailsFormatter {
     ProjectStatusWsResponse.Status qualityGateStatus = measureLevelToQualityGateStatus(json.get("level").getAsString());
     projectStatusBuilder.setStatus(qualityGateStatus);
 
+    formatIgnoredConditions(json);
     formatConditions(json.getAsJsonArray("conditions"));
     formatPeriods();
 
     return projectStatusBuilder.build();
   }
 
+  private void formatIgnoredConditions(JsonObject json) {
+    JsonElement ignoredConditions = json.get("ignoredConditions");
+    if (ignoredConditions != null) {
+      projectStatusBuilder.setIgnoredConditions(ignoredConditions.getAsBoolean());
+    } else {
+      projectStatusBuilder.setIgnoredConditions(false);
+    }
+  }
+
   private void formatPeriods() {
     if (!optionalSnapshot.isPresent()) {
       return;
index 31308d8f45ce530daff7e47db58370e28f86a80e..946f9c4fdb38d207bc080278d8f1e4ec1cf3369d 100644 (file)
@@ -1,6 +1,7 @@
 {
   "projectStatus": {
     "status": "ERROR",
+    "ignoredConditions": false,
     "conditions": [
       {
         "status": "ERROR",
index 46a97eb845ec53bc0fa6e9d7a476f509835aeb8b..ac2730c9d483982185210174948dd0cefb90ff10 100644 (file)
@@ -31,17 +31,17 @@ import org.sonar.test.JsonAssert;
 public class QualityGateDetailsDataTest {
   @Test(expected = NullPointerException.class)
   public void constructor_throws_NPE_if_Level_arg_is_null() {
-    new QualityGateDetailsData(null, Collections.<EvaluatedCondition>emptyList());
+    new QualityGateDetailsData(null, Collections.<EvaluatedCondition>emptyList(), false);
   }
 
   @Test(expected = NullPointerException.class)
   public void constructor_throws_NPE_if_Iterable_arg_is_null() {
-    new QualityGateDetailsData(Measure.Level.OK, null);
+    new QualityGateDetailsData(Measure.Level.OK, null, false);
   }
 
   @Test
   public void verify_json_when_there_is_no_condition() {
-    String actualJson = new QualityGateDetailsData(Measure.Level.OK, Collections.<EvaluatedCondition>emptyList()).toJson();
+    String actualJson = new QualityGateDetailsData(Measure.Level.OK, Collections.<EvaluatedCondition>emptyList(), false).toJson();
 
     JsonAssert.assertJson(actualJson).isSimilarTo("{" +
       "\"level\":\"OK\"," +
@@ -57,7 +57,7 @@ public class QualityGateDetailsDataTest {
       new EvaluatedCondition(condition, Measure.Level.OK, value),
       new EvaluatedCondition(condition, Measure.Level.WARN, value),
       new EvaluatedCondition(condition, Measure.Level.ERROR, value));
-    String actualJson = new QualityGateDetailsData(Measure.Level.OK, evaluatedConditions).toJson();
+    String actualJson = new QualityGateDetailsData(Measure.Level.OK, evaluatedConditions, false).toJson();
 
     JsonAssert.assertJson(actualJson).isSimilarTo("{" +
       "\"level\":\"OK\"," +
@@ -92,4 +92,14 @@ public class QualityGateDetailsDataTest {
       "]" +
       "}");
   }
+
+  @Test
+  public void verify_json_for_small_leak() {
+    String actualJson = new QualityGateDetailsData(Measure.Level.OK, Collections.<EvaluatedCondition>emptyList(), false).toJson();
+    JsonAssert.assertJson(actualJson).isSimilarTo("{\"ignoredConditions\": false}");
+
+    String actualJson2 = new QualityGateDetailsData(Measure.Level.OK, Collections.<EvaluatedCondition>emptyList(), true).toJson();
+    JsonAssert.assertJson(actualJson2).isSimilarTo("{\"ignoredConditions\": true}");
+  }
+
 }
index 1b6860e5d1fa0831f92762c44530b48faaa5ab80..92fdda5700e2bd25fe564b232ab555a21f75cfb6 100644 (file)
@@ -161,7 +161,7 @@ public class QualityGateMeasuresStepTest {
       .hasQualityGateLevel(OK)
       .hasQualityGateText("");
     assertThat(getQGDetailsMeasure())
-      .hasValue(new QualityGateDetailsData(OK, Collections.<EvaluatedCondition>emptyList()).toJson());
+      .hasValue(new QualityGateDetailsData(OK, Collections.<EvaluatedCondition>emptyList(), false).toJson());
 
     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
       .hasStatus(QualityGateStatus.OK)
@@ -189,7 +189,7 @@ public class QualityGateMeasuresStepTest {
       .hasQualityGateLevel(OK)
       .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
     assertThat(getQGDetailsMeasure().get())
-      .hasValue(new QualityGateDetailsData(OK, of(new EvaluatedCondition(equals2Condition, OK, rawValue))).toJson());
+      .hasValue(new QualityGateDetailsData(OK, of(new EvaluatedCondition(equals2Condition, OK, rawValue)), false).toJson());
 
     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
       .hasStatus(QualityGateStatus.OK)
@@ -226,7 +226,7 @@ public class QualityGateMeasuresStepTest {
     assertThat(getQGDetailsMeasure())
       .hasValue(new QualityGateDetailsData(ERROR, of(
         new EvaluatedCondition(equals1ErrorCondition, ERROR, rawValue),
-        new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson());
+        new EvaluatedCondition(equals1WarningCondition, WARN, rawValue)), false).toJson());
 
     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
       .hasStatus(QualityGateStatus.ERROR)
@@ -264,7 +264,7 @@ public class QualityGateMeasuresStepTest {
     assertThat(getQGDetailsMeasure())
       .hasValue(new QualityGateDetailsData(WARN, of(
         new EvaluatedCondition(equals2Condition, OK, rawValue),
-        new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson());
+        new EvaluatedCondition(equals1WarningCondition, WARN, rawValue)), false).toJson());
 
     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
       .hasStatus(QualityGateStatus.WARN)
index 0f41d0bf7cfae8f2e2f0ae5888d59920d79131ef..482ef242a6fb3ca96bb88c1ffefced46c87d46c2 100644 (file)
@@ -61,9 +61,9 @@ public class SmallChangesetQualityGateSpecialCaseTest {
     Component project = generateNewRootProject();
     measureRepository.addRawMeasure(PROJECT_REF, CoreMetrics.NEW_LINES_KEY, newMeasureBuilder().setVariation(19).create(1000));
 
-    QualityGateMeasuresStep.MetricEvaluationResult result = underTest.applyIfNeeded(project, metricEvaluationResult);
+    boolean result = underTest.appliesTo(project, metricEvaluationResult);
 
-    assertThat(result.evaluationResult.getLevel()).isSameAs(OK);
+    assertThat(result).isTrue();
   }
 
   @Test
@@ -72,9 +72,9 @@ public class SmallChangesetQualityGateSpecialCaseTest {
     Component project = generateNewRootProject();
     measureRepository.addRawMeasure(PROJECT_REF, CoreMetrics.NEW_LINES_KEY, newMeasureBuilder().setVariation(19).create(1000));
 
-    QualityGateMeasuresStep.MetricEvaluationResult result = underTest.applyIfNeeded(project, metricEvaluationResult);
+    boolean result = underTest.appliesTo(project, metricEvaluationResult);
 
-    assertThat(result.evaluationResult.getLevel()).isSameAs(OK);
+    assertThat(result).isTrue();
   }
 
   @Test
@@ -83,9 +83,9 @@ public class SmallChangesetQualityGateSpecialCaseTest {
     Component project = generateNewRootProject();
     measureRepository.addRawMeasure(PROJECT_REF, CoreMetrics.NEW_LINES_KEY, newMeasureBuilder().setVariation(20).create(1000));
 
-    QualityGateMeasuresStep.MetricEvaluationResult result = underTest.applyIfNeeded(project, metricEvaluationResult);
+    boolean result = underTest.appliesTo(project, metricEvaluationResult);
 
-    assertThat(result.evaluationResult.getLevel()).isSameAs(ERROR);
+    assertThat(result).isFalse();
   }
 
   @Test
@@ -94,9 +94,9 @@ public class SmallChangesetQualityGateSpecialCaseTest {
     Component project = generateNewRootProject();
     measureRepository.addRawMeasure(PROJECT_REF, CoreMetrics.NEW_LINES_KEY, newMeasureBuilder().setVariation(19).create(1000));
 
-    QualityGateMeasuresStep.MetricEvaluationResult result = underTest.applyIfNeeded(project, metricEvaluationResult);
+    boolean result = underTest.appliesTo(project, metricEvaluationResult);
 
-    assertThat(result.evaluationResult.getLevel()).isSameAs(ERROR);
+    assertThat(result).isFalse();
   }
 
   @Test
@@ -105,9 +105,9 @@ public class SmallChangesetQualityGateSpecialCaseTest {
     Component project = generateNewRootProject();
     measureRepository.addRawMeasure(PROJECT_REF, CoreMetrics.NEW_LINES_KEY, newMeasureBuilder().setVariation(19).create(1000));
 
-    QualityGateMeasuresStep.MetricEvaluationResult result = underTest.applyIfNeeded(project, metricEvaluationResult);
+    boolean result = underTest.appliesTo(project, metricEvaluationResult);
 
-    assertThat(result.evaluationResult.getLevel()).isSameAs(OK);
+    assertThat(result).isFalse();
   }
 
   @Test
@@ -115,17 +115,31 @@ public class SmallChangesetQualityGateSpecialCaseTest {
     QualityGateMeasuresStep.MetricEvaluationResult metricEvaluationResult = generateEvaluationResult(NEW_COVERAGE_KEY, ERROR);
     Component project = generateNewRootProject();
 
-    QualityGateMeasuresStep.MetricEvaluationResult result = underTest.applyIfNeeded(project, metricEvaluationResult);
+    boolean result = underTest.appliesTo(project, metricEvaluationResult);
 
-    assertThat(result.evaluationResult.getLevel()).isSameAs(ERROR);
+    assertThat(result).isFalse();
   }
 
   @Test
   public void should_silently_ignore_null_values() throws Exception {
 
-    QualityGateMeasuresStep.MetricEvaluationResult result = underTest.applyIfNeeded(mock(Component.class), null);
+    boolean result = underTest.appliesTo(mock(Component.class), null);
 
-    assertThat(result).isNull();
+    assertThat(result).isFalse();
+  }
+
+  @Test
+  public void apply() throws Exception {
+    Comparable<?> value = mock(Comparable.class);
+    Condition condition = mock(Condition.class);
+    QualityGateMeasuresStep.MetricEvaluationResult original = new QualityGateMeasuresStep.MetricEvaluationResult(
+      new EvaluationResult(Measure.Level.ERROR, value), condition);
+
+    QualityGateMeasuresStep.MetricEvaluationResult modified = underTest.apply(original);
+
+    assertThat(modified.evaluationResult.getLevel()).isSameAs(OK);
+    assertThat(modified.evaluationResult.getValue()).isSameAs(value);
+    assertThat(modified.condition).isSameAs(condition);
   }
 
   private Component generateNewRootProject() {
index 13305eb54db76d67b60c8b807f837049f10c68e7..8e96d3fd2d5806d1970bb21d813abea4274d1cee 100644 (file)
@@ -26,6 +26,8 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.server.ws.Change;
+import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
@@ -47,6 +49,7 @@ import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse.Status;
 
 import static java.lang.String.format;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
 import static org.sonar.db.component.SnapshotTesting.newAnalysis;
 import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
 import static org.sonar.db.metric.MetricTesting.newMetricDto;
@@ -77,6 +80,15 @@ public class ProjectStatusActionTest {
     ws = new WsActionTester(new ProjectStatusAction(dbClient, TestComponentFinder.from(db), userSession));
   }
 
+  @Test
+  public void definition() throws Exception {
+    WebService.Action def = ws.getDef();
+    assertThat(def.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("analysisId", "projectKey", "projectId");
+    assertThat(def.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactly(
+      tuple("6.4", "The field 'ignoredConditions' is added to the response")
+    );
+  }
+
   @Test
   public void json_example() throws IOException {
     ComponentDto project = db.components().insertPrivateProject(db.organizations().insert());
index 92d2be56d33bbf018769a6513e4cced4dbbc291b..8444dbac729b09452f49c8c124ea5eac2bd31d36 100644 (file)
@@ -32,6 +32,7 @@ message ProjectStatusWsResponse {
     optional Status status = 1;
     repeated Condition conditions = 2;
     repeated Period periods = 3;
+    optional bool ignoredConditions = 4;
   }
 
   message Condition {
index 07c227857e1657a3534db51ca8891f9c87d23573..98fd9a53ddad3adb143113d755b4a64201d78e3d 100644 (file)
 2,user2,2014-04-01
 2,user2,2014-04-01
 2,user2,2014-04-01
-3,user3,2014-04-02
\ No newline at end of file
+3,user3,2014-04-03
\ No newline at end of file
index 715b80cab80d6d569d5dafc47eeb48590716b828..ca4c99e47dc539d4bcfbc6656b8b3df98fbe0b43 100644 (file)
@@ -21,17 +21,29 @@ package org.sonarqube.tests.qualityGate;
 
 import com.sonar.orchestrator.Orchestrator;
 import com.sonar.orchestrator.build.SonarScanner;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Properties;
+import org.apache.commons.io.FileUtils;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonarqube.tests.Category6Suite;
 import org.sonarqube.tests.Tester;
+import org.sonarqube.ws.MediaTypes;
 import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsCe;
 import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
 import org.sonarqube.ws.WsQualityGates;
 import org.sonarqube.ws.WsQualityGates.CreateWsResponse;
 import org.sonarqube.ws.WsUsers;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsResponse;
 import org.sonarqube.ws.client.qualitygate.CreateConditionRequest;
+import org.sonarqube.ws.client.qualitygate.ProjectStatusWsRequest;
+import org.sonarqube.ws.client.qualitygate.UpdateConditionRequest;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static util.ItUtils.getMeasure;
@@ -64,6 +76,7 @@ public class QualityGateForSmallChangesetsTest {
     String password = "password1";
     WsUsers.CreateWsResponse.User user = tester.users().generateAdministrator(organization, u -> u.setPassword(password));
 
+    // no leak => use usual behaviour
     SonarScanner analysis = SonarScanner
       .create(projectDir("qualitygate/small-changesets/v1-1000-lines"))
       .setProperty("sonar.projectKey", project.getKey())
@@ -76,7 +89,9 @@ public class QualityGateForSmallChangesetsTest {
       .setDebugLogs(true);
     orchestrator.executeBuild(analysis);
     assertThat(getMeasure(orchestrator, project.getKey(), "alert_status").getValue()).isEqualTo("OK");
+    assertIgnoredConditions(project, "qualitygate/small-changesets/v1-1000-lines", false);
 
+    // small leak => ignore coverage warning or error
     SonarScanner analysis2 = SonarScanner
       .create(projectDir("qualitygate/small-changesets/v2-1019-lines"))
       .setProperty("sonar.projectKey", project.getKey())
@@ -89,7 +104,40 @@ public class QualityGateForSmallChangesetsTest {
       .setDebugLogs(true);
     orchestrator.executeBuild(analysis2);
     assertThat(getMeasure(orchestrator, project.getKey(), "alert_status").getValue()).isEqualTo("OK");
+    assertIgnoredConditions(project, "qualitygate/small-changesets/v2-1019-lines", true);
 
+    // small leak => if coverage is OK anyways, we do not have to ignore anything
+    tester.wsClient().qualityGates().updateCondition(UpdateConditionRequest.builder()
+      .setConditionId(condition.getId())
+      .setMetricKey("new_coverage")
+      .setOperator("LT")
+      .setWarning("10")
+      .setError("20")
+      .setPeriod(1)
+      .build());
+    SonarScanner analysis3 = SonarScanner
+      .create(projectDir("qualitygate/small-changesets/v2-1019-lines"))
+      .setProperty("sonar.projectKey", project.getKey())
+      .setProperty("sonar.organization", organization.getKey())
+      .setProperty("sonar.login", user.getLogin())
+      .setProperty("sonar.password", password)
+      .setProperty("sonar.scm.provider", "xoo")
+      .setProperty("sonar.scm.disabled", "false")
+      .setProperty("sonar.projectDate", "2014-04-02")
+      .setDebugLogs(true);
+    orchestrator.executeBuild(analysis3);
+    assertThat(getMeasure(orchestrator, project.getKey(), "alert_status").getValue()).isEqualTo("OK");
+    assertIgnoredConditions(project, "qualitygate/small-changesets/v2-1019-lines", false);
+
+    // big leak => use usual behaviour
+    tester.wsClient().qualityGates().updateCondition(UpdateConditionRequest.builder()
+      .setConditionId(condition.getId())
+      .setMetricKey("new_coverage")
+      .setOperator("LT")
+      .setWarning(null)
+      .setError("70")
+      .setPeriod(1)
+      .build());
     SonarScanner analysis4 = SonarScanner
       .create(projectDir("qualitygate/small-changesets/v2-1020-lines"))
       .setProperty("sonar.projectKey", project.getKey())
@@ -98,9 +146,40 @@ public class QualityGateForSmallChangesetsTest {
       .setProperty("sonar.password", password)
       .setProperty("sonar.scm.provider", "xoo")
       .setProperty("sonar.scm.disabled", "false")
-      .setProperty("sonar.projectDate", "2014-04-02")
+      .setProperty("sonar.projectDate", "2014-04-03")
       .setDebugLogs(true);
     orchestrator.executeBuild(analysis4);
     assertThat(getMeasure(orchestrator, project.getKey(), "alert_status").getValue()).isEqualTo("ERROR");
+    assertIgnoredConditions(project, "qualitygate/small-changesets/v2-1020-lines", false);
+  }
+
+  private void assertIgnoredConditions(Project project, String projectDir, boolean expected) throws IOException {
+    String analysisId = getAnalysisId(getTaskIdInLocalReport(projectDir(projectDir)));
+    boolean ignoredConditions = tester.wsClient().qualityGates()
+      .projectStatus(new ProjectStatusWsRequest().setAnalysisId(analysisId))
+      .getProjectStatus()
+      .getIgnoredConditions();
+    assertThat(ignoredConditions).isEqualTo(expected);
+  }
+
+  private String getAnalysisId(String taskId) throws IOException {
+    WsResponse activity = tester.wsClient()
+      .wsConnector()
+      .call(new GetRequest("api/ce/task")
+        .setParam("id", taskId)
+        .setMediaType(MediaTypes.PROTOBUF));
+    WsCe.TaskResponse activityWsResponse = WsCe.TaskResponse.parseFrom(activity.contentStream());
+    return activityWsResponse.getTask().getAnalysisId();
+  }
+
+  private String getTaskIdInLocalReport(File projectDirectory) throws IOException {
+    File metadata = new File(projectDirectory, ".sonar/report-task.txt");
+    assertThat(metadata).exists().isFile();
+    // verify properties
+    Properties props = new Properties();
+    props.load(new StringReader(FileUtils.readFileToString(metadata, StandardCharsets.UTF_8)));
+    assertThat(props.getProperty("ceTaskId")).isNotEmpty();
+
+    return props.getProperty("ceTaskId");
   }
 }