]> source.dussan.org Git - sonarqube.git/blob
914c8185a90be97b0297aef4834181871a10b17d
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2024 SonarSource SA
4  * mailto:info 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.ce.task.projectanalysis.issue;
21
22 import org.junit.Test;
23 import org.sonar.api.rules.RuleType;
24 import org.sonar.api.utils.Duration;
25 import org.sonar.ce.task.projectanalysis.analysis.Branch;
26 import org.sonar.ce.task.projectanalysis.component.Component;
27 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
28 import org.sonar.ce.task.projectanalysis.measure.Measure;
29 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
30 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
31 import org.sonar.ce.task.projectanalysis.period.PeriodHolderRule;
32 import org.sonar.core.issue.DefaultIssue;
33 import org.sonar.core.util.UuidFactoryFast;
34 import org.sonar.db.component.BranchType;
35
36 import static org.assertj.core.api.Assertions.assertThat;
37 import static org.mockito.ArgumentMatchers.any;
38 import static org.mockito.ArgumentMatchers.eq;
39 import static org.mockito.Mockito.mock;
40 import static org.mockito.Mockito.when;
41 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
42 import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT;
43 import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT_KEY;
44 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT;
45 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT_KEY;
46 import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT;
47 import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT_KEY;
48 import static org.sonar.api.rules.RuleType.BUG;
49 import static org.sonar.api.rules.RuleType.CODE_SMELL;
50 import static org.sonar.api.rules.RuleType.VULNERABILITY;
51
52 public class NewEffortAggregatorTest {
53   private static final Component FILE = ReportComponent.builder(Component.Type.FILE, 1).setUuid("FILE").build();
54   private static final Component PROJECT = ReportComponent.builder(Component.Type.PROJECT, 2).addChildren(FILE).build();
55
56   @org.junit.Rule
57   public PeriodHolderRule periodsHolder = new PeriodHolderRule();
58   @org.junit.Rule
59   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
60     .add(NEW_TECHNICAL_DEBT)
61     .add(NEW_RELIABILITY_REMEDIATION_EFFORT)
62     .add(NEW_SECURITY_REMEDIATION_EFFORT);
63   @org.junit.Rule
64   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create();
65   private final NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
66   private final NewEffortAggregator underTest = new NewEffortAggregator(metricRepository, measureRepository, newIssueClassifier);
67
68   @Test
69   public void sum_new_maintainability_effort_of_issues() {
70     when(newIssueClassifier.isEnabled()).thenReturn(true);
71     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
72     DefaultIssue unresolved1 = newCodeSmellIssue(10L);
73     DefaultIssue old1 = oldCodeSmellIssue(100L);
74     DefaultIssue unresolved2 = newCodeSmellIssue(30L);
75     DefaultIssue old2 = oldCodeSmellIssue(300L);
76     DefaultIssue unresolvedWithoutDebt = newCodeSmellIssueWithoutEffort();
77     DefaultIssue resolved = newCodeSmellIssue(50L).setResolution(RESOLUTION_FIXED);
78
79     underTest.beforeComponent(FILE);
80     underTest.onIssue(FILE, unresolved1);
81     underTest.onIssue(FILE, old1);
82     underTest.onIssue(FILE, unresolved2);
83     underTest.onIssue(FILE, old2);
84     underTest.onIssue(FILE, unresolvedWithoutDebt);
85     underTest.onIssue(FILE, resolved);
86     underTest.afterComponent(FILE);
87
88     assertValue(FILE, NEW_TECHNICAL_DEBT_KEY, 10 + 30);
89   }
90
91   @Test
92   public void new_maintainability_effort_is_only_computed_using_code_smell_issues() {
93     when(newIssueClassifier.isEnabled()).thenReturn(true);
94     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
95     DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
96     DefaultIssue oldSmellIssue = oldCodeSmellIssue(100);
97     // Issues of type BUG and VULNERABILITY should be ignored
98     DefaultIssue bugIssue = newBugIssue(15);
99     DefaultIssue oldBugIssue = oldBugIssue(150);
100     DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
101     DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
102
103     underTest.beforeComponent(FILE);
104     underTest.onIssue(FILE, codeSmellIssue);
105     underTest.onIssue(FILE, oldSmellIssue);
106     underTest.onIssue(FILE, bugIssue);
107     underTest.onIssue(FILE, oldBugIssue);
108     underTest.onIssue(FILE, vulnerabilityIssue);
109     underTest.onIssue(FILE, oldVulnerabilityIssue);
110     underTest.afterComponent(FILE);
111
112     // Only effort of CODE SMELL issue is used
113     assertValue(FILE, NEW_TECHNICAL_DEBT_KEY, 10);
114   }
115
116   @Test
117   public void sum_new_reliability_effort_of_issues() {
118     when(newIssueClassifier.isEnabled()).thenReturn(true);
119     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
120     DefaultIssue unresolved1 = newBugIssue(10L);
121     DefaultIssue old1 = oldBugIssue(100L);
122     DefaultIssue unresolved2 = newBugIssue(30L);
123
124     DefaultIssue old2 = oldBugIssue(300L);
125     DefaultIssue unresolvedWithoutDebt = newBugIssueWithoutEffort();
126     DefaultIssue resolved = newBugIssue(50L).setResolution(RESOLUTION_FIXED);
127
128     underTest.beforeComponent(FILE);
129     underTest.onIssue(FILE, unresolved1);
130     underTest.onIssue(FILE, old1);
131     underTest.onIssue(FILE, unresolved2);
132     underTest.onIssue(FILE, old2);
133     underTest.onIssue(FILE, unresolvedWithoutDebt);
134     underTest.onIssue(FILE, resolved);
135     underTest.afterComponent(FILE);
136
137     assertValue(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 10 + 30);
138   }
139
140   @Test
141   public void new_reliability_effort_is_only_computed_using_bug_issues() {
142     when(newIssueClassifier.isEnabled()).thenReturn(true);
143     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
144     DefaultIssue bugIssue = newBugIssue(15);
145     DefaultIssue oldBugIssue = oldBugIssue(150);
146     // Issues of type CODE SMELL and VULNERABILITY should be ignored
147     DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
148     DefaultIssue oldCodeSmellIssue = oldCodeSmellIssue(100);
149     DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
150     DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
151
152     underTest.beforeComponent(FILE);
153     underTest.onIssue(FILE, bugIssue);
154     underTest.onIssue(FILE, oldBugIssue);
155     underTest.onIssue(FILE, codeSmellIssue);
156     underTest.onIssue(FILE, oldCodeSmellIssue);
157     underTest.onIssue(FILE, vulnerabilityIssue);
158     underTest.onIssue(FILE, oldVulnerabilityIssue);
159     underTest.afterComponent(FILE);
160
161     // Only effort of BUG issue is used
162     assertValue(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 15);
163   }
164
165   @Test
166   public void sum_new_vulnerability_effort_of_issues() {
167     when(newIssueClassifier.isEnabled()).thenReturn(true);
168     DefaultIssue unresolved1 = newVulnerabilityIssue(10L);
169     DefaultIssue old1 = oldVulnerabilityIssue(100L);
170     DefaultIssue unresolved2 = newVulnerabilityIssue(30L);
171     DefaultIssue old2 = oldVulnerabilityIssue(300L);
172     DefaultIssue unresolvedWithoutDebt = newVulnerabilityIssueWithoutEffort();
173     DefaultIssue resolved = newVulnerabilityIssue(50L).setResolution(RESOLUTION_FIXED);
174     DefaultIssue oldResolved = oldVulnerabilityIssue(500L).setResolution(RESOLUTION_FIXED);
175
176     underTest.beforeComponent(FILE);
177     underTest.onIssue(FILE, unresolved1);
178     underTest.onIssue(FILE, old1);
179     underTest.onIssue(FILE, unresolved2);
180     underTest.onIssue(FILE, old2);
181     underTest.onIssue(FILE, unresolvedWithoutDebt);
182     underTest.onIssue(FILE, resolved);
183     underTest.onIssue(FILE, oldResolved);
184     underTest.afterComponent(FILE);
185
186     assertValue(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 10 + 30);
187   }
188
189   @Test
190   public void new_security_effort_is_only_computed_using_vulnerability_issues() {
191     when(newIssueClassifier.isEnabled()).thenReturn(true);
192     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
193     DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
194     DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
195     // Issues of type CODE SMELL and BUG should be ignored
196     DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
197     DefaultIssue oldCodeSmellIssue = oldCodeSmellIssue(100);
198     DefaultIssue bugIssue = newBugIssue(15);
199     DefaultIssue oldBugIssue = oldBugIssue(150);
200
201     underTest.beforeComponent(FILE);
202     underTest.onIssue(FILE, codeSmellIssue);
203     underTest.onIssue(FILE, oldCodeSmellIssue);
204     underTest.onIssue(FILE, bugIssue);
205     underTest.onIssue(FILE, oldBugIssue);
206     underTest.onIssue(FILE, vulnerabilityIssue);
207     underTest.onIssue(FILE, oldVulnerabilityIssue);
208     underTest.afterComponent(FILE);
209
210     // Only effort of VULNERABILITY issue is used
211     assertValue(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 12);
212   }
213
214   @Test
215   public void aggregate_new_characteristic_measures_of_children() {
216     when(newIssueClassifier.isEnabled()).thenReturn(true);
217     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
218
219     DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
220     DefaultIssue oldCodeSmellIssue = oldCodeSmellIssue(100);
221     DefaultIssue bugIssue = newBugIssue(8);
222     DefaultIssue oldBugIssue = oldBugIssue(80);
223     DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
224     DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
225
226     DefaultIssue codeSmellProjectIssue = newCodeSmellIssue(30);
227     DefaultIssue oldCodeSmellProjectIssue = oldCodeSmellIssue(300);
228     DefaultIssue bugProjectIssue = newBugIssue(28);
229     DefaultIssue oldBugProjectIssue = oldBugIssue(280);
230     DefaultIssue vulnerabilityProjectIssue = newVulnerabilityIssue(32);
231     DefaultIssue oldVulnerabilityProjectIssue = oldVulnerabilityIssue(320);
232
233     underTest.beforeComponent(FILE);
234     underTest.onIssue(FILE, codeSmellIssue);
235     underTest.onIssue(FILE, oldCodeSmellIssue);
236     underTest.onIssue(FILE, bugIssue);
237     underTest.onIssue(FILE, oldBugIssue);
238     underTest.onIssue(FILE, vulnerabilityIssue);
239     underTest.onIssue(FILE, oldVulnerabilityIssue);
240     underTest.afterComponent(FILE);
241     underTest.beforeComponent(PROJECT);
242     underTest.onIssue(PROJECT, codeSmellProjectIssue);
243     underTest.onIssue(PROJECT, oldCodeSmellProjectIssue);
244     underTest.onIssue(PROJECT, bugProjectIssue);
245     underTest.onIssue(PROJECT, oldBugProjectIssue);
246     underTest.onIssue(PROJECT, vulnerabilityProjectIssue);
247     underTest.onIssue(PROJECT, oldVulnerabilityProjectIssue);
248     underTest.afterComponent(PROJECT);
249
250     assertValue(PROJECT, NEW_TECHNICAL_DEBT_KEY, 10 + 30);
251     assertValue(PROJECT, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 8 + 28);
252     assertValue(PROJECT, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 12 + 32);
253   }
254
255   @Test
256   public void no_measures_if_no_periods() {
257     when(newIssueClassifier.isEnabled()).thenReturn(false);
258     Branch branch = mock(Branch.class);
259     when(branch.getType()).thenReturn(BranchType.BRANCH);
260     periodsHolder.setPeriod(null);
261     DefaultIssue unresolved = newCodeSmellIssue(10);
262
263     underTest.beforeComponent(FILE);
264     underTest.onIssue(FILE, unresolved);
265     underTest.afterComponent(FILE);
266
267     assertThat(measureRepository.getRawMeasures(FILE)).isEmpty();
268   }
269
270   @Test
271   public void should_have_empty_measures_if_no_issues() {
272     when(newIssueClassifier.isEnabled()).thenReturn(true);
273     when(newIssueClassifier.isNew(any(), any())).thenReturn(true);
274
275     underTest.beforeComponent(FILE);
276     underTest.afterComponent(FILE);
277
278     assertValue(FILE, NEW_TECHNICAL_DEBT_KEY, 0);
279     assertValue(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 0);
280     assertValue(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 0);
281   }
282
283   private void assertValue(Component component, String metricKey, int value) {
284     Measure newMeasure = measureRepository.getRawMeasure(component, metricRepository.getByKey(metricKey)).get();
285     assertThat(newMeasure.getLongValue()).isEqualTo(value);
286   }
287
288   private DefaultIssue newCodeSmellIssue(long effort) {
289     return createIssue(CODE_SMELL, effort, true);
290   }
291
292   private DefaultIssue oldCodeSmellIssue(long effort) {
293     return createIssue(CODE_SMELL, effort, false);
294   }
295
296   private DefaultIssue newBugIssue(long effort) {
297     return createIssue(BUG, effort, true);
298   }
299
300   private DefaultIssue oldBugIssue(long effort) {
301     return createIssue(BUG, effort, false);
302   }
303
304   private DefaultIssue newVulnerabilityIssue(long effort) {
305     return createIssue(VULNERABILITY, effort, true);
306   }
307
308   private DefaultIssue oldVulnerabilityIssue(long effort) {
309     return createIssue(VULNERABILITY, effort, false);
310   }
311
312   private DefaultIssue newCodeSmellIssueWithoutEffort() {
313     DefaultIssue defaultIssue = new DefaultIssue()
314       .setKey(UuidFactoryFast.getInstance().create())
315       .setType(CODE_SMELL);
316     when(newIssueClassifier.isNew(any(), eq(defaultIssue))).thenReturn(true);
317     return defaultIssue;
318   }
319
320   private DefaultIssue createIssue(RuleType type, long effort, boolean isNew) {
321     DefaultIssue defaultIssue = new DefaultIssue()
322       .setKey(UuidFactoryFast.getInstance().create())
323       .setEffort(Duration.create(effort))
324       .setType(type);
325     when(newIssueClassifier.isNew(any(), eq(defaultIssue))).thenReturn(isNew);
326     return defaultIssue;
327   }
328
329   private static DefaultIssue newBugIssueWithoutEffort() {
330     return new DefaultIssue().setType(BUG);
331   }
332
333   private static DefaultIssue newVulnerabilityIssueWithoutEffort() {
334     return new DefaultIssue().setType(VULNERABILITY);
335   }
336 }