3 * Copyright (C) 2009-2021 SonarSource SA
4 * mailto:info 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.ce.task.projectanalysis.step;
22 import com.google.common.collect.ImmutableList;
23 import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
24 import org.sonar.ce.task.projectanalysis.component.Component;
25 import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
26 import org.sonar.ce.task.projectanalysis.component.DepthTraversalTypeAwareCrawler;
27 import org.sonar.ce.task.projectanalysis.component.PathAwareCrawler;
28 import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
29 import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter;
30 import org.sonar.ce.task.projectanalysis.formula.Formula;
31 import org.sonar.ce.task.projectanalysis.formula.FormulaExecutorComponentVisitor;
32 import org.sonar.ce.task.projectanalysis.formula.coverage.LinesAndConditionsWithUncoveredFormula;
33 import org.sonar.ce.task.projectanalysis.formula.coverage.LinesAndConditionsWithUncoveredMetricKeys;
34 import org.sonar.ce.task.projectanalysis.formula.coverage.SingleWithUncoveredFormula;
35 import org.sonar.ce.task.projectanalysis.formula.coverage.SingleWithUncoveredMetricKeys;
36 import org.sonar.ce.task.projectanalysis.measure.Measure;
37 import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
38 import org.sonar.ce.task.projectanalysis.metric.Metric;
39 import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
40 import org.sonar.ce.task.step.ComputationStep;
41 import org.sonar.core.util.CloseableIterator;
42 import org.sonar.scanner.protocol.output.ScannerReport;
44 import static java.lang.Math.min;
45 import static org.sonar.api.measures.CoreMetrics.BRANCH_COVERAGE_KEY;
46 import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER_KEY;
47 import static org.sonar.api.measures.CoreMetrics.COVERAGE_KEY;
48 import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER_KEY;
49 import static org.sonar.api.measures.CoreMetrics.LINE_COVERAGE_KEY;
50 import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS_KEY;
51 import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES_KEY;
52 import static org.sonar.ce.task.projectanalysis.formula.SumFormula.createIntSumFormula;
55 * Computes coverage measures on files and then aggregates them on higher components.
57 public class CoverageMeasuresStep implements ComputationStep {
58 private static final ImmutableList<Formula> COVERAGE_FORMULAS = ImmutableList.of(
59 createIntSumFormula(LINES_TO_COVER_KEY),
60 createIntSumFormula(UNCOVERED_LINES_KEY),
61 createIntSumFormula(CONDITIONS_TO_COVER_KEY),
62 createIntSumFormula(UNCOVERED_CONDITIONS_KEY),
63 new CodeCoverageFormula(),
64 new BranchCoverageFormula(),
65 new LineCoverageFormula());
67 private final TreeRootHolder treeRootHolder;
68 private final MetricRepository metricRepository;
69 private final MeasureRepository measureRepository;
70 private final BatchReportReader reportReader;
71 private final Metric linesToCoverMetric;
72 private final Metric uncoveredLinesMetric;
73 private final Metric conditionsToCoverMetric;
74 private final Metric uncoveredConditionsMetric;
77 * Constructor used when processing a Report (ie. a {@link BatchReportReader} instance is available in the container)
79 public CoverageMeasuresStep(TreeRootHolder treeRootHolder, MetricRepository metricRepository, MeasureRepository measureRepository, BatchReportReader reportReader) {
80 this.treeRootHolder = treeRootHolder;
81 this.metricRepository = metricRepository;
82 this.measureRepository = measureRepository;
83 this.reportReader = reportReader;
84 this.linesToCoverMetric = metricRepository.getByKey(LINES_TO_COVER_KEY);
85 this.uncoveredLinesMetric = metricRepository.getByKey(UNCOVERED_LINES_KEY);
86 this.conditionsToCoverMetric = metricRepository.getByKey(CONDITIONS_TO_COVER_KEY);
87 this.uncoveredConditionsMetric = metricRepository.getByKey(UNCOVERED_CONDITIONS_KEY);
91 * Constructor used when processing Views (ie. no {@link BatchReportReader} instance is available in the container)
93 public CoverageMeasuresStep(TreeRootHolder treeRootHolder, MetricRepository metricRepository, MeasureRepository measureRepository) {
94 this.treeRootHolder = treeRootHolder;
95 this.metricRepository = metricRepository;
96 this.measureRepository = measureRepository;
97 this.linesToCoverMetric = metricRepository.getByKey(LINES_TO_COVER_KEY);
98 this.uncoveredLinesMetric = metricRepository.getByKey(UNCOVERED_LINES_KEY);
99 this.conditionsToCoverMetric = metricRepository.getByKey(CONDITIONS_TO_COVER_KEY);
100 this.uncoveredConditionsMetric = metricRepository.getByKey(UNCOVERED_CONDITIONS_KEY);
101 this.reportReader = null;
105 public void execute(ComputationStep.Context context) {
106 if (reportReader != null) {
107 new DepthTraversalTypeAwareCrawler(new FileCoverageVisitor(reportReader)).visit(treeRootHolder.getReportTreeRoot());
109 new PathAwareCrawler<>(
110 FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository).buildFor(COVERAGE_FORMULAS))
111 .visit(treeRootHolder.getReportTreeRoot());
114 private class FileCoverageVisitor extends TypeAwareVisitorAdapter {
116 private final BatchReportReader reportReader;
118 private FileCoverageVisitor(BatchReportReader reportReader) {
119 super(CrawlerDepthLimit.FILE, Order.POST_ORDER);
120 this.reportReader = reportReader;
124 public void visitFile(Component file) {
125 try (CloseableIterator<ScannerReport.LineCoverage> lineCoverage = reportReader.readComponentCoverage(file.getReportAttributes().getRef())) {
126 int linesToCover = 0;
127 int coveredLines = 0;
128 int conditionsToCover = 0;
129 int coveredConditions = 0;
130 while (lineCoverage.hasNext()) {
131 final ScannerReport.LineCoverage line = lineCoverage.next();
132 if (line.getHasHitsCase() == ScannerReport.LineCoverage.HasHitsCase.HITS) {
134 if (line.getHits()) {
138 if (line.getHasCoveredConditionsCase() == ScannerReport.LineCoverage.HasCoveredConditionsCase.COVERED_CONDITIONS) {
139 conditionsToCover += line.getConditions();
140 coveredConditions += min(line.getCoveredConditions(), line.getConditions());
143 if (linesToCover > 0) {
144 measureRepository.add(file, linesToCoverMetric, Measure.newMeasureBuilder().create(linesToCover));
145 measureRepository.add(file, uncoveredLinesMetric, Measure.newMeasureBuilder().create(linesToCover - coveredLines));
147 if (conditionsToCover > 0) {
148 measureRepository.add(file, conditionsToCoverMetric, Measure.newMeasureBuilder().create(conditionsToCover));
149 measureRepository.add(file, uncoveredConditionsMetric, Measure.newMeasureBuilder().create(conditionsToCover - coveredConditions));
155 private static class CodeCoverageFormula extends LinesAndConditionsWithUncoveredFormula {
156 public CodeCoverageFormula() {
158 new LinesAndConditionsWithUncoveredMetricKeys(
159 LINES_TO_COVER_KEY, CONDITIONS_TO_COVER_KEY,
160 UNCOVERED_LINES_KEY, UNCOVERED_CONDITIONS_KEY),
165 private static class BranchCoverageFormula extends SingleWithUncoveredFormula {
166 public BranchCoverageFormula() {
168 new SingleWithUncoveredMetricKeys(
169 CONDITIONS_TO_COVER_KEY, UNCOVERED_CONDITIONS_KEY),
170 BRANCH_COVERAGE_KEY);
174 private static class LineCoverageFormula extends SingleWithUncoveredFormula {
175 public LineCoverageFormula() {
177 new SingleWithUncoveredMetricKeys(LINES_TO_COVER_KEY, UNCOVERED_LINES_KEY),
183 public String getDescription() {
184 return "Compute coverage measures";