/* | |||||
* 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"; | |||||
} | |||||
} |
* {@link CoreMetrics#ALERT_STATUS_KEY}</li> | * {@link CoreMetrics#ALERT_STATUS_KEY}</li> | ||||
* </ul> | * </ul> | ||||
* | * | ||||
* It must be executed after the computation of differential measures {@link ComputeMeasureVariationsStep} | |||||
*/ | */ | ||||
public class QualityGateMeasuresStep implements ComputationStep { | public class QualityGateMeasuresStep implements ComputationStep { | ||||
private final TreeRootHolder treeRootHolder; | private final TreeRootHolder treeRootHolder; |
PostMeasuresComputationChecksStep.class, | PostMeasuresComputationChecksStep.class, | ||||
// Must be executed after computation of all measures | |||||
ComputeMeasureVariationsStep.class, | |||||
// Must be executed after computation of differential measures | |||||
QualityGateMeasuresStep.class, | QualityGateMeasuresStep.class, | ||||
// Must be executed after computation of language distribution | // Must be executed after computation of language distribution | ||||
ComputeQProfileMeasureStep.class, | ComputeQProfileMeasureStep.class, |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} |