Skip to content

Commit

Permalink
Copy custom measures in compute engine
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Brandhof committed Jun 19, 2015
1 parent e5bd8f4 commit 0825f36
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 13 deletions.
Expand Up @@ -53,6 +53,7 @@ public List<Class<? extends ComputationStep>> orderedStepClasses() {
FeedPeriodsStep.class,

// data computation
CustomMeasuresCopyStep.class,
QualityProfileEventsStep.class,
QualityGateEventsStep.class,
FillMeasuresWithVariationsStep.class,
Expand Down
@@ -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";
}
}
Expand Up @@ -69,14 +69,14 @@ public List<CustomMeasureDto> selectByMetricId(DbSession session, int metricId)
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);
}
Expand Down
Expand Up @@ -99,6 +99,23 @@ public List<Component> getChildren() {
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);
}
Expand Down
@@ -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();
}

}
Expand Up @@ -89,15 +89,15 @@ public void delete() {
}

@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");
}
}
@@ -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>
Expand Up @@ -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);

Expand Down
Expand Up @@ -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">
Expand Down

0 comments on commit 0825f36

Please sign in to comment.