3 * Copyright (C) 2009-2016 SonarSource SA
4 * mailto:contact AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.computation.task.projectanalysis.step;
22 import com.google.common.base.Optional;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.Iterables;
25 import java.util.Collections;
26 import java.util.List;
28 import javax.annotation.CheckForNull;
29 import javax.annotation.Nullable;
30 import javax.annotation.concurrent.Immutable;
31 import org.apache.commons.lang.ObjectUtils;
32 import org.sonar.api.measures.CoreMetrics;
33 import org.sonar.api.utils.KeyValueFormat;
34 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader;
35 import org.sonar.server.computation.task.projectanalysis.component.Component;
36 import org.sonar.server.computation.task.projectanalysis.component.PathAwareCrawler;
37 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
38 import org.sonar.server.computation.task.projectanalysis.formula.CounterInitializationContext;
39 import org.sonar.server.computation.task.projectanalysis.formula.CreateMeasureContext;
40 import org.sonar.server.computation.task.projectanalysis.formula.Formula;
41 import org.sonar.server.computation.task.projectanalysis.formula.FormulaExecutorComponentVisitor;
42 import org.sonar.server.computation.task.projectanalysis.formula.VariationSumFormula;
43 import org.sonar.server.computation.task.projectanalysis.formula.counter.IntVariationValue;
44 import org.sonar.server.computation.task.projectanalysis.formula.coverage.LinesAndConditionsWithUncoveredMetricKeys;
45 import org.sonar.server.computation.task.projectanalysis.formula.coverage.LinesAndConditionsWithUncoveredVariationFormula;
46 import org.sonar.server.computation.task.projectanalysis.formula.coverage.SingleWithUncoveredMetricKeys;
47 import org.sonar.server.computation.task.projectanalysis.formula.coverage.SingleWithUncoveredVariationFormula;
48 import org.sonar.server.computation.task.projectanalysis.measure.Measure;
49 import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository;
50 import org.sonar.server.computation.task.projectanalysis.measure.MeasureVariations;
51 import org.sonar.server.computation.task.projectanalysis.metric.Metric;
52 import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
53 import org.sonar.server.computation.task.projectanalysis.period.Period;
54 import org.sonar.server.computation.task.projectanalysis.period.PeriodsHolder;
55 import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo;
56 import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoRepository;
57 import org.sonar.server.computation.task.step.ComputationStep;
59 import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;
60 import static org.sonar.server.computation.task.projectanalysis.period.PeriodPredicates.viewsRestrictedPeriods;
63 * Computes measures related to the New Coverage. These measures do not have values, only variations.
65 public class NewCoverageMeasuresStep implements ComputationStep {
67 private static final List<Formula> FORMULAS = ImmutableList.<Formula>of(
69 new NewCoverageFormula(),
70 new NewBranchCoverageFormula(),
71 new NewLineCoverageFormula(),
73 new NewItCoverageFormula(),
74 new NewItBranchCoverageFormula(),
75 new NewItLinesCoverageFormula(),
77 new NewOverallCodeCoverageFormula(),
78 new NewOverallBranchCoverageFormula(),
79 new NewOverallLineCoverageFormula());
81 private final TreeRootHolder treeRootHolder;
82 private final PeriodsHolder periodsHolder;
83 private final MetricRepository metricRepository;
84 private final MeasureRepository measureRepository;
86 private final ScmInfoRepository scmInfoRepository;
89 * Constructor used when processing a Report (ie. a {@link BatchReportReader} instance is available in the container)
91 public NewCoverageMeasuresStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder,
92 MeasureRepository measureRepository, final MetricRepository metricRepository, ScmInfoRepository scmInfoRepository) {
93 this.treeRootHolder = treeRootHolder;
94 this.periodsHolder = periodsHolder;
95 this.metricRepository = metricRepository;
96 this.measureRepository = measureRepository;
97 this.scmInfoRepository = scmInfoRepository;
101 * Constructor used when processing Views (ie. no {@link BatchReportReader} instance is available in the container)
103 public NewCoverageMeasuresStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder,
104 MeasureRepository measureRepository, final MetricRepository metricRepository) {
105 this.treeRootHolder = treeRootHolder;
106 this.periodsHolder = periodsHolder;
107 this.metricRepository = metricRepository;
108 this.measureRepository = measureRepository;
109 this.scmInfoRepository = null;
113 public void execute() {
114 new PathAwareCrawler<>(
115 FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
116 .withVariationSupport(periodsHolder)
119 NewLinesAndConditionsCoverageFormula.from(scmInfoRepository),
120 NewItLinesAndConditionsCoverageFormula.from(scmInfoRepository),
121 NewOverallLinesAndConditionsCoverageFormula.from(scmInfoRepository),
123 .visit(treeRootHolder.getRoot());
127 public String getDescription() {
128 return "Compute new coverage";
131 private static class NewLinesAndConditionsCoverageFormula extends NewLinesAndConditionsFormula {
133 private static final NewCoverageOutputMetricKeys OUTPUT_METRIC_KEYS = new NewCoverageOutputMetricKeys(
134 CoreMetrics.NEW_LINES_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_LINES_KEY,
135 CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY);
136 private static final Iterable<Formula<?>> VIEWS_FORMULAS = variationSumFormulas(OUTPUT_METRIC_KEYS);
138 private NewLinesAndConditionsCoverageFormula(ScmInfoRepository scmInfoRepository) {
139 super(scmInfoRepository,
140 new NewCoverageInputMetricKeys(
141 CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, CoreMetrics.CONDITIONS_BY_LINE_KEY, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY),
145 public static Iterable<Formula<?>> from(@Nullable ScmInfoRepository scmInfoRepository) {
146 if (scmInfoRepository == null) {
147 return VIEWS_FORMULAS;
149 return Collections.<Formula<?>>singleton(new NewLinesAndConditionsCoverageFormula(scmInfoRepository));
153 private static class NewCoverageFormula extends LinesAndConditionsWithUncoveredVariationFormula {
154 public NewCoverageFormula() {
156 new LinesAndConditionsWithUncoveredMetricKeys(
157 CoreMetrics.NEW_LINES_TO_COVER_KEY, CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY,
158 CoreMetrics.NEW_UNCOVERED_LINES_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY),
159 CoreMetrics.NEW_COVERAGE_KEY);
163 private static class NewBranchCoverageFormula extends SingleWithUncoveredVariationFormula {
164 public NewBranchCoverageFormula() {
166 new SingleWithUncoveredMetricKeys(CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY),
167 CoreMetrics.NEW_BRANCH_COVERAGE_KEY);
171 private static class NewLineCoverageFormula extends SingleWithUncoveredVariationFormula {
172 public NewLineCoverageFormula() {
174 new SingleWithUncoveredMetricKeys(CoreMetrics.NEW_LINES_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_LINES_KEY),
175 CoreMetrics.NEW_LINE_COVERAGE_KEY);
179 private static class NewItLinesAndConditionsCoverageFormula extends NewLinesAndConditionsFormula {
181 private static final NewCoverageOutputMetricKeys OUTPUT_METRIC_KEYS = new NewCoverageOutputMetricKeys(
182 CoreMetrics.NEW_IT_LINES_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY,
183 CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY);
184 private static final Iterable<Formula<?>> VIEWS_FORMULAS = variationSumFormulas(OUTPUT_METRIC_KEYS);
186 private NewItLinesAndConditionsCoverageFormula(ScmInfoRepository scmInfoRepository) {
187 super(scmInfoRepository,
188 new NewCoverageInputMetricKeys(
189 CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY, CoreMetrics.IT_CONDITIONS_BY_LINE_KEY, CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY),
193 public static Iterable<Formula<?>> from(@Nullable ScmInfoRepository scmInfoRepository) {
194 if (scmInfoRepository == null) {
195 return VIEWS_FORMULAS;
197 return Collections.<Formula<?>>singleton(new NewItLinesAndConditionsCoverageFormula(scmInfoRepository));
201 private static class NewItCoverageFormula extends LinesAndConditionsWithUncoveredVariationFormula {
202 private NewItCoverageFormula() {
204 new LinesAndConditionsWithUncoveredMetricKeys(
205 CoreMetrics.NEW_IT_LINES_TO_COVER_KEY, CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY,
206 CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY),
207 CoreMetrics.NEW_IT_COVERAGE_KEY);
211 private static class NewItBranchCoverageFormula extends SingleWithUncoveredVariationFormula {
212 public NewItBranchCoverageFormula() {
214 new SingleWithUncoveredMetricKeys(
215 CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY),
216 CoreMetrics.NEW_IT_BRANCH_COVERAGE_KEY);
220 private static class NewItLinesCoverageFormula extends SingleWithUncoveredVariationFormula {
221 public NewItLinesCoverageFormula() {
223 new SingleWithUncoveredMetricKeys(CoreMetrics.NEW_IT_LINES_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY),
224 CoreMetrics.NEW_IT_LINE_COVERAGE_KEY);
228 private static class NewOverallLinesAndConditionsCoverageFormula extends NewLinesAndConditionsFormula {
230 private static final NewCoverageOutputMetricKeys OUTPUT_METRIC_KEYS = new NewCoverageOutputMetricKeys(
231 CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY,
232 CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY);
233 private static final Iterable<Formula<?>> VIEWS_FORMULAS = variationSumFormulas(OUTPUT_METRIC_KEYS);
235 private NewOverallLinesAndConditionsCoverageFormula(ScmInfoRepository scmInfoRepository) {
236 super(scmInfoRepository,
237 new NewCoverageInputMetricKeys(
238 CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY, CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY),
242 public static Iterable<Formula<?>> from(@Nullable ScmInfoRepository scmInfoRepository) {
243 if (scmInfoRepository == null) {
244 return VIEWS_FORMULAS;
246 return Collections.<Formula<?>>singleton(new NewOverallLinesAndConditionsCoverageFormula(scmInfoRepository));
250 private static class NewOverallCodeCoverageFormula extends LinesAndConditionsWithUncoveredVariationFormula {
251 public NewOverallCodeCoverageFormula() {
253 new LinesAndConditionsWithUncoveredMetricKeys(
254 CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY,
255 CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY),
256 CoreMetrics.NEW_OVERALL_COVERAGE_KEY);
260 private static class NewOverallBranchCoverageFormula extends SingleWithUncoveredVariationFormula {
261 public NewOverallBranchCoverageFormula() {
263 new SingleWithUncoveredMetricKeys(
264 CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY),
265 CoreMetrics.NEW_OVERALL_BRANCH_COVERAGE_KEY);
269 private static class NewOverallLineCoverageFormula extends SingleWithUncoveredVariationFormula {
270 public NewOverallLineCoverageFormula() {
272 new SingleWithUncoveredMetricKeys(
273 CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY),
274 CoreMetrics.NEW_OVERALL_LINE_COVERAGE_KEY);
279 * Creates a List of {@link org.sonar.server.computation.task.projectanalysis.formula.SumFormula.IntSumFormula} for each
280 * metric key of the specified {@link NewCoverageOutputMetricKeys} instance.
282 private static Iterable<Formula<?>> variationSumFormulas(NewCoverageOutputMetricKeys outputMetricKeys) {
283 return ImmutableList.<Formula<?>>of(
284 new VariationSumFormula(outputMetricKeys.getNewLinesToCover(), viewsRestrictedPeriods()),
285 new VariationSumFormula(outputMetricKeys.getNewUncoveredLines(), viewsRestrictedPeriods()),
286 new VariationSumFormula(outputMetricKeys.getNewConditionsToCover(), viewsRestrictedPeriods()),
287 new VariationSumFormula(outputMetricKeys.getNewUncoveredConditions(), viewsRestrictedPeriods()));
290 public static class NewLinesAndConditionsFormula implements Formula<NewCoverageCounter> {
291 private final ScmInfoRepository scmInfoRepository;
292 private final NewCoverageInputMetricKeys inputMetricKeys;
293 private final NewCoverageOutputMetricKeys outputMetricKeys;
295 public NewLinesAndConditionsFormula(ScmInfoRepository scmInfoRepository, NewCoverageInputMetricKeys inputMetricKeys, NewCoverageOutputMetricKeys outputMetricKeys) {
296 this.scmInfoRepository = scmInfoRepository;
297 this.inputMetricKeys = inputMetricKeys;
298 this.outputMetricKeys = outputMetricKeys;
302 public NewCoverageCounter createNewCounter() {
303 return new NewCoverageCounter(scmInfoRepository, inputMetricKeys);
307 public Optional<Measure> createMeasure(NewCoverageCounter counter, CreateMeasureContext context) {
308 MeasureVariations.Builder builder = MeasureVariations.newMeasureVariationsBuilder();
309 for (Period period : context.getPeriods()) {
310 if (counter.hasNewCode(period)) {
311 int value = computeValueForMetric(counter, period, context.getMetric());
312 builder.setVariation(period, value);
315 if (builder.isEmpty()) {
316 return Optional.absent();
318 return Optional.of(newMeasureBuilder().setVariations(builder.build()).createNoValue());
321 private int computeValueForMetric(NewCoverageCounter counter, Period period, Metric metric) {
322 if (metric.getKey().equals(outputMetricKeys.getNewLinesToCover())) {
323 return counter.getNewLines(period);
325 if (metric.getKey().equals(outputMetricKeys.getNewUncoveredLines())) {
326 return counter.getNewLines(period) - counter.getNewCoveredLines(period);
328 if (metric.getKey().equals(outputMetricKeys.getNewConditionsToCover())) {
329 return counter.getNewConditions(period);
331 if (metric.getKey().equals(outputMetricKeys.getNewUncoveredConditions())) {
332 return counter.getNewConditions(period) - counter.getNewCoveredConditions(period);
334 throw new IllegalArgumentException("Unsupported metric " + metric.getKey());
338 public String[] getOutputMetricKeys() {
339 return new String[] {
340 outputMetricKeys.getNewLinesToCover(),
341 outputMetricKeys.getNewUncoveredLines(),
342 outputMetricKeys.getNewConditionsToCover(),
343 outputMetricKeys.getNewUncoveredConditions()
348 public static final class NewCoverageCounter implements org.sonar.server.computation.task.projectanalysis.formula.Counter<NewCoverageCounter> {
349 private final IntVariationValue.Array newLines = IntVariationValue.newArray();
350 private final IntVariationValue.Array newCoveredLines = IntVariationValue.newArray();
351 private final IntVariationValue.Array newConditions = IntVariationValue.newArray();
352 private final IntVariationValue.Array newCoveredConditions = IntVariationValue.newArray();
353 private final ScmInfoRepository scmInfoRepository;
354 private final NewCoverageInputMetricKeys metricKeys;
356 public NewCoverageCounter(ScmInfoRepository scmInfoRepository, NewCoverageInputMetricKeys metricKeys) {
357 this.scmInfoRepository = scmInfoRepository;
358 this.metricKeys = metricKeys;
362 public void aggregate(NewCoverageCounter counter) {
363 newLines.incrementAll(counter.newLines);
364 newCoveredLines.incrementAll(counter.newCoveredLines);
365 newConditions.incrementAll(counter.newConditions);
366 newCoveredConditions.incrementAll(counter.newCoveredConditions);
370 public void initialize(CounterInitializationContext context) {
371 Component fileComponent = context.getLeaf();
372 Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(fileComponent);
373 if (!scmInfoOptional.isPresent()) {
376 ScmInfo componentScm = scmInfoOptional.get();
378 Optional<Measure> hitsByLineMeasure = context.getMeasure(metricKeys.getCoverageLineHitsData());
379 if (!hitsByLineMeasure.isPresent() || hitsByLineMeasure.get().getValueType() == Measure.ValueType.NO_VALUE) {
383 Map<Integer, Integer> hitsByLine = parseCountByLine(hitsByLineMeasure);
384 Map<Integer, Integer> conditionsByLine = parseCountByLine(context.getMeasure(metricKeys.getConditionsByLine()));
385 Map<Integer, Integer> coveredConditionsByLine = parseCountByLine(context.getMeasure(metricKeys.getCoveredConditionsByLine()));
387 for (Map.Entry<Integer, Integer> entry : hitsByLine.entrySet()) {
388 int lineId = entry.getKey();
389 int hits = entry.getValue();
390 int conditions = (Integer) ObjectUtils.defaultIfNull(conditionsByLine.get(lineId), 0);
391 int coveredConditions = (Integer) ObjectUtils.defaultIfNull(coveredConditionsByLine.get(lineId), 0);
392 long date = componentScm.getChangesetForLine(lineId).getDate();
393 analyze(context.getPeriods(), date, hits, conditions, coveredConditions);
397 private static Map<Integer, Integer> parseCountByLine(Optional<Measure> measure) {
398 if (measure.isPresent() && measure.get().getValueType() != Measure.ValueType.NO_VALUE) {
399 return KeyValueFormat.parseIntInt(measure.get().getStringValue());
401 return Collections.emptyMap();
404 public void analyze(List<Period> periods, @Nullable Long lineDate, int hits, int conditions, int coveredConditions) {
405 if (lineDate == null) {
408 for (Period period : periods) {
409 if (isLineInPeriod(lineDate, period)) {
410 incrementLines(period, hits);
411 incrementConditions(period, conditions, coveredConditions);
417 * A line belongs to a Period if its date is older than the SNAPSHOT's date of the period.
419 private static boolean isLineInPeriod(long lineDate, Period period) {
420 return lineDate > period.getSnapshotDate();
423 private void incrementLines(Period period, int hits) {
424 newLines.increment(period, 1);
426 newCoveredLines.increment(period, 1);
430 private void incrementConditions(Period period, int conditions, int coveredConditions) {
431 newConditions.increment(period, conditions);
432 if (conditions > 0) {
433 newCoveredConditions.increment(period, coveredConditions);
437 public boolean hasNewCode(Period period) {
438 return newLines.get(period).isSet();
441 public int getNewLines(Period period) {
442 return newLines.get(period).getValue();
445 public int getNewCoveredLines(Period period) {
446 return newCoveredLines.get(period).getValue();
449 public int getNewConditions(Period period) {
450 return newConditions.get(period).getValue();
453 public int getNewCoveredConditions(Period period) {
454 return newCoveredConditions.get(period).getValue();
459 public static final class NewCoverageOutputMetricKeys {
460 private final String newLinesToCover;
461 private final String newUncoveredLines;
462 private final String newConditionsToCover;
463 private final String newUncoveredConditions;
465 public NewCoverageOutputMetricKeys(String newLinesToCover, String newUncoveredLines, String newConditionsToCover, String newUncoveredConditions) {
466 this.newLinesToCover = newLinesToCover;
467 this.newUncoveredLines = newUncoveredLines;
468 this.newConditionsToCover = newConditionsToCover;
469 this.newUncoveredConditions = newUncoveredConditions;
472 public String getNewLinesToCover() {
473 return newLinesToCover;
476 public String getNewUncoveredLines() {
477 return newUncoveredLines;
480 public String getNewConditionsToCover() {
481 return newConditionsToCover;
484 public String getNewUncoveredConditions() {
485 return newUncoveredConditions;
490 public static class NewCoverageInputMetricKeys {
491 private final String coverageLineHitsData;
492 private final String conditionsByLine;
493 private final String coveredConditionsByLine;
495 public NewCoverageInputMetricKeys(String coverageLineHitsData, String conditionsByLine, String coveredConditionsByLine) {
496 this.coverageLineHitsData = coverageLineHitsData;
497 this.conditionsByLine = conditionsByLine;
498 this.coveredConditionsByLine = coveredConditionsByLine;
501 public String getCoverageLineHitsData() {
502 return coverageLineHitsData;
505 public String getConditionsByLine() {
506 return conditionsByLine;
509 public String getCoveredConditionsByLine() {
510 return coveredConditionsByLine;