]> source.dussan.org Git - sonarqube.git/blob
ac4b52ea09c97a65d7b4a779025dc97702c2a0c6
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2014 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
6  * SonarQube 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  * SonarQube 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.step;
21
22 import com.google.common.base.Optional;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Date;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Set;
29 import javax.annotation.Nullable;
30 import org.junit.Before;
31 import org.junit.Rule;
32 import org.junit.Test;
33 import org.mockito.ArgumentCaptor;
34 import org.mockito.invocation.InvocationOnMock;
35 import org.mockito.stubbing.Answer;
36 import org.sonar.api.measures.CoreMetrics;
37 import org.sonar.api.resources.AbstractLanguage;
38 import org.sonar.api.resources.Language;
39 import org.sonar.core.util.UtcDateUtils;
40 import org.sonar.server.computation.batch.TreeRootHolderRule;
41 import org.sonar.server.computation.component.Component;
42 import org.sonar.server.computation.component.ReportComponent;
43 import org.sonar.server.computation.event.Event;
44 import org.sonar.server.computation.event.EventRepository;
45 import org.sonar.server.computation.language.LanguageRepository;
46 import org.sonar.server.computation.measure.Measure;
47 import org.sonar.server.computation.measure.MeasureRepository;
48 import org.sonar.server.computation.metric.Metric;
49 import org.sonar.server.computation.metric.MetricRepository;
50 import org.sonar.server.computation.qualityprofile.QPMeasureData;
51 import org.sonar.server.computation.qualityprofile.QualityProfile;
52
53 import static org.assertj.core.api.Assertions.assertThat;
54 import static org.mockito.Matchers.any;
55 import static org.mockito.Matchers.anyString;
56 import static org.mockito.Matchers.eq;
57 import static org.mockito.Mockito.doAnswer;
58 import static org.mockito.Mockito.mock;
59 import static org.mockito.Mockito.never;
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.api.utils.DateUtils.parseDateTime;
64
65 public class QualityProfileEventsStepTest {
66   @Rule
67   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
68
69   private static final String QP_NAME_1 = "qp_1";
70   private static final String QP_NAME_2 = "qp_2";
71   private static final String LANGUAGE_KEY_1 = "language_key1";
72   private static final String LANGUAGE_KEY_2 = "language_key_2";
73   private static final String LANGUAGE_KEY_3 = "languageKey3";
74
75   private MetricRepository metricRepository = mock(MetricRepository.class);
76   private MeasureRepository measureRepository = mock(MeasureRepository.class);
77   private LanguageRepository languageRepository = mock(LanguageRepository.class);
78   private EventRepository eventRepository = mock(EventRepository.class);
79   private ArgumentCaptor<Event> eventArgumentCaptor = ArgumentCaptor.forClass(Event.class);
80
81   private Metric qualityProfileMetric = mock(Metric.class);
82
83   private QualityProfileEventsStep underTest = new QualityProfileEventsStep(treeRootHolder, metricRepository, measureRepository, languageRepository, eventRepository);
84
85   @Before
86   public void setUp() {
87     when(metricRepository.getByKey(CoreMetrics.QUALITY_PROFILES_KEY)).thenReturn(qualityProfileMetric);
88     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("uuid").setKey("key").build());
89   }
90
91   @Test
92   public void no_effect_if_no_base_measure() {
93     when(measureRepository.getBaseMeasure(treeRootHolder.getRoot(), qualityProfileMetric)).thenReturn(Optional.<Measure>absent());
94
95     underTest.execute();
96
97     verifyNoMoreInteractions(eventRepository);
98   }
99
100   @Test(expected = IllegalStateException.class)
101   public void ISE_if_no_raw_measure() {
102     when(measureRepository.getBaseMeasure(treeRootHolder.getRoot(), qualityProfileMetric)).thenReturn(Optional.of(newMeasure()));
103     when(measureRepository.getRawMeasure(treeRootHolder.getRoot(), qualityProfileMetric)).thenReturn(Optional.<Measure>absent());
104
105     underTest.execute();
106   }
107
108   @Test
109   public void no_event_if_no_base_nor_row_QualityProfile_measure() {
110     mockMeasures(treeRootHolder.getRoot(), null, null);
111
112     underTest.execute();
113
114     verifyNoMoreInteractions(eventRepository);
115   }
116
117   @Test
118   public void added_event_if_one_new_qp() {
119     QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1);
120
121     Language language = mockLanguageInRepository(LANGUAGE_KEY_1);
122     mockMeasures(treeRootHolder.getRoot(), null, arrayOf(qp));
123
124     underTest.execute();
125
126     verify(eventRepository).add(eq(treeRootHolder.getRoot()), eventArgumentCaptor.capture());
127     verifyNoMoreInteractions(eventRepository);
128     verifyEvent(eventArgumentCaptor.getValue(), "Use '" + qp.getQpName() + "' (" + language.getName() + ")", null);
129   }
130
131   @Test
132   public void added_event_uses_language_key_in_message_if_language_not_found() {
133     QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1);
134
135     mockLanguageNotInRepository(LANGUAGE_KEY_1);
136     mockMeasures(treeRootHolder.getRoot(), null, arrayOf(qp));
137
138     underTest.execute();
139
140     verify(eventRepository).add(eq(treeRootHolder.getRoot()), eventArgumentCaptor.capture());
141     verifyNoMoreInteractions(eventRepository);
142     verifyEvent(eventArgumentCaptor.getValue(), "Use '" + qp.getQpName() + "' (" + qp.getLanguageKey() + ")", null);
143   }
144
145   @Test
146   public void no_more_used_event_if_qp_no_more_listed() {
147     QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1);
148
149     mockMeasures(treeRootHolder.getRoot(), arrayOf(qp), null);
150     Language language = mockLanguageInRepository(LANGUAGE_KEY_1);
151
152     underTest.execute();
153
154     verify(eventRepository).add(eq(treeRootHolder.getRoot()), eventArgumentCaptor.capture());
155     verifyNoMoreInteractions(eventRepository);
156     verifyEvent(eventArgumentCaptor.getValue(), "Stop using '" + qp.getQpName() + "' (" + language.getName() + ")", null);
157   }
158
159   @Test
160   public void no_more_used_event_uses_language_key_in_message_if_language_not_found() {
161     QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1);
162
163     mockMeasures(treeRootHolder.getRoot(), arrayOf(qp), null);
164     mockLanguageNotInRepository(LANGUAGE_KEY_1);
165
166     underTest.execute();
167
168     verify(eventRepository).add(eq(treeRootHolder.getRoot()), eventArgumentCaptor.capture());
169     verifyNoMoreInteractions(eventRepository);
170     verifyEvent(eventArgumentCaptor.getValue(), "Stop using '" + qp.getQpName() + "' (" + qp.getLanguageKey() + ")", null);
171   }
172
173   @Test
174   public void no_event_if_same_qp_with_same_date() {
175     QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1);
176
177     mockMeasures(treeRootHolder.getRoot(), arrayOf(qp), arrayOf(qp));
178
179     underTest.execute();
180
181     verify(eventRepository, never()).add(any(Component.class), any(Event.class));
182   }
183
184   @Test
185   public void changed_event_if_same_qp_but_no_same_date() {
186     QualityProfile qp1 = qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:13+0100"));
187     QualityProfile qp2 = qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:17+0100"));
188
189     mockMeasures(treeRootHolder.getRoot(), arrayOf(qp1), arrayOf(qp2));
190     Language language = mockLanguageInRepository(LANGUAGE_KEY_1);
191
192     underTest.execute();
193
194     verify(eventRepository).add(eq(treeRootHolder.getRoot()), eventArgumentCaptor.capture());
195     verifyNoMoreInteractions(eventRepository);
196     verifyEvent(eventArgumentCaptor.getValue(),
197       "Changes in '" + qp2.getQpName() + "' (" + language.getName() + ")",
198       "from=" + UtcDateUtils.formatDateTime(parseDateTime("2011-04-25T01:05:14+0100")) + ";key=" + qp1.getQpKey() + ";to="
199         + UtcDateUtils.formatDateTime(parseDateTime("2011-04-25T01:05:18+0100")));
200   }
201
202   @Test
203   public void verify_detection_with_complex_mix_of_qps() {
204     final Set<Event> events = new HashSet<>();
205     doAnswer(new Answer() {
206       @Override
207       public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
208         events.add((Event) invocationOnMock.getArguments()[1]);
209         return null;
210       }
211     }).when(eventRepository).add(eq(treeRootHolder.getRoot()), any(Event.class));
212
213     mockMeasures(
214       treeRootHolder.getRoot(), arrayOf(
215         qp(QP_NAME_2, LANGUAGE_KEY_1),
216         qp(QP_NAME_2, LANGUAGE_KEY_2),
217         qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:13+0100"))
218       ),
219       arrayOf(
220         qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:17+0100")),
221         qp(QP_NAME_2, LANGUAGE_KEY_2),
222         qp(QP_NAME_2, LANGUAGE_KEY_3)
223       ));
224     mockNoLanguageInRepository();
225
226     underTest.execute();
227
228     assertThat(events).extracting("name").containsOnly(
229       "Stop using '" + QP_NAME_2 + "' (" + LANGUAGE_KEY_1 + ")",
230       "Use '" + QP_NAME_2 + "' (" + LANGUAGE_KEY_3 + ")",
231       "Changes in '" + QP_NAME_1 + "' (" + LANGUAGE_KEY_1 + ")"
232       );
233
234   }
235
236   private Language mockLanguageInRepository(String languageKey) {
237     Language language = new AbstractLanguage(languageKey, languageKey + "_name") {
238       @Override
239       public String[] getFileSuffixes() {
240         return new String[0];
241       }
242     };
243     when(languageRepository.find(languageKey)).thenReturn(Optional.of(language));
244     return language;
245   }
246
247   private void mockLanguageNotInRepository(String languageKey) {
248     when(languageRepository.find(languageKey)).thenReturn(Optional.<Language>absent());
249   }
250
251   private void mockNoLanguageInRepository() {
252     when(languageRepository.find(anyString())).thenReturn(Optional.<Language>absent());
253   }
254
255   private void mockMeasures(Component component, @Nullable QualityProfile[] previous, @Nullable QualityProfile[] current) {
256     when(measureRepository.getBaseMeasure(component, qualityProfileMetric)).thenReturn(Optional.of(newMeasure(previous)));
257     when(measureRepository.getRawMeasure(component, qualityProfileMetric)).thenReturn(Optional.of(newMeasure(current)));
258   }
259
260   private static void verifyEvent(Event event, String expectedName, @Nullable String expectedData) {
261     assertThat(event.getName()).isEqualTo(expectedName);
262     assertThat(event.getData()).isEqualTo(expectedData);
263     assertThat(event.getCategory()).isEqualTo(Event.Category.PROFILE);
264     assertThat(event.getDescription()).isNull();
265   }
266
267   private static QualityProfile qp(String qpName, String languageKey) {
268     return qp(qpName, languageKey, new Date());
269   }
270
271   private static QualityProfile qp(String qpName, String languageKey, Date date) {
272     return new QualityProfile(qpName + "-" + languageKey, qpName, languageKey, date);
273   }
274
275   /**
276    * Just a trick to use variable args which is shorter than writing new QualityProfile[] { }
277    */
278   private static QualityProfile[] arrayOf(QualityProfile... qps) {
279     return qps;
280   }
281
282   private static Measure newMeasure(@Nullable QualityProfile... qps) {
283     return Measure.newMeasureBuilder().create(toJson(qps));
284   }
285
286   private static String toJson(@Nullable QualityProfile... qps) {
287     List<QualityProfile> qualityProfiles = qps == null ? Collections.<QualityProfile>emptyList() : Arrays.asList(qps);
288     return QPMeasureData.toJson(new QPMeasureData(qualityProfiles));
289   }
290
291 }