3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info 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.measure.live;
22 import java.util.Date;
23 import java.util.List;
25 import org.junit.Before;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.sonar.api.config.Configuration;
29 import org.sonar.api.config.internal.MapSettings;
30 import org.sonar.api.measures.Metric;
31 import org.sonar.api.rules.RuleType;
32 import org.sonar.db.DbTester;
33 import org.sonar.db.component.BranchDto;
34 import org.sonar.db.component.BranchType;
35 import org.sonar.db.component.ComponentDto;
36 import org.sonar.db.component.ComponentTesting;
37 import org.sonar.db.component.SnapshotDto;
38 import org.sonar.db.issue.IssueDto;
39 import org.sonar.db.measure.LiveMeasureDto;
40 import org.sonar.db.metric.MetricDto;
41 import org.sonar.db.newcodeperiod.NewCodePeriodType;
42 import org.sonar.db.rule.RuleDto;
44 import static org.assertj.core.api.Assertions.assertThat;
45 import static org.sonar.api.CoreProperties.RATING_GRID;
46 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY;
47 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_KEY;
48 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
49 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
50 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
51 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_KEY;
52 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
53 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
55 public class LiveMeasureTreeUpdaterImplIT {
57 public DbTester db = DbTester.create();
59 private final Configuration config = new MapSettings().setProperty(RATING_GRID, "0.05,0.1,0.2,0.5").asConfig();
60 private LiveMeasureTreeUpdaterImpl treeUpdater;
61 private ComponentIndexImpl componentIndex;
62 private MeasureMatrix matrix;
63 private MetricDto metricDto;
64 private Metric metric;
65 private ComponentDto project;
66 private BranchDto branch;
67 private ComponentDto dir;
68 private ComponentDto file1;
69 private ComponentDto file2;
70 private SnapshotDto snapshot;
74 // insert project and file structure
75 project = db.components().insertPrivateProject().getMainBranchComponent();
76 branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), project.uuid()).get();
77 dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java"));
78 file1 = db.components().insertComponent(ComponentTesting.newFileDto(project, dir));
79 file2 = db.components().insertComponent(ComponentTesting.newFileDto(project, dir));
82 metricDto = db.measures().insertMetric(m -> m.setValueType(Metric.ValueType.INT.name()));
83 metric = new Metric.Builder(metricDto.getKey(), metricDto.getShortName(), Metric.ValueType.valueOf(metricDto.getValueType())).create();
84 matrix = new MeasureMatrix(List.of(project, dir, file1, file2), List.of(metricDto), List.of());
85 componentIndex = new ComponentIndexImpl(db.getDbClient());
89 public void should_aggregate_values_up_the_hierarchy() {
90 snapshot = db.components().insertSnapshot(project);
91 treeUpdater = new LiveMeasureTreeUpdaterImpl(db.getDbClient(), new AggregateValuesFormula());
93 componentIndex.load(db.getSession(), List.of(file1));
94 List<LiveMeasureDto> initialValues = List.of(
95 new LiveMeasureDto().setComponentUuid(file1.uuid()).setValue(1d).setMetricUuid(metricDto.getUuid()),
96 new LiveMeasureDto().setComponentUuid(file2.uuid()).setValue(1d).setMetricUuid(metricDto.getUuid()),
97 new LiveMeasureDto().setComponentUuid(dir.uuid()).setValue(1d).setMetricUuid(metricDto.getUuid()),
98 new LiveMeasureDto().setComponentUuid(project.uuid()).setValue(1d).setMetricUuid(metricDto.getUuid())
100 matrix = new MeasureMatrix(List.of(project, dir, file1, file2), List.of(metricDto), initialValues);
101 treeUpdater.update(db.getSession(), snapshot, config, componentIndex, branch, matrix);
103 assertThat(matrix.getChanged()).extracting(LiveMeasureDto::getComponentUuid).containsOnly(project.uuid(), dir.uuid());
104 assertThat(matrix.getMeasure(project, metric.getKey()).get().getValue()).isEqualTo(4d);
105 assertThat(matrix.getMeasure(dir, metric.getKey()).get().getValue()).isEqualTo(3d);
106 assertThat(matrix.getMeasure(file1, metric.getKey()).get().getValue()).isEqualTo(1d);
107 assertThat(matrix.getMeasure(file2, metric.getKey()).get().getValue()).isEqualTo(1d);
111 public void should_set_values_up_the_hierarchy() {
112 snapshot = db.components().insertSnapshot(project);
113 treeUpdater = new LiveMeasureTreeUpdaterImpl(db.getDbClient(), new SetValuesFormula());
115 componentIndex.load(db.getSession(), List.of(file1));
116 treeUpdater.update(db.getSession(), snapshot, config, componentIndex, branch, matrix);
118 assertThat(matrix.getChanged()).extracting(LiveMeasureDto::getComponentUuid).containsOnly(project.uuid(), dir.uuid(), file1.uuid());
119 assertThat(matrix.getMeasure(project, metric.getKey()).get().getValue()).isEqualTo(1d);
120 assertThat(matrix.getMeasure(dir, metric.getKey()).get().getValue()).isEqualTo(1d);
121 assertThat(matrix.getMeasure(file1, metric.getKey()).get().getValue()).isEqualTo(1d);
122 assertThat(matrix.getMeasure(file2, metric.getKey())).isEmpty();
126 public void dont_use_leak_formulas_if_no_period() {
127 snapshot = db.components().insertSnapshot(project, s -> s.setPeriodDate(null));
128 treeUpdater = new LiveMeasureTreeUpdaterImpl(db.getDbClient(), new CountUnresolvedInLeak());
130 componentIndex.load(db.getSession(), List.of(file1));
131 treeUpdater.update(db.getSession(), snapshot, config, componentIndex, branch, matrix);
133 assertThat(matrix.getChanged()).extracting(LiveMeasureDto::getComponentUuid).isEmpty();
137 public void use_leak_formulas_if_pr() {
138 snapshot = db.components().insertSnapshot(project, s -> s.setPeriodDate(null));
139 branch.setBranchType(BranchType.PULL_REQUEST);
140 treeUpdater = new LiveMeasureTreeUpdaterImpl(db.getDbClient(), new CountUnresolvedInLeak());
142 componentIndex.load(db.getSession(), List.of(file1));
143 treeUpdater.update(db.getSession(), snapshot, config, componentIndex, branch, matrix);
145 assertThat(matrix.getChanged()).extracting(LiveMeasureDto::getComponentUuid).containsOnly(project.uuid(), dir.uuid(), file1.uuid());
149 public void calculate_new_metrics_if_using_new_code_branch_reference() {
150 snapshot = db.components().insertSnapshot(project, s -> s.setPeriodMode(NewCodePeriodType.REFERENCE_BRANCH.name()));
151 treeUpdater = new LiveMeasureTreeUpdaterImpl(db.getDbClient(), new CountUnresolvedInLeak());
153 componentIndex.load(db.getSession(), List.of(file1));
154 treeUpdater.update(db.getSession(), snapshot, config, componentIndex, branch, matrix);
156 assertThat(matrix.getChanged()).extracting(LiveMeasureDto::getComponentUuid).containsOnly(project.uuid(), dir.uuid(), file1.uuid());
160 public void issue_counter_based_on_new_code_branch_reference() {
161 snapshot = db.components().insertSnapshot(project, s -> s.setPeriodMode(NewCodePeriodType.REFERENCE_BRANCH.name()));
162 treeUpdater = new LiveMeasureTreeUpdaterImpl(db.getDbClient(), new CountUnresolvedInLeak());
164 RuleDto rule = db.rules().insert(r -> r.setType(RuleType.BUG));
165 IssueDto issue1 = db.issues().insertIssue(rule, project, file1);
166 IssueDto issue2 = db.issues().insertIssue(rule, project, file1);
167 db.issues().insertNewCodeReferenceIssue(issue1);
169 componentIndex.load(db.getSession(), List.of(file1));
170 treeUpdater.update(db.getSession(), snapshot, config, componentIndex, branch, matrix);
171 assertThat(matrix.getMeasure(file1, metric.getKey()).get().getValue()).isEqualTo(1d);
175 public void issue_counter_uses_begin_of_leak() {
176 snapshot = db.components().insertSnapshot(project, s -> s.setPeriodDate(1000L));
177 treeUpdater = new LiveMeasureTreeUpdaterImpl(db.getDbClient(), new CountUnresolvedInLeak());
179 db.issues().insertIssue(i -> i.setIssueCreationDate(new Date(999)).setComponentUuid(file1.uuid()));
180 db.issues().insertIssue(i -> i.setIssueCreationDate(new Date(1001)).setComponentUuid(file1.uuid()));
181 db.issues().insertIssue(i -> i.setIssueCreationDate(new Date(1002)).setComponentUuid(file1.uuid()));
183 componentIndex.load(db.getSession(), List.of(file1));
184 treeUpdater.update(db.getSession(), snapshot, config, componentIndex, branch, matrix);
186 assertThat(matrix.getMeasure(file1, metric.getKey()).get().getValue()).isEqualTo(2d);
190 public void context_calculates_hotspot_counts_from_percentage() {
191 List<MetricDto> metrics = List.of(new MetricDto().setKey(SECURITY_HOTSPOTS_KEY), new MetricDto().setKey(SECURITY_HOTSPOTS_REVIEWED_KEY),
192 new MetricDto().setKey(SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY), new MetricDto().setKey(SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY));
193 componentIndex.load(db.getSession(), List.of(file1));
194 matrix = new MeasureMatrix(List.of(project, dir, file1, file2), metrics, List.of());
196 LiveMeasureTreeUpdaterImpl.FormulaContextImpl context = new LiveMeasureTreeUpdaterImpl.FormulaContextImpl(matrix, componentIndex, null);
197 matrix.setValue(file1, SECURITY_HOTSPOTS_KEY, 4d);
198 matrix.setValue(file1, SECURITY_HOTSPOTS_REVIEWED_KEY, 33d);
200 matrix.setValue(file2, SECURITY_HOTSPOTS_KEY, 2d);
201 matrix.setValue(file2, SECURITY_HOTSPOTS_REVIEWED_KEY, 50d);
203 context.change(dir, null);
204 assertThat(context.getChildrenHotspotsToReview()).isEqualTo(6);
205 assertThat(context.getChildrenHotspotsReviewed()).isEqualTo(4);
209 public void context_calculates_new_hotspot_counts_from_percentage() {
210 List<MetricDto> metrics = List.of(new MetricDto().setKey(NEW_SECURITY_HOTSPOTS_KEY), new MetricDto().setKey(NEW_SECURITY_HOTSPOTS_REVIEWED_KEY),
211 new MetricDto().setKey(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY), new MetricDto().setKey(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY));
212 componentIndex.load(db.getSession(), List.of(file1));
213 matrix = new MeasureMatrix(List.of(project, dir, file1, file2), metrics, List.of());
215 LiveMeasureTreeUpdaterImpl.FormulaContextImpl context = new LiveMeasureTreeUpdaterImpl.FormulaContextImpl(matrix, componentIndex, null);
216 matrix.setValue(file1, NEW_SECURITY_HOTSPOTS_KEY, 4d);
217 matrix.setValue(file1, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY, 33d);
219 matrix.setValue(file2, NEW_SECURITY_HOTSPOTS_KEY, 2d);
220 matrix.setValue(file2, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY, 50d);
222 context.change(dir, null);
223 assertThat(context.getChildrenNewHotspotsToReview()).isEqualTo(6);
224 assertThat(context.getChildrenNewHotspotsReviewed()).isEqualTo(4);
228 public void context_returns_hotspots_counts_from_measures() {
229 List<MetricDto> metrics = List.of(new MetricDto().setKey(SECURITY_HOTSPOTS_KEY), new MetricDto().setKey(SECURITY_HOTSPOTS_REVIEWED_KEY),
230 new MetricDto().setKey(SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY), new MetricDto().setKey(SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY));
231 componentIndex.load(db.getSession(), List.of(file1));
232 matrix = new MeasureMatrix(List.of(project, dir, file1, file2), metrics, List.of());
234 LiveMeasureTreeUpdaterImpl.FormulaContextImpl context = new LiveMeasureTreeUpdaterImpl.FormulaContextImpl(matrix, componentIndex, null);
235 matrix.setValue(file1, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY, 5D);
236 matrix.setValue(file1, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY, 5D);
237 matrix.setValue(file1, SECURITY_HOTSPOTS_KEY, 6d);
238 matrix.setValue(file1, SECURITY_HOTSPOTS_REVIEWED_KEY, 33d);
240 matrix.setValue(file2, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY, 5D);
241 matrix.setValue(file2, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY, 5D);
242 matrix.setValue(file2, SECURITY_HOTSPOTS_KEY, 4d);
243 matrix.setValue(file2, SECURITY_HOTSPOTS_REVIEWED_KEY, 50d);
245 context.change(dir, null);
246 assertThat(context.getChildrenHotspotsToReview()).isEqualTo(10);
247 assertThat(context.getChildrenHotspotsReviewed()).isEqualTo(10);
250 private class AggregateValuesFormula implements MeasureUpdateFormulaFactory {
252 public List<MeasureUpdateFormula> getFormulas() {
253 return List.of(new MeasureUpdateFormula(metric, false, new MeasureUpdateFormulaFactoryImpl.AddChildren(), (c, i) -> {
258 public Set<Metric> getFormulaMetrics() {
259 return Set.of(metric);
263 private class SetValuesFormula implements MeasureUpdateFormulaFactory {
265 public List<MeasureUpdateFormula> getFormulas() {
266 return List.of(new MeasureUpdateFormula(metric, false, (c, m) -> {
267 }, (c, i) -> c.setValue(1d)));
271 public Set<Metric> getFormulaMetrics() {
272 return Set.of(metric);
276 private class CountUnresolvedInLeak implements MeasureUpdateFormulaFactory {
278 public List<MeasureUpdateFormula> getFormulas() {
279 return List.of(new MeasureUpdateFormula(metric, true, (c, m) -> {
280 }, (c, i) -> c.setValue(i.countUnresolved(true))));
284 public Set<Metric> getFormulaMetrics() {
285 return Set.of(metric);