]> source.dussan.org Git - sonarqube.git/blob
bc5acfc0c44b0bd2b4e5f631f78440882d87ad53
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2021 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.ce.task.projectanalysis.step;
21
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;
43
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;
53
54 /**
55  * Computes coverage measures on files and then aggregates them on higher components.
56  */
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());
66
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;
75
76   /**
77    * Constructor used when processing a Report (ie. a {@link BatchReportReader} instance is available in the container)
78    */
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);
88   }
89
90   /**
91    * Constructor used when processing Views (ie. no {@link BatchReportReader} instance is available in the container)
92    */
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;
102   }
103
104   @Override
105   public void execute(ComputationStep.Context context) {
106     if (reportReader != null) {
107       new DepthTraversalTypeAwareCrawler(new FileCoverageVisitor(reportReader)).visit(treeRootHolder.getReportTreeRoot());
108     }
109     new PathAwareCrawler<>(
110       FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository).buildFor(COVERAGE_FORMULAS))
111         .visit(treeRootHolder.getReportTreeRoot());
112   }
113
114   private class FileCoverageVisitor extends TypeAwareVisitorAdapter {
115
116     private final BatchReportReader reportReader;
117
118     private FileCoverageVisitor(BatchReportReader reportReader) {
119       super(CrawlerDepthLimit.FILE, Order.POST_ORDER);
120       this.reportReader = reportReader;
121     }
122
123     @Override
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) {
133             linesToCover++;
134             if (line.getHits()) {
135               coveredLines++;
136             }
137           }
138           if (line.getHasCoveredConditionsCase() == ScannerReport.LineCoverage.HasCoveredConditionsCase.COVERED_CONDITIONS) {
139             conditionsToCover += line.getConditions();
140             coveredConditions += min(line.getCoveredConditions(), line.getConditions());
141           }
142         }
143         if (linesToCover > 0) {
144           measureRepository.add(file, linesToCoverMetric, Measure.newMeasureBuilder().create(linesToCover));
145           measureRepository.add(file, uncoveredLinesMetric, Measure.newMeasureBuilder().create(linesToCover - coveredLines));
146         }
147         if (conditionsToCover > 0) {
148           measureRepository.add(file, conditionsToCoverMetric, Measure.newMeasureBuilder().create(conditionsToCover));
149           measureRepository.add(file, uncoveredConditionsMetric, Measure.newMeasureBuilder().create(conditionsToCover - coveredConditions));
150         }
151       }
152     }
153   }
154
155   private static class CodeCoverageFormula extends LinesAndConditionsWithUncoveredFormula {
156     public CodeCoverageFormula() {
157       super(
158         new LinesAndConditionsWithUncoveredMetricKeys(
159           LINES_TO_COVER_KEY, CONDITIONS_TO_COVER_KEY,
160           UNCOVERED_LINES_KEY, UNCOVERED_CONDITIONS_KEY),
161         COVERAGE_KEY);
162     }
163   }
164
165   private static class BranchCoverageFormula extends SingleWithUncoveredFormula {
166     public BranchCoverageFormula() {
167       super(
168         new SingleWithUncoveredMetricKeys(
169           CONDITIONS_TO_COVER_KEY, UNCOVERED_CONDITIONS_KEY),
170         BRANCH_COVERAGE_KEY);
171     }
172   }
173
174   private static class LineCoverageFormula extends SingleWithUncoveredFormula {
175     public LineCoverageFormula() {
176       super(
177         new SingleWithUncoveredMetricKeys(LINES_TO_COVER_KEY, UNCOVERED_LINES_KEY),
178         LINE_COVERAGE_KEY);
179     }
180   }
181
182   @Override
183   public String getDescription() {
184     return "Compute coverage measures";
185   }
186 }