]> source.dussan.org Git - sonarqube.git/blob
13af0bd445045fac0529e9fede400285ff6ab020
[sonarqube.git] /
1 /*
2  * Sonar, open source software quality management tool.
3  * Copyright (C) 2008-2012 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
6  * Sonar 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  * Sonar 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
17  * License along with Sonar; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
19  */
20 package org.sonar.plugins.core.sensors;
21
22 import org.apache.commons.lang.NotImplementedException;
23 import org.junit.Before;
24 import org.junit.Test;
25 import org.mockito.ArgumentMatcher;
26 import org.mockito.Mockito;
27 import org.sonar.api.batch.DecoratorContext;
28 import org.sonar.api.database.model.Snapshot;
29 import org.sonar.api.i18n.I18n;
30 import org.sonar.api.measures.CoreMetrics;
31 import org.sonar.api.measures.Measure;
32 import org.sonar.api.measures.Metric;
33 import org.sonar.api.profiles.Alert;
34 import org.sonar.api.profiles.RulesProfile;
35 import org.sonar.api.resources.Project;
36 import org.sonar.api.resources.Qualifiers;
37 import org.sonar.api.resources.Resource;
38 import org.sonar.api.test.IsMeasure;
39 import org.sonar.core.timemachine.Periods;
40
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Locale;
44
45 import static org.junit.Assert.assertFalse;
46 import static org.mockito.Matchers.argThat;
47 import static org.mockito.Mockito.any;
48 import static org.mockito.Mockito.mock;
49 import static org.mockito.Mockito.never;
50 import static org.mockito.Mockito.verify;
51 import static org.mockito.Mockito.when;
52
53 public class CheckAlertThresholdsTest {
54
55   private CheckAlertThresholds decorator;
56   private DecoratorContext context;
57   private RulesProfile profile;
58
59   private Measure measureClasses;
60   private Measure measureCoverage;
61   private Measure measureComplexity;
62
63   private Resource project;
64   private Snapshot snapshot;
65   private Periods periods;
66   private I18n i18n;
67
68   @Before
69   public void setup() {
70     context = mock(DecoratorContext.class);
71     periods = mock(Periods.class);
72     i18n = mock(I18n.class);
73     when(i18n.message(Mockito.any(Locale.class), Mockito.eq("variation"), Mockito.isNull(String.class))).thenReturn("variation");
74
75     measureClasses = new Measure(CoreMetrics.CLASSES, 20d);
76     measureCoverage = new Measure(CoreMetrics.COVERAGE, 35d);
77     measureComplexity = new Measure(CoreMetrics.COMPLEXITY, 50d);
78
79     when(context.getMeasure(CoreMetrics.CLASSES)).thenReturn(measureClasses);
80     when(context.getMeasure(CoreMetrics.COVERAGE)).thenReturn(measureCoverage);
81     when(context.getMeasure(CoreMetrics.COMPLEXITY)).thenReturn(measureComplexity);
82
83     snapshot = mock(Snapshot.class);
84     profile = mock(RulesProfile.class);
85     decorator = new CheckAlertThresholds(snapshot, profile, periods, i18n);
86     project = mock(Resource.class);
87     when(project.getQualifier()).thenReturn(Qualifiers.PROJECT);
88   }
89
90   @Test
91   public void shouldNotCreateAlertsWhenNoThresholds() {
92     when(profile.getAlerts()).thenReturn(new ArrayList<Alert>());
93     assertFalse(decorator.shouldExecuteOnProject(new Project("key")));
94   }
95
96   @Test
97   public void shouldBeOkWhenNoAlert() {
98     when(profile.getAlerts()).thenReturn(Arrays.asList(
99         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20"),
100         new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_GREATER, null, "35.0")));
101
102     decorator.decorate(project, context);
103
104     verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.ALERT_STATUS, Metric.Level.OK.toString())));
105     verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
106     verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK)));
107   }
108
109   @Test
110   public void checkRootProjectsOnly() {
111     when(project.getQualifier()).thenReturn(Resource.QUALIFIER_FILE);
112     when(profile.getAlerts()).thenReturn(Arrays.asList(
113         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20"),
114         new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_GREATER, null, "35.0")));
115
116     decorator.decorate(project, context);
117
118     verify(context, never()).saveMeasure(any(Measure.class));
119   }
120
121   @Test
122   public void shouldGenerateWarnings() {
123     when(profile.getAlerts()).thenReturn(Arrays.asList(
124         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "100"),
125         new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "95.0"))); // generates warning because coverage 35% < 95%
126
127     decorator.decorate(project, context);
128
129     verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
130
131     verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
132     verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
133
134   }
135
136   @Test
137   public void globalStatusShouldBeErrorIfWarningsAndErrors() {
138     when(profile.getAlerts()).thenReturn(Arrays.asList(
139         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_SMALLER, null, "100"), // generates warning because classes 20 < 100
140         new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, "50.0", "80.0"))); // generates error because coverage 35% < 50%
141
142     decorator.decorate(project, context);
143
144     verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, null)));
145
146     verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
147     verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.ERROR)));
148   }
149
150   @Test
151   public void globalLabelShouldAggregateAllLabels() {
152     when(i18n.message(Mockito.any(Locale.class), Mockito.eq("metric.classes.name"), Mockito.isNull(String.class))).thenReturn("Classes");
153     when(i18n.message(Mockito.any(Locale.class), Mockito.eq("metric.coverage.name"), Mockito.isNull(String.class))).thenReturn("Coverages");
154     when(profile.getAlerts()).thenReturn(Arrays.asList(
155         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_SMALLER, null, "10000"), // there are 20 classes, error threshold is higher => alert
156         new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, "50.0", "80.0"))); // coverage is 35%, warning threshold is higher => alert
157
158     decorator.decorate(project, context);
159
160     verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "Classes < 10000, Coverages < 50.0")));
161   }
162
163   @Test
164   public void shouldBeOkIfPeriodVariationIsEnough() {
165     measureClasses.setVariation1(0d);
166     measureCoverage.setVariation2(50d);
167     measureComplexity.setVariation3(2d);
168
169     when(profile.getAlerts()).thenReturn(Arrays.asList(
170         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "10", 1), // ok because no variation
171         new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "40.0", 2), // ok because coverage increases of 50%, which is more than 40%
172         new Alert(null, CoreMetrics.COMPLEXITY, Alert.OPERATOR_GREATER, null, "5", 3) // ok because complexity increases of 2, which is less than 5
173     ));
174
175     decorator.decorate(project, context);
176
177     verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
178
179     verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
180     verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK)));
181     verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.OK)));
182   }
183
184   @Test
185   public void shouldGenerateWarningIfPeriodVariationIsNotEnough() {
186     measureClasses.setVariation1(40d);
187     measureCoverage.setVariation2(5d);
188     measureComplexity.setVariation3(70d);
189
190     when(profile.getAlerts()).thenReturn(Arrays.asList(
191         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 1),  // generates warning because classes increases of 40, which is greater than 30
192         new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "10.0", 2), // generates warning because coverage increases of 5%, which is smaller than 10%
193         new Alert(null, CoreMetrics.COMPLEXITY, Alert.OPERATOR_GREATER, null, "60", 3) // generates warning because complexity increases of 70, which is smaller than 60
194     ));
195
196     decorator.decorate(project, context);
197
198     verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
199
200     verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
201     verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
202     verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.WARN)));
203   }
204
205   @Test
206   public void shouldBeOkIfVariationIsNull() {
207     measureClasses.setVariation1(null);
208
209     when(profile.getAlerts()).thenReturn(Arrays.asList(
210         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "10", 1)
211     ));
212
213     decorator.decorate(project, context);
214
215     verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
216     verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
217   }
218
219   @Test
220   public void shouldVariationPeriodValueCouldBeUsedForRatingMetric() {
221     Metric ratingMetric = new Metric.Builder("key.rating", "name.rating", Metric.ValueType.RATING).create();
222     Measure measureRatingMetric = new Measure(ratingMetric, 150d);
223     measureRatingMetric.setVariation1(50d);
224     when(context.getMeasure(ratingMetric)).thenReturn(measureRatingMetric);
225
226     when(profile.getAlerts()).thenReturn(Arrays.asList(
227         new Alert(null, ratingMetric, Alert.OPERATOR_GREATER, null, "100", 1)
228     ));
229
230     decorator.decorate(project, context);
231
232     verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
233     verify(context).saveMeasure(argThat(hasLevel(measureRatingMetric, Metric.Level.OK)));
234   }
235
236   @Test(expected = IllegalStateException.class)
237   public void shouldAllowOnlyVariationPeriodOneGlobalPeriods() {
238     measureClasses.setVariation4(40d);
239
240     when(profile.getAlerts()).thenReturn(Arrays.asList(
241         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 4)
242     ));
243
244     decorator.decorate(project, context);
245   }
246
247   @Test(expected = NotImplementedException.class)
248   public void shouldNotAllowPeriodVariationAlertOnStringMetric() {
249     Measure measure = new Measure(CoreMetrics.SCM_AUTHORS_BY_LINE, 100d);
250     measure.setVariation1(50d);
251     when(context.getMeasure(CoreMetrics.SCM_AUTHORS_BY_LINE)).thenReturn(measure);
252
253     when(profile.getAlerts()).thenReturn(Arrays.asList(
254         new Alert(null, CoreMetrics.SCM_AUTHORS_BY_LINE, Alert.OPERATOR_GREATER, null, "30", 1)
255     ));
256
257     decorator.decorate(project, context);
258   }
259
260   @Test
261   public void shouldLabelAlertContainsPeriod() {
262     measureClasses.setVariation1(40d);
263
264     when(i18n.message(Mockito.any(Locale.class), Mockito.eq("metric.classes.name"), Mockito.isNull(String.class))).thenReturn("Classes");
265     when(periods.label(snapshot, 1)).thenReturn("since someday");
266
267     when(profile.getAlerts()).thenReturn(Arrays.asList(
268         new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 1) // generates warning because classes increases of 40, which is greater than 30
269     ));
270
271     decorator.decorate(project, context);
272
273     verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, "Classes variation > 30 since someday")));
274   }
275
276   private ArgumentMatcher<Measure> matchesMetric(final Metric metric, final Metric.Level alertStatus, final String alertText) {
277     return new ArgumentMatcher<Measure>() {
278       @Override
279       public boolean matches(Object arg) {
280         boolean result = ((Measure) arg).getMetric().equals(metric) && ((Measure) arg).getAlertStatus() == alertStatus;
281         if (result && alertText != null) {
282           result = alertText.equals(((Measure) arg).getAlertText());
283         }
284         return result;
285       }
286     };
287   }
288
289   private ArgumentMatcher<Measure> hasLevel(final Measure measure, final Metric.Level alertStatus) {
290     return new ArgumentMatcher<Measure>() {
291       @Override
292       public boolean matches(Object arg) {
293         return arg == measure && ((Measure) arg).getAlertStatus().equals(alertStatus);
294       }
295     };
296   }
297
298 }