3 * Copyright (C) 2009-2017 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.computation.task.projectanalysis.step;
22 import com.google.common.base.Optional;
23 import java.util.Collections;
25 import java.util.Objects;
26 import javax.annotation.Nullable;
27 import org.assertj.core.api.AbstractAssert;
28 import org.junit.Before;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.junit.rules.ExpectedException;
32 import org.mockito.invocation.InvocationOnMock;
33 import org.mockito.stubbing.Answer;
34 import org.sonar.api.measures.CoreMetrics;
35 import org.sonar.server.computation.task.projectanalysis.component.Component;
36 import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
37 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
38 import org.sonar.server.computation.task.projectanalysis.measure.Measure;
39 import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepositoryRule;
40 import org.sonar.server.computation.task.projectanalysis.measure.qualitygatedetails.EvaluatedCondition;
41 import org.sonar.server.computation.task.projectanalysis.measure.qualitygatedetails.QualityGateDetailsData;
42 import org.sonar.server.computation.task.projectanalysis.metric.Metric;
43 import org.sonar.server.computation.task.projectanalysis.metric.MetricImpl;
44 import org.sonar.server.computation.task.projectanalysis.metric.MetricRepositoryRule;
45 import org.sonar.server.computation.task.projectanalysis.qualitygate.Condition;
46 import org.sonar.server.computation.task.projectanalysis.qualitygate.ConditionStatus;
47 import org.sonar.server.computation.task.projectanalysis.qualitygate.EvaluationResult;
48 import org.sonar.server.computation.task.projectanalysis.qualitygate.EvaluationResultTextConverter;
49 import org.sonar.server.computation.task.projectanalysis.qualitygate.MutableQualityGateStatusHolderRule;
50 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate;
51 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateHolderRule;
52 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateStatus;
53 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateStatusHolder;
55 import static com.google.common.collect.ImmutableList.of;
56 import static org.mockito.Matchers.any;
57 import static org.mockito.Mockito.mock;
58 import static org.mockito.Mockito.when;
59 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
60 import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;
61 import static org.sonar.server.computation.task.projectanalysis.measure.Measure.Level.ERROR;
62 import static org.sonar.server.computation.task.projectanalysis.measure.Measure.Level.OK;
63 import static org.sonar.server.computation.task.projectanalysis.measure.Measure.Level.WARN;
64 import static org.sonar.server.computation.task.projectanalysis.measure.MeasureAssert.assertThat;
66 public class QualityGateMeasuresStepTest {
67 private static final MetricImpl INT_METRIC_1 = createIntMetric(1);
68 private static final String INT_METRIC_1_KEY = INT_METRIC_1.getKey();
69 private static final MetricImpl INT_METRIC_2 = createIntMetric(2);
70 private static final String INT_METRIC_2_KEY = INT_METRIC_2.getKey();
72 private static final int PROJECT_REF = 1;
73 private static final ReportComponent PROJECT_COMPONENT = ReportComponent.builder(Component.Type.PROJECT, PROJECT_REF).build();
74 private static final long SOME_QG_ID = 7521551;
75 private static final String SOME_QG_NAME = "name";
78 public ExpectedException expectedException = ExpectedException.none();
80 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
82 public QualityGateHolderRule qualityGateHolder = new QualityGateHolderRule();
84 public MutableQualityGateStatusHolderRule qualityGateStatusHolder = new MutableQualityGateStatusHolderRule();
86 public MetricRepositoryRule metricRepository = new MetricRepositoryRule();
88 public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
90 private EvaluationResultTextConverter resultTextConverter = mock(EvaluationResultTextConverter.class);
91 private QualityGateMeasuresStep underTest = new QualityGateMeasuresStep(treeRootHolder, qualityGateHolder, qualityGateStatusHolder, measureRepository, metricRepository,
92 resultTextConverter, new SmallChangesetQualityGateSpecialCase(measureRepository, metricRepository));
97 .add(CoreMetrics.ALERT_STATUS)
98 .add(CoreMetrics.QUALITY_GATE_DETAILS)
101 treeRootHolder.setRoot(PROJECT_COMPONENT);
103 // mock response of asText to any argument to return the result of dumbResultTextAnswer method
104 when(resultTextConverter.asText(any(Condition.class), any(EvaluationResult.class))).thenAnswer(new Answer<String>() {
106 public String answer(InvocationOnMock invocation) throws Throwable {
107 Condition condition = (Condition) invocation.getArguments()[0];
108 EvaluationResult evaluationResult = (EvaluationResult) invocation.getArguments()[1];
109 return dumbResultTextAnswer(condition, evaluationResult.getLevel(), evaluationResult.getValue());
114 private static String dumbResultTextAnswer(Condition condition, Measure.Level level, Object value) {
115 return condition.toString() + level + value;
119 public void no_measure_if_tree_has_no_project() {
120 ReportComponent notAProjectComponent = ReportComponent.builder(Component.Type.MODULE, 1).build();
122 treeRootHolder.setRoot(notAProjectComponent);
126 measureRepository.getAddedRawMeasures(1).isEmpty();
130 public void no_measure_if_there_is_no_qualitygate() {
131 qualityGateHolder.setQualityGate(null);
135 measureRepository.getAddedRawMeasures(PROJECT_COMPONENT).isEmpty();
139 public void mutableQualityGateStatusHolder_is_not_populated_if_there_is_no_qualitygate() {
140 qualityGateHolder.setQualityGate(null);
144 expectedException.expect(IllegalStateException.class);
145 expectedException.expectMessage("Quality gate status has not been set yet");
147 qualityGateStatusHolder.getStatus();
151 public void new_measures_are_created_even_if_there_is_no_rawMeasure_for_metric_of_condition() {
152 Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
153 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition)));
157 Optional<Measure> addedRawMeasure = measureRepository.getAddedRawMeasure(PROJECT_COMPONENT, INT_METRIC_1_KEY);
159 assertThat(addedRawMeasure).isAbsent();
160 assertThat(getAlertStatusMeasure())
161 .hasQualityGateLevel(OK)
162 .hasQualityGateText("");
163 assertThat(getQGDetailsMeasure())
164 .hasValue(new QualityGateDetailsData(OK, Collections.<EvaluatedCondition>emptyList()).toJson());
166 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
167 .hasStatus(QualityGateStatus.OK)
168 .hasConditionCount(1)
169 .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.NO_VALUE, null);
173 public void rawMeasure_is_updated_if_present_and_new_measures_are_created_if_project_has_measure_for_metric_of_condition() {
175 Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
176 Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
178 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition)));
179 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
183 Optional<Measure> addedRawMeasure = measureRepository.getAddedRawMeasure(PROJECT_COMPONENT, INT_METRIC_1_KEY);
185 assertThat(addedRawMeasure)
186 .hasQualityGateLevel(OK)
187 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
188 assertThat(getAlertStatusMeasure())
189 .hasQualityGateLevel(OK)
190 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
191 assertThat(getQGDetailsMeasure().get())
192 .hasValue(new QualityGateDetailsData(OK, of(new EvaluatedCondition(equals2Condition, OK, rawValue))).toJson());
194 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
195 .hasStatus(QualityGateStatus.OK)
196 .hasConditionCount(1)
197 .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue));
201 public void new_measures_have_ERROR_level_if_at_least_one_updated_measure_has_ERROR_level() {
203 Condition equals1ErrorCondition = createEqualsCondition(INT_METRIC_1, "1", null);
204 Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1");
205 Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
207 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals1ErrorCondition, equals1WarningCondition)));
208 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
209 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_2_KEY, rawMeasure);
213 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
214 Optional<Measure> rawMeasure2 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_2_KEY);
216 assertThat(rawMeasure1.get())
217 .hasQualityGateLevel(ERROR)
218 .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue));
219 assertThat(rawMeasure2.get())
220 .hasQualityGateLevel(WARN)
221 .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
222 assertThat(getAlertStatusMeasure())
223 .hasQualityGateLevel(ERROR)
224 .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue) + ", "
225 + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
226 assertThat(getQGDetailsMeasure())
227 .hasValue(new QualityGateDetailsData(ERROR, of(
228 new EvaluatedCondition(equals1ErrorCondition, ERROR, rawValue),
229 new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson());
231 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
232 .hasStatus(QualityGateStatus.ERROR)
233 .hasConditionCount(2)
234 .hasCondition(equals1ErrorCondition, ConditionStatus.EvaluationStatus.ERROR, String.valueOf(rawValue))
235 .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue));
239 public void new_measures_have_WARNING_level_if_no_updated_measure_has_ERROR_level() {
241 Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
242 Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1");
243 Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
245 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition, equals1WarningCondition)));
246 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
247 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_2_KEY, rawMeasure);
251 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
252 Optional<Measure> rawMeasure2 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_2_KEY);
254 assertThat(rawMeasure1.get())
255 .hasQualityGateLevel(OK)
256 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
257 assertThat(rawMeasure2.get())
258 .hasQualityGateLevel(WARN)
259 .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
260 assertThat(getAlertStatusMeasure())
261 .hasQualityGateLevel(WARN)
262 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue) + ", "
263 + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
264 assertThat(getQGDetailsMeasure())
265 .hasValue(new QualityGateDetailsData(WARN, of(
266 new EvaluatedCondition(equals2Condition, OK, rawValue),
267 new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson());
269 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
270 .hasStatus(QualityGateStatus.WARN)
271 .hasConditionCount(2)
272 .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue))
273 .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue));
277 public void new_measure_has_ERROR_level_of_all_conditions_for_a_specific_metric_if_its_the_worst() {
279 Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
280 Condition periodCondition = createEqualsCondition(INT_METRIC_1, null, "2", true);
282 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(fixedCondition, periodCondition)));
283 Measure measure = newMeasureBuilder().create(rawValue, null);
284 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, measure);
288 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
289 assertThat(rawMeasure1.get())
290 .hasQualityGateLevel(ERROR)
291 .hasQualityGateText(dumbResultTextAnswer(fixedCondition, ERROR, rawValue));
295 public void new_measure_has_WARN_level_of_all_conditions_for_a_specific_metric_if_its_the_worst() {
297 Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
298 Condition periodCondition = createEqualsCondition(INT_METRIC_1, null, "2", true);
300 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(fixedCondition, periodCondition)));
301 Measure measure = newMeasureBuilder()
302 .setVariation(rawValue)
303 .create(rawValue, null);
304 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, measure);
308 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
309 assertThat(rawMeasure1.get())
310 .hasQualityGateLevel(WARN)
311 .hasQualityGateText(dumbResultTextAnswer(periodCondition, WARN, rawValue));
315 public void new_measure_has_condition_on_leak_period_when_all_conditions_on_specific_metric_has_same_QG_level() {
317 Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
318 Condition periodCondition = createEqualsCondition(INT_METRIC_1, "1", null, true);
320 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(fixedCondition, periodCondition)));
321 Measure measure = newMeasureBuilder()
322 .setVariation(rawValue)
323 .create(rawValue, null);
324 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, measure);
328 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
329 assertThat(rawMeasure1.get())
330 .hasQualityGateLevel(ERROR)
331 .hasQualityGateText(dumbResultTextAnswer(periodCondition, ERROR, rawValue));
334 private Measure getAlertStatusMeasure() {
335 return measureRepository.getAddedRawMeasure(PROJECT_REF, ALERT_STATUS_KEY).get();
338 private Optional<Measure> getQGDetailsMeasure() {
339 return measureRepository.getAddedRawMeasure(PROJECT_REF, CoreMetrics.QUALITY_GATE_DETAILS_KEY);
342 private static Condition createEqualsCondition(Metric metric, @Nullable String errorThreshold, @Nullable String warningThreshold) {
343 return new Condition(metric, Condition.Operator.EQUALS.getDbValue(), errorThreshold, warningThreshold, false);
346 private static Condition createEqualsCondition(Metric metric, @Nullable String errorThreshold, @Nullable String warningThreshold, boolean hasPeriod) {
347 return new Condition(metric, Condition.Operator.EQUALS.getDbValue(), errorThreshold, warningThreshold, hasPeriod);
350 private static MetricImpl createIntMetric(int index) {
351 return new MetricImpl(index, "metricKey" + index, "metricName" + index, Metric.MetricType.INT);
354 private static class QualityGateStatusHolderAssertions extends AbstractAssert<QualityGateStatusHolderAssertions, QualityGateStatusHolder> {
356 private QualityGateStatusHolderAssertions(QualityGateStatusHolder actual) {
357 super(actual, QualityGateStatusHolderAssertions.class);
360 public static QualityGateStatusHolderAssertions assertThat(QualityGateStatusHolder holder) {
361 return new QualityGateStatusHolderAssertions(holder);
364 public QualityGateStatusHolderAssertions hasStatus(QualityGateStatus status) {
365 if (actual.getStatus() != status) {
367 "Expected QualityGateStatusHolder to have global status <%s> but was <%s>",
368 status, actual.getStatus());
374 public QualityGateStatusHolderAssertions hasConditionCount(int count) {
375 int conditionCount = actual.getStatusPerConditions().size();
376 if (conditionCount != count) {
378 "Expected QualityGateStatusHolder to have <%s> conditions but it has <%s>",
379 count, conditionCount);
385 public QualityGateStatusHolderAssertions hasCondition(Condition condition, ConditionStatus.EvaluationStatus evaluationStatus, @Nullable String expectedValue) {
386 for (Map.Entry<Condition, ConditionStatus> entry : actual.getStatusPerConditions().entrySet()) {
387 if (entry.getKey() == condition) {
388 ConditionStatus.EvaluationStatus actualStatus = entry.getValue().getStatus();
389 if (actualStatus != evaluationStatus) {
391 "Expected Status of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
392 condition, evaluationStatus, actualStatus);
394 String actualValue = entry.getValue().getValue();
395 if (!Objects.equals(expectedValue, actualValue)) {
397 "Expected Value of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
398 condition, expectedValue, actualValue);
405 "Expected QualityGateStatusHolder to have an entry for <%s> but none was found",