]> source.dussan.org Git - sonarqube.git/blob
8b39421392e4f4564ba45e595fd240b0845ae3ab
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2019 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.ce.task.projectanalysis.step;
21
22 import java.util.Optional;
23 import java.util.Collections;
24 import java.util.Map;
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.ce.task.projectanalysis.component.Component;
36 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
37 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
38 import org.sonar.ce.task.projectanalysis.measure.Measure;
39 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
40 import org.sonar.ce.task.projectanalysis.measure.qualitygatedetails.EvaluatedCondition;
41 import org.sonar.ce.task.projectanalysis.measure.qualitygatedetails.QualityGateDetailsData;
42 import org.sonar.ce.task.projectanalysis.metric.Metric;
43 import org.sonar.ce.task.projectanalysis.metric.MetricImpl;
44 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
45 import org.sonar.ce.task.projectanalysis.qualitygate.Condition;
46 import org.sonar.ce.task.projectanalysis.qualitygate.ConditionStatus;
47 import org.sonar.ce.task.projectanalysis.qualitygate.EvaluationResult;
48 import org.sonar.ce.task.projectanalysis.qualitygate.EvaluationResultTextConverter;
49 import org.sonar.ce.task.projectanalysis.qualitygate.MutableQualityGateStatusHolderRule;
50 import org.sonar.ce.task.projectanalysis.qualitygate.QualityGate;
51 import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateHolderRule;
52 import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateStatus;
53 import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateStatusHolder;
54 import org.sonar.ce.task.step.TestComputationStepContext;
55
56 import static com.google.common.collect.ImmutableList.of;
57 import static org.mockito.ArgumentMatchers.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.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
62 import static org.sonar.ce.task.projectanalysis.measure.Measure.Level.ERROR;
63 import static org.sonar.ce.task.projectanalysis.measure.Measure.Level.OK;
64 import static org.sonar.ce.task.projectanalysis.measure.Measure.Level.WARN;
65 import static org.sonar.ce.task.projectanalysis.measure.MeasureAssert.assertThat;
66
67 public class QualityGateMeasuresStepTest {
68   private static final MetricImpl INT_METRIC_1 = createIntMetric(1);
69   private static final String INT_METRIC_1_KEY = INT_METRIC_1.getKey();
70   private static final MetricImpl INT_METRIC_2 = createIntMetric(2);
71   private static final String INT_METRIC_2_KEY = INT_METRIC_2.getKey();
72
73   private static final int PROJECT_REF = 1;
74   private static final ReportComponent PROJECT_COMPONENT = ReportComponent.builder(Component.Type.PROJECT, PROJECT_REF).build();
75   private static final long SOME_QG_ID = 7521551;
76   private static final String SOME_QG_NAME = "name";
77
78   @Rule
79   public ExpectedException expectedException = ExpectedException.none();
80   @Rule
81   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
82   @Rule
83   public QualityGateHolderRule qualityGateHolder = new QualityGateHolderRule();
84   @Rule
85   public MutableQualityGateStatusHolderRule qualityGateStatusHolder = new MutableQualityGateStatusHolderRule();
86   @Rule
87   public MetricRepositoryRule metricRepository = new MetricRepositoryRule();
88   @Rule
89   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
90
91   private EvaluationResultTextConverter resultTextConverter = mock(EvaluationResultTextConverter.class);
92   private QualityGateMeasuresStep underTest = new QualityGateMeasuresStep(treeRootHolder, qualityGateHolder, qualityGateStatusHolder, measureRepository, metricRepository,
93     resultTextConverter, new SmallChangesetQualityGateSpecialCase(measureRepository, metricRepository));
94
95   @Before
96   public void setUp() {
97     metricRepository
98       .add(CoreMetrics.ALERT_STATUS)
99       .add(CoreMetrics.QUALITY_GATE_DETAILS)
100       .add(INT_METRIC_1)
101       .add(INT_METRIC_2);
102     treeRootHolder.setRoot(PROJECT_COMPONENT);
103
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>() {
106       @Override
107       public String answer(InvocationOnMock invocation) {
108         Condition condition = (Condition) invocation.getArguments()[0];
109         EvaluationResult evaluationResult = (EvaluationResult) invocation.getArguments()[1];
110         return dumbResultTextAnswer(condition, evaluationResult.getLevel(), evaluationResult.getValue());
111       }
112     });
113   }
114
115   private static String dumbResultTextAnswer(Condition condition, Measure.Level level, Object value) {
116     return condition.toString() + level + value;
117   }
118
119   @Test
120   public void no_measure_if_tree_has_no_project() {
121     ReportComponent notAProjectComponent = ReportComponent.builder(Component.Type.MODULE, 1).build();
122
123     treeRootHolder.setRoot(notAProjectComponent);
124
125     underTest.execute(new TestComputationStepContext());
126
127     measureRepository.getAddedRawMeasures(1).isEmpty();
128   }
129
130   @Test
131   public void no_measure_if_there_is_no_qualitygate() {
132     qualityGateHolder.setQualityGate(null);
133
134     underTest.execute(new TestComputationStepContext());
135
136     measureRepository.getAddedRawMeasures(PROJECT_COMPONENT).isEmpty();
137   }
138
139   @Test
140   public void mutableQualityGateStatusHolder_is_not_populated_if_there_is_no_qualitygate() {
141     qualityGateHolder.setQualityGate(null);
142
143     underTest.execute(new TestComputationStepContext());
144
145     expectedException.expect(IllegalStateException.class);
146     expectedException.expectMessage("Quality gate status has not been set yet");
147
148     qualityGateStatusHolder.getStatus();
149   }
150
151   @Test
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
156     underTest.execute(new TestComputationStepContext());
157
158     Optional<Measure> addedRawMeasure = measureRepository.getAddedRawMeasure(PROJECT_COMPONENT, INT_METRIC_1_KEY);
159
160     assertThat(addedRawMeasure).isAbsent();
161     assertThat(getAlertStatusMeasure())
162       .hasQualityGateLevel(OK)
163       .hasQualityGateText("");
164     assertThat(getQGDetailsMeasure())
165       .hasValue(new QualityGateDetailsData(OK, Collections.emptyList(), false).toJson());
166
167     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
168       .hasStatus(QualityGateStatus.OK)
169       .hasConditionCount(1)
170       .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.NO_VALUE, null);
171   }
172
173   @Test
174   public void rawMeasure_is_updated_if_present_and_new_measures_are_created_if_project_has_measure_for_metric_of_condition() {
175     int rawValue = 1;
176     Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
177     Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
178
179     qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition)));
180     measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
181
182     underTest.execute(new TestComputationStepContext());
183
184     Optional<Measure> addedRawMeasure = measureRepository.getAddedRawMeasure(PROJECT_COMPONENT, INT_METRIC_1_KEY);
185
186     assertThat(addedRawMeasure)
187       .hasQualityGateLevel(OK)
188       .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
189     assertThat(getAlertStatusMeasure())
190       .hasQualityGateLevel(OK)
191       .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
192     assertThat(getQGDetailsMeasure().get())
193       .hasValue(new QualityGateDetailsData(OK, of(new EvaluatedCondition(equals2Condition, OK, rawValue)), false).toJson());
194
195     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
196       .hasStatus(QualityGateStatus.OK)
197       .hasConditionCount(1)
198       .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue));
199   }
200
201   @Test
202   public void new_measures_have_ERROR_level_if_at_least_one_updated_measure_has_ERROR_level() {
203     int rawValue = 1;
204     Condition equals1ErrorCondition = createEqualsCondition(INT_METRIC_1, "1", null);
205     Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1");
206     Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
207
208     qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals1ErrorCondition, equals1WarningCondition)));
209     measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
210     measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_2_KEY, rawMeasure);
211
212     underTest.execute(new TestComputationStepContext());
213
214     Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
215     Optional<Measure> rawMeasure2 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_2_KEY);
216
217     assertThat(rawMeasure1.get())
218       .hasQualityGateLevel(ERROR)
219       .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue));
220     assertThat(rawMeasure2.get())
221       .hasQualityGateLevel(WARN)
222       .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
223     assertThat(getAlertStatusMeasure())
224       .hasQualityGateLevel(ERROR)
225       .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue) + ", "
226         + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
227     assertThat(getQGDetailsMeasure())
228       .hasValue(new QualityGateDetailsData(ERROR, of(
229         new EvaluatedCondition(equals1ErrorCondition, ERROR, rawValue),
230         new EvaluatedCondition(equals1WarningCondition, WARN, rawValue)), false).toJson());
231
232     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
233       .hasStatus(QualityGateStatus.ERROR)
234       .hasConditionCount(2)
235       .hasCondition(equals1ErrorCondition, ConditionStatus.EvaluationStatus.ERROR, String.valueOf(rawValue))
236       .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue));
237   }
238
239   @Test
240   public void new_measures_have_WARNING_level_if_no_updated_measure_has_ERROR_level() {
241     int rawValue = 1;
242     Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
243     Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1");
244     Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
245
246     qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition, equals1WarningCondition)));
247     measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
248     measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_2_KEY, rawMeasure);
249
250     underTest.execute(new TestComputationStepContext());
251
252     Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
253     Optional<Measure> rawMeasure2 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_2_KEY);
254
255     assertThat(rawMeasure1.get())
256       .hasQualityGateLevel(OK)
257       .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue));
258     assertThat(rawMeasure2.get())
259       .hasQualityGateLevel(WARN)
260       .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
261     assertThat(getAlertStatusMeasure())
262       .hasQualityGateLevel(WARN)
263       .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue) + ", "
264         + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue));
265     assertThat(getQGDetailsMeasure())
266       .hasValue(new QualityGateDetailsData(WARN, of(
267         new EvaluatedCondition(equals2Condition, OK, rawValue),
268         new EvaluatedCondition(equals1WarningCondition, WARN, rawValue)), false).toJson());
269
270     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
271       .hasStatus(QualityGateStatus.WARN)
272       .hasConditionCount(2)
273       .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue))
274       .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue));
275   }
276
277   @Test
278   public void new_measure_has_ERROR_level_of_all_conditions_for_a_specific_metric_if_its_the_worst() {
279     int rawValue = 1;
280     Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
281     Condition periodCondition = createEqualsCondition(INT_METRIC_1, null, "2");
282
283     qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(fixedCondition, periodCondition)));
284     Measure measure = newMeasureBuilder().create(rawValue, null);
285     measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, measure);
286
287     underTest.execute(new TestComputationStepContext());
288
289     Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
290     assertThat(rawMeasure1.get())
291       .hasQualityGateLevel(ERROR)
292       .hasQualityGateText(dumbResultTextAnswer(fixedCondition, ERROR, rawValue));
293   }
294
295   @Test
296   public void new_measure_has_condition_on_leak_period_when_all_conditions_on_specific_metric_has_same_QG_level() {
297     int rawValue = 1;
298     Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
299     Condition periodCondition = createEqualsCondition(INT_METRIC_1, "1", null);
300
301     qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(fixedCondition, periodCondition)));
302     Measure measure = newMeasureBuilder()
303       .setVariation(rawValue)
304       .create(rawValue, null);
305     measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, measure);
306
307     underTest.execute(new TestComputationStepContext());
308
309     Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
310     assertThat(rawMeasure1.get())
311       .hasQualityGateLevel(ERROR)
312       .hasQualityGateText(dumbResultTextAnswer(periodCondition, ERROR, rawValue));
313   }
314
315   private Measure getAlertStatusMeasure() {
316     return measureRepository.getAddedRawMeasure(PROJECT_REF, ALERT_STATUS_KEY).get();
317   }
318
319   private Optional<Measure> getQGDetailsMeasure() {
320     return measureRepository.getAddedRawMeasure(PROJECT_REF, CoreMetrics.QUALITY_GATE_DETAILS_KEY);
321   }
322
323   private static Condition createEqualsCondition(Metric metric, @Nullable String errorThreshold, @Nullable String warningThreshold) {
324     return new Condition(metric, Condition.Operator.EQUALS.getDbValue(), errorThreshold, warningThreshold);
325   }
326
327   private static MetricImpl createIntMetric(int index) {
328     return new MetricImpl(index, "metricKey" + index, "metricName" + index, Metric.MetricType.INT);
329   }
330
331   private static class QualityGateStatusHolderAssertions extends AbstractAssert<QualityGateStatusHolderAssertions, QualityGateStatusHolder> {
332
333     private QualityGateStatusHolderAssertions(QualityGateStatusHolder actual) {
334       super(actual, QualityGateStatusHolderAssertions.class);
335     }
336
337     public static QualityGateStatusHolderAssertions assertThat(QualityGateStatusHolder holder) {
338       return new QualityGateStatusHolderAssertions(holder);
339     }
340
341     public QualityGateStatusHolderAssertions hasStatus(QualityGateStatus status) {
342       if (actual.getStatus() != status) {
343         failWithMessage(
344           "Expected QualityGateStatusHolder to have global status <%s> but was <%s>",
345           status, actual.getStatus());
346       }
347
348       return this;
349     }
350
351     public QualityGateStatusHolderAssertions hasConditionCount(int count) {
352       int conditionCount = actual.getStatusPerConditions().size();
353       if (conditionCount != count) {
354         failWithMessage(
355           "Expected QualityGateStatusHolder to have <%s> conditions but it has <%s>",
356           count, conditionCount);
357       }
358
359       return this;
360     }
361
362     public QualityGateStatusHolderAssertions hasCondition(Condition condition, ConditionStatus.EvaluationStatus evaluationStatus, @Nullable String expectedValue) {
363       for (Map.Entry<Condition, ConditionStatus> entry : actual.getStatusPerConditions().entrySet()) {
364         if (entry.getKey() == condition) {
365           ConditionStatus.EvaluationStatus actualStatus = entry.getValue().getStatus();
366           if (actualStatus != evaluationStatus) {
367             failWithMessage(
368               "Expected Status of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
369               condition, evaluationStatus, actualStatus);
370           }
371           String actualValue = entry.getValue().getValue();
372           if (!Objects.equals(expectedValue, actualValue)) {
373             failWithMessage(
374               "Expected Value of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
375               condition, expectedValue, actualValue);
376           }
377           return this;
378         }
379       }
380
381       failWithMessage(
382         "Expected QualityGateStatusHolder to have an entry for <%s> but none was found",
383         condition);
384
385       return this;
386     }
387   }
388
389 }