]> source.dussan.org Git - sonarqube.git/blob
f4829de256fa15436674b62870593ea8b9b07f7d
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 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.step;
21
22 import java.util.Optional;
23 import org.junit.Before;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.sonar.api.measures.Metric;
27 import org.sonar.api.utils.System2;
28 import org.sonar.ce.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
29 import org.sonar.ce.task.projectanalysis.component.Component;
30 import org.sonar.ce.task.projectanalysis.component.FileStatuses;
31 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
32 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
33 import org.sonar.ce.task.projectanalysis.component.ViewsComponent;
34 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
35 import org.sonar.ce.task.projectanalysis.measure.MeasureToMeasureDto;
36 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
37 import org.sonar.ce.task.step.ComputationStep;
38 import org.sonar.ce.task.step.TestComputationStepContext;
39 import org.sonar.db.DbClient;
40 import org.sonar.db.DbTester;
41 import org.sonar.db.component.ComponentDto;
42 import org.sonar.db.measure.LiveMeasureDto;
43 import org.sonar.db.metric.MetricDto;
44 import org.sonar.server.project.Project;
45
46 import static java.util.Collections.emptyList;
47 import static org.assertj.core.api.Assertions.assertThat;
48 import static org.mockito.ArgumentMatchers.any;
49 import static org.mockito.Mockito.mock;
50 import static org.mockito.Mockito.when;
51 import static org.sonar.api.measures.CoreMetrics.BUGS;
52 import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
53 import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
54 import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
55 import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT_VIEW;
56 import static org.sonar.ce.task.projectanalysis.component.Component.Type.SUBVIEW;
57 import static org.sonar.ce.task.projectanalysis.component.Component.Type.VIEW;
58 import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
59 import static org.sonar.db.measure.MeasureTesting.newLiveMeasure;
60
61 public class PersistLiveMeasuresStepIT extends BaseStepTest {
62
63   private static final Metric STRING_METRIC = new Metric.Builder("string-metric", "String metric", Metric.ValueType.STRING).create();
64   private static final Metric INT_METRIC = new Metric.Builder("int-metric", "int metric", Metric.ValueType.INT).create();
65   private static final Metric METRIC_WITH_BEST_VALUE = new Metric.Builder("best-value-metric", "best value metric", Metric.ValueType.INT)
66     .setBestValue(0.0)
67     .setOptimizedBestValue(true)
68     .create();
69
70   private static final int REF_1 = 1;
71   private static final int REF_2 = 2;
72   private static final int REF_3 = 3;
73   private static final int REF_4 = 4;
74
75   @Rule
76   public DbTester db = DbTester.create(System2.INSTANCE);
77   @Rule
78   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
79   @Rule
80   public MetricRepositoryRule metricRepository = new MetricRepositoryRule();
81   @Rule
82   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
83   @Rule
84   public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();
85
86   private final FileStatuses fileStatuses = mock(FileStatuses.class);
87   private final DbClient dbClient = db.getDbClient();
88   private final TestComputationStepContext context = new TestComputationStepContext();
89
90   @Before
91   public void setUp() {
92     MetricDto stringMetricDto = db.measures().insertMetric(m -> m.setKey(STRING_METRIC.getKey()).setValueType(Metric.ValueType.STRING.name()));
93     MetricDto intMetricDto = db.measures().insertMetric(m -> m.setKey(INT_METRIC.getKey()).setValueType(Metric.ValueType.INT.name()));
94     MetricDto bestValueMMetricDto = db.measures()
95       .insertMetric(m -> m.setKey(METRIC_WITH_BEST_VALUE.getKey()).setValueType(Metric.ValueType.INT.name()).setOptimizedBestValue(true).setBestValue(0.0));
96     MetricDto bugs = db.measures().insertMetric(m -> m.setKey(BUGS.getKey()));
97     metricRepository.add(stringMetricDto.getUuid(), STRING_METRIC);
98     metricRepository.add(intMetricDto.getUuid(), INT_METRIC);
99     metricRepository.add(bestValueMMetricDto.getUuid(), METRIC_WITH_BEST_VALUE);
100     metricRepository.add(bugs.getUuid(), BUGS);
101   }
102
103   @Test
104   public void persist_live_measures_of_project_analysis() {
105     prepareProject();
106
107     // the computed measures
108     measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value"));
109     measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("dir-value"));
110     measureRepository.addRawMeasure(REF_4, STRING_METRIC.getKey(), newMeasureBuilder().create("file-value"));
111
112     step().execute(context);
113
114     // all measures are persisted, from project to file
115     assertThat(db.countRowsOfTable("live_measures")).isEqualTo(3);
116     assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("project-value");
117     assertThat(selectMeasure("dir-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("dir-value");
118     assertThat(selectMeasure("file-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("file-value");
119     verifyStatistics(context, 3);
120   }
121
122   @Test
123   public void measures_without_value_are_not_persisted() {
124     prepareProject();
125     measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().createNoValue());
126     measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().createNoValue());
127
128     step().execute(context);
129
130     assertThatMeasureIsNotPersisted("project-uuid", STRING_METRIC);
131     assertThatMeasureIsNotPersisted("project-uuid", INT_METRIC);
132     verifyStatistics(context, 0);
133   }
134
135   @Test
136   public void measures_on_new_code_period_are_persisted() {
137     prepareProject();
138     measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().create(42.0));
139
140     step().execute(context);
141
142     LiveMeasureDto persistedMeasure = selectMeasure("project-uuid", INT_METRIC).get();
143     assertThat(persistedMeasure.getValue()).isEqualTo(42.0);
144     verifyStatistics(context, 1);
145   }
146
147   @Test
148   public void delete_measures_from_db_if_no_longer_computed() {
149     prepareProject();
150     // measure to be updated
151     LiveMeasureDto measureOnFileInProject = insertMeasure("file-uuid", "project-uuid", INT_METRIC);
152     // measure to be deleted because not computed anymore
153     LiveMeasureDto otherMeasureOnFileInProject = insertMeasure("file-uuid", "project-uuid", STRING_METRIC);
154     // measure in another project, not touched
155     LiveMeasureDto measureInOtherProject = insertMeasure("other-file-uuid", "other-project-uuid", INT_METRIC);
156     db.commit();
157
158     measureRepository.addRawMeasure(REF_4, INT_METRIC.getKey(), newMeasureBuilder().create(42));
159
160     step().execute(context);
161
162     assertThatMeasureHasValue(measureOnFileInProject, 42);
163     assertThatMeasureDoesNotExist(otherMeasureOnFileInProject);
164     assertThatMeasureHasValue(measureInOtherProject, (int) measureInOtherProject.getValue().doubleValue());
165     verifyStatistics(context, 1);
166   }
167
168   @Test
169   public void do_not_persist_file_measures_with_best_value() {
170     prepareProject();
171     // measure to be deleted because new value matches the metric best value
172     LiveMeasureDto oldMeasure = insertMeasure("file-uuid", "project-uuid", INT_METRIC);
173     db.commit();
174
175     // project measure with metric best value -> persist with value 0
176     measureRepository.addRawMeasure(REF_1, METRIC_WITH_BEST_VALUE.getKey(), newMeasureBuilder().create(0));
177     // file measure with metric best value -> do not persist
178     measureRepository.addRawMeasure(REF_4, METRIC_WITH_BEST_VALUE.getKey(), newMeasureBuilder().create(0));
179
180     step().execute(context);
181
182     assertThatMeasureDoesNotExist(oldMeasure);
183     assertThatMeasureHasValue("project-uuid", METRIC_WITH_BEST_VALUE, 0);
184     verifyStatistics(context, 1);
185   }
186
187   @Test
188   public void keep_measures_for_unchanged_files() {
189     prepareProject();
190     LiveMeasureDto oldMeasure = insertMeasure("file-uuid", "project-uuid", BUGS);
191     db.commit();
192     when(fileStatuses.isDataUnchanged(any(Component.class))).thenReturn(true);
193     // this new value won't be persisted
194     measureRepository.addRawMeasure(REF_4, BUGS.getKey(), newMeasureBuilder().create(oldMeasure.getValue() + 1, 0));
195     step().execute(context);
196     assertThat(selectMeasure("file-uuid", BUGS).get().getValue()).isEqualTo(oldMeasure.getValue());
197   }
198
199   @Test
200   public void dont_keep_measures_for_unchanged_files() {
201     prepareProject();
202     LiveMeasureDto oldMeasure = insertMeasure("file-uuid", "project-uuid", BUGS);
203     db.commit();
204     when(fileStatuses.isDataUnchanged(any(Component.class))).thenReturn(false);
205     // this new value will be persisted
206     measureRepository.addRawMeasure(REF_4, BUGS.getKey(), newMeasureBuilder().create(oldMeasure.getValue() + 1, 0));
207     step().execute(context);
208     assertThat(selectMeasure("file-uuid", BUGS).get().getValue()).isEqualTo(oldMeasure.getValue() + 1);
209   }
210
211   @Test
212   public void persist_live_measures_of_portfolio_analysis() {
213     preparePortfolio();
214
215     // the computed measures
216     measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("view-value"));
217     measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("subview-value"));
218     measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value"));
219
220     step().execute(context);
221
222     assertThat(db.countRowsOfTable("live_measures")).isEqualTo(3);
223     assertThat(selectMeasure("view-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("view-value");
224     assertThat(selectMeasure("subview-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("subview-value");
225     assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("project-value");
226     verifyStatistics(context, 3);
227   }
228
229   private LiveMeasureDto insertMeasure(String componentUuid, String projectUuid, Metric metric) {
230     LiveMeasureDto measure = newLiveMeasure()
231       .setComponentUuid(componentUuid)
232       .setProjectUuid(projectUuid)
233       .setMetricUuid(metricRepository.getByKey(metric.getKey()).getUuid());
234     dbClient.liveMeasureDao().insertOrUpdate(db.getSession(), measure);
235     return measure;
236   }
237
238   private void assertThatMeasureHasValue(LiveMeasureDto template, int expectedValue) {
239     Optional<LiveMeasureDto> persisted = dbClient.liveMeasureDao().selectMeasure(db.getSession(),
240       template.getComponentUuid(), metricRepository.getByUuid(template.getMetricUuid()).getKey());
241     assertThat(persisted).isPresent();
242     assertThat(persisted.get().getValue()).isEqualTo(expectedValue);
243   }
244
245   private void assertThatMeasureHasValue(String componentUuid, Metric metric, int expectedValue) {
246     Optional<LiveMeasureDto> persisted = dbClient.liveMeasureDao().selectMeasure(db.getSession(),
247       componentUuid, metric.getKey());
248     assertThat(persisted).isPresent();
249     assertThat(persisted.get().getValue()).isEqualTo(expectedValue);
250   }
251
252   private void assertThatMeasureDoesNotExist(LiveMeasureDto template) {
253     assertThat(dbClient.liveMeasureDao().selectMeasure(db.getSession(),
254       template.getComponentUuid(), metricRepository.getByUuid(template.getMetricUuid()).getKey()))
255       .isEmpty();
256   }
257
258   private void prepareProject() {
259     // tree of components as defined by scanner report
260     Component project = ReportComponent.builder(PROJECT, REF_1).setUuid("project-uuid")
261       .addChildren(
262         ReportComponent.builder(DIRECTORY, REF_3).setUuid("dir-uuid")
263           .addChildren(
264             ReportComponent.builder(FILE, REF_4).setUuid("file-uuid")
265               .build())
266           .build())
267       .build();
268     treeRootHolder.setRoot(project);
269     analysisMetadataHolder.setProject(new Project(project.getUuid(), project.getKey(), project.getName(), project.getDescription(), emptyList()));
270
271     // components as persisted in db
272     ComponentDto projectDto = insertComponent("project-key", "project-uuid");
273     ComponentDto dirDto = insertComponent("dir-key", "dir-uuid");
274     ComponentDto fileDto = insertComponent("file-key", "file-uuid");
275   }
276
277   private void preparePortfolio() {
278     // tree of components
279     Component portfolio = ViewsComponent.builder(VIEW, REF_1).setUuid("view-uuid")
280       .addChildren(
281         ViewsComponent.builder(SUBVIEW, REF_2).setUuid("subview-uuid")
282           .addChildren(
283             ViewsComponent.builder(PROJECT_VIEW, REF_3).setUuid("project-uuid")
284               .build())
285           .build())
286       .build();
287     treeRootHolder.setRoot(portfolio);
288
289     // components as persisted in db
290     ComponentDto portfolioDto = insertComponent("view-key", "view-uuid");
291     ComponentDto subViewDto = insertComponent("subview-key", "subview-uuid");
292     ComponentDto projectDto = insertComponent("project-key", "project-uuid");
293     analysisMetadataHolder.setProject(Project.from(portfolioDto));
294   }
295
296   private void assertThatMeasureIsNotPersisted(String componentUuid, Metric metric) {
297     assertThat(selectMeasure(componentUuid, metric)).isEmpty();
298   }
299
300   private Optional<LiveMeasureDto> selectMeasure(String componentUuid, Metric metric) {
301     return dbClient.liveMeasureDao().selectMeasure(db.getSession(), componentUuid, metric.getKey());
302   }
303
304   private ComponentDto insertComponent(String key, String uuid) {
305     ComponentDto componentDto = new ComponentDto()
306       .setKey(key)
307       .setUuid(uuid)
308       .setUuidPath(uuid + ".")
309       .setBranchUuid(uuid);
310     db.components().insertComponent(componentDto);
311     return componentDto;
312   }
313
314   @Override
315   protected ComputationStep step() {
316     return new PersistLiveMeasuresStep(dbClient, metricRepository, new MeasureToMeasureDto(analysisMetadataHolder, treeRootHolder), treeRootHolder, measureRepository,
317       Optional.of(fileStatuses));
318   }
319
320   private static void verifyStatistics(TestComputationStepContext context, int expectedInsertsOrUpdates) {
321     context.getStatistics().assertValue("insertsOrUpdates", expectedInsertsOrUpdates);
322   }
323 }