@@ -1,219 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.ce.task.projectanalysis.step; | |||
import java.util.Collection; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.function.Predicate; | |||
import java.util.stream.StreamSupport; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.sonar.ce.task.projectanalysis.component.Component; | |||
import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit; | |||
import org.sonar.ce.task.projectanalysis.component.DepthTraversalTypeAwareCrawler; | |||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; | |||
import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter; | |||
import org.sonar.ce.task.projectanalysis.measure.Measure; | |||
import org.sonar.ce.task.projectanalysis.measure.MeasureKey; | |||
import org.sonar.ce.task.projectanalysis.measure.MeasureRepository; | |||
import org.sonar.ce.task.projectanalysis.metric.Metric; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricRepository; | |||
import org.sonar.ce.task.projectanalysis.period.Period; | |||
import org.sonar.ce.task.projectanalysis.period.PeriodHolder; | |||
import org.sonar.ce.task.step.ComputationStep; | |||
import org.sonar.core.util.stream.MoreCollectors; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.measure.PastMeasureDto; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static java.lang.String.format; | |||
import static java.util.function.Function.identity; | |||
import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY; | |||
import static org.sonar.ce.task.projectanalysis.component.Component.Type.SUBVIEW; | |||
import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER; | |||
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; | |||
/** | |||
* Set variations on all numeric measures found in the repository. | |||
* This step MUST be executed after all steps that create some measures | |||
* <p/> | |||
* Note that measures on developer are not handle yet. | |||
*/ | |||
public class ComputeMeasureVariationsStep implements ComputationStep { | |||
private final DbClient dbClient; | |||
private final TreeRootHolder treeRootHolder; | |||
private final PeriodHolder periodHolder; | |||
private final MetricRepository metricRepository; | |||
private final MeasureRepository measureRepository; | |||
public ComputeMeasureVariationsStep(DbClient dbClient, TreeRootHolder treeRootHolder, PeriodHolder periodHolder, MetricRepository metricRepository, | |||
MeasureRepository measureRepository) { | |||
this.dbClient = dbClient; | |||
this.treeRootHolder = treeRootHolder; | |||
this.periodHolder = periodHolder; | |||
this.metricRepository = metricRepository; | |||
this.measureRepository = measureRepository; | |||
} | |||
@Override | |||
public void execute(ComputationStep.Context context) { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
List<Metric> metrics = StreamSupport.stream(metricRepository.getAll().spliterator(), false).filter(isNumeric()).collect(MoreCollectors.toList()); | |||
new DepthTraversalTypeAwareCrawler(new VariationMeasuresVisitor(dbSession, metrics)) | |||
.visit(treeRootHolder.getRoot()); | |||
} | |||
} | |||
private class VariationMeasuresVisitor extends TypeAwareVisitorAdapter { | |||
private final DbSession session; | |||
private final Set<Integer> metricIds; | |||
private final List<Metric> metrics; | |||
VariationMeasuresVisitor(DbSession session, List<Metric> metrics) { | |||
// measures on files are currently purged, so past measures are not available on files | |||
super(CrawlerDepthLimit.reportMaxDepth(DIRECTORY).withViewsMaxDepth(SUBVIEW), PRE_ORDER); | |||
this.session = session; | |||
this.metricIds = metrics.stream().map(Metric::getId).collect(MoreCollectors.toSet()); | |||
this.metrics = metrics; | |||
} | |||
@Override | |||
public void visitAny(Component component) { | |||
MeasuresWithVariationRepository measuresWithVariationRepository = computeMeasuresWithVariations(component); | |||
processMeasuresWithVariation(component, measuresWithVariationRepository); | |||
} | |||
private MeasuresWithVariationRepository computeMeasuresWithVariations(Component component) { | |||
MeasuresWithVariationRepository measuresWithVariationRepository = new MeasuresWithVariationRepository(); | |||
if (periodHolder.hasPeriod()) { | |||
Period period = periodHolder.getPeriod(); | |||
List<PastMeasureDto> pastMeasures = dbClient.measureDao() | |||
.selectPastMeasures(session, component.getUuid(), period.getAnalysisUuid(), metricIds); | |||
setVariationMeasures(component, pastMeasures, measuresWithVariationRepository); | |||
} | |||
return measuresWithVariationRepository; | |||
} | |||
private void setVariationMeasures(Component component, List<PastMeasureDto> pastMeasures, MeasuresWithVariationRepository measuresWithVariationRepository) { | |||
Map<MeasureKey, PastMeasureDto> pastMeasuresByMeasureKey = pastMeasures | |||
.stream() | |||
.collect(uniqueIndex(m -> new MeasureKey(metricRepository.getById((long) m.getMetricId()).getKey(), null), identity())); | |||
for (Metric metric : metrics) { | |||
Optional<Measure> measure = measureRepository.getRawMeasure(component, metric); | |||
if (measure.isPresent() && !measure.get().hasVariation()) { | |||
PastMeasureDto pastMeasure = pastMeasuresByMeasureKey.get(new MeasureKey(metric.getKey(), null)); | |||
double pastValue = (pastMeasure != null && pastMeasure.hasValue()) ? pastMeasure.getValue() : 0d; | |||
measuresWithVariationRepository.add(metric, measure.get(), computeVariation(measure.get(), pastValue)); | |||
} | |||
} | |||
} | |||
private double computeVariation(Measure measure, double pastValue) { | |||
switch (measure.getValueType()) { | |||
case INT: | |||
return measure.getIntValue() - pastValue; | |||
case LONG: | |||
return measure.getLongValue() - pastValue; | |||
case DOUBLE: | |||
return measure.getDoubleValue() - pastValue; | |||
case BOOLEAN: | |||
return (measure.getBooleanValue() ? 1d : 0d) - pastValue; | |||
default: | |||
throw new IllegalArgumentException(format("Unsupported Measure.ValueType on measure '%s'", measure)); | |||
} | |||
} | |||
private void processMeasuresWithVariation(Component component, MeasuresWithVariationRepository measuresWithVariationRepository) { | |||
for (MeasureWithVariation measureWithVariation : measuresWithVariationRepository.measures()) { | |||
Metric metric = measureWithVariation.getMetric(); | |||
Measure measure = Measure.updatedMeasureBuilder(measureWithVariation.getMeasure()) | |||
.setVariation(measureWithVariation.getVariation()) | |||
.create(); | |||
measureRepository.update(component, metric, measure); | |||
} | |||
} | |||
} | |||
private static final class MeasuresWithVariationRepository { | |||
private final Map<MeasureKey, MeasureWithVariation> measuresWithVariations = new HashMap<>(); | |||
public void add(Metric metric, final Measure measure, double variationValue) { | |||
checkArgument(measure.getDeveloper() == null, "%s does not support computing variations of Measures for Developer", getClass().getSimpleName()); | |||
MeasureKey measureKey = new MeasureKey(metric.getKey(), null); | |||
MeasureWithVariation measureWithVariation = measuresWithVariations.computeIfAbsent(measureKey, k -> new MeasureWithVariation(metric, measure)); | |||
measureWithVariation.setVariation(variationValue); | |||
} | |||
public Collection<MeasureWithVariation> measures() { | |||
return measuresWithVariations.values(); | |||
} | |||
} | |||
private static final class MeasureWithVariation { | |||
private final Metric metric; | |||
private final Measure measure; | |||
private Double variation; | |||
MeasureWithVariation(Metric metric, Measure measure) { | |||
this.metric = metric; | |||
this.measure = measure; | |||
} | |||
public Measure getMeasure() { | |||
return measure; | |||
} | |||
public Metric getMetric() { | |||
return metric; | |||
} | |||
public void setVariation(@Nullable Double value) { | |||
this.variation = value; | |||
} | |||
@CheckForNull | |||
public Double getVariation() { | |||
return variation; | |||
} | |||
} | |||
private static Predicate<Metric> isNumeric() { | |||
return metric -> { | |||
Measure.ValueType valueType = metric.getType().getValueType(); | |||
return Measure.ValueType.INT.equals(valueType) | |||
|| Measure.ValueType.LONG.equals(valueType) | |||
|| Measure.ValueType.DOUBLE.equals(valueType) | |||
|| Measure.ValueType.BOOLEAN.equals(valueType); | |||
}; | |||
} | |||
@Override | |||
public String getDescription() { | |||
return "Compute measure variations"; | |||
} | |||
} |
@@ -70,7 +70,6 @@ import static org.sonar.ce.task.projectanalysis.qualitygate.ConditionStatus.crea | |||
* {@link CoreMetrics#ALERT_STATUS_KEY}</li> | |||
* </ul> | |||
* | |||
* It must be executed after the computation of differential measures {@link ComputeMeasureVariationsStep} | |||
*/ | |||
public class QualityGateMeasuresStep implements ComputationStep { | |||
private final TreeRootHolder treeRootHolder; |
@@ -79,10 +79,6 @@ public class ReportComputationSteps extends AbstractComputationSteps { | |||
PostMeasuresComputationChecksStep.class, | |||
// Must be executed after computation of all measures | |||
ComputeMeasureVariationsStep.class, | |||
// Must be executed after computation of differential measures | |||
QualityGateMeasuresStep.class, | |||
// Must be executed after computation of language distribution | |||
ComputeQProfileMeasureStep.class, |
@@ -1,288 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.ce.task.projectanalysis.step; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule; | |||
import org.sonar.ce.task.projectanalysis.component.Component; | |||
import org.sonar.ce.task.projectanalysis.component.DumbDeveloper; | |||
import org.sonar.ce.task.projectanalysis.component.ReportComponent; | |||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; | |||
import org.sonar.ce.task.projectanalysis.measure.Measure; | |||
import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule; | |||
import org.sonar.ce.task.projectanalysis.metric.Metric; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricImpl; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule; | |||
import org.sonar.ce.task.projectanalysis.period.Period; | |||
import org.sonar.ce.task.projectanalysis.period.PeriodHolderRule; | |||
import org.sonar.ce.task.step.TestComputationStepContext; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.ComponentTesting; | |||
import org.sonar.db.component.SnapshotDto; | |||
import org.sonar.db.measure.MeasureDto; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder; | |||
import static org.sonar.db.component.SnapshotTesting.newAnalysis; | |||
public class ReportComputeMeasureVariationsStepTest { | |||
private static final Metric ISSUES_METRIC = new MetricImpl(1, "violations", "violations", Metric.MetricType.INT); | |||
private static final Metric DEBT_METRIC = new MetricImpl(2, "sqale_index", "sqale_index", Metric.MetricType.WORK_DUR); | |||
private static final Metric FILE_COMPLEXITY_METRIC = new MetricImpl(3, "file_complexity", "file_complexity", Metric.MetricType.FLOAT); | |||
private static final Metric BUILD_BREAKER_METRIC = new MetricImpl(4, "build_breaker", "build_breaker", Metric.MetricType.BOOL); | |||
private static final Metric NEW_DEBT = new MetricImpl(5, "new_debt", "new_debt", Metric.MetricType.WORK_DUR); | |||
private static final String PROJECT_UUID = "prj uuid"; | |||
private static final int PROJECT_REF = 1; | |||
private static final Component PROJECT = ReportComponent.builder(Component.Type.PROJECT, PROJECT_REF).setUuid(PROJECT_UUID).build(); | |||
@Rule | |||
public DbTester dbTester = DbTester.create(System2.INSTANCE); | |||
@Rule | |||
public BatchReportReaderRule reportReader = new BatchReportReaderRule(); | |||
@Rule | |||
public PeriodHolderRule periodsHolder = new PeriodHolderRule(); | |||
@Rule | |||
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); | |||
@Rule | |||
public MetricRepositoryRule metricRepository = new MetricRepositoryRule() | |||
.add(ISSUES_METRIC) | |||
.add(DEBT_METRIC) | |||
.add(FILE_COMPLEXITY_METRIC) | |||
.add(BUILD_BREAKER_METRIC) | |||
.add(NEW_DEBT); | |||
@Rule | |||
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); | |||
private ComponentDto project; | |||
private DbSession session = dbTester.getSession(); | |||
private DbClient dbClient = dbTester.getDbClient(); | |||
private ComputeMeasureVariationsStep underTest = new ComputeMeasureVariationsStep(dbClient, treeRootHolder, periodsHolder, metricRepository, measureRepository); | |||
@Before | |||
public void setUp() { | |||
project = dbTester.components().insertPrivateProject(dbTester.organizations().insert(), PROJECT_UUID); | |||
} | |||
@Test | |||
public void do_nothing_when_no_raw_measure() { | |||
SnapshotDto period1ProjectSnapshot = newAnalysis(project); | |||
dbClient.snapshotDao().insert(session, period1ProjectSnapshot); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_UUID, period1ProjectSnapshot.getUuid(), 60d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(period1ProjectSnapshot)); | |||
treeRootHolder.setRoot(PROJECT); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasures(PROJECT).keys()).isEmpty(); | |||
} | |||
@Test | |||
public void do_nothing_when_no_period() { | |||
Component project = ReportComponent.builder(Component.Type.PROJECT, 1).setUuid(PROJECT_UUID).build(); | |||
treeRootHolder.setRoot(project); | |||
periodsHolder.setPeriod(null); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasures(project).keys()).isEmpty(); | |||
} | |||
@Test | |||
public void set_variation() { | |||
// Project | |||
SnapshotDto period1Snapshot = newAnalysis(project); | |||
dbClient.snapshotDao().insert(session, period1Snapshot); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_UUID, period1Snapshot.getUuid(), 60d)); | |||
// Directory | |||
ComponentDto directoryDto = ComponentTesting.newDirectory(project, "dir"); | |||
dbClient.componentDao().insert(session, directoryDto); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), directoryDto.uuid(), period1Snapshot.getUuid(), 10d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(period1Snapshot)); | |||
Component directory = ReportComponent.builder(Component.Type.DIRECTORY, 2).setUuid(directoryDto.uuid()).build(); | |||
Component project = ReportComponent.builder(Component.Type.PROJECT, 1).setUuid(PROJECT_UUID).addChildren(directory).build(); | |||
treeRootHolder.setRoot(project); | |||
addRawMeasure(project, ISSUES_METRIC, newMeasureBuilder().create(80, null)); | |||
addRawMeasure(directory, ISSUES_METRIC, newMeasureBuilder().create(20, null)); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasure(project, ISSUES_METRIC).get().getVariation()).isEqualTo(20d); | |||
assertThat(measureRepository.getRawMeasure(directory, ISSUES_METRIC).get().getVariation()).isEqualTo(10d); | |||
} | |||
@Test | |||
public void set_zero_variation_when_no_change() { | |||
// Project | |||
SnapshotDto period1Snapshot = newAnalysis(project); | |||
dbClient.snapshotDao().insert(session, period1Snapshot); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_UUID, period1Snapshot.getUuid(), 60d)); | |||
// Directory | |||
ComponentDto directoryDto = ComponentTesting.newDirectory(project, "dir"); | |||
dbClient.componentDao().insert(session, directoryDto); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), directoryDto.uuid(), period1Snapshot.getUuid(), 10d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(period1Snapshot)); | |||
Component directory = ReportComponent.builder(Component.Type.DIRECTORY, 2).setUuid(directoryDto.uuid()).build(); | |||
Component project = ReportComponent.builder(Component.Type.PROJECT, 1).setUuid(PROJECT_UUID).addChildren(directory).build(); | |||
treeRootHolder.setRoot(project); | |||
addRawMeasure(project, ISSUES_METRIC, newMeasureBuilder().create(60, null)); | |||
addRawMeasure(directory, ISSUES_METRIC, newMeasureBuilder().create(10, null)); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasure(project, ISSUES_METRIC).get().getVariation()).isEqualTo(0d); | |||
assertThat(measureRepository.getRawMeasure(directory, ISSUES_METRIC).get().getVariation()).isEqualTo(0d); | |||
} | |||
@Test | |||
public void set_variation_to_raw_value_on_new_component() { | |||
// Project | |||
SnapshotDto past1ProjectSnapshot = newAnalysis(project).setCreatedAt(1000_000_000L); | |||
SnapshotDto currentProjectSnapshot = newAnalysis(project).setCreatedAt(2000_000_000L); | |||
dbClient.snapshotDao().insert(session, past1ProjectSnapshot); | |||
dbClient.snapshotDao().insert(session, currentProjectSnapshot); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_UUID, past1ProjectSnapshot.getUuid(), 60d)); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_UUID, currentProjectSnapshot.getUuid(), 60d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(past1ProjectSnapshot)); | |||
// Directory has just been added => no snapshot | |||
Component directory = ReportComponent.builder(Component.Type.DIRECTORY, 2).setUuid("DIRECTORY").build(); | |||
Component project = ReportComponent.builder(Component.Type.PROJECT, 1).setUuid(PROJECT_UUID).addChildren(directory).build(); | |||
treeRootHolder.setRoot(project); | |||
addRawMeasure(project, ISSUES_METRIC, newMeasureBuilder().create(90, null)); | |||
addRawMeasure(directory, ISSUES_METRIC, newMeasureBuilder().create(10, null)); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasure(project, ISSUES_METRIC).get().getVariation()).isEqualTo(30d); | |||
// Variation should be the raw value | |||
assertThat(measureRepository.getRawMeasure(directory, ISSUES_METRIC).get().getVariation()).isEqualTo(10d); | |||
} | |||
@Test | |||
public void set_variation_on_all_numeric_metrics() { | |||
SnapshotDto period1ProjectSnapshot = newAnalysis(project); | |||
dbClient.snapshotDao().insert(session, period1ProjectSnapshot); | |||
dbClient.measureDao().insert(session, | |||
newMeasureDto(ISSUES_METRIC.getId(), PROJECT_UUID, period1ProjectSnapshot.getUuid(), 60d), | |||
newMeasureDto(DEBT_METRIC.getId(), PROJECT_UUID, period1ProjectSnapshot.getUuid(), 10d), | |||
newMeasureDto(FILE_COMPLEXITY_METRIC.getId(), PROJECT_UUID, period1ProjectSnapshot.getUuid(), 2d), | |||
newMeasureDto(BUILD_BREAKER_METRIC.getId(), PROJECT_UUID, period1ProjectSnapshot.getUuid(), 1d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(period1ProjectSnapshot)); | |||
treeRootHolder.setRoot(PROJECT); | |||
addRawMeasure(PROJECT, ISSUES_METRIC, newMeasureBuilder().create(80, null)); | |||
addRawMeasure(PROJECT, DEBT_METRIC, newMeasureBuilder().create(5L, null)); | |||
addRawMeasure(PROJECT, FILE_COMPLEXITY_METRIC, newMeasureBuilder().create(3d, 1, null)); | |||
addRawMeasure(PROJECT, BUILD_BREAKER_METRIC, newMeasureBuilder().create(false, null)); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasures(PROJECT).keys()).hasSize(4); | |||
assertThat(measureRepository.getRawMeasure(PROJECT, ISSUES_METRIC).get().getVariation()).isEqualTo(20d); | |||
assertThat(measureRepository.getRawMeasure(PROJECT, DEBT_METRIC).get().getVariation()).isEqualTo(-5d); | |||
assertThat(measureRepository.getRawMeasure(PROJECT, FILE_COMPLEXITY_METRIC).get().getVariation()).isEqualTo(1d); | |||
assertThat(measureRepository.getRawMeasure(PROJECT, BUILD_BREAKER_METRIC).get().getVariation()).isEqualTo(-1d); | |||
} | |||
@Test | |||
public void do_not_set_variation_on_numeric_metric_for_developer() { | |||
SnapshotDto period1ProjectSnapshot = newAnalysis(project); | |||
dbClient.snapshotDao().insert(session, period1ProjectSnapshot); | |||
dbClient.measureDao().insert(session, | |||
newMeasureDto(ISSUES_METRIC.getId(), PROJECT_UUID, period1ProjectSnapshot.getUuid(), 60d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(period1ProjectSnapshot)); | |||
treeRootHolder.setRoot(PROJECT); | |||
DumbDeveloper developer = new DumbDeveloper("a"); | |||
measureRepository.addRawMeasure(PROJECT_REF, ISSUES_METRIC.getKey(), newMeasureBuilder().forDeveloper(developer).create(80, null)); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasures(PROJECT).keys()).hasSize(1); | |||
assertThat(measureRepository.getRawMeasure(PROJECT, ISSUES_METRIC, developer).get().hasVariation()).isFalse(); | |||
} | |||
@Test | |||
public void does_not_update_existing_variation() { | |||
SnapshotDto period1ProjectSnapshot = newAnalysis(project); | |||
dbClient.snapshotDao().insert(session, period1ProjectSnapshot); | |||
dbClient.measureDao().insert(session, newMeasureDto(NEW_DEBT.getId(), PROJECT_UUID, period1ProjectSnapshot.getUuid(), 60d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(period1ProjectSnapshot)); | |||
treeRootHolder.setRoot(PROJECT); | |||
addRawMeasure(PROJECT, NEW_DEBT, newMeasureBuilder().setVariation(10d).createNoValue()); | |||
underTest.execute(new TestComputationStepContext()); | |||
// As the measure has already variations it has been ignored, then variations will be the same | |||
assertThat(measureRepository.getRawMeasure(PROJECT, NEW_DEBT).get().getVariation()).isEqualTo(10d); | |||
} | |||
private static MeasureDto newMeasureDto(int metricId, String componentUuid, String analysisUuid, double value) { | |||
return new MeasureDto() | |||
.setMetricId(metricId) | |||
.setComponentUuid(componentUuid) | |||
.setAnalysisUuid(analysisUuid) | |||
.setValue(value); | |||
} | |||
private static Period newPeriod(SnapshotDto snapshotDto) { | |||
return new Period("mode", null, snapshotDto.getCreatedAt(), snapshotDto.getUuid()); | |||
} | |||
private void addRawMeasure(Component component, Metric metric, Measure measure) { | |||
measureRepository.addRawMeasure(component.getReportAttributes().getRef(), metric.getKey(), measure); | |||
} | |||
} |
@@ -1,193 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.ce.task.projectanalysis.step; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule; | |||
import org.sonar.ce.task.projectanalysis.component.Component; | |||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; | |||
import org.sonar.ce.task.projectanalysis.component.ViewsComponent; | |||
import org.sonar.ce.task.projectanalysis.measure.Measure; | |||
import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule; | |||
import org.sonar.ce.task.projectanalysis.metric.Metric; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricImpl; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule; | |||
import org.sonar.ce.task.projectanalysis.period.Period; | |||
import org.sonar.ce.task.projectanalysis.period.PeriodHolderRule; | |||
import org.sonar.ce.task.step.TestComputationStepContext; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.ComponentTesting; | |||
import org.sonar.db.component.SnapshotDto; | |||
import org.sonar.db.measure.MeasureDto; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.db.component.SnapshotTesting.newAnalysis; | |||
public class ViewsComputeMeasureVariationsStepTest { | |||
private static final Metric ISSUES_METRIC = new MetricImpl(1, "violations", "violations", Metric.MetricType.INT); | |||
private static final Metric DEBT_METRIC = new MetricImpl(2, "sqale_index", "sqale_index", Metric.MetricType.WORK_DUR); | |||
private static final Metric FILE_COMPLEXITY_METRIC = new MetricImpl(3, "file_complexity", "file_complexity", Metric.MetricType.FLOAT); | |||
private static final Metric BUILD_BREAKER_METRIC = new MetricImpl(4, "build_breaker", "build_breaker", Metric.MetricType.BOOL); | |||
private static final String VIEW_UUID = "view uuid"; | |||
private static final Component VIEW = ViewsComponent.builder(Component.Type.VIEW, 1).setUuid(VIEW_UUID).build(); | |||
@Rule | |||
public DbTester dbTester = DbTester.create(System2.INSTANCE); | |||
@Rule | |||
public BatchReportReaderRule reportReader = new BatchReportReaderRule(); | |||
@Rule | |||
public PeriodHolderRule periodsHolder = new PeriodHolderRule(); | |||
@Rule | |||
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); | |||
@Rule | |||
public MetricRepositoryRule metricRepository = new MetricRepositoryRule() | |||
.add(ISSUES_METRIC) | |||
.add(DEBT_METRIC) | |||
.add(FILE_COMPLEXITY_METRIC) | |||
.add(BUILD_BREAKER_METRIC); | |||
@Rule | |||
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); | |||
private DbSession session = dbTester.getSession(); | |||
private DbClient dbClient = dbTester.getDbClient(); | |||
private ComponentDto view; | |||
private ComputeMeasureVariationsStep underTest = new ComputeMeasureVariationsStep(dbClient, treeRootHolder, periodsHolder, metricRepository, measureRepository); | |||
@Before | |||
public void setUp() { | |||
view = dbTester.components().insertView(dbTester.organizations().insert(), VIEW_UUID); | |||
} | |||
@Test | |||
public void do_nothing_when_no_raw_measure() { | |||
SnapshotDto period1ViewSnapshot = newAnalysis(view); | |||
dbClient.snapshotDao().insert(session, period1ViewSnapshot); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), VIEW_UUID, period1ViewSnapshot.getUuid(), 60d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(period1ViewSnapshot)); | |||
treeRootHolder.setRoot(VIEW); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasures(VIEW).keys()).isEmpty(); | |||
} | |||
@Test | |||
public void do_nothing_when_no_period() { | |||
Component view = ViewsComponent.builder(Component.Type.VIEW, 1).setUuid(VIEW_UUID).build(); | |||
treeRootHolder.setRoot(view); | |||
periodsHolder.setPeriod(null); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasures(view).keys()).isEmpty(); | |||
} | |||
@Test | |||
public void set_variation() { | |||
// View | |||
SnapshotDto period1Snapshot = newAnalysis(view); | |||
dbClient.snapshotDao().insert(session, period1Snapshot); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), VIEW_UUID, period1Snapshot.getUuid(), 60d)); | |||
// SubView | |||
ComponentDto subviewDto = ComponentTesting.newSubView(view, "dir", "key"); | |||
dbClient.componentDao().insert(session, subviewDto); | |||
dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), subviewDto.uuid(), period1Snapshot.getUuid(), 10d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(period1Snapshot)); | |||
Component subview = ViewsComponent.builder(Component.Type.SUBVIEW, 2).setUuid(subviewDto.uuid()).build(); | |||
Component view = ViewsComponent.builder(Component.Type.VIEW, 1).setUuid(VIEW_UUID).addChildren(subview).build(); | |||
treeRootHolder.setRoot(view); | |||
addRawMeasure(view, ISSUES_METRIC, Measure.newMeasureBuilder().create(80, null)); | |||
addRawMeasure(subview, ISSUES_METRIC, Measure.newMeasureBuilder().create(20, null)); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasure(view, ISSUES_METRIC).get().getVariation()).isEqualTo(20d); | |||
assertThat(measureRepository.getRawMeasure(subview, ISSUES_METRIC).get().getVariation()).isEqualTo(10d); | |||
} | |||
@Test | |||
public void set_variation_on_all_numeric_metrics() { | |||
SnapshotDto period1ViewSnapshot = newAnalysis(view); | |||
dbClient.snapshotDao().insert(session, period1ViewSnapshot); | |||
dbClient.measureDao().insert(session, | |||
newMeasureDto(ISSUES_METRIC.getId(), VIEW_UUID, period1ViewSnapshot.getUuid(), 60d), | |||
newMeasureDto(DEBT_METRIC.getId(), VIEW_UUID, period1ViewSnapshot.getUuid(), 10d), | |||
newMeasureDto(FILE_COMPLEXITY_METRIC.getId(), VIEW_UUID, period1ViewSnapshot.getUuid(), 2d), | |||
newMeasureDto(BUILD_BREAKER_METRIC.getId(), VIEW_UUID, period1ViewSnapshot.getUuid(), 1d)); | |||
session.commit(); | |||
periodsHolder.setPeriod(newPeriod(period1ViewSnapshot)); | |||
treeRootHolder.setRoot(VIEW); | |||
addRawMeasure(VIEW, ISSUES_METRIC, Measure.newMeasureBuilder().create(80, null)); | |||
addRawMeasure(VIEW, DEBT_METRIC, Measure.newMeasureBuilder().create(5L, null)); | |||
addRawMeasure(VIEW, FILE_COMPLEXITY_METRIC, Measure.newMeasureBuilder().create(3d, 1)); | |||
addRawMeasure(VIEW, BUILD_BREAKER_METRIC, Measure.newMeasureBuilder().create(false, null)); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(measureRepository.getRawMeasures(VIEW).keys()).hasSize(4); | |||
assertThat(measureRepository.getRawMeasure(VIEW, ISSUES_METRIC).get().getVariation()).isEqualTo(20d); | |||
assertThat(measureRepository.getRawMeasure(VIEW, DEBT_METRIC).get().getVariation()).isEqualTo(-5d); | |||
assertThat(measureRepository.getRawMeasure(VIEW, FILE_COMPLEXITY_METRIC).get().getVariation()).isEqualTo(1d); | |||
assertThat(measureRepository.getRawMeasure(VIEW, BUILD_BREAKER_METRIC).get().getVariation()).isEqualTo(-1d); | |||
} | |||
private static MeasureDto newMeasureDto(int metricId, String componentUuid, String analysisUuid, double value) { | |||
return new MeasureDto() | |||
.setMetricId(metricId) | |||
.setComponentUuid(componentUuid) | |||
.setAnalysisUuid(analysisUuid) | |||
.setValue(value); | |||
} | |||
private static Period newPeriod(SnapshotDto snapshotDto) { | |||
return new Period("mode", null, snapshotDto.getCreatedAt(), snapshotDto.getUuid()); | |||
} | |||
private void addRawMeasure(Component component, Metric metric, Measure measure) { | |||
measureRepository.add(component, metric, measure); | |||
} | |||
} |