3 * Copyright (C) 2009-2016 SonarSource SA
4 * mailto:contact 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.TreeRootHolderRule;
36 import org.sonar.server.computation.task.projectanalysis.component.Component;
37 import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
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.period.Period;
46 import org.sonar.server.computation.task.projectanalysis.qualitygate.Condition;
47 import org.sonar.server.computation.task.projectanalysis.qualitygate.ConditionStatus;
48 import org.sonar.server.computation.task.projectanalysis.qualitygate.EvaluationResult;
49 import org.sonar.server.computation.task.projectanalysis.qualitygate.EvaluationResultTextConverter;
50 import org.sonar.server.computation.task.projectanalysis.qualitygate.MutableQualityGateStatusHolderRule;
51 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate;
52 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateHolderRule;
53 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateStatus;
54 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateStatusHolder;
56 import static com.google.common.collect.ImmutableList.of;
57 import static org.mockito.Matchers.any;
58 import static org.mockito.Mockito.mock;
59 import static org.mockito.Mockito.when;
60 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
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.Measure.newMeasureBuilder;
65 import static org.sonar.server.computation.task.projectanalysis.measure.MeasureAssert.assertThat;
66 import static org.sonar.server.computation.task.projectanalysis.measure.MeasureVariations.newMeasureVariationsBuilder;
68 public class QualityGateMeasuresStepTest {
69 private static final MetricImpl INT_METRIC_1 = createIntMetric(1);
70 private static final String INT_METRIC_1_KEY = INT_METRIC_1.getKey();
71 private static final MetricImpl INT_METRIC_2 = createIntMetric(2);
72 private static final String INT_METRIC_2_KEY = INT_METRIC_2.getKey();
74 private static final int PROJECT_REF = 1;
75 private static final ReportComponent PROJECT_COMPONENT = ReportComponent.builder(Component.Type.PROJECT, PROJECT_REF).build();
76 private static final long SOME_QG_ID = 7521551;
77 private static final String SOME_QG_NAME = "name";
80 public ExpectedException expectedException = ExpectedException.none();
82 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
84 public QualityGateHolderRule qualityGateHolder = new QualityGateHolderRule();
86 public MutableQualityGateStatusHolderRule qualityGateStatusHolder = new MutableQualityGateStatusHolderRule();
88 public MetricRepositoryRule metricRepository = new MetricRepositoryRule();
90 public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
92 private EvaluationResultTextConverter resultTextConverter = mock(EvaluationResultTextConverter.class);
93 private QualityGateMeasuresStep underTest = new QualityGateMeasuresStep(treeRootHolder, qualityGateHolder, qualityGateStatusHolder, measureRepository, metricRepository,
99 .add(CoreMetrics.ALERT_STATUS)
100 .add(CoreMetrics.QUALITY_GATE_DETAILS)
103 treeRootHolder.setRoot(PROJECT_COMPONENT);
105 // mock response of asText to any argument to return the result of dumbResultTextAnswer method
106 when(resultTextConverter.asText(any(Condition.class), any(EvaluationResult.class))).thenAnswer(new Answer<String>() {
108 public String answer(InvocationOnMock invocation) throws Throwable {
109 Condition condition = (Condition) invocation.getArguments()[0];
110 EvaluationResult evaluationResult = (EvaluationResult) invocation.getArguments()[1];
111 return dumbResultTextAnswer(condition, evaluationResult.getLevel(), evaluationResult.getValue());
116 private static String dumbResultTextAnswer(Condition condition, Measure.Level level, Object value) {
117 return condition.toString() + level + value;
121 public void no_measure_if_tree_has_no_project() {
122 ReportComponent notAProjectComponent = ReportComponent.builder(Component.Type.MODULE, 1).build();
124 treeRootHolder.setRoot(notAProjectComponent);
128 measureRepository.getAddedRawMeasures(1).isEmpty();
132 public void no_measure_if_there_is_no_qualitygate() {
133 qualityGateHolder.setQualityGate(null);
137 measureRepository.getAddedRawMeasures(PROJECT_COMPONENT).isEmpty();
141 public void mutableQualityGateStatusHolder_is_not_populated_if_there_is_no_qualitygate() {
142 qualityGateHolder.setQualityGate(null);
146 expectedException.expect(IllegalStateException.class);
147 expectedException.expectMessage("Quality gate status has not been set yet");
149 qualityGateStatusHolder.getStatus();
153 public void new_measures_are_created_even_if_there_is_no_rawMeasure_for_metric_of_condition() {
154 Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
155 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition)));
159 Optional<Measure> addedRawMeasure = measureRepository.getAddedRawMeasure(PROJECT_COMPONENT, INT_METRIC_1_KEY);
161 assertThat(addedRawMeasure).isAbsent();
162 assertThat(getAlertStatusMeasure())
163 .hasQualityGateLevel(OK)
164 .hasQualityGateText("");
165 assertThat(getQGDetailsMeasure())
166 .hasValue(new QualityGateDetailsData(OK, Collections.<EvaluatedCondition>emptyList()).toJson());
168 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
169 .hasStatus(QualityGateStatus.OK)
170 .hasConditionCount(1)
171 .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.NO_VALUE, null);
175 public void rawMeasure_is_updated_if_present_and_new_measures_are_created_if_project_has_measure_for_metric_of_condition() {
177 Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
178 Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
180 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition)));
181 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
185 Optional<Measure> addedRawMeasure = measureRepository.getAddedRawMeasure(PROJECT_COMPONENT, INT_METRIC_1_KEY);
187 assertThat(addedRawMeasure)
188 .hasQualityGateLevel(OK)
189 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
190 assertThat(getAlertStatusMeasure())
191 .hasQualityGateLevel(OK)
192 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
193 assertThat(getQGDetailsMeasure().get())
194 .hasValue(new QualityGateDetailsData(OK, of(new EvaluatedCondition(equals2Condition, OK, rawValue))).toJson());
196 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
197 .hasStatus(QualityGateStatus.OK)
198 .hasConditionCount(1)
199 .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue));
203 public void new_measures_have_ERROR_level_if_at_least_one_updated_measure_has_ERROR_level() {
205 Condition equals1ErrorCondition = createEqualsCondition(INT_METRIC_1, "1", null);
206 Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1");
207 Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
209 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals1ErrorCondition, equals1WarningCondition)));
210 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
211 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_2_KEY, rawMeasure);
215 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
216 Optional<Measure> rawMeasure2 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_2_KEY);
218 assertThat(rawMeasure1.get())
219 .hasQualityGateLevel(ERROR)
220 .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue));
221 assertThat(rawMeasure2.get())
222 .hasQualityGateLevel(WARN)
223 .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
224 assertThat(getAlertStatusMeasure())
225 .hasQualityGateLevel(ERROR)
226 .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue) + ", "
227 + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
228 assertThat(getQGDetailsMeasure())
229 .hasValue(new QualityGateDetailsData(ERROR, of(
230 new EvaluatedCondition(equals1ErrorCondition, ERROR, rawValue),
231 new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson());
233 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
234 .hasStatus(QualityGateStatus.ERROR)
235 .hasConditionCount(2)
236 .hasCondition(equals1ErrorCondition, ConditionStatus.EvaluationStatus.ERROR, String.valueOf(rawValue))
237 .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue));
241 public void new_measures_have_WARNING_level_if_no_updated_measure_has_ERROR_level() {
243 Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
244 Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1");
245 Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
247 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition, equals1WarningCondition)));
248 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
249 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_2_KEY, rawMeasure);
253 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
254 Optional<Measure> rawMeasure2 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_2_KEY);
256 assertThat(rawMeasure1.get())
257 .hasQualityGateLevel(OK)
258 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
259 assertThat(rawMeasure2.get())
260 .hasQualityGateLevel(WARN)
261 .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
262 assertThat(getAlertStatusMeasure())
263 .hasQualityGateLevel(WARN)
264 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue) + ", "
265 + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
266 assertThat(getQGDetailsMeasure())
267 .hasValue(new QualityGateDetailsData(WARN, of(
268 new EvaluatedCondition(equals2Condition, OK, rawValue),
269 new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson());
271 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
272 .hasStatus(QualityGateStatus.WARN)
273 .hasConditionCount(2)
274 .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue))
275 .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue));
279 public void new_measure_has_ERROR_level_of_all_conditions_for_a_specific_metric_if_its_the_worst() {
281 Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
282 Condition periodCondition = createEqualsCondition(INT_METRIC_1, null, "2", 1);
284 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(fixedCondition, periodCondition)));
285 Measure measure = newMeasureBuilder().create(rawValue, null);
286 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, measure);
290 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
291 assertThat(rawMeasure1.get())
292 .hasQualityGateLevel(ERROR)
293 .hasQualityGateText(dumbResultTextAnswer(fixedCondition, ERROR, rawValue));
297 public void new_measure_has_WARN_level_of_all_conditions_for_a_specific_metric_if_its_the_worst() {
299 Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
300 Condition periodCondition = createEqualsCondition(INT_METRIC_1, null, "2", 1);
302 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(fixedCondition, periodCondition)));
303 Measure measure = newMeasureBuilder()
304 .setVariations(newMeasureVariationsBuilder().setVariation(new Period(1, "mode", null, 1212, "u1"), rawValue).build())
305 .create(rawValue, null);
306 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, measure);
310 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
311 assertThat(rawMeasure1.get())
312 .hasQualityGateLevel(WARN)
313 .hasQualityGateText(dumbResultTextAnswer(periodCondition, WARN, rawValue));
317 public void new_measure_has_condition_on_leak_period_when_all_conditions_on_specific_metric_has_same_QG_level() {
319 Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
320 Condition periodCondition = createEqualsCondition(INT_METRIC_1, "1", null, 1);
322 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(fixedCondition, periodCondition)));
323 Measure measure = newMeasureBuilder()
324 .setVariations(newMeasureVariationsBuilder().setVariation(new Period(1, "mode", null, 1212, "u1"), rawValue).build())
325 .create(rawValue, null);
326 measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, measure);
330 Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
331 assertThat(rawMeasure1.get())
332 .hasQualityGateLevel(ERROR)
333 .hasQualityGateText(dumbResultTextAnswer(periodCondition, ERROR, rawValue));
336 private Measure getAlertStatusMeasure() {
337 return measureRepository.getAddedRawMeasure(PROJECT_REF, ALERT_STATUS_KEY).get();
340 private Optional<Measure> getQGDetailsMeasure() {
341 return measureRepository.getAddedRawMeasure(PROJECT_REF, CoreMetrics.QUALITY_GATE_DETAILS_KEY);
344 private static Condition createEqualsCondition(Metric metric, @Nullable String errorThreshold, @Nullable String warningThreshold) {
345 return new Condition(metric, Condition.Operator.EQUALS.getDbValue(), errorThreshold, warningThreshold, null);
348 private static Condition createEqualsCondition(Metric metric, @Nullable String errorThreshold, @Nullable String warningThreshold, @Nullable Integer period) {
349 return new Condition(metric, Condition.Operator.EQUALS.getDbValue(), errorThreshold, warningThreshold, period);
352 private static MetricImpl createIntMetric(int index) {
353 return new MetricImpl(index, "metricKey" + index, "metricName" + index, Metric.MetricType.INT);
356 private static class QualityGateStatusHolderAssertions extends AbstractAssert<QualityGateStatusHolderAssertions, QualityGateStatusHolder> {
358 private QualityGateStatusHolderAssertions(QualityGateStatusHolder actual) {
359 super(actual, QualityGateStatusHolderAssertions.class);
362 public static QualityGateStatusHolderAssertions assertThat(QualityGateStatusHolder holder) {
363 return new QualityGateStatusHolderAssertions(holder);
366 public QualityGateStatusHolderAssertions hasStatus(QualityGateStatus status) {
367 if (actual.getStatus() != status) {
369 "Expected QualityGateStatusHolder to have global status <%s> but was <%s>",
370 status, actual.getStatus());
376 public QualityGateStatusHolderAssertions hasConditionCount(int count) {
377 int conditionCount = actual.getStatusPerConditions().size();
378 if (conditionCount != count) {
380 "Expected QualityGateStatusHolder to have <%s> conditions but it has <%s>",
381 count, conditionCount);
387 public QualityGateStatusHolderAssertions hasCondition(Condition condition, ConditionStatus.EvaluationStatus evaluationStatus, @Nullable String expectedValue) {
388 for (Map.Entry<Condition, ConditionStatus> entry : actual.getStatusPerConditions().entrySet()) {
389 if (entry.getKey() == condition) {
390 ConditionStatus.EvaluationStatus actualStatus = entry.getValue().getStatus();
391 if (actualStatus != evaluationStatus) {
393 "Expected Status of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
394 condition, evaluationStatus, actualStatus);
396 String actualValue = entry.getValue().getValue();
397 if (!Objects.equals(expectedValue, actualValue)) {
399 "Expected Value of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
400 condition, expectedValue, actualValue);
407 "Expected QualityGateStatusHolder to have an entry for <%s> but none was found",