diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-06-10 14:00:21 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-06-19 17:21:13 +0200 |
commit | 0825f36322508d39e6bdcdadb4ea8d4aaeb17671 (patch) | |
tree | 9014404491e588fa1d3ad8ad8b395ff79870173e | |
parent | e5bd8f4cb3936952c79ac741815cab69dfd1312e (diff) | |
download | sonarqube-0825f36322508d39e6bdcdadb4ea8d4aaeb17671.tar.gz sonarqube-0825f36322508d39e6bdcdadb4ea8d4aaeb17671.zip |
Copy custom measures in compute engine
9 files changed, 311 insertions, 13 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java index 8f174cd37af..bddb6c28a09 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java @@ -53,6 +53,7 @@ public class ComputationSteps { FeedPeriodsStep.class, // data computation + CustomMeasuresCopyStep.class, QualityProfileEventsStep.class, QualityGateEventsStep.class, FillMeasuresWithVariationsStep.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/CustomMeasuresCopyStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/CustomMeasuresCopyStep.java new file mode 100644 index 00000000000..f9a1f02201a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/CustomMeasuresCopyStep.java @@ -0,0 +1,108 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.computation.step; + +import com.google.common.annotations.VisibleForTesting; +import java.util.List; +import org.apache.commons.lang.math.NumberUtils; +import org.sonar.core.measure.custom.db.CustomMeasureDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; +import org.sonar.server.computation.component.TreeRootHolder; +import org.sonar.server.computation.measure.Measure; +import org.sonar.server.computation.measure.MeasureRepository; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricRepository; +import org.sonar.server.db.DbClient; + +public class CustomMeasuresCopyStep implements ComputationStep { + + private final TreeRootHolder treeRootHolder; + private final DbClient dbClient; + private final MetricRepository metricRepository; + private final MeasureRepository measureRepository; + + public CustomMeasuresCopyStep(TreeRootHolder treeRootHolder, DbClient dbClient, + MetricRepository metricRepository, MeasureRepository measureRepository) { + this.treeRootHolder = treeRootHolder; + this.dbClient = dbClient; + this.metricRepository = metricRepository; + this.measureRepository = measureRepository; + } + + @Override + public void execute() { + new DepthTraversalTypeAwareVisitor(Component.Type.FILE, DepthTraversalTypeAwareVisitor.Order.PRE_ORDER) { + @Override + public void visitAny(Component component) { + copy(component); + } + }.visit(treeRootHolder.getRoot()); + } + + private void copy(Component component) { + for (CustomMeasureDto dto : loadCustomMeasures(component)) { + Metric metric = metricRepository.getById(dto.getMetricId()); + // else metric is not found and an exception is raised + Measure measure = dtoToMeasure(dto, metric); + measureRepository.add(component, metric, measure); + } + } + + private List<CustomMeasureDto> loadCustomMeasures(Component component) { + DbSession session = dbClient.openSession(false); + try { + return dbClient.customMeasureDao().selectByComponentUuid(session, component.getUuid()); + } finally { + MyBatis.closeQuietly(session); + } + } + + @VisibleForTesting + static Measure dtoToMeasure(CustomMeasureDto dto, Metric metric) { + switch (metric.getType()) { + case INT: + case RATING: + case MILLISEC: + return Measure.newMeasureBuilder().create((int) dto.getValue()); + case WORK_DUR: + return Measure.newMeasureBuilder().create((long) dto.getValue()); + case FLOAT: + case PERCENT: + return Measure.newMeasureBuilder().create(dto.getValue()); + case BOOL: + return Measure.newMeasureBuilder().create(NumberUtils.compare(dto.getValue(), 1.0) == 0); + case STRING: + case DISTRIB: + case DATA: + case LEVEL: + return Measure.newMeasureBuilder().create(dto.getTextValue()); + default: + throw new IllegalArgumentException(String.format("Custom measures do not support the metric type [%s]", metric.getType())); + } + } + + @Override + public String getDescription() { + return "Copy custom measures"; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/persistence/CustomMeasureDao.java b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/persistence/CustomMeasureDao.java index 0c609c98615..ea62814d830 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/persistence/CustomMeasureDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/persistence/CustomMeasureDao.java @@ -69,14 +69,14 @@ public class CustomMeasureDao implements DaoComponent { return mapper(session).selectByMetricId(metricId); } - public List<CustomMeasureDto> selectByComponentId(DbSession session, long componentId) { - return mapper(session).selectByComponentId(componentId); - } - public int countByComponentIdAndMetricId(DbSession session, String componentUuid, int metricId) { return mapper(session).countByComponentIdAndMetricId(componentUuid, metricId); } + public List<CustomMeasureDto> selectByComponentUuid(DbSession session, String componentUuid) { + return mapper(session).selectByComponentUuid(componentUuid); + } + private CustomMeasureMapper mapper(DbSession session) { return session.getMapper(CustomMeasureMapper.class); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/DumbComponent.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/DumbComponent.java index 54e3c0fe64c..3d5b3e61a00 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/DumbComponent.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/DumbComponent.java @@ -99,6 +99,23 @@ public class DumbComponent implements Component { return children; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DumbComponent that = (DumbComponent) o; + return ref == that.ref; + } + + @Override + public int hashCode() { + return ref; + } + public static Builder builder(Type type, int ref) { return new Builder(type, ref); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/CustomMeasuresCopyStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/CustomMeasuresCopyStepTest.java new file mode 100644 index 00000000000..1176757999c --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/CustomMeasuresCopyStepTest.java @@ -0,0 +1,148 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.computation.step; + +import org.assertj.core.data.Offset; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.mockito.ArgumentCaptor; +import org.sonar.core.measure.custom.db.CustomMeasureDto; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.computation.batch.BatchReportReaderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; +import org.sonar.server.computation.component.MutableTreeRootHolderRule; +import org.sonar.server.computation.measure.Measure; +import org.sonar.server.computation.measure.MeasureRepository; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricImpl; +import org.sonar.server.computation.metric.MetricRepository; +import org.sonar.server.db.DbClient; +import org.sonar.server.measure.custom.persistence.CustomMeasureDao; +import org.sonar.test.DbTests; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.sonar.server.computation.step.CustomMeasuresCopyStep.dtoToMeasure; + +@Category(DbTests.class) +public class CustomMeasuresCopyStepTest { + + @ClassRule + public static final DbTester dbTester = new DbTester(); + + @Rule + public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + + @Rule + public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule(); + + MetricRepository metricRepository = mock(MetricRepository.class); + MeasureRepository measureRepository = mock(MeasureRepository.class); + + CustomMeasuresCopyStep sut; + + @Before + public void setUp() throws Exception { + DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new CustomMeasureDao()); + sut = new CustomMeasuresCopyStep(treeRootHolder, dbClient, metricRepository, measureRepository); + } + + @Test + public void copy_custom_measures() throws Exception { + dbTester.prepareDbUnit(getClass(), "custom-measures.xml"); + + // custom metrics + MetricImpl floatMetric = new MetricImpl(10, "float_metric", "Float Metric", Metric.MetricType.FLOAT); + when(metricRepository.getById(floatMetric.getId())).thenReturn(floatMetric); + MetricImpl stringMetric = new MetricImpl(11, "string_metric", "String Metric", Metric.MetricType.STRING); + when(metricRepository.getById(stringMetric.getId())).thenReturn(stringMetric); + + // components. File1 and project have custom measures, but not file2 + Component file1 = DumbComponent.builder(Component.Type.FILE, 1).setUuid("FILE1").build(); + Component file2 = DumbComponent.builder(Component.Type.FILE, 2).setUuid("FILE2").build(); + Component project = DumbComponent.builder(Component.Type.PROJECT, 3).setUuid("PROJECT1").addChildren(file1, file2).build(); + treeRootHolder.setRoot(project); + + sut.execute(); + + ArgumentCaptor<Measure> measureCaptor = ArgumentCaptor.forClass(Measure.class); + verify(measureRepository).add(eq(file1), eq(floatMetric), measureCaptor.capture()); + Measure fileMeasure = measureCaptor.getValue(); + assertThat(fileMeasure.getDoubleValue()).isEqualTo(3.14, Offset.offset(0.001)); + + verify(measureRepository).add(eq(project), eq(stringMetric), measureCaptor.capture()); + Measure projectMeasure = measureCaptor.getValue(); + assertThat(projectMeasure.getStringValue()).isEqualTo("good"); + + verifyNoMoreInteractions(measureRepository); + } + + @Test + public void test_float_value_type() throws Exception { + CustomMeasureDto dto = new CustomMeasureDto(); + dto.setValue(10.0); + assertThat(dtoToMeasure(dto, new MetricImpl(1, "m", "M", Metric.MetricType.FLOAT)).getDoubleValue()).isEqualTo(10.0); + } + + @Test + public void test_int_value_type() throws Exception { + CustomMeasureDto dto = new CustomMeasureDto(); + dto.setValue(10.0); + assertThat(dtoToMeasure(dto, new MetricImpl(1, "m", "M", Metric.MetricType.INT)).getIntValue()).isEqualTo(10); + } + + @Test + public void test_long_value_type() throws Exception { + CustomMeasureDto dto = new CustomMeasureDto(); + dto.setValue(10.0); + assertThat(dtoToMeasure(dto, new MetricImpl(1, "m", "M", Metric.MetricType.WORK_DUR)).getLongValue()).isEqualTo(10); + } + + @Test + public void test_percent_value_type() throws Exception { + CustomMeasureDto dto = new CustomMeasureDto(); + dto.setValue(10.0); + assertThat(dtoToMeasure(dto, new MetricImpl(1, "m", "M", Metric.MetricType.PERCENT)).getDoubleValue()).isEqualTo(10); + } + + @Test + public void test_string_value_type() throws Exception { + CustomMeasureDto dto = new CustomMeasureDto(); + dto.setTextValue("foo"); + assertThat(dtoToMeasure(dto, new MetricImpl(1, "m", "M", Metric.MetricType.STRING)).getStringValue()).isEqualTo("foo"); + } + + @Test + public void test_boolean_value_type() throws Exception { + MetricImpl booleanMetric = new MetricImpl(1, "m", "M", Metric.MetricType.BOOL); + CustomMeasureDto dto = new CustomMeasureDto(); + assertThat(dtoToMeasure(dto.setValue(1.0), booleanMetric).getBooleanValue()).isTrue(); + assertThat(dtoToMeasure(dto.setValue(0.0), booleanMetric).getBooleanValue()).isFalse(); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/custom/persistence/CustomMeasureDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/custom/persistence/CustomMeasureDaoTest.java index 2fa5021aefa..9eb752b0358 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/custom/persistence/CustomMeasureDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/custom/persistence/CustomMeasureDaoTest.java @@ -89,15 +89,15 @@ public class CustomMeasureDaoTest { } @Test - public void select_by_component_id() { - sut.insert(session, newCustomMeasureDto().setComponentId(1)); - sut.insert(session, newCustomMeasureDto().setComponentId(1)); - sut.insert(session, newCustomMeasureDto().setComponentId(2)); + public void select_by_component_uuid() { + sut.insert(session, newCustomMeasureDto().setComponentUuid("u1")); + sut.insert(session, newCustomMeasureDto().setComponentUuid("u1")); + sut.insert(session, newCustomMeasureDto().setComponentUuid("u2")); session.commit(); - List<CustomMeasureDto> result = sut.selectByComponentId(session, 1L); + List<CustomMeasureDto> result = sut.selectByComponentUuid(session, "u1"); assertThat(result).hasSize(2); - assertThat(result).extracting("componentId").containsOnly(1L); + assertThat(result).extracting("componentUuid").containsOnly("u1"); } } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/step/CustomMeasuresCopyStepTest/custom-measures.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/step/CustomMeasuresCopyStepTest/custom-measures.xml new file mode 100644 index 00000000000..2b538a24cf5 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/step/CustomMeasuresCopyStepTest/custom-measures.xml @@ -0,0 +1,24 @@ +<dataset> + <manual_measures id="1" + metric_id="10" + resource_id="100" + component_uuid="FILE1" + value="3.14" + text_value="[null]" + user_login="roger" + description="float measure" + created_at="123456789" + updated_at="123456789"/> + + <manual_measures id="2" + metric_id="11" + resource_id="101" + component_uuid="PROJECT1" + value="[null]" + text_value="good" + user_login="simone" + description="string measure" + created_at="123456789" + updated_at="123456789"/> + +</dataset> diff --git a/sonar-core/src/main/java/org/sonar/core/measure/custom/db/CustomMeasureMapper.java b/sonar-core/src/main/java/org/sonar/core/measure/custom/db/CustomMeasureMapper.java index 4b609b7fee2..bfc7eb929ea 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/custom/db/CustomMeasureMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/custom/db/CustomMeasureMapper.java @@ -32,7 +32,7 @@ public interface CustomMeasureMapper { List<CustomMeasureDto> selectByMetricId(int id); - List<CustomMeasureDto> selectByComponentId(long id); + List<CustomMeasureDto> selectByComponentUuid(String s); void delete(long id); diff --git a/sonar-core/src/main/resources/org/sonar/core/measure/custom/db/CustomMeasureMapper.xml b/sonar-core/src/main/resources/org/sonar/core/measure/custom/db/CustomMeasureMapper.xml index a3465ed2b90..12a61bdf359 100644 --- a/sonar-core/src/main/resources/org/sonar/core/measure/custom/db/CustomMeasureMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/measure/custom/db/CustomMeasureMapper.xml @@ -29,11 +29,11 @@ where m.metric_id=#{metricId} </select> - <select id="selectByComponentId" resultType="CustomMeasure"> + <select id="selectByComponentUuid" resultType="CustomMeasure"> select <include refid="selectColumns"/> from manual_measures m - where m.resource_id=#{componentId} + where m.component_uuid=#{componentUuid} </select> <insert id="insert" parameterType="CustomMeasure" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> |