]> source.dussan.org Git - sonarqube.git/blob
4dd134731240ca9e2448040627edbba75d11dad4
[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.task.projectanalysis.qualitymodel;
21
22 import org.junit.Before;
23 import org.junit.Rule;
24 import org.junit.Test;
25 import org.sonar.server.computation.task.projectanalysis.component.Component;
26 import org.sonar.server.computation.task.projectanalysis.component.FileAttributes;
27 import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
28 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
29 import org.sonar.server.computation.task.projectanalysis.component.VisitorsCrawler;
30 import org.sonar.server.computation.task.projectanalysis.issue.ComponentIssuesRepositoryRule;
31 import org.sonar.server.computation.task.projectanalysis.measure.Measure;
32 import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepositoryRule;
33 import org.sonar.server.computation.task.projectanalysis.metric.MetricRepositoryRule;
34 import org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating;
35
36 import static java.util.Collections.singletonList;
37 import static org.assertj.core.api.Assertions.assertThat;
38 import static org.mockito.Mockito.mock;
39 import static org.mockito.Mockito.when;
40 import static org.sonar.api.measures.CoreMetrics.DEVELOPMENT_COST;
41 import static org.sonar.api.measures.CoreMetrics.DEVELOPMENT_COST_KEY;
42 import static org.sonar.api.measures.CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A;
43 import static org.sonar.api.measures.CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY;
44 import static org.sonar.api.measures.CoreMetrics.NCLOC;
45 import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
46 import static org.sonar.api.measures.CoreMetrics.SQALE_DEBT_RATIO;
47 import static org.sonar.api.measures.CoreMetrics.SQALE_DEBT_RATIO_KEY;
48 import static org.sonar.api.measures.CoreMetrics.SQALE_RATING;
49 import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY;
50 import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT;
51 import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
52 import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.DIRECTORY;
53 import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.FILE;
54 import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.MODULE;
55 import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT;
56 import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
57 import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;
58 import static org.sonar.server.computation.task.projectanalysis.measure.MeasureRepoEntry.entryOf;
59 import static org.sonar.server.computation.task.projectanalysis.measure.MeasureRepoEntry.toEntries;
60 import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.A;
61 import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.C;
62 import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.E;
63
64 public class MaintainabilityMeasuresVisitorForReportTest {
65
66   static final String LANGUAGE_KEY_1 = "lKey1";
67   static final String LANGUAGE_KEY_2 = "lKey2";
68
69   static final double[] RATING_GRID = new double[] {0.1, 0.2, 0.5, 1};
70
71   static final long DEV_COST_LANGUAGE_1 = 30;
72   static final long DEV_COST_LANGUAGE_2 = 42;
73
74   static final int PROJECT_REF = 1;
75   static final int MODULE_REF = 12;
76   static final int DIRECTORY_REF = 123;
77   static final int FILE_1_REF = 1231;
78   static final int FILE_2_REF = 1232;
79
80   static final Component ROOT_PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project")
81     .addChildren(
82       builder(MODULE, MODULE_REF).setKey("module")
83         .addChildren(
84           builder(DIRECTORY, DIRECTORY_REF).setKey("directory")
85             .addChildren(
86               builder(FILE, FILE_1_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_KEY_1)).setKey("file1").build(),
87               builder(FILE, FILE_2_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_KEY_1)).setKey("file2").build())
88             .build())
89         .build())
90     .build();
91
92   @Rule
93   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
94
95   @Rule
96   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
97     .add(NCLOC)
98     .add(DEVELOPMENT_COST)
99     .add(TECHNICAL_DEBT)
100     .add(SQALE_DEBT_RATIO)
101     .add(SQALE_RATING)
102     .add(EFFORT_TO_REACH_MAINTAINABILITY_RATING_A);
103
104   @Rule
105   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
106
107   @Rule
108   public ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder);
109
110   private RatingSettings ratingSettings = mock(RatingSettings.class);
111
112   private VisitorsCrawler underTest;
113
114   @Before
115   public void setUp() {
116     // assumes rating configuration is consistent
117     when(ratingSettings.getRatingGrid()).thenReturn(new RatingGrid(RATING_GRID));
118     when(ratingSettings.getDevCost(LANGUAGE_KEY_1)).thenReturn(DEV_COST_LANGUAGE_1);
119     when(ratingSettings.getDevCost(LANGUAGE_KEY_2)).thenReturn(DEV_COST_LANGUAGE_2);
120
121     underTest = new VisitorsCrawler(singletonList(new MaintainabilityMeasuresVisitor(metricRepository, measureRepository, ratingSettings)));
122   }
123
124   @Test
125   public void measures_created_for_project_are_all_zero_when_they_have_no_FILE_child() {
126     ReportComponent root = builder(PROJECT, 1).build();
127     treeRootHolder.setRoot(root);
128
129     underTest.visit(root);
130
131     assertThat(toEntries(measureRepository.getRawMeasures(root)))
132       .containsOnly(
133         entryOf(DEVELOPMENT_COST_KEY, newMeasureBuilder().create("0")),
134         entryOf(SQALE_DEBT_RATIO_KEY, newMeasureBuilder().create(0d, 1)),
135         entryOf(SQALE_RATING_KEY, createMaintainabilityRatingMeasure(A)),
136         entryOf(EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY, newMeasureBuilder().create(0L)));
137   }
138
139   @Test
140   public void compute_development_cost() {
141     ReportComponent root = builder(PROJECT, 1)
142       .addChildren(
143         builder(MODULE, 11)
144           .addChildren(
145             builder(DIRECTORY, 111)
146               .addChildren(
147                 createFileComponent(LANGUAGE_KEY_1, 1111),
148                 createFileComponent(LANGUAGE_KEY_2, 1112),
149                 // Unit test should not be ignored
150                 builder(FILE, 1113).setFileAttributes(new FileAttributes(true, LANGUAGE_KEY_1)).build())
151               .build(),
152             builder(DIRECTORY, 112)
153               .addChildren(
154                 createFileComponent(LANGUAGE_KEY_2, 1121))
155               .build())
156           .build(),
157         builder(MODULE, 12)
158           .addChildren(
159             builder(DIRECTORY, 121)
160               .addChildren(
161                 createFileComponent(LANGUAGE_KEY_1, 1211))
162               .build(),
163             builder(DIRECTORY, 122).build())
164           .build(),
165         builder(MODULE, 13).build())
166       .build();
167
168     treeRootHolder.setRoot(root);
169
170     int ncloc1111 = 10;
171     addRawMeasure(NCLOC_KEY, 1111, ncloc1111);
172
173     int ncloc1112 = 12;
174     addRawMeasure(NCLOC_KEY, 1112, ncloc1112);
175
176     int ncloc1113 = 15;
177     addRawMeasure(NCLOC_KEY, 1113, ncloc1113);
178
179     int nclocValue1121 = 30;
180     addRawMeasure(NCLOC_KEY, 1121, nclocValue1121);
181
182     int ncloc1211 = 20;
183     addRawMeasure(NCLOC_KEY, 1211, ncloc1211);
184
185     underTest.visit(root);
186
187     // verify measures on files
188     verifyAddedRawMeasure(1111, DEVELOPMENT_COST_KEY, Long.toString(ncloc1111 * DEV_COST_LANGUAGE_1));
189     verifyAddedRawMeasure(1112, DEVELOPMENT_COST_KEY, Long.toString(ncloc1112 * DEV_COST_LANGUAGE_2));
190     verifyAddedRawMeasure(1113, DEVELOPMENT_COST_KEY, Long.toString(ncloc1113 * DEV_COST_LANGUAGE_1));
191     verifyAddedRawMeasure(1121, DEVELOPMENT_COST_KEY, Long.toString(nclocValue1121 * DEV_COST_LANGUAGE_2));
192     verifyAddedRawMeasure(1211, DEVELOPMENT_COST_KEY, Long.toString(ncloc1211 * DEV_COST_LANGUAGE_1));
193
194     // directory has no children => no file => 0 everywhere and A rating
195     verifyAddedRawMeasure(122, DEVELOPMENT_COST_KEY, "0");
196
197     // directory has children => dev cost is aggregated
198     verifyAddedRawMeasure(111, DEVELOPMENT_COST_KEY, Long.toString(
199       ncloc1111 * DEV_COST_LANGUAGE_1 +
200         ncloc1112 * DEV_COST_LANGUAGE_2 +
201         ncloc1113 * DEV_COST_LANGUAGE_1));
202     verifyAddedRawMeasure(112, DEVELOPMENT_COST_KEY, Long.toString(nclocValue1121 * DEV_COST_LANGUAGE_2));
203     verifyAddedRawMeasure(121, DEVELOPMENT_COST_KEY, Long.toString(ncloc1211 * DEV_COST_LANGUAGE_1));
204
205     // just for fun, we didn't define any debt on module => they must all have rating A
206     verifyAddedRawMeasure(11, DEVELOPMENT_COST_KEY, Long.toString(
207       ncloc1111 * DEV_COST_LANGUAGE_1 +
208         ncloc1112 * DEV_COST_LANGUAGE_2 +
209         ncloc1113 * DEV_COST_LANGUAGE_1 +
210         nclocValue1121 * DEV_COST_LANGUAGE_2));
211     verifyAddedRawMeasure(12, DEVELOPMENT_COST_KEY, Long.toString(ncloc1211 * DEV_COST_LANGUAGE_1));
212     verifyAddedRawMeasure(13, DEVELOPMENT_COST_KEY, "0");
213     verifyAddedRawMeasure(1, DEVELOPMENT_COST_KEY, Long.toString(
214       ncloc1111 * DEV_COST_LANGUAGE_1 +
215         ncloc1112 * DEV_COST_LANGUAGE_2 +
216         ncloc1113 * DEV_COST_LANGUAGE_1 +
217         nclocValue1121 * DEV_COST_LANGUAGE_2 +
218         ncloc1211 * DEV_COST_LANGUAGE_1));
219   }
220
221   @Test
222   public void compute_maintainability_debt_ratio_measure() throws Exception {
223     treeRootHolder.setRoot(ROOT_PROJECT);
224
225     int file1Ncloc = 10;
226     addRawMeasure(NCLOC_KEY, FILE_1_REF, file1Ncloc);
227     long file1MaintainabilityCost = 100L;
228     addRawMeasure(TECHNICAL_DEBT_KEY, FILE_1_REF, file1MaintainabilityCost);
229
230     int file2Ncloc = 5;
231     addRawMeasure(NCLOC_KEY, FILE_2_REF, file2Ncloc);
232     long file2MaintainabilityCost = 1L;
233     addRawMeasure(TECHNICAL_DEBT_KEY, FILE_2_REF, file2MaintainabilityCost);
234
235     long directoryMaintainabilityCost = 100L;
236     addRawMeasure(TECHNICAL_DEBT_KEY, DIRECTORY_REF, directoryMaintainabilityCost);
237
238     long moduleMaintainabilityCost = 100L;
239     addRawMeasure(TECHNICAL_DEBT_KEY, MODULE_REF, moduleMaintainabilityCost);
240
241     long projectMaintainabilityCost = 1000L;
242     addRawMeasure(TECHNICAL_DEBT_KEY, PROJECT_REF, projectMaintainabilityCost);
243
244     underTest.visit(ROOT_PROJECT);
245
246     verifyAddedRawMeasure(FILE_1_REF, SQALE_DEBT_RATIO_KEY, file1MaintainabilityCost * 1d / (file1Ncloc * DEV_COST_LANGUAGE_1) * 100);
247     verifyAddedRawMeasure(FILE_2_REF, SQALE_DEBT_RATIO_KEY, file2MaintainabilityCost * 1d / (file2Ncloc * DEV_COST_LANGUAGE_1) * 100);
248     verifyAddedRawMeasure(DIRECTORY_REF, SQALE_DEBT_RATIO_KEY, directoryMaintainabilityCost * 1d / ((file1Ncloc + file2Ncloc) * DEV_COST_LANGUAGE_1) * 100);
249     verifyAddedRawMeasure(MODULE_REF, SQALE_DEBT_RATIO_KEY, moduleMaintainabilityCost * 1d / ((file1Ncloc + file2Ncloc) * DEV_COST_LANGUAGE_1) * 100);
250     verifyAddedRawMeasure(PROJECT_REF, SQALE_DEBT_RATIO_KEY, projectMaintainabilityCost * 1d / ((file1Ncloc + file2Ncloc) * DEV_COST_LANGUAGE_1) * 100);
251   }
252
253   @Test
254   public void compute_maintainability_rating_measure() throws Exception {
255     treeRootHolder.setRoot(ROOT_PROJECT);
256
257     addRawMeasure(NCLOC_KEY, FILE_1_REF, 10);
258     addRawMeasure(TECHNICAL_DEBT_KEY, FILE_1_REF, 100L);
259
260     addRawMeasure(NCLOC_KEY, FILE_2_REF, 5);
261     addRawMeasure(TECHNICAL_DEBT_KEY, FILE_2_REF, 1L);
262
263     addRawMeasure(TECHNICAL_DEBT_KEY, DIRECTORY_REF, 100L);
264     addRawMeasure(TECHNICAL_DEBT_KEY, MODULE_REF, 100L);
265     addRawMeasure(TECHNICAL_DEBT_KEY, PROJECT_REF, 1000L);
266
267     underTest.visit(ROOT_PROJECT);
268
269     verifyAddedRawMeasure(FILE_1_REF, SQALE_RATING_KEY, C);
270     verifyAddedRawMeasure(FILE_2_REF, SQALE_RATING_KEY, A);
271     verifyAddedRawMeasure(DIRECTORY_REF, SQALE_RATING_KEY, C);
272     verifyAddedRawMeasure(MODULE_REF, SQALE_RATING_KEY, C);
273     verifyAddedRawMeasure(PROJECT_REF, SQALE_RATING_KEY, E);
274   }
275
276   @Test
277   public void compute_effort_to_maintainability_rating_A_measure() throws Exception {
278     treeRootHolder.setRoot(ROOT_PROJECT);
279
280     int file1Ncloc = 10;
281     long file1Effort = 100L;
282     addRawMeasure(NCLOC_KEY, FILE_1_REF, file1Ncloc);
283     addRawMeasure(TECHNICAL_DEBT_KEY, FILE_1_REF, file1Effort);
284
285     int file2Ncloc = 5;
286     long file2Effort = 20L;
287     addRawMeasure(NCLOC_KEY, FILE_2_REF, file2Ncloc);
288     addRawMeasure(TECHNICAL_DEBT_KEY, FILE_2_REF, file2Effort);
289
290     long dirEffort = 120L;
291     addRawMeasure(TECHNICAL_DEBT_KEY, DIRECTORY_REF, dirEffort);
292
293     long moduleEffort = 120L;
294     addRawMeasure(TECHNICAL_DEBT_KEY, MODULE_REF, moduleEffort);
295
296     long projectEffort = 150L;
297     addRawMeasure(TECHNICAL_DEBT_KEY, PROJECT_REF, projectEffort);
298
299     underTest.visit(ROOT_PROJECT);
300
301     verifyAddedRawMeasure(FILE_1_REF, EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY,
302       (long) (file1Effort - RATING_GRID[0] * file1Ncloc * DEV_COST_LANGUAGE_1));
303     verifyAddedRawMeasure(FILE_2_REF, EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY,
304       (long) (file2Effort - RATING_GRID[0] * file2Ncloc * DEV_COST_LANGUAGE_1));
305     verifyAddedRawMeasure(DIRECTORY_REF, EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY,
306       (long) (dirEffort - RATING_GRID[0] * (file1Ncloc + file2Ncloc) * DEV_COST_LANGUAGE_1));
307     verifyAddedRawMeasure(MODULE_REF, EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY,
308       (long) (moduleEffort - RATING_GRID[0] * (file1Ncloc + file2Ncloc) * DEV_COST_LANGUAGE_1));
309     verifyAddedRawMeasure(PROJECT_REF, EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY,
310       (long) (projectEffort - RATING_GRID[0] * (file1Ncloc + file2Ncloc) * DEV_COST_LANGUAGE_1));
311   }
312
313   @Test
314   public void compute_0_effort_to_maintainability_rating_A_when_effort_is_lower_than_dev_cost() throws Exception {
315     treeRootHolder.setRoot(ROOT_PROJECT);
316
317     addRawMeasure(NCLOC_KEY, FILE_1_REF, 10);
318     addRawMeasure(TECHNICAL_DEBT_KEY, FILE_1_REF, 2L);
319
320     underTest.visit(ROOT_PROJECT);
321
322     verifyAddedRawMeasure(FILE_1_REF, EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY, 0L);
323   }
324
325   @Test
326   public void effort_to_maintainability_rating_A_is_same_as_effort_when_no_dev_cost() throws Exception {
327     treeRootHolder.setRoot(ROOT_PROJECT);
328
329     addRawMeasure(TECHNICAL_DEBT_KEY, FILE_1_REF, 100L);
330
331     underTest.visit(ROOT_PROJECT);
332
333     verifyAddedRawMeasure(FILE_1_REF, EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY, 100);
334   }
335
336   private void addRawMeasure(String metricKey, int componentRef, long value) {
337     measureRepository.addRawMeasure(componentRef, metricKey, newMeasureBuilder().create(value));
338   }
339
340   private void addRawMeasure(String metricKey, int componentRef, int value) {
341     measureRepository.addRawMeasure(componentRef, metricKey, newMeasureBuilder().create(value));
342   }
343
344   private void verifyAddedRawMeasure(int componentRef, String metricKey, long value) {
345     assertThat(toEntries(measureRepository.getAddedRawMeasures(componentRef))).contains(entryOf(metricKey, newMeasureBuilder().create(value)));
346   }
347
348   private void verifyAddedRawMeasure(int componentRef, String metricKey, double value) {
349     assertThat(toEntries(measureRepository.getAddedRawMeasures(componentRef))).contains(entryOf(metricKey, newMeasureBuilder().create(value, 1)));
350   }
351
352   private void verifyAddedRawMeasure(int componentRef, String metricKey, Rating rating) {
353     assertThat(toEntries(measureRepository.getAddedRawMeasures(componentRef))).contains(entryOf(metricKey, newMeasureBuilder().create(rating.getIndex(), rating.name())));
354   }
355
356   private void verifyAddedRawMeasure(int componentRef, String metricKey, String value) {
357     assertThat(toEntries(measureRepository.getAddedRawMeasures(componentRef))).contains(entryOf(metricKey, newMeasureBuilder().create(value)));
358   }
359
360   private static ReportComponent createFileComponent(String languageKey1, int fileRef) {
361     return builder(FILE, fileRef).setFileAttributes(new FileAttributes(false, languageKey1)).build();
362   }
363
364   private static Measure createMaintainabilityRatingMeasure(Rating rating) {
365     return newMeasureBuilder().create(rating.getIndex(), rating.name());
366   }
367
368 }