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.ce.task.projectanalysis.measure;
22 import com.google.common.base.Function;
23 import com.google.common.collect.ImmutableList;
24 import com.tngtech.java.junit.dataprovider.DataProvider;
25 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
26 import com.tngtech.java.junit.dataprovider.UseDataProvider;
27 import java.util.List;
29 import java.util.Optional;
30 import javax.annotation.Nullable;
31 import org.junit.Before;
32 import org.junit.Rule;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.sonar.api.utils.System2;
36 import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
37 import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
38 import org.sonar.ce.task.projectanalysis.component.Component;
39 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
40 import org.sonar.ce.task.projectanalysis.metric.Metric;
41 import org.sonar.ce.task.projectanalysis.metric.MetricImpl;
42 import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
43 import org.sonar.ce.task.projectanalysis.metric.ReportMetricValidator;
44 import org.sonar.db.DbClient;
45 import org.sonar.db.DbSession;
46 import org.sonar.db.DbTester;
47 import org.sonar.db.component.ComponentDto;
48 import org.sonar.db.component.SnapshotDto;
49 import org.sonar.db.measure.MeasureDto;
50 import org.sonar.db.metric.MetricDto;
51 import org.sonar.scanner.protocol.output.ScannerReport;
52 import org.sonar.scanner.protocol.output.ScannerReport.Measure.StringValue;
54 import static com.google.common.collect.FluentIterable.from;
55 import static java.lang.String.format;
56 import static org.assertj.core.api.Assertions.assertThat;
57 import static org.assertj.core.api.Assertions.assertThatThrownBy;
58 import static org.junit.Assert.fail;
59 import static org.mockito.Mockito.mock;
60 import static org.mockito.Mockito.verifyNoInteractions;
61 import static org.mockito.Mockito.verifyNoMoreInteractions;
62 import static org.mockito.Mockito.when;
63 import static org.sonar.db.component.ComponentTesting.newFileDto;
65 @RunWith(DataProviderRunner.class)
66 public class MeasureRepositoryImplIT {
70 public DbTester dbTester = DbTester.create(System2.INSTANCE);
72 public BatchReportReaderRule reportReader = new BatchReportReaderRule();
74 private static final String FILE_COMPONENT_KEY = "file cpt key";
75 private static final ReportComponent FILE_COMPONENT = ReportComponent.builder(Component.Type.FILE, 1).setKey(FILE_COMPONENT_KEY).build();
76 private static final ReportComponent OTHER_COMPONENT = ReportComponent.builder(Component.Type.FILE, 2).setKey("some other key").build();
77 private static final String METRIC_KEY_1 = "metric 1";
78 private static final int METRIC_ID_1 = 1;
79 private static final String METRIC_KEY_2 = "metric 2";
80 private static final int METRIC_ID_2 = 2;
81 private final Metric metric1 = mock(Metric.class);
82 private final Metric metric2 = mock(Metric.class);
83 private static final String LAST_ANALYSIS_UUID = "u123";
84 private static final String OTHER_ANALYSIS_UUID = "u369";
85 private static final Measure SOME_MEASURE = Measure.newMeasureBuilder().create("some value");
86 private static final String SOME_DATA = "some data";
88 private ReportMetricValidator reportMetricValidator = mock(ReportMetricValidator.class);
90 private DbClient dbClient = dbTester.getDbClient();
91 private MetricRepository metricRepository = mock(MetricRepository.class);
92 private MeasureRepositoryImpl underTest = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository, reportMetricValidator);
94 private DbClient mockedDbClient = mock(DbClient.class);
95 private BatchReportReader mockBatchReportReader = mock(BatchReportReader.class);
96 private MeasureRepositoryImpl underTestWithMock = new MeasureRepositoryImpl(mockedDbClient, mockBatchReportReader, metricRepository, reportMetricValidator);
98 private DbSession dbSession = dbTester.getSession();
101 public void setUp() {
102 when(metric1.getKey()).thenReturn(METRIC_KEY_1);
103 when(metric1.getType()).thenReturn(Metric.MetricType.STRING);
104 when(metric2.getKey()).thenReturn(METRIC_KEY_2);
105 when(metric2.getType()).thenReturn(Metric.MetricType.STRING);
107 // references to metrics are consistent with DB by design
108 when(metricRepository.getByKey(METRIC_KEY_1)).thenReturn(metric1);
109 when(metricRepository.getByKey(METRIC_KEY_2)).thenReturn(metric2);
113 public void getBaseMeasure_throws_NPE_and_does_not_open_session_if_component_is_null() {
115 underTestWithMock.getBaseMeasure(null, metric1);
116 fail("an NPE should have been raised");
117 } catch (NullPointerException e) {
118 verifyNoInteractions(mockedDbClient);
123 public void getBaseMeasure_throws_NPE_and_does_not_open_session_if_metric_is_null() {
125 underTestWithMock.getBaseMeasure(FILE_COMPONENT, null);
126 fail("an NPE should have been raised");
127 } catch (NullPointerException e) {
128 verifyNoInteractions(mockedDbClient);
133 public void getBaseMeasure_returns_absent_if_measure_does_not_exist_in_DB() {
134 Optional<Measure> res = underTest.getBaseMeasure(FILE_COMPONENT, metric1);
136 assertThat(res).isNotPresent();
140 public void getBaseMeasure_returns_Measure_if_measure_of_last_snapshot_only_in_DB() {
141 ComponentDto project = dbTester.components().insertPrivateProject().getMainBranchComponent();
142 dbTester.components().insertComponent(newFileDto(project).setUuid(FILE_COMPONENT.getUuid()));
143 SnapshotDto lastAnalysis = dbTester.components().insertSnapshot(project, t -> t.setLast(true));
144 SnapshotDto oldAnalysis = dbTester.components().insertSnapshot(project, t -> t.setLast(false));
145 MetricDto metric1 = dbTester.measures().insertMetric(t -> t.setValueType(org.sonar.api.measures.Metric.ValueType.STRING.name()));
146 MetricDto metric2 = dbTester.measures().insertMetric(t -> t.setValueType(org.sonar.api.measures.Metric.ValueType.STRING.name()));
147 dbClient.measureDao().insert(dbSession, createMeasureDto(metric1.getUuid(), FILE_COMPONENT.getUuid(), lastAnalysis.getUuid()));
148 dbClient.measureDao().insert(dbSession, createMeasureDto(metric1.getUuid(), FILE_COMPONENT.getUuid(), oldAnalysis.getUuid()));
151 // metric 1 is associated to snapshot with "last=true"
152 assertThat(underTest.getBaseMeasure(FILE_COMPONENT, metricOf(metric1)).get().getStringValue())
153 .isEqualTo(SOME_DATA);
154 // metric 2 is associated to snapshot with "last=false" => not retrieved
155 assertThat(underTest.getBaseMeasure(FILE_COMPONENT, metricOf(metric2))).isNotPresent();
158 private Metric metricOf(MetricDto metricDto) {
159 Metric res = mock(Metric.class);
160 when(res.getKey()).thenReturn(metricDto.getKey());
161 when(res.getUuid()).thenReturn(metricDto.getUuid());
162 when(res.getType()).thenReturn(Metric.MetricType.valueOf(metricDto.getValueType()));
167 public void add_throws_NPE_if_Component_argument_is_null() {
168 assertThatThrownBy(() -> underTest.add(null, metric1, SOME_MEASURE))
169 .isInstanceOf(NullPointerException.class);
173 public void add_throws_NPE_if_Component_metric_is_null() {
174 assertThatThrownBy(() -> underTest.add(FILE_COMPONENT, null, SOME_MEASURE))
175 .isInstanceOf(NullPointerException.class);
179 public void add_throws_NPE_if_Component_measure_is_null() {
180 assertThatThrownBy(() -> underTest.add(FILE_COMPONENT, metric1, null))
181 .isInstanceOf(NullPointerException.class);
185 public void add_throws_UOE_if_measure_already_exists() {
186 assertThatThrownBy(() -> {
187 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
188 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
190 .isInstanceOf(UnsupportedOperationException.class);
194 public void update_throws_NPE_if_Component_metric_is_null() {
195 assertThatThrownBy(() -> underTest.update(FILE_COMPONENT, null, SOME_MEASURE))
196 .isInstanceOf(NullPointerException.class);
200 public void update_throws_NPE_if_Component_measure_is_null() {
201 assertThatThrownBy(() -> underTest.update(FILE_COMPONENT, metric1, null))
202 .isInstanceOf(NullPointerException.class);
206 public void update_throws_UOE_if_measure_does_not_exists() {
207 assertThatThrownBy(() -> underTest.update(FILE_COMPONENT, metric1, SOME_MEASURE))
208 .isInstanceOf(UnsupportedOperationException.class);
211 private static final List<Measure> MEASURES = ImmutableList.of(
212 Measure.newMeasureBuilder().create(1),
213 Measure.newMeasureBuilder().create(1L),
214 Measure.newMeasureBuilder().create(1d, 1),
215 Measure.newMeasureBuilder().create(true),
216 Measure.newMeasureBuilder().create(false),
217 Measure.newMeasureBuilder().create("sds"),
218 Measure.newMeasureBuilder().create(Measure.Level.OK),
219 Measure.newMeasureBuilder().createNoValue());
222 public static Object[][] measures() {
223 return from(MEASURES).transform(new Function<Measure, Object[]>() {
226 public Object[] apply(Measure input) {
227 return new Measure[] {input};
229 }).toArray(Object[].class);
233 public void add_accepts_NO_VALUE_as_measure_arg() {
234 for (Metric.MetricType metricType : Metric.MetricType.values()) {
235 underTest.add(FILE_COMPONENT, new MetricImpl("1", "key" + metricType, "name" + metricType, metricType), Measure.newMeasureBuilder().createNoValue());
240 @UseDataProvider("measures")
241 public void update_throws_IAE_if_valueType_of_Measure_is_not_the_same_as_the_Metric_valueType_unless_NO_VALUE(Measure measure) {
242 for (Metric.MetricType metricType : Metric.MetricType.values()) {
243 if (metricType.getValueType() == measure.getValueType() || measure.getValueType() == Measure.ValueType.NO_VALUE) {
248 final MetricImpl metric = new MetricImpl("1", "key" + metricType, "name" + metricType, metricType);
249 underTest.add(FILE_COMPONENT, metric, getSomeMeasureByValueType(metricType));
250 underTest.update(FILE_COMPONENT, metric, measure);
251 fail("An IllegalArgumentException should have been raised");
252 } catch (IllegalArgumentException e) {
253 assertThat(e).hasMessage(format(
254 "Measure's ValueType (%s) is not consistent with the Metric's ValueType (%s)",
255 measure.getValueType(), metricType.getValueType()));
261 public void update_accepts_NO_VALUE_as_measure_arg() {
262 for (Metric.MetricType metricType : Metric.MetricType.values()) {
263 MetricImpl metric = new MetricImpl("1", "key" + metricType, "name" + metricType, metricType);
264 underTest.add(FILE_COMPONENT, metric, getSomeMeasureByValueType(metricType));
265 underTest.update(FILE_COMPONENT, metric, Measure.newMeasureBuilder().createNoValue());
269 private Measure getSomeMeasureByValueType(final Metric.MetricType metricType) {
270 return MEASURES.stream().filter(input -> input.getValueType() == metricType.getValueType()).findFirst().get();
274 public void update_supports_updating_to_the_same_value() {
275 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
276 underTest.update(FILE_COMPONENT, metric1, SOME_MEASURE);
280 public void update_updates_the_stored_value() {
281 Measure newMeasure = Measure.updatedMeasureBuilder(SOME_MEASURE).create();
283 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
284 underTest.update(FILE_COMPONENT, metric1, newMeasure);
286 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric1)).containsSame(newMeasure);
290 public void getRawMeasure_throws_NPE_without_reading_batch_report_if_component_arg_is_null() {
292 underTestWithMock.getRawMeasure(null, metric1);
293 fail("an NPE should have been raised");
294 } catch (NullPointerException e) {
295 verifyNoMoreInteractions(mockBatchReportReader);
300 public void getRawMeasure_throws_NPE_without_reading_batch_report_if_metric_arg_is_null() {
302 underTestWithMock.getRawMeasure(FILE_COMPONENT, null);
303 fail("an NPE should have been raised");
304 } catch (NullPointerException e) {
305 verifyNoMoreInteractions(mockBatchReportReader);
310 public void getRawMeasure_returns_measure_added_through_add_method() {
311 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
313 Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
317 .containsSame(SOME_MEASURE);
319 // make sure we really match on the specified component and metric
320 assertThat(underTest.getRawMeasure(OTHER_COMPONENT, metric1)).isNotPresent();
321 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isNotPresent();
325 public void getRawMeasure_returns_measure_from_batch_if_not_added_through_add_method() {
326 String value = "trololo";
328 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
330 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
331 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue(value)).build()));
333 Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
335 assertThat(res).isPresent();
336 assertThat(res.get().getStringValue()).isEqualTo(value);
338 // make sure we really match on the specified component and metric
339 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isNotPresent();
340 assertThat(underTest.getRawMeasure(OTHER_COMPONENT, metric1)).isNotPresent();
344 public void getRawMeasure_returns_only_validate_measure_from_batch_if_not_added_through_add_method() {
345 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
346 when(reportMetricValidator.validate(METRIC_KEY_2)).thenReturn(false);
348 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
349 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("value1")).build(),
350 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_2).setStringValue(StringValue.newBuilder().setValue("value2")).build()));
352 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric1)).isPresent();
353 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isNotPresent();
357 public void getRawMeasure_retrieves_added_measure_over_batch_measure() {
358 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
359 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
360 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("some value")).build()));
362 Measure addedMeasure = SOME_MEASURE;
363 underTest.add(FILE_COMPONENT, metric1, addedMeasure);
365 Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
369 .containsSame(addedMeasure);
373 public void getRawMeasure_retrieves_measure_from_batch_and_caches_it_locally_so_that_it_can_be_updated() {
374 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
375 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
376 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("some value")).build()));
378 Optional<Measure> measure = underTest.getRawMeasure(FILE_COMPONENT, metric1);
380 underTest.update(FILE_COMPONENT, metric1, Measure.updatedMeasureBuilder(measure.get()).create());
384 public void getRawMeasures_returns_added_measures_over_batch_measures() {
385 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
386 when(reportMetricValidator.validate(METRIC_KEY_2)).thenReturn(true);
387 ScannerReport.Measure batchMeasure1 = ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("some value")).build();
388 ScannerReport.Measure batchMeasure2 = ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_2).setStringValue(StringValue.newBuilder().setValue("some value")).build();
389 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(batchMeasure1, batchMeasure2));
391 Measure addedMeasure = SOME_MEASURE;
392 underTest.add(FILE_COMPONENT, metric1, addedMeasure);
394 Map<String, Measure> rawMeasures = underTest.getRawMeasures(FILE_COMPONENT);
396 assertThat(rawMeasures.keySet()).hasSize(2);
397 assertThat(rawMeasures).containsEntry(METRIC_KEY_1, addedMeasure);
398 assertThat(rawMeasures.get(METRIC_KEY_2)).extracting(Measure::getStringValue).isEqualTo("some value");
401 private static MeasureDto createMeasureDto(String metricUuid, String componentUuid, String analysisUuid) {
402 return new MeasureDto()
403 .setComponentUuid(componentUuid)
404 .setAnalysisUuid(analysisUuid)
406 .setMetricUuid(metricUuid);