]> source.dussan.org Git - sonarqube.git/blob
71a8e2775788b37852d947f51eb64b8c593508a7
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2016 SonarSource SA
4  * mailto:contact 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.sqale;
21
22 import com.google.common.collect.ImmutableMap;
23 import com.google.common.collect.ImmutableSet;
24 import com.google.common.collect.Ordering;
25 import java.util.Arrays;
26 import java.util.Set;
27 import org.assertj.core.data.Offset;
28 import org.junit.Before;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.sonar.api.measures.CoreMetrics;
32 import org.sonar.api.utils.KeyValueFormat;
33 import org.sonar.server.computation.batch.TreeRootHolderRule;
34 import org.sonar.server.computation.component.Component;
35 import org.sonar.server.computation.component.ComponentVisitor;
36 import org.sonar.server.computation.component.FileAttributes;
37 import org.sonar.server.computation.component.ReportComponent;
38 import org.sonar.server.computation.component.VisitorsCrawler;
39 import org.sonar.server.computation.measure.Measure;
40 import org.sonar.server.computation.measure.MeasureRepositoryRule;
41 import org.sonar.server.computation.measure.MeasureVariations;
42 import org.sonar.server.computation.metric.MetricRepositoryRule;
43 import org.sonar.server.computation.period.Period;
44 import org.sonar.server.computation.period.PeriodsHolderRule;
45 import org.sonar.server.computation.scm.Changeset;
46 import org.sonar.server.computation.scm.ScmInfoRepositoryRule;
47
48 import static com.google.common.base.Preconditions.checkArgument;
49 import static org.mockito.Mockito.mock;
50 import static org.mockito.Mockito.when;
51 import static org.sonar.api.measures.CoreMetrics.NCLOC_DATA_KEY;
52 import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT_KEY;
53 import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
54 import static org.sonar.server.computation.component.Component.Type.FILE;
55 import static org.sonar.server.computation.component.Component.Type.MODULE;
56 import static org.sonar.server.computation.component.Component.Type.PROJECT;
57 import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
58 import static org.sonar.server.computation.measure.MeasureAssert.assertThat;
59
60 public class SqaleNewMeasuresVisitorTest {
61   private static final String LANGUAGE_1_KEY = "language 1 key";
62   private static final long LANGUAGE_1_DEV_COST = 30l;
63   private static final long PERIOD_2_SNAPSHOT_DATE = 12323l;
64   private static final long PERIOD_5_SNAPSHOT_DATE = 99999999l;
65   private static final long SOME_SNAPSHOT_ID = 9993l;
66   private static final String SOME_PERIOD_MODE = "some mode";
67   private static final int ROOT_REF = 1;
68   private static final int LANGUAGE_1_FILE_REF = 11111;
69   private static final Offset<Double> VARIATION_COMPARISON_OFFSET = Offset.offset(0.01);
70
71   @Rule
72   public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule();
73   @Rule
74   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
75   @Rule
76   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
77     .add(CoreMetrics.NEW_TECHNICAL_DEBT)
78     .add(CoreMetrics.NCLOC_DATA)
79     .add(CoreMetrics.NEW_SQALE_DEBT_RATIO);
80   @Rule
81   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
82   @Rule
83   public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
84
85   private SqaleRatingSettings sqaleRatingSettings = mock(SqaleRatingSettings.class);
86
87   private VisitorsCrawler underTest = new VisitorsCrawler(Arrays.<ComponentVisitor>asList(new SqaleNewMeasuresVisitor(metricRepository, measureRepository, scmInfoRepository,
88     periodsHolder, sqaleRatingSettings)));
89
90   @Before
91   public void setUp() throws Exception {
92     periodsHolder.setPeriods(
93       new Period(2, SOME_PERIOD_MODE, null, PERIOD_2_SNAPSHOT_DATE, SOME_SNAPSHOT_ID),
94       new Period(4, SOME_PERIOD_MODE, null, PERIOD_5_SNAPSHOT_DATE, SOME_SNAPSHOT_ID));
95   }
96
97   @Test
98   public void project_has_new_debt_ratio_variation_for_each_defined_period() {
99     treeRootHolder.setRoot(builder(PROJECT, ROOT_REF).build());
100
101     underTest.visit(treeRootHolder.getRoot());
102
103     assertNewDebtRatioValues(ROOT_REF, 0, 0);
104   }
105
106   @Test
107   public void project_has_no_new_debt_ratio_variation_if_there_is_no_period() {
108     periodsHolder.setPeriods();
109     treeRootHolder.setRoot(builder(PROJECT, ROOT_REF).build());
110
111     underTest.visit(treeRootHolder.getRoot());
112
113     assertNoNewDebtRatioMeasure(ROOT_REF);
114   }
115
116   @Test
117   public void file_has_no_new_debt_ratio_variation_if_there_is_no_period() {
118     periodsHolder.setPeriods();
119     when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
120     setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
121
122     underTest.visit(treeRootHolder.getRoot());
123
124     assertNoNewDebtRatioMeasure(LANGUAGE_1_FILE_REF);
125     assertNoNewDebtRatioMeasure(ROOT_REF);
126   }
127
128   @Test
129   public void file_has_0_new_debt_ratio_if_all_scm_dates_are_before_snapshot_dates() {
130     treeRootHolder.setRoot(
131       builder(PROJECT, ROOT_REF)
132         .addChildren(
133           builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build()
134         )
135         .build()
136       );
137     measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(50, 12));
138     measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NCLOC_DATA_KEY, createNclocDataMeasure(2, 3, 4));
139     scmInfoRepository.setScmInfo(LANGUAGE_1_FILE_REF, createChangesets(PERIOD_2_SNAPSHOT_DATE - 100, 4));
140
141     underTest.visit(treeRootHolder.getRoot());
142
143     assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 0, 0);
144     assertNewDebtRatioValues(ROOT_REF, 0, 0);
145   }
146
147   @Test
148   public void file_has_new_debt_ratio_if_some_scm_dates_are_after_snapshot_dates() {
149     when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
150     setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
151
152     underTest.visit(treeRootHolder.getRoot());
153
154     assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 83.33, 0);
155     assertNewDebtRatioValues(ROOT_REF, 83.33, 0);
156   }
157
158   @Test
159   public void new_debt_ratio_changes_with_language_cost() {
160     when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST * 10);
161     setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
162
163     underTest.visit(treeRootHolder.getRoot());
164
165     assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 8.33, 0);
166     assertNewDebtRatioValues(ROOT_REF, 8.33, 0);
167   }
168
169   @Test
170   public void new_debt_ratio_changes_with_new_technical_debt() {
171     when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
172     setupOneFileAloneInAProject(500, 120, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
173
174     underTest.visit(treeRootHolder.getRoot());
175
176     assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 833.33, 0);
177     assertNewDebtRatioValues(ROOT_REF, 833.33, 0);
178   }
179
180   @Test
181   public void no_new_debt_ratio_when_file_is_unit_test() {
182     when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
183     setupOneFileAloneInAProject(50, 12, Flag.UT_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
184
185     underTest.visit(treeRootHolder.getRoot());
186
187     assertNoNewDebtRatioMeasure(LANGUAGE_1_FILE_REF);
188     assertNewDebtRatioValues(ROOT_REF, 0, 0);
189   }
190
191   @Test
192   public void new_debt_ratio_is_0_when_file_has_no_changesets() {
193     when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
194     setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.NO_CHANGESET);
195
196     underTest.visit(treeRootHolder.getRoot());
197
198     assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 0, 0);
199     assertNewDebtRatioValues(ROOT_REF, 0, 0);
200   }
201
202   @Test
203   public void new_debt_ratio_is_0_when_there_is_no_ncloc_in_file() {
204     when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
205     setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.NO_NCLOC, Flag.WITH_CHANGESET);
206
207     underTest.visit(treeRootHolder.getRoot());
208
209     assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 0, 0);
210     assertNewDebtRatioValues(ROOT_REF, 0, 0);
211   }
212
213   @Test
214   public void new_debt_ratio_is_0_when_ncloc_measure_is_missing() {
215     when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
216     setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.MISSING_MEASURE_NCLOC, Flag.WITH_CHANGESET);
217
218     underTest.visit(treeRootHolder.getRoot());
219
220     assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 0, 0);
221     assertNewDebtRatioValues(ROOT_REF, 0, 0);
222   }
223
224   @Test
225   public void no_leaf_components_always_have_a_measure_when_at_least_one_period_exist() {
226     when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
227     treeRootHolder.setRoot(
228       builder(PROJECT, ROOT_REF)
229         .addChildren(
230           builder(MODULE, 11)
231             .addChildren(
232               builder(DIRECTORY, 111)
233                 .addChildren(
234                   builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build()
235                 ).build()
236             ).build()
237         ).build()
238       );
239
240     Measure newDebtMeasure = createNewDebtMeasure(50, 12);
241     measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
242     measureRepository.addRawMeasure(111, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
243     measureRepository.addRawMeasure(11, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
244     measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
245     // 4 lines file, only first one is not ncloc
246     measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NCLOC_DATA_KEY, createNclocDataMeasure(2, 3, 4));
247     // first 2 lines are before all snapshots, 2 last lines are after PERIOD 2's snapshot date
248     scmInfoRepository.setScmInfo(LANGUAGE_1_FILE_REF, createChangesets(PERIOD_2_SNAPSHOT_DATE - 100, 2, PERIOD_2_SNAPSHOT_DATE + 100, 2));
249
250     underTest.visit(treeRootHolder.getRoot());
251
252     assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 83.33, 0);
253     assertNewDebtRatioValues(111, 83.33, 0);
254     assertNewDebtRatioValues(11, 83.33, 0);
255     assertNewDebtRatioValues(ROOT_REF, 83.33, 0);
256   }
257
258   private void setupOneFileAloneInAProject(int newDebtPeriod2, int newDebtPeriod4, Flag isUnitTest, Flag withNclocLines, Flag withChangeSets) {
259     checkArgument(isUnitTest == Flag.UT_FILE || isUnitTest == Flag.SRC_FILE);
260     checkArgument(withNclocLines == Flag.WITH_NCLOC || withNclocLines == Flag.NO_NCLOC || withNclocLines == Flag.MISSING_MEASURE_NCLOC);
261     checkArgument(withChangeSets == Flag.WITH_CHANGESET || withChangeSets == Flag.NO_CHANGESET);
262
263     treeRootHolder.setRoot(
264       builder(PROJECT, ROOT_REF)
265         .addChildren(
266           builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(isUnitTest == Flag.UT_FILE, LANGUAGE_1_KEY)).build()
267         )
268         .build()
269       );
270
271     Measure newDebtMeasure = createNewDebtMeasure(newDebtPeriod2, newDebtPeriod4);
272     measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
273     measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
274     if (withNclocLines == Flag.WITH_NCLOC) {
275       // 4 lines file, only first one is not ncloc
276       measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NCLOC_DATA_KEY, createNclocDataMeasure(2, 3, 4));
277     } else if (withNclocLines == Flag.NO_NCLOC) {
278       // 4 lines file, none of which is ncloc
279       measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NCLOC_DATA_KEY, createNoNclocDataMeasure(4));
280     }
281     if (withChangeSets == Flag.WITH_CHANGESET) {
282       // first 2 lines are before all snapshots, 2 last lines are after PERIOD 2's snapshot date
283       scmInfoRepository.setScmInfo(LANGUAGE_1_FILE_REF, createChangesets(PERIOD_2_SNAPSHOT_DATE - 100, 2, PERIOD_2_SNAPSHOT_DATE + 100, 2));
284     }
285   }
286
287   private enum Flag {
288     UT_FILE, SRC_FILE, NO_CHANGESET, WITH_CHANGESET, WITH_NCLOC, NO_NCLOC, MISSING_MEASURE_NCLOC
289   }
290
291   public static ReportComponent.Builder builder(Component.Type type, int ref) {
292     return ReportComponent.builder(type, ref).setKey(String.valueOf(ref));
293   }
294
295   private Measure createNewDebtMeasure(double period2Value, double period4Value) {
296     return newMeasureBuilder().setVariations(new MeasureVariations(null, period2Value, null, period4Value, null)).createNoValue();
297   }
298
299   private static Measure createNclocDataMeasure(Integer... nclocLines) {
300     Set<Integer> nclocLinesSet = ImmutableSet.copyOf(nclocLines);
301     int max = Ordering.<Integer>natural().max(nclocLinesSet);
302     ImmutableMap.Builder<Integer, Integer> builder = ImmutableMap.builder();
303     for (int i = 1; i <= max; i++) {
304       builder.put(i, nclocLinesSet.contains(i) ? 1 : 0);
305     }
306     return newMeasureBuilder().create(KeyValueFormat.format(builder.build(), KeyValueFormat.newIntegerConverter(), KeyValueFormat.newIntegerConverter()));
307   }
308
309   private static Measure createNoNclocDataMeasure(int lineCount) {
310     ImmutableMap.Builder<Integer, Integer> builder = ImmutableMap.builder();
311     for (int i = 1; i <= lineCount; i++) {
312       builder.put(i, 0);
313     }
314     return newMeasureBuilder().create(KeyValueFormat.format(builder.build(), KeyValueFormat.newIntegerConverter(), KeyValueFormat.newIntegerConverter()));
315   }
316
317   /**
318    * Creates changesets of {@code lines} lines which all have the same date {@code scmDate}.
319    */
320   private static Changeset[] createChangesets(long scmDate, int lines) {
321     Changeset changetset = Changeset.newChangesetBuilder().setDate(scmDate).setRevision("rev-1").build();
322     Changeset[] changesets = new Changeset[lines];
323     for (int i = 0; i < lines; i++) {
324       changesets[i] = changetset;
325     }
326     return changesets;
327   }
328
329   /**
330    * Creates a changeset of {@code lineCount} lines which have the date {@code scmDate} and {@code otherLineCount} lines which
331    * have the date {@code otherScmDate}.
332    */
333   private static Changeset[] createChangesets(long scmDate, int lineCount, long otherScmDate, int otherLineCount) {
334     Changeset[] changesets = new Changeset[lineCount + otherLineCount];
335     Changeset changetset1 = Changeset.newChangesetBuilder().setDate(scmDate).setRevision("rev-1").build();
336     for (int i = 0; i < lineCount; i++) {
337       changesets[i] = changetset1;
338     }
339     Changeset changetset2 = Changeset.newChangesetBuilder().setDate(otherScmDate).setRevision("rev-2").build();
340     for (int i = lineCount; i < lineCount + otherLineCount; i++) {
341       changesets[i] = changetset2;
342     }
343     return changesets;
344   }
345
346   private void assertNoNewDebtRatioMeasure(int componentRef) {
347     assertThat(measureRepository.getAddedRawMeasure(componentRef, CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY))
348       .isAbsent();
349   }
350
351   private void assertNewDebtRatioValues(int componentRef, double expectedPeriod2Value, double expectedPeriod4Value) {
352     assertThat(measureRepository.getAddedRawMeasure(componentRef, CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY))
353       .hasVariation2(expectedPeriod2Value, VARIATION_COMPARISON_OFFSET)
354       .hasVariation4(expectedPeriod4Value, VARIATION_COMPARISON_OFFSET);
355   }
356 }