3 * Copyright (C) 2009-2016 SonarSource SA
4 * mailto:contact 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.computation.measure;
22 import com.google.common.base.Function;
23 import com.google.common.base.Optional;
24 import com.google.common.base.Predicate;
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.SetMultimap;
27 import com.tngtech.java.junit.dataprovider.DataProvider;
28 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
29 import com.tngtech.java.junit.dataprovider.UseDataProvider;
30 import java.util.List;
31 import javax.annotation.Nullable;
32 import org.junit.Before;
33 import org.junit.Rule;
34 import org.junit.Test;
35 import org.junit.rules.ExpectedException;
36 import org.junit.runner.RunWith;
37 import org.sonar.api.utils.System2;
38 import org.sonar.db.DbClient;
39 import org.sonar.db.DbSession;
40 import org.sonar.db.DbTester;
41 import org.sonar.db.measure.MeasureDto;
42 import org.sonar.scanner.protocol.output.ScannerReport;
43 import org.sonar.scanner.protocol.output.ScannerReport.Measure.StringValue;
44 import org.sonar.server.computation.batch.BatchReportReader;
45 import org.sonar.server.computation.batch.BatchReportReaderRule;
46 import org.sonar.server.computation.component.Component;
47 import org.sonar.server.computation.component.ReportComponent;
48 import org.sonar.server.computation.metric.Metric;
49 import org.sonar.server.computation.metric.MetricImpl;
50 import org.sonar.server.computation.metric.MetricRepository;
51 import org.sonar.server.computation.metric.ReportMetricValidator;
53 import static com.google.common.collect.FluentIterable.from;
54 import static java.lang.String.format;
55 import static org.assertj.core.api.Assertions.assertThat;
56 import static org.assertj.guava.api.Assertions.assertThat;
57 import static org.junit.Assert.fail;
58 import static org.mockito.Mockito.mock;
59 import static org.mockito.Mockito.verifyNoMoreInteractions;
60 import static org.mockito.Mockito.verifyZeroInteractions;
61 import static org.mockito.Mockito.when;
63 @RunWith(DataProviderRunner.class)
64 public class MeasureRepositoryImplTest {
67 public final ExpectedException expectedException = ExpectedException.none();
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 long LAST_SNAPSHOT_ID = 123;
84 private static final long OTHER_SNAPSHOT_ID = 369;
85 private static final String COMPONENT_UUID = "UUID1";
86 private static final Measure SOME_MEASURE = Measure.newMeasureBuilder().create("some value");
87 private static final String SOME_DATA = "some data";
89 private ReportMetricValidator reportMetricValidator = mock(ReportMetricValidator.class);
91 private DbClient dbClient = dbTester.getDbClient();
92 private MetricRepository metricRepository = mock(MetricRepository.class);
93 private MeasureRepositoryImpl underTest = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository, reportMetricValidator);
95 private DbClient mockedDbClient = mock(DbClient.class);
96 private BatchReportReader mockBatchReportReader = mock(BatchReportReader.class);
97 private MeasureRepositoryImpl underTestWithMock = new MeasureRepositoryImpl(mockedDbClient, mockBatchReportReader, metricRepository, reportMetricValidator);
99 private DbSession dbSession = dbTester.getSession();
102 public void setUp() {
103 when(metric1.getKey()).thenReturn(METRIC_KEY_1);
104 when(metric1.getType()).thenReturn(Metric.MetricType.STRING);
105 when(metric2.getKey()).thenReturn(METRIC_KEY_2);
106 when(metric2.getType()).thenReturn(Metric.MetricType.STRING);
108 // references to metrics are consistent with DB by design
109 when(metricRepository.getByKey(METRIC_KEY_1)).thenReturn(metric1);
110 when(metricRepository.getByKey(METRIC_KEY_2)).thenReturn(metric2);
114 public void getBaseMeasure_throws_NPE_and_does_not_open_session_if_component_is_null() {
116 underTestWithMock.getBaseMeasure(null, metric1);
117 fail("an NPE should have been raised");
118 } catch (NullPointerException e) {
119 verifyZeroInteractions(mockedDbClient);
124 public void getBaseMeasure_throws_NPE_and_does_not_open_session_if_metric_is_null() {
126 underTestWithMock.getBaseMeasure(FILE_COMPONENT, null);
127 fail("an NPE should have been raised");
128 } catch (NullPointerException e) {
129 verifyZeroInteractions(mockedDbClient);
134 public void getBaseMeasure_returns_absent_if_measure_does_not_exist_in_DB() {
135 Optional<Measure> res = underTest.getBaseMeasure(FILE_COMPONENT, metric1);
137 assertThat(res).isAbsent();
141 public void getBaseMeasure_returns_Measure_if_measure_of_last_snapshot_only_in_DB() {
142 dbTester.prepareDbUnit(getClass(), "shared.xml");
143 dbClient.measureDao().insert(dbSession, createMeasureDto(METRIC_ID_1, FILE_COMPONENT.getUuid(), LAST_SNAPSHOT_ID));
144 dbClient.measureDao().insert(dbSession, createMeasureDto(METRIC_ID_2, FILE_COMPONENT.getUuid(), OTHER_SNAPSHOT_ID));
147 // metric 1 is associated to snapshot with "last=true"
148 Optional<Measure> res = underTest.getBaseMeasure(FILE_COMPONENT, metric1);
150 assertThat(res).isPresent();
151 assertThat(res.get().getStringValue()).isEqualTo(SOME_DATA);
153 // metric 2 is associated to snapshot with "last=false" => not retrieved
154 res = underTest.getBaseMeasure(FILE_COMPONENT, metric2);
156 assertThat(res).isAbsent();
160 public void add_throws_NPE_if_Component_argument_is_null() {
161 expectedException.expect(NullPointerException.class);
162 underTest.add(null, metric1, SOME_MEASURE);
166 public void add_throws_NPE_if_Component_metric_is_null() {
167 expectedException.expect(NullPointerException.class);
168 underTest.add(FILE_COMPONENT, null, SOME_MEASURE);
172 public void add_throws_NPE_if_Component_measure_is_null() {
173 expectedException.expect(NullPointerException.class);
174 underTest.add(FILE_COMPONENT, metric1, null);
178 public void add_throws_UOE_if_measure_already_exists() {
179 expectedException.expect(UnsupportedOperationException.class);
180 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
181 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
185 public void update_throws_NPE_if_Component_argument_is_null() {
186 expectedException.expect(NullPointerException.class);
187 underTest.update(null, metric1, SOME_MEASURE);
191 public void update_throws_NPE_if_Component_metric_is_null() {
192 expectedException.expect(NullPointerException.class);
193 underTest.update(FILE_COMPONENT, null, SOME_MEASURE);
197 public void update_throws_NPE_if_Component_measure_is_null() {
198 expectedException.expect(NullPointerException.class);
199 expectedException.expect(NullPointerException.class);
200 underTest.update(FILE_COMPONENT, metric1, null);
204 public void update_throws_UOE_if_measure_does_not_exists() {
205 expectedException.expect(UnsupportedOperationException.class);
206 underTest.update(FILE_COMPONENT, metric1, SOME_MEASURE);
209 private static final List<Measure> MEASURES = ImmutableList.of(
210 Measure.newMeasureBuilder().create(1),
211 Measure.newMeasureBuilder().create(1l),
212 Measure.newMeasureBuilder().create(1d, 1),
213 Measure.newMeasureBuilder().create(true),
214 Measure.newMeasureBuilder().create(false),
215 Measure.newMeasureBuilder().create("sds"),
216 Measure.newMeasureBuilder().create(Measure.Level.OK),
217 Measure.newMeasureBuilder().createNoValue());
220 public static Object[][] measures() {
221 return from(MEASURES).transform(new Function<Measure, Object[]>() {
224 public Object[] apply(Measure input) {
225 return new Measure[] {input};
227 }).toArray(Object[].class);
231 public void add_accepts_NO_VALUE_as_measure_arg() {
232 for (Metric.MetricType metricType : Metric.MetricType.values()) {
233 underTest.add(FILE_COMPONENT, new MetricImpl(1, "key" + metricType, "name" + metricType, metricType), Measure.newMeasureBuilder().createNoValue());
238 @UseDataProvider("measures")
239 public void update_throws_IAE_if_valueType_of_Measure_is_not_the_same_as_the_Metric_valueType_unless_NO_VALUE(Measure measure) {
240 for (Metric.MetricType metricType : Metric.MetricType.values()) {
241 if (metricType.getValueType() == measure.getValueType() || measure.getValueType() == Measure.ValueType.NO_VALUE) {
246 final MetricImpl metric = new MetricImpl(1, "key" + metricType, "name" + metricType, metricType);
247 underTest.add(FILE_COMPONENT, metric, getSomeMeasureByValueType(metricType));
248 underTest.update(FILE_COMPONENT, metric, measure);
249 fail("An IllegalArgumentException should have been raised");
250 } catch (IllegalArgumentException e) {
251 assertThat(e).hasMessage(format(
252 "Measure's ValueType (%s) is not consistent with the Metric's ValueType (%s)",
253 measure.getValueType(), metricType.getValueType()));
259 public void update_accepts_NO_VALUE_as_measure_arg() {
260 for (Metric.MetricType metricType : Metric.MetricType.values()) {
261 MetricImpl metric = new MetricImpl(1, "key" + metricType, "name" + metricType, metricType);
262 underTest.add(FILE_COMPONENT, metric, getSomeMeasureByValueType(metricType));
263 underTest.update(FILE_COMPONENT, metric, Measure.newMeasureBuilder().createNoValue());
267 private Measure getSomeMeasureByValueType(final Metric.MetricType metricType) {
268 return from(MEASURES).filter(new Predicate<Measure>() {
270 public boolean apply(@Nullable Measure input) {
271 return input.getValueType() == metricType.getValueType();
277 public void update_supports_updating_to_the_same_value() {
278 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
279 underTest.update(FILE_COMPONENT, metric1, SOME_MEASURE);
283 public void update_updates_the_stored_value() {
284 Measure newMeasure = Measure.updatedMeasureBuilder(SOME_MEASURE).create();
286 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
287 underTest.update(FILE_COMPONENT, metric1, newMeasure);
289 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric1).get()).isSameAs(newMeasure);
293 public void getRawMeasure_throws_NPE_without_reading_batch_report_if_component_arg_is_null() {
295 underTestWithMock.getRawMeasure(null, metric1);
296 fail("an NPE should have been raised");
297 } catch (NullPointerException e) {
298 verifyNoMoreInteractions(mockBatchReportReader);
303 public void getRawMeasure_throws_NPE_without_reading_batch_report_if_metric_arg_is_null() {
305 underTestWithMock.getRawMeasure(FILE_COMPONENT, null);
306 fail("an NPE should have been raised");
307 } catch (NullPointerException e) {
308 verifyNoMoreInteractions(mockBatchReportReader);
313 public void getRawMeasure_returns_measure_added_through_add_method() {
314 underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
316 Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
318 assertThat(res).isPresent();
319 assertThat(res.get()).isSameAs(SOME_MEASURE);
321 // make sure we really match on the specified component and metric
322 assertThat(underTest.getRawMeasure(OTHER_COMPONENT, metric1)).isAbsent();
323 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isAbsent();
327 public void getRawMeasure_returns_measure_from_batch_if_not_added_through_add_method() {
328 String value = "trololo";
330 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
332 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
333 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue(value)).build()));
335 Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
337 assertThat(res).isPresent();
338 assertThat(res.get().getStringValue()).isEqualTo(value);
340 // make sure we really match on the specified component and metric
341 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isAbsent();
342 assertThat(underTest.getRawMeasure(OTHER_COMPONENT, metric1)).isAbsent();
346 public void getRawMeasure_returns_only_validate_measure_from_batch_if_not_added_through_add_method() {
347 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
348 when(reportMetricValidator.validate(METRIC_KEY_2)).thenReturn(false);
350 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
351 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("value1")).build(),
352 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_2).setStringValue(StringValue.newBuilder().setValue("value2")).build()));
354 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric1)).isPresent();
355 assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isAbsent();
359 public void getRawMeasure_retrieves_added_measure_over_batch_measure() {
360 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
361 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
362 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("some value")).build()));
364 Measure addedMeasure = SOME_MEASURE;
365 underTest.add(FILE_COMPONENT, metric1, addedMeasure);
367 Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
369 assertThat(res).isPresent();
370 assertThat(res.get()).isSameAs(addedMeasure);
374 public void getRawMeasure_retrieves_measure_from_batch_and_caches_it_locally_so_that_it_can_be_updated() {
375 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
376 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
377 ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("some value")).build()));
379 Optional<Measure> measure = underTest.getRawMeasure(FILE_COMPONENT, metric1);
381 underTest.update(FILE_COMPONENT, metric1, Measure.updatedMeasureBuilder(measure.get()).create());
385 public void getRawMeasures_for_metric_throws_NPE_if_Component_arg_is_null() {
386 expectedException.expect(NullPointerException.class);
387 underTest.getRawMeasures(null, metric1);
391 public void getRawMeasures_for_metric_throws_NPE_if_Metric_arg_is_null() {
392 expectedException.expect(NullPointerException.class);
393 underTest.getRawMeasures(FILE_COMPONENT, null);
397 public void getRawMeasures_for_metric_returns_empty_if_repository_is_empty() {
398 assertThat(underTest.getRawMeasures(FILE_COMPONENT, metric1)).isEmpty();
402 public void getRawMeasures_returns_added_measures_over_batch_measures() {
403 when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
404 when(reportMetricValidator.validate(METRIC_KEY_2)).thenReturn(true);
405 ScannerReport.Measure batchMeasure1 = ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("some value")).build();
406 ScannerReport.Measure batchMeasure2 = ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_2).setStringValue(StringValue.newBuilder().setValue("some value")).build();
407 reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(batchMeasure1, batchMeasure2));
409 Measure addedMeasure = SOME_MEASURE;
410 underTest.add(FILE_COMPONENT, metric1, addedMeasure);
412 SetMultimap<String, Measure> rawMeasures = underTest.getRawMeasures(FILE_COMPONENT);
414 assertThat(rawMeasures.keySet()).hasSize(2);
415 assertThat(rawMeasures.get(METRIC_KEY_1)).containsOnly(addedMeasure);
416 assertThat(rawMeasures.get(METRIC_KEY_2)).containsOnly(Measure.newMeasureBuilder().create("some value"));
419 private static MeasureDto createMeasureDto(int metricId, String componentUuid, long snapshotId) {
420 return new MeasureDto()
421 .setComponentUuid(componentUuid)
422 .setSnapshotId(snapshotId)
424 .setMetricId(metricId);