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