]> source.dussan.org Git - sonarqube.git/blob
1b6860e5d1fa0831f92762c44530b48faaa5ab80
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2017 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.server.computation.task.projectanalysis.step;
21
22 import com.google.common.base.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.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;
54
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;
65
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();
71
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";
76
77   @Rule
78   public ExpectedException expectedException = ExpectedException.none();
79   @Rule
80   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
81   @Rule
82   public QualityGateHolderRule qualityGateHolder = new QualityGateHolderRule();
83   @Rule
84   public MutableQualityGateStatusHolderRule qualityGateStatusHolder = new MutableQualityGateStatusHolderRule();
85   @Rule
86   public MetricRepositoryRule metricRepository = new MetricRepositoryRule();
87   @Rule
88   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
89
90   private EvaluationResultTextConverter resultTextConverter = mock(EvaluationResultTextConverter.class);
91   private QualityGateMeasuresStep underTest = new QualityGateMeasuresStep(treeRootHolder, qualityGateHolder, qualityGateStatusHolder, measureRepository, metricRepository,
92     resultTextConverter, new SmallChangesetQualityGateSpecialCase(measureRepository, metricRepository));
93
94   @Before
95   public void setUp() {
96     metricRepository
97       .add(CoreMetrics.ALERT_STATUS)
98       .add(CoreMetrics.QUALITY_GATE_DETAILS)
99       .add(INT_METRIC_1)
100       .add(INT_METRIC_2);
101     treeRootHolder.setRoot(PROJECT_COMPONENT);
102
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>() {
105       @Override
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());
110       }
111     });
112   }
113
114   private static String dumbResultTextAnswer(Condition condition, Measure.Level level, Object value) {
115     return condition.toString() + level + value;
116   }
117
118   @Test
119   public void no_measure_if_tree_has_no_project() {
120     ReportComponent notAProjectComponent = ReportComponent.builder(Component.Type.MODULE, 1).build();
121
122     treeRootHolder.setRoot(notAProjectComponent);
123
124     underTest.execute();
125
126     measureRepository.getAddedRawMeasures(1).isEmpty();
127   }
128
129   @Test
130   public void no_measure_if_there_is_no_qualitygate() {
131     qualityGateHolder.setQualityGate(null);
132
133     underTest.execute();
134
135     measureRepository.getAddedRawMeasures(PROJECT_COMPONENT).isEmpty();
136   }
137
138   @Test
139   public void mutableQualityGateStatusHolder_is_not_populated_if_there_is_no_qualitygate() {
140     qualityGateHolder.setQualityGate(null);
141
142     underTest.execute();
143
144     expectedException.expect(IllegalStateException.class);
145     expectedException.expectMessage("Quality gate status has not been set yet");
146
147     qualityGateStatusHolder.getStatus();
148   }
149
150   @Test
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)));
154
155     underTest.execute();
156
157     Optional<Measure> addedRawMeasure = measureRepository.getAddedRawMeasure(PROJECT_COMPONENT, INT_METRIC_1_KEY);
158
159     assertThat(addedRawMeasure).isAbsent();
160     assertThat(getAlertStatusMeasure())
161       .hasQualityGateLevel(OK)
162       .hasQualityGateText("");
163     assertThat(getQGDetailsMeasure())
164       .hasValue(new QualityGateDetailsData(OK, Collections.<EvaluatedCondition>emptyList()).toJson());
165
166     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
167       .hasStatus(QualityGateStatus.OK)
168       .hasConditionCount(1)
169       .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.NO_VALUE, null);
170   }
171
172   @Test
173   public void rawMeasure_is_updated_if_present_and_new_measures_are_created_if_project_has_measure_for_metric_of_condition() {
174     int rawValue = 1;
175     Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null);
176     Measure rawMeasure = newMeasureBuilder().create(rawValue, null);
177
178     qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition)));
179     measureRepository.addRawMeasure(PROJECT_REF, INT_METRIC_1_KEY, rawMeasure);
180
181     underTest.execute();
182
183     Optional<Measure> addedRawMeasure = measureRepository.getAddedRawMeasure(PROJECT_COMPONENT, INT_METRIC_1_KEY);
184
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());
193
194     QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder)
195       .hasStatus(QualityGateStatus.OK)
196       .hasConditionCount(1)
197       .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue));
198   }
199
200   @Test
201   public void new_measures_have_ERROR_level_if_at_least_one_updated_measure_has_ERROR_level() {
202     int rawValue = 1;
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);
206
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);
210
211     underTest.execute();
212
213     Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
214     Optional<Measure> rawMeasure2 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_2_KEY);
215
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());
230
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));
236   }
237
238   @Test
239   public void new_measures_have_WARNING_level_if_no_updated_measure_has_ERROR_level() {
240     int rawValue = 1;
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);
244
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);
248
249     underTest.execute();
250
251     Optional<Measure> rawMeasure1 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_1_KEY);
252     Optional<Measure> rawMeasure2 = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_2_KEY);
253
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());
268
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));
274   }
275
276   @Test
277   public void new_measure_has_ERROR_level_of_all_conditions_for_a_specific_metric_if_its_the_worst() {
278     int rawValue = 1;
279     Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
280     Condition periodCondition = createEqualsCondition(INT_METRIC_1, null, "2", true);
281
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);
285
286     underTest.execute();
287
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));
292   }
293
294   @Test
295   public void new_measure_has_WARN_level_of_all_conditions_for_a_specific_metric_if_its_the_worst() {
296     int rawValue = 2;
297     Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
298     Condition periodCondition = createEqualsCondition(INT_METRIC_1, null, "2", true);
299
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);
305
306     underTest.execute();
307
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));
312   }
313
314   @Test
315   public void new_measure_has_condition_on_leak_period_when_all_conditions_on_specific_metric_has_same_QG_level() {
316     int rawValue = 1;
317     Condition fixedCondition = createEqualsCondition(INT_METRIC_1, "1", null);
318     Condition periodCondition = createEqualsCondition(INT_METRIC_1, "1", null, true);
319
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);
325
326     underTest.execute();
327
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));
332   }
333
334   private Measure getAlertStatusMeasure() {
335     return measureRepository.getAddedRawMeasure(PROJECT_REF, ALERT_STATUS_KEY).get();
336   }
337
338   private Optional<Measure> getQGDetailsMeasure() {
339     return measureRepository.getAddedRawMeasure(PROJECT_REF, CoreMetrics.QUALITY_GATE_DETAILS_KEY);
340   }
341
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);
344   }
345
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);
348   }
349
350   private static MetricImpl createIntMetric(int index) {
351     return new MetricImpl(index, "metricKey" + index, "metricName" + index, Metric.MetricType.INT);
352   }
353
354   private static class QualityGateStatusHolderAssertions extends AbstractAssert<QualityGateStatusHolderAssertions, QualityGateStatusHolder> {
355
356     private QualityGateStatusHolderAssertions(QualityGateStatusHolder actual) {
357       super(actual, QualityGateStatusHolderAssertions.class);
358     }
359
360     public static QualityGateStatusHolderAssertions assertThat(QualityGateStatusHolder holder) {
361       return new QualityGateStatusHolderAssertions(holder);
362     }
363
364     public QualityGateStatusHolderAssertions hasStatus(QualityGateStatus status) {
365       if (actual.getStatus() != status) {
366         failWithMessage(
367           "Expected QualityGateStatusHolder to have global status <%s> but was <%s>",
368           status, actual.getStatus());
369       }
370
371       return this;
372     }
373
374     public QualityGateStatusHolderAssertions hasConditionCount(int count) {
375       int conditionCount = actual.getStatusPerConditions().size();
376       if (conditionCount != count) {
377         failWithMessage(
378           "Expected QualityGateStatusHolder to have <%s> conditions but it has <%s>",
379           count, conditionCount);
380       }
381
382       return this;
383     }
384
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) {
390             failWithMessage(
391               "Expected Status of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
392               condition, evaluationStatus, actualStatus);
393           }
394           String actualValue = entry.getValue().getValue();
395           if (!Objects.equals(expectedValue, actualValue)) {
396             failWithMessage(
397               "Expected Value of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>",
398               condition, expectedValue, actualValue);
399           }
400           return this;
401         }
402       }
403
404       failWithMessage(
405         "Expected QualityGateStatusHolder to have an entry for <%s> but none was found",
406         condition);
407
408       return this;
409     }
410   }
411
412 }