3 * Copyright (C) 2009-2016 SonarSource SA
4 * mailto:contact AT sonarsource DOT com
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.
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.
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.sqale;
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;
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;
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;
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);
72 public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule();
74 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
76 public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
77 .add(CoreMetrics.NEW_TECHNICAL_DEBT)
78 .add(CoreMetrics.NCLOC_DATA)
79 .add(CoreMetrics.NEW_SQALE_DEBT_RATIO);
81 public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
83 public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
85 private SqaleRatingSettings sqaleRatingSettings = mock(SqaleRatingSettings.class);
87 private VisitorsCrawler underTest = new VisitorsCrawler(Arrays.<ComponentVisitor>asList(new SqaleNewMeasuresVisitor(metricRepository, measureRepository, scmInfoRepository,
88 periodsHolder, sqaleRatingSettings)));
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));
98 public void project_has_new_debt_ratio_variation_for_each_defined_period() {
99 treeRootHolder.setRoot(builder(PROJECT, ROOT_REF).build());
101 underTest.visit(treeRootHolder.getRoot());
103 assertNewDebtRatioValues(ROOT_REF, 0, 0);
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());
111 underTest.visit(treeRootHolder.getRoot());
113 assertNoNewDebtRatioMeasure(ROOT_REF);
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);
122 underTest.visit(treeRootHolder.getRoot());
124 assertNoNewDebtRatioMeasure(LANGUAGE_1_FILE_REF);
125 assertNoNewDebtRatioMeasure(ROOT_REF);
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)
133 builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build()
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));
141 underTest.visit(treeRootHolder.getRoot());
143 assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 0, 0);
144 assertNewDebtRatioValues(ROOT_REF, 0, 0);
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);
152 underTest.visit(treeRootHolder.getRoot());
154 assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 83.33, 0);
155 assertNewDebtRatioValues(ROOT_REF, 83.33, 0);
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);
163 underTest.visit(treeRootHolder.getRoot());
165 assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 8.33, 0);
166 assertNewDebtRatioValues(ROOT_REF, 8.33, 0);
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);
174 underTest.visit(treeRootHolder.getRoot());
176 assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 833.33, 0);
177 assertNewDebtRatioValues(ROOT_REF, 833.33, 0);
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);
185 underTest.visit(treeRootHolder.getRoot());
187 assertNoNewDebtRatioMeasure(LANGUAGE_1_FILE_REF);
188 assertNewDebtRatioValues(ROOT_REF, 0, 0);
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);
196 underTest.visit(treeRootHolder.getRoot());
198 assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 0, 0);
199 assertNewDebtRatioValues(ROOT_REF, 0, 0);
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);
207 underTest.visit(treeRootHolder.getRoot());
209 assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 0, 0);
210 assertNewDebtRatioValues(ROOT_REF, 0, 0);
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);
218 underTest.visit(treeRootHolder.getRoot());
220 assertNewDebtRatioValues(LANGUAGE_1_FILE_REF, 0, 0);
221 assertNewDebtRatioValues(ROOT_REF, 0, 0);
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)
232 builder(DIRECTORY, 111)
234 builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build()
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));
250 underTest.visit(treeRootHolder.getRoot());
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);
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);
263 treeRootHolder.setRoot(
264 builder(PROJECT, ROOT_REF)
266 builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(isUnitTest == Flag.UT_FILE, LANGUAGE_1_KEY)).build()
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));
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));
288 UT_FILE, SRC_FILE, NO_CHANGESET, WITH_CHANGESET, WITH_NCLOC, NO_NCLOC, MISSING_MEASURE_NCLOC
291 public static ReportComponent.Builder builder(Component.Type type, int ref) {
292 return ReportComponent.builder(type, ref).setKey(String.valueOf(ref));
295 private Measure createNewDebtMeasure(double period2Value, double period4Value) {
296 return newMeasureBuilder().setVariations(new MeasureVariations(null, period2Value, null, period4Value, null)).createNoValue();
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);
306 return newMeasureBuilder().create(KeyValueFormat.format(builder.build(), KeyValueFormat.newIntegerConverter(), KeyValueFormat.newIntegerConverter()));
309 private static Measure createNoNclocDataMeasure(int lineCount) {
310 ImmutableMap.Builder<Integer, Integer> builder = ImmutableMap.builder();
311 for (int i = 1; i <= lineCount; i++) {
314 return newMeasureBuilder().create(KeyValueFormat.format(builder.build(), KeyValueFormat.newIntegerConverter(), KeyValueFormat.newIntegerConverter()));
318 * Creates changesets of {@code lines} lines which all have the same date {@code scmDate}.
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;
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}.
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;
339 Changeset changetset2 = Changeset.newChangesetBuilder().setDate(otherScmDate).setRevision("rev-2").build();
340 for (int i = lineCount; i < lineCount + otherLineCount; i++) {
341 changesets[i] = changetset2;
346 private void assertNoNewDebtRatioMeasure(int componentRef) {
347 assertThat(measureRepository.getAddedRawMeasure(componentRef, CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY))
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);