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.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.ArgumentCaptor;
33 import org.mockito.invocation.InvocationOnMock;
34 import org.mockito.stubbing.Answer;
35 import org.sonar.api.measures.CoreMetrics;
36 import org.sonar.server.computation.batch.TreeRootHolderRule;
37 import org.sonar.server.computation.component.Component;
38 import org.sonar.server.computation.component.ReportComponent;
39 import org.sonar.server.computation.measure.Measure;
40 import org.sonar.server.computation.measure.MeasureRepository;
41 import org.sonar.server.computation.measure.qualitygatedetails.EvaluatedCondition;
42 import org.sonar.server.computation.measure.qualitygatedetails.QualityGateDetailsData;
43 import org.sonar.server.computation.metric.Metric;
44 import org.sonar.server.computation.metric.MetricImpl;
45 import org.sonar.server.computation.metric.MetricRepository;
46 import org.sonar.server.computation.qualitygate.Condition;
47 import org.sonar.server.computation.qualitygate.ConditionStatus;
48 import org.sonar.server.computation.qualitygate.EvaluationResult;
49 import org.sonar.server.computation.qualitygate.EvaluationResultTextConverter;
50 import org.sonar.server.computation.qualitygate.MutableQualityGateStatusHolderRule;
51 import org.sonar.server.computation.qualitygate.QualityGate;
52 import org.sonar.server.computation.qualitygate.QualityGateHolderRule;
53 import org.sonar.server.computation.qualitygate.QualityGateStatus;
54 import org.sonar.server.computation.qualitygate.QualityGateStatusHolder;
56 import static com.google.common.collect.ImmutableList.of;
57 import static org.mockito.Matchers.any;
58 import static org.mockito.Matchers.same;
59 import static org.mockito.Mockito.mock;
60 import static org.mockito.Mockito.verify;
61 import static org.mockito.Mockito.verifyNoMoreInteractions;
62 import static org.mockito.Mockito.when;
63 import static org.sonar.server.computation.measure.Measure.Level.ERROR;
64 import static org.sonar.server.computation.measure.Measure.Level.OK;
65 import static org.sonar.server.computation.measure.Measure.Level.WARN;
66 import static org.sonar.server.computation.measure.MeasureAssert.assertThat;
68 public class QualityGateMeasuresStepTest {
69 private static final MetricImpl INT_METRIC_1 = createIntMetric(1);
70 private static final MetricImpl INT_METRIC_2 = createIntMetric(2);
72 private static final ReportComponent PROJECT_COMPONENT = ReportComponent.builder(Component.Type.PROJECT, 1).build();
73 private static final long SOME_QG_ID = 7521551;
74 private static final String SOME_QG_NAME = "name";
77 public ExpectedException expectedException = ExpectedException.none();
79 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
81 public QualityGateHolderRule qualityGateHolder = new QualityGateHolderRule();
83 public MutableQualityGateStatusHolderRule qualityGateStatusHolder = new MutableQualityGateStatusHolderRule();
85 private static final Metric ALERT_STATUS_METRIC = mock(Metric.class);
86 private static final Metric QUALITY_GATE_DETAILS_METRIC = mock(Metric.class);
88 private ArgumentCaptor<Measure> alertStatusMeasureCaptor = ArgumentCaptor.forClass(Measure.class);
89 private ArgumentCaptor<Measure> qgDetailsMeasureCaptor = ArgumentCaptor.forClass(Measure.class);
91 private MeasureRepository measureRepository = mock(MeasureRepository.class);
92 private MetricRepository metricRepository = mock(MetricRepository.class);
93 private EvaluationResultTextConverter resultTextConverter = mock(EvaluationResultTextConverter.class);
94 private QualityGateMeasuresStep underTest = new QualityGateMeasuresStep(treeRootHolder, qualityGateHolder, qualityGateStatusHolder, measureRepository, metricRepository,
99 treeRootHolder.setRoot(PROJECT_COMPONENT);
101 when(metricRepository.getByKey(CoreMetrics.ALERT_STATUS_KEY)).thenReturn(ALERT_STATUS_METRIC);
102 when(metricRepository.getByKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY)).thenReturn(QUALITY_GATE_DETAILS_METRIC);
104 // mock response of asText to any argument to return the result of dumbResultTextAnswer method
105 when(resultTextConverter.asText(any(Condition.class), any(EvaluationResult.class))).thenAnswer(new Answer<String>() {
107 public String answer(InvocationOnMock invocation) throws Throwable {
108 Condition condition = (Condition) invocation.getArguments()[0];
109 EvaluationResult evaluationResult = (EvaluationResult) invocation.getArguments()[1];
110 return dumbResultTextAnswer(condition, evaluationResult.getLevel(), evaluationResult.getValue());
115 private static String dumbResultTextAnswer(Condition condition, Measure.Level level, Object value) {
116 return condition.toString() + level + value;
120 public void no_measure_if_tree_has_no_project() {
121 ReportComponent notAProjectComponent = ReportComponent.builder(Component.Type.MODULE, 1).build();
123 treeRootHolder.setRoot(notAProjectComponent);
127 verifyNoMoreInteractions(measureRepository);
131 public void no_measure_if_there_is_no_qualitygate() {
132 qualityGateHolder.setQualityGate(null);
136 verifyNoMoreInteractions(measureRepository);
140 public void mutableQualityGateStatusHolder_is_not_populated_if_there_is_no_qualitygate() {
141 qualityGateHolder.setQualityGate(null);
145 expectedException.expect(IllegalStateException.class);
146 expectedException.expectMessage("Quality gate status has not been set yet");
148 qualityGateStatusHolder.getStatus();
152 public void new_measures_are_created_even_if_there_is_no_rawMeasure_for_metric_of_condition() {
153 Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
154 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition)));
155 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1)).thenReturn(Optional.<Measure>absent());
159 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1);
160 verify(measureRepository).add(same(PROJECT_COMPONENT), same(ALERT_STATUS_METRIC), alertStatusMeasureCaptor.capture());
161 verify(measureRepository).add(same(PROJECT_COMPONENT), same(QUALITY_GATE_DETAILS_METRIC), qgDetailsMeasureCaptor.capture());
162 verifyNoMoreInteractions(measureRepository);
164 assertThat(alertStatusMeasureCaptor.getValue())
165 .hasQualityGateLevel(OK)
166 .hasQualityGateText("");
167 assertThat(qgDetailsMeasureCaptor.getValue())
168 .hasValue(new QualityGateDetailsData(OK, Collections.<EvaluatedCondition>emptyList()).toJson());
170 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
171 .hasStatus(QualityGateStatus.OK)
172 .hasConditionCount(1)
173 .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.NO_VALUE, null);
177 public void rawMeasure_is_updated_if_present_and_new_measures_are_created_if_project_has_measure_for_metric_of_condition() {
179 Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
180 Measure rawMeasure = Measure.newMeasureBuilder().create(rawValue, null);
182 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition)));
183 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1)).thenReturn(Optional.of(rawMeasure));
187 ArgumentCaptor<Measure> equals2ConditionMeasureCaptor = ArgumentCaptor.forClass(Measure.class);
189 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1);
190 verify(measureRepository).update(same(PROJECT_COMPONENT), same(INT_METRIC_1), equals2ConditionMeasureCaptor.capture());
191 verify(measureRepository).add(same(PROJECT_COMPONENT), same(ALERT_STATUS_METRIC), alertStatusMeasureCaptor.capture());
192 verify(measureRepository).add(same(PROJECT_COMPONENT), same(QUALITY_GATE_DETAILS_METRIC), qgDetailsMeasureCaptor.capture());
193 verifyNoMoreInteractions(measureRepository);
195 assertThat(equals2ConditionMeasureCaptor.getValue())
196 .hasQualityGateLevel(OK)
197 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
198 assertThat(alertStatusMeasureCaptor.getValue())
199 .hasQualityGateLevel(OK)
200 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
201 assertThat(qgDetailsMeasureCaptor.getValue())
202 .hasValue(new QualityGateDetailsData(OK, of(new EvaluatedCondition(equals2Condition, OK, rawValue))).toJson());
204 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
205 .hasStatus(QualityGateStatus.OK)
206 .hasConditionCount(1)
207 .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue));
211 public void new_measures_have_ERROR_level_if_at_least_one_updated_measure_has_ERROR_level() {
213 Condition equals1ErrorCondition = createEqualsCondition(INT_METRIC_1, "1", null);
214 Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1");
215 Measure rawMeasure = Measure.newMeasureBuilder().create(rawValue, null);
217 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals1ErrorCondition, equals1WarningCondition)));
218 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1)).thenReturn(Optional.of(rawMeasure));
219 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_2)).thenReturn(Optional.of(rawMeasure));
223 ArgumentCaptor<Measure> equals1ErrorConditionMeasureCaptor = ArgumentCaptor.forClass(Measure.class);
224 ArgumentCaptor<Measure> equals1WarningConditionMeasureCaptor = ArgumentCaptor.forClass(Measure.class);
226 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1);
227 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, INT_METRIC_2);
228 verify(measureRepository).update(same(PROJECT_COMPONENT), same(INT_METRIC_1), equals1ErrorConditionMeasureCaptor.capture());
229 verify(measureRepository).update(same(PROJECT_COMPONENT), same(INT_METRIC_2), equals1WarningConditionMeasureCaptor.capture());
230 verify(measureRepository).add(same(PROJECT_COMPONENT), same(ALERT_STATUS_METRIC), alertStatusMeasureCaptor.capture());
231 verify(measureRepository).add(same(PROJECT_COMPONENT), same(QUALITY_GATE_DETAILS_METRIC), qgDetailsMeasureCaptor.capture());
232 verifyNoMoreInteractions(measureRepository);
234 assertThat(equals1ErrorConditionMeasureCaptor.getValue())
235 .hasQualityGateLevel(ERROR)
236 .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue));
237 assertThat(equals1WarningConditionMeasureCaptor.getValue())
238 .hasQualityGateLevel(WARN)
239 .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
240 assertThat(alertStatusMeasureCaptor.getValue())
241 .hasQualityGateLevel(ERROR)
242 .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue) + ", "
243 + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
244 assertThat(qgDetailsMeasureCaptor.getValue())
245 .hasValue(new QualityGateDetailsData(ERROR, of(
246 new EvaluatedCondition(equals1ErrorCondition, ERROR, rawValue),
247 new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson());
249 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
250 .hasStatus(QualityGateStatus.ERROR)
251 .hasConditionCount(2)
252 .hasCondition(equals1ErrorCondition, ConditionStatus.EvaluationStatus.ERROR, String.valueOf(rawValue))
253 .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue));
257 public void new_measures_have_WARNING_level_if_no_updated_measure_has_ERROR_level() {
259 Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
260 Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1");
261 Measure rawMeasure = Measure.newMeasureBuilder().create(rawValue, null);
263 qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition, equals1WarningCondition)));
264 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1)).thenReturn(Optional.of(rawMeasure));
265 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_2)).thenReturn(Optional.of(rawMeasure));
269 ArgumentCaptor<Measure> equals2ConditionMeasureCaptor = ArgumentCaptor.forClass(Measure.class);
270 ArgumentCaptor<Measure> equals1WarningConditionMeasureCaptor = ArgumentCaptor.forClass(Measure.class);
272 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1);
273 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, INT_METRIC_2);
274 verify(measureRepository).update(same(PROJECT_COMPONENT), same(INT_METRIC_1), equals2ConditionMeasureCaptor.capture());
275 verify(measureRepository).update(same(PROJECT_COMPONENT), same(INT_METRIC_2), equals1WarningConditionMeasureCaptor.capture());
276 verify(measureRepository).add(same(PROJECT_COMPONENT), same(ALERT_STATUS_METRIC), alertStatusMeasureCaptor.capture());
277 verify(measureRepository).add(same(PROJECT_COMPONENT), same(QUALITY_GATE_DETAILS_METRIC), qgDetailsMeasureCaptor.capture());
278 verifyNoMoreInteractions(measureRepository);
280 assertThat(equals2ConditionMeasureCaptor.getValue())
281 .hasQualityGateLevel(OK)
282 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
283 assertThat(equals1WarningConditionMeasureCaptor.getValue())
284 .hasQualityGateLevel(WARN)
285 .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
286 assertThat(alertStatusMeasureCaptor.getValue())
287 .hasQualityGateLevel(WARN)
288 .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue) + ", "
289 + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
290 assertThat(qgDetailsMeasureCaptor.getValue())
291 .hasValue(new QualityGateDetailsData(WARN, of(
292 new EvaluatedCondition(equals2Condition, OK, rawValue),
293 new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson());
295 QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
296 .hasStatus(QualityGateStatus.WARN)
297 .hasConditionCount(2)
298 .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue))
299 .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue));
302 private static Condition createEqualsCondition(Metric metric, @Nullable String errorThreshold, @Nullable String warningThreshold) {
303 return new Condition(metric, Condition.Operator.EQUALS.getDbValue(), errorThreshold, warningThreshold, null);
306 private static MetricImpl createIntMetric(int index) {
307 return new MetricImpl(index, "metricKey" + index, "metricName" + index, Metric.MetricType.INT);
310 private static class QualityGateStatusHolderAssertions extends AbstractAssert<QualityGateStatusHolderAssertions, QualityGateStatusHolder> {
312 private QualityGateStatusHolderAssertions(QualityGateStatusHolder actual) {
313 super(actual, QualityGateStatusHolderAssertions.class);
316 public static QualityGateStatusHolderAssertions assertThat(QualityGateStatusHolder holder) {
317 return new QualityGateStatusHolderAssertions(holder);
320 public QualityGateStatusHolderAssertions hasStatus(QualityGateStatus status) {
321 if (actual.getStatus() != status) {
323 "Expected QualityGateStatusHolder to have global status <%s> but was <%s>",
324 status, actual.getStatus());
330 public QualityGateStatusHolderAssertions hasConditionCount(int count) {
331 int conditionCount = actual.getStatusPerConditions().size();
332 if (conditionCount != count) {
334 "Expected QualityGateStatusHolder to have <%s> conditions but it has <%s>",
335 count, conditionCount);
341 public QualityGateStatusHolderAssertions hasCondition(Condition condition, ConditionStatus.EvaluationStatus evaluationStatus, @Nullable String expectedValue) {
342 for (Map.Entry<Condition, ConditionStatus> entry : actual.getStatusPerConditions().entrySet()) {
343 if (entry.getKey() == condition) {
344 ConditionStatus.EvaluationStatus actualStatus = entry.getValue().getStatus();
345 if (actualStatus != evaluationStatus) {
347 "Expected Status of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
348 condition, evaluationStatus, actualStatus);
350 String actualValue = entry.getValue().getValue();
351 if (!Objects.equals(expectedValue, actualValue)) {
353 "Expected Value of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
354 condition, expectedValue, actualValue);
361 "Expected QualityGateStatusHolder to have an entry for <%s> but none was found",