2 * SonarQube, open source software quality management tool.
3 * Copyright (C) 2008-2014 SonarSource
4 * mailto:contact AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.computation.step;
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;
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;
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;
65 public class QualityProfileEventsStepTest {
67 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
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";
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);
81 private Metric qualityProfileMetric = mock(Metric.class);
83 private QualityProfileEventsStep underTest = new QualityProfileEventsStep(treeRootHolder, metricRepository, measureRepository, languageRepository, eventRepository);
87 when(metricRepository.getByKey(CoreMetrics.QUALITY_PROFILES_KEY)).thenReturn(qualityProfileMetric);
88 treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("uuid").setKey("key").build());
92 public void no_effect_if_no_base_measure() {
93 when(measureRepository.getBaseMeasure(treeRootHolder.getRoot(), qualityProfileMetric)).thenReturn(Optional.<Measure>absent());
97 verifyNoMoreInteractions(eventRepository);
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());
109 public void no_event_if_no_base_nor_row_QualityProfile_measure() {
110 mockMeasures(treeRootHolder.getRoot(), null, null);
114 verifyNoMoreInteractions(eventRepository);
118 public void added_event_if_one_new_qp() {
119 QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1);
121 Language language = mockLanguageInRepository(LANGUAGE_KEY_1);
122 mockMeasures(treeRootHolder.getRoot(), null, arrayOf(qp));
126 verify(eventRepository).add(eq(treeRootHolder.getRoot()), eventArgumentCaptor.capture());
127 verifyNoMoreInteractions(eventRepository);
128 verifyEvent(eventArgumentCaptor.getValue(), "Use '" + qp.getQpName() + "' (" + language.getName() + ")", null);
132 public void added_event_uses_language_key_in_message_if_language_not_found() {
133 QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1);
135 mockLanguageNotInRepository(LANGUAGE_KEY_1);
136 mockMeasures(treeRootHolder.getRoot(), null, arrayOf(qp));
140 verify(eventRepository).add(eq(treeRootHolder.getRoot()), eventArgumentCaptor.capture());
141 verifyNoMoreInteractions(eventRepository);
142 verifyEvent(eventArgumentCaptor.getValue(), "Use '" + qp.getQpName() + "' (" + qp.getLanguageKey() + ")", null);
146 public void no_more_used_event_if_qp_no_more_listed() {
147 QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1);
149 mockMeasures(treeRootHolder.getRoot(), arrayOf(qp), null);
150 Language language = mockLanguageInRepository(LANGUAGE_KEY_1);
154 verify(eventRepository).add(eq(treeRootHolder.getRoot()), eventArgumentCaptor.capture());
155 verifyNoMoreInteractions(eventRepository);
156 verifyEvent(eventArgumentCaptor.getValue(), "Stop using '" + qp.getQpName() + "' (" + language.getName() + ")", null);
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);
163 mockMeasures(treeRootHolder.getRoot(), arrayOf(qp), null);
164 mockLanguageNotInRepository(LANGUAGE_KEY_1);
168 verify(eventRepository).add(eq(treeRootHolder.getRoot()), eventArgumentCaptor.capture());
169 verifyNoMoreInteractions(eventRepository);
170 verifyEvent(eventArgumentCaptor.getValue(), "Stop using '" + qp.getQpName() + "' (" + qp.getLanguageKey() + ")", null);
174 public void no_event_if_same_qp_with_same_date() {
175 QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1);
177 mockMeasures(treeRootHolder.getRoot(), arrayOf(qp), arrayOf(qp));
181 verify(eventRepository, never()).add(any(Component.class), any(Event.class));
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"));
189 mockMeasures(treeRootHolder.getRoot(), arrayOf(qp1), arrayOf(qp2));
190 Language language = mockLanguageInRepository(LANGUAGE_KEY_1);
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")));
203 public void verify_detection_with_complex_mix_of_qps() {
204 final Set<Event> events = new HashSet<>();
205 doAnswer(new Answer() {
207 public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
208 events.add((Event) invocationOnMock.getArguments()[1]);
211 }).when(eventRepository).add(eq(treeRootHolder.getRoot()), any(Event.class));
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"))
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)
224 mockNoLanguageInRepository();
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 + ")"
236 private Language mockLanguageInRepository(String languageKey) {
237 Language language = new AbstractLanguage(languageKey, languageKey + "_name") {
239 public String[] getFileSuffixes() {
240 return new String[0];
243 when(languageRepository.find(languageKey)).thenReturn(Optional.of(language));
247 private void mockLanguageNotInRepository(String languageKey) {
248 when(languageRepository.find(languageKey)).thenReturn(Optional.<Language>absent());
251 private void mockNoLanguageInRepository() {
252 when(languageRepository.find(anyString())).thenReturn(Optional.<Language>absent());
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)));
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();
267 private static QualityProfile qp(String qpName, String languageKey) {
268 return qp(qpName, languageKey, new Date());
271 private static QualityProfile qp(String qpName, String languageKey, Date date) {
272 return new QualityProfile(qpName + "-" + languageKey, qpName, languageKey, date);
276 * Just a trick to use variable args which is shorter than writing new QualityProfile[] { }
278 private static QualityProfile[] arrayOf(QualityProfile... qps) {
282 private static Measure newMeasure(@Nullable QualityProfile... qps) {
283 return Measure.newMeasureBuilder().create(toJson(qps));
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));