@@ -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(); | |||
} | |||
@@ -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> { |
@@ -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); | |||
} | |||
@@ -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") |
@@ -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; |
@@ -1,6 +1,7 @@ | |||
{ | |||
"projectStatus": { | |||
"status": "ERROR", | |||
"ignoredConditions": false, | |||
"conditions": [ | |||
{ | |||
"status": "ERROR", |
@@ -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}"); | |||
} | |||
} |
@@ -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) |
@@ -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() { |
@@ -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()); |
@@ -32,6 +32,7 @@ message ProjectStatusWsResponse { | |||
optional Status status = 1; | |||
repeated Condition conditions = 2; | |||
repeated Period periods = 3; | |||
optional bool ignoredConditions = 4; | |||
} | |||
message Condition { |
@@ -1017,4 +1017,4 @@ | |||
2,user2,2014-04-01 | |||
2,user2,2014-04-01 | |||
2,user2,2014-04-01 | |||
3,user3,2014-04-02 | |||
3,user3,2014-04-03 |
@@ -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"); | |||
} | |||
} |