]> source.dussan.org Git - sonarqube.git/blob
299cea60b216e727084ab4592ca453bcde7f9638
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2016 SonarSource SA
4  * mailto:contact 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.server.computation.task.projectanalysis.step;
21
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;
27 import java.util.Map;
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;
58
59 import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;
60 import static org.sonar.server.computation.task.projectanalysis.period.PeriodPredicates.viewsRestrictedPeriods;
61
62 /**
63  * Computes measures related to the New Coverage. These measures do not have values, only variations.
64  */
65 public class NewCoverageMeasuresStep implements ComputationStep {
66
67   private static final List<Formula> FORMULAS = ImmutableList.<Formula>of(
68     // UT coverage
69     new NewCoverageFormula(),
70     new NewBranchCoverageFormula(),
71     new NewLineCoverageFormula(),
72     // IT File coverage
73     new NewItCoverageFormula(),
74     new NewItBranchCoverageFormula(),
75     new NewItLinesCoverageFormula(),
76     // Overall coverage
77     new NewOverallCodeCoverageFormula(),
78     new NewOverallBranchCoverageFormula(),
79     new NewOverallLineCoverageFormula());
80
81   private final TreeRootHolder treeRootHolder;
82   private final PeriodsHolder periodsHolder;
83   private final MetricRepository metricRepository;
84   private final MeasureRepository measureRepository;
85   @CheckForNull
86   private final ScmInfoRepository scmInfoRepository;
87
88   /**
89    * Constructor used when processing a Report (ie. a {@link BatchReportReader} instance is available in the container)
90    */
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;
98   }
99
100   /**
101    * Constructor used when processing Views (ie. no {@link BatchReportReader} instance is available in the container)
102    */
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;
110   }
111
112   @Override
113   public void execute() {
114     new PathAwareCrawler<>(
115       FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
116         .withVariationSupport(periodsHolder)
117         .buildFor(
118           Iterables.concat(
119             NewLinesAndConditionsCoverageFormula.from(scmInfoRepository),
120             NewItLinesAndConditionsCoverageFormula.from(scmInfoRepository),
121             NewOverallLinesAndConditionsCoverageFormula.from(scmInfoRepository),
122             FORMULAS)))
123       .visit(treeRootHolder.getRoot());
124   }
125
126   @Override
127   public String getDescription() {
128     return "Compute new coverage";
129   }
130
131   private static class NewLinesAndConditionsCoverageFormula extends NewLinesAndConditionsFormula {
132
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);
137
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),
142         OUTPUT_METRIC_KEYS);
143     }
144
145     public static Iterable<Formula<?>> from(@Nullable ScmInfoRepository scmInfoRepository) {
146       if (scmInfoRepository == null) {
147         return VIEWS_FORMULAS;
148       }
149       return Collections.<Formula<?>>singleton(new NewLinesAndConditionsCoverageFormula(scmInfoRepository));
150     }
151   }
152
153   private static class NewCoverageFormula extends LinesAndConditionsWithUncoveredVariationFormula {
154     public NewCoverageFormula() {
155       super(
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);
160     }
161   }
162
163   private static class NewBranchCoverageFormula extends SingleWithUncoveredVariationFormula {
164     public NewBranchCoverageFormula() {
165       super(
166         new SingleWithUncoveredMetricKeys(CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY),
167         CoreMetrics.NEW_BRANCH_COVERAGE_KEY);
168     }
169   }
170
171   private static class NewLineCoverageFormula extends SingleWithUncoveredVariationFormula {
172     public NewLineCoverageFormula() {
173       super(
174         new SingleWithUncoveredMetricKeys(CoreMetrics.NEW_LINES_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_LINES_KEY),
175         CoreMetrics.NEW_LINE_COVERAGE_KEY);
176     }
177   }
178
179   private static class NewItLinesAndConditionsCoverageFormula extends NewLinesAndConditionsFormula {
180
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);
185
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),
190         OUTPUT_METRIC_KEYS);
191     }
192
193     public static Iterable<Formula<?>> from(@Nullable ScmInfoRepository scmInfoRepository) {
194       if (scmInfoRepository == null) {
195         return VIEWS_FORMULAS;
196       }
197       return Collections.<Formula<?>>singleton(new NewItLinesAndConditionsCoverageFormula(scmInfoRepository));
198     }
199   }
200
201   private static class NewItCoverageFormula extends LinesAndConditionsWithUncoveredVariationFormula {
202     private NewItCoverageFormula() {
203       super(
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);
208     }
209   }
210
211   private static class NewItBranchCoverageFormula extends SingleWithUncoveredVariationFormula {
212     public NewItBranchCoverageFormula() {
213       super(
214         new SingleWithUncoveredMetricKeys(
215           CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY),
216         CoreMetrics.NEW_IT_BRANCH_COVERAGE_KEY);
217     }
218   }
219
220   private static class NewItLinesCoverageFormula extends SingleWithUncoveredVariationFormula {
221     public NewItLinesCoverageFormula() {
222       super(
223         new SingleWithUncoveredMetricKeys(CoreMetrics.NEW_IT_LINES_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY),
224         CoreMetrics.NEW_IT_LINE_COVERAGE_KEY);
225     }
226   }
227
228   private static class NewOverallLinesAndConditionsCoverageFormula extends NewLinesAndConditionsFormula {
229
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);
234
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),
239         OUTPUT_METRIC_KEYS);
240     }
241
242     public static Iterable<Formula<?>> from(@Nullable ScmInfoRepository scmInfoRepository) {
243       if (scmInfoRepository == null) {
244         return VIEWS_FORMULAS;
245       }
246       return Collections.<Formula<?>>singleton(new NewOverallLinesAndConditionsCoverageFormula(scmInfoRepository));
247     }
248   }
249
250   private static class NewOverallCodeCoverageFormula extends LinesAndConditionsWithUncoveredVariationFormula {
251     public NewOverallCodeCoverageFormula() {
252       super(
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);
257     }
258   }
259
260   private static class NewOverallBranchCoverageFormula extends SingleWithUncoveredVariationFormula {
261     public NewOverallBranchCoverageFormula() {
262       super(
263         new SingleWithUncoveredMetricKeys(
264           CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY),
265         CoreMetrics.NEW_OVERALL_BRANCH_COVERAGE_KEY);
266     }
267   }
268
269   private static class NewOverallLineCoverageFormula extends SingleWithUncoveredVariationFormula {
270     public NewOverallLineCoverageFormula() {
271       super(
272         new SingleWithUncoveredMetricKeys(
273           CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY),
274         CoreMetrics.NEW_OVERALL_LINE_COVERAGE_KEY);
275     }
276   }
277
278   /**
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.
281    */
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()));
288   }
289
290   public static class NewLinesAndConditionsFormula implements Formula<NewCoverageCounter> {
291     private final ScmInfoRepository scmInfoRepository;
292     private final NewCoverageInputMetricKeys inputMetricKeys;
293     private final NewCoverageOutputMetricKeys outputMetricKeys;
294
295     public NewLinesAndConditionsFormula(ScmInfoRepository scmInfoRepository, NewCoverageInputMetricKeys inputMetricKeys, NewCoverageOutputMetricKeys outputMetricKeys) {
296       this.scmInfoRepository = scmInfoRepository;
297       this.inputMetricKeys = inputMetricKeys;
298       this.outputMetricKeys = outputMetricKeys;
299     }
300
301     @Override
302     public NewCoverageCounter createNewCounter() {
303       return new NewCoverageCounter(scmInfoRepository, inputMetricKeys);
304     }
305
306     @Override
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);
313         }
314       }
315       if (builder.isEmpty()) {
316         return Optional.absent();
317       }
318       return Optional.of(newMeasureBuilder().setVariations(builder.build()).createNoValue());
319     }
320
321     private int computeValueForMetric(NewCoverageCounter counter, Period period, Metric metric) {
322       if (metric.getKey().equals(outputMetricKeys.getNewLinesToCover())) {
323         return counter.getNewLines(period);
324       }
325       if (metric.getKey().equals(outputMetricKeys.getNewUncoveredLines())) {
326         return counter.getNewLines(period) - counter.getNewCoveredLines(period);
327       }
328       if (metric.getKey().equals(outputMetricKeys.getNewConditionsToCover())) {
329         return counter.getNewConditions(period);
330       }
331       if (metric.getKey().equals(outputMetricKeys.getNewUncoveredConditions())) {
332         return counter.getNewConditions(period) - counter.getNewCoveredConditions(period);
333       }
334       throw new IllegalArgumentException("Unsupported metric " + metric.getKey());
335     }
336
337     @Override
338     public String[] getOutputMetricKeys() {
339       return new String[] {
340         outputMetricKeys.getNewLinesToCover(),
341         outputMetricKeys.getNewUncoveredLines(),
342         outputMetricKeys.getNewConditionsToCover(),
343         outputMetricKeys.getNewUncoveredConditions()
344       };
345     }
346   }
347
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;
355
356     public NewCoverageCounter(ScmInfoRepository scmInfoRepository, NewCoverageInputMetricKeys metricKeys) {
357       this.scmInfoRepository = scmInfoRepository;
358       this.metricKeys = metricKeys;
359     }
360
361     @Override
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);
367     }
368
369     @Override
370     public void initialize(CounterInitializationContext context) {
371       Component fileComponent = context.getLeaf();
372       Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(fileComponent);
373       if (!scmInfoOptional.isPresent()) {
374         return;
375       }
376       ScmInfo componentScm = scmInfoOptional.get();
377
378       Optional<Measure> hitsByLineMeasure = context.getMeasure(metricKeys.getCoverageLineHitsData());
379       if (!hitsByLineMeasure.isPresent() || hitsByLineMeasure.get().getValueType() == Measure.ValueType.NO_VALUE) {
380         return;
381       }
382
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()));
386
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);
394       }
395     }
396
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());
400       }
401       return Collections.emptyMap();
402     }
403
404     public void analyze(List<Period> periods, @Nullable Long lineDate, int hits, int conditions, int coveredConditions) {
405       if (lineDate == null) {
406         return;
407       }
408       for (Period period : periods) {
409         if (isLineInPeriod(lineDate, period)) {
410           incrementLines(period, hits);
411           incrementConditions(period, conditions, coveredConditions);
412         }
413       }
414     }
415
416     /**
417      * A line belongs to a Period if its date is older than the SNAPSHOT's date of the period.
418      */
419     private static boolean isLineInPeriod(long lineDate, Period period) {
420       return lineDate > period.getSnapshotDate();
421     }
422
423     private void incrementLines(Period period, int hits) {
424       newLines.increment(period, 1);
425       if (hits > 0) {
426         newCoveredLines.increment(period, 1);
427       }
428     }
429
430     private void incrementConditions(Period period, int conditions, int coveredConditions) {
431       newConditions.increment(period, conditions);
432       if (conditions > 0) {
433         newCoveredConditions.increment(period, coveredConditions);
434       }
435     }
436
437     public boolean hasNewCode(Period period) {
438       return newLines.get(period).isSet();
439     }
440
441     public int getNewLines(Period period) {
442       return newLines.get(period).getValue();
443     }
444
445     public int getNewCoveredLines(Period period) {
446       return newCoveredLines.get(period).getValue();
447     }
448
449     public int getNewConditions(Period period) {
450       return newConditions.get(period).getValue();
451     }
452
453     public int getNewCoveredConditions(Period period) {
454       return newCoveredConditions.get(period).getValue();
455     }
456   }
457
458   @Immutable
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;
464
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;
470     }
471
472     public String getNewLinesToCover() {
473       return newLinesToCover;
474     }
475
476     public String getNewUncoveredLines() {
477       return newUncoveredLines;
478     }
479
480     public String getNewConditionsToCover() {
481       return newConditionsToCover;
482     }
483
484     public String getNewUncoveredConditions() {
485       return newUncoveredConditions;
486     }
487   }
488
489   @Immutable
490   public static class NewCoverageInputMetricKeys {
491     private final String coverageLineHitsData;
492     private final String conditionsByLine;
493     private final String coveredConditionsByLine;
494
495     public NewCoverageInputMetricKeys(String coverageLineHitsData, String conditionsByLine, String coveredConditionsByLine) {
496       this.coverageLineHitsData = coverageLineHitsData;
497       this.conditionsByLine = conditionsByLine;
498       this.coveredConditionsByLine = coveredConditionsByLine;
499     }
500
501     public String getCoverageLineHitsData() {
502       return coverageLineHitsData;
503     }
504
505     public String getConditionsByLine() {
506       return conditionsByLine;
507     }
508
509     public String getCoveredConditionsByLine() {
510       return coveredConditionsByLine;
511     }
512   }
513 }