]> source.dussan.org Git - sonarqube.git/commitdiff
add support for Views Component tree to NewCoverageVariationSumFormula
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 25 Aug 2015 17:16:57 +0000 (19:16 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Sat, 29 Aug 2015 13:58:41 +0000 (15:58 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitor.java
server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/CoverageUtils.java
server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/CoverageVariationFormula.java
server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/ElementsAndCoveredElementsVariationCounter.java
server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/LinesAndConditionsWithUncoveredVariationCounter.java
server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/NewCoverageVariationSumFormula.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/SingleWithUncoveredVariationCounter.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/NewCoverageMeasuresStep.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageMeasuresStepTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportNewCoverageMeasuresStepTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/step/ViewsNewCoverageMeasuresStepTest.java [new file with mode: 0644]

index d5039b00107c2e6248550c8b3b3b1aed21d9674d..4cc724156672e0dce4755b2382bbabdbeacd205b 100644 (file)
@@ -65,7 +65,7 @@ public class FormulaExecutorComponentVisitor extends PathAwareVisitorAdapter<For
   private final MeasureRepository measureRepository;
   private final List<Formula> formulas;
 
-  private FormulaExecutorComponentVisitor(Builder builder, List<Formula> formulas) {
+  private FormulaExecutorComponentVisitor(Builder builder, Iterable<Formula> formulas) {
     super(CrawlerDepthLimit.LEAVES, ComponentVisitor.Order.POST_ORDER, COUNTERS_FACTORY);
     this.periodsHolder = builder.periodsHolder;
     this.measureRepository = builder.measureRepository;
@@ -97,7 +97,7 @@ public class FormulaExecutorComponentVisitor extends PathAwareVisitorAdapter<For
       return this;
     }
 
-    public FormulaExecutorComponentVisitor buildFor(List<Formula> formulas) {
+    public FormulaExecutorComponentVisitor buildFor(Iterable<Formula> formulas) {
       return new FormulaExecutorComponentVisitor(this, formulas);
     }
   }
index 4646c06bd9b7ec62f87c8d673ecba785dd06ceac..58e4ce1efa376c8293fe47de59b6d3e159a897af 100644 (file)
 package org.sonar.server.computation.formula.coverage;
 
 import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.formula.CounterInitializationContext;
+import org.sonar.server.computation.formula.CreateMeasureContext;
 import org.sonar.server.computation.measure.Measure;
 import org.sonar.server.computation.measure.MeasureVariations;
 import org.sonar.server.computation.period.Period;
 
+import static com.google.common.collect.FluentIterable.from;
 import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
 
 public final class CoverageUtils {
@@ -64,4 +70,36 @@ public final class CoverageUtils {
     }
     return 0L;
   }
+
+  /**
+   * Since Periods 4 and 5 can be customized per project and/or per view/subview, aggregating values on this period
+   * will only generate garbage data which will make no sense. These Periods should be ignored when processing views/subviews.
+   */
+  static Iterable<Period> supportedPeriods(CreateMeasureContext context) {
+    return supportedPeriods(context.getComponent().getType(), context.getPeriods());
+  }
+
+  /**
+   * Since Periods 4 and 5 can be customized per project and/or per view/subview, aggregating values on this period
+   * will only generate garbage data which will make no sense. These Periods should be ignored when processing views/subviews.
+   */
+  public static Iterable<Period> supportedPeriods(CounterInitializationContext context) {
+    return supportedPeriods(context.getLeaf().getType(), context.getPeriods());
+  }
+
+  private static Iterable<Period> supportedPeriods(Component.Type type, List<Period> periods) {
+    if (type.isReportType()) {
+      return periods;
+    }
+    return from(periods).filter(ViewsSupportedPeriods.INSTANCE);
+  }
+
+  private enum ViewsSupportedPeriods implements Predicate<Period> {
+    INSTANCE;
+
+    @Override
+    public boolean apply(@Nonnull Period input) {
+      return input.getIndex() < 4;
+    }
+  }
 }
index e7b3ef3cf1c8575a28e6bba90f15d23a12f27563..47cfb845a03b9e79171c3afb320685a9173b1d10 100644 (file)
@@ -28,6 +28,7 @@ import org.sonar.server.computation.measure.MeasureVariations;
 import org.sonar.server.computation.period.Period;
 
 import static org.sonar.server.computation.formula.coverage.CoverageUtils.calculateCoverage;
+import static org.sonar.server.computation.formula.coverage.CoverageUtils.supportedPeriods;
 import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
 
 /**
@@ -48,7 +49,7 @@ public abstract class CoverageVariationFormula<T extends ElementsAndCoveredEleme
 
   private MeasureVariations.Builder createAndPopulateBuilder(T counter, CreateMeasureContext context) {
     MeasureVariations.Builder builder = MeasureVariations.newMeasureVariationsBuilder();
-    for (Period period : context.getPeriods()) {
+    for (Period period : supportedPeriods(context)) {
       LongVariationValue elements = counter.elements.get(period);
       if (elements.isSet() && elements.getValue() > 0d) {
         LongVariationValue coveredElements = counter.coveredElements.get(period);
@@ -57,5 +58,4 @@ public abstract class CoverageVariationFormula<T extends ElementsAndCoveredEleme
     }
     return builder;
   }
-
 }
index f260bb6ac47739f27faf9c5082c514c0b13c8403..6b5eee72a5dd282cb214e5ee93075d20be562177 100644 (file)
@@ -38,7 +38,7 @@ public abstract class ElementsAndCoveredElementsVariationCounter implements Coun
 
   @Override
   public void initialize(CounterInitializationContext context) {
-    if (context.getLeaf().getFileAttributes().isUnitTest()) {
+    if (context.getLeaf().getType().isReportType() && context.getLeaf().getFileAttributes().isUnitTest()) {
       return;
     }
     initializeForSupportedLeaf(context);
index bb28272c194179271f95e9b2e9d15bed8075fff3..96fc9422021c18b54f04036a9afd338d38539b0e 100644 (file)
@@ -25,6 +25,8 @@ import org.sonar.server.computation.measure.Measure;
 import org.sonar.server.computation.measure.MeasureVariations;
 import org.sonar.server.computation.period.Period;
 
+import static org.sonar.server.computation.formula.coverage.CoverageUtils.supportedPeriods;
+
 public final class LinesAndConditionsWithUncoveredVariationCounter extends ElementsAndCoveredElementsVariationCounter {
   private final LinesAndConditionsWithUncoveredMetricKeys metricKeys;
 
@@ -43,7 +45,7 @@ public final class LinesAndConditionsWithUncoveredVariationCounter extends Eleme
     MeasureVariations newConditions = CoverageUtils.getMeasureVariations(counterContext, metricKeys.getConditions());
     MeasureVariations uncoveredLines = CoverageUtils.getMeasureVariations(counterContext, metricKeys.getUncoveredLines());
     MeasureVariations uncoveredConditions = CoverageUtils.getMeasureVariations(counterContext, metricKeys.getUncoveredConditions());
-    for (Period period : counterContext.getPeriods()) {
+    for (Period period : supportedPeriods(counterContext)) {
       if (!newLines.hasVariation(period.getIndex())) {
         continue;
       }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/NewCoverageVariationSumFormula.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/NewCoverageVariationSumFormula.java
new file mode 100644 (file)
index 0000000..6bcf790
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.server.computation.formula.coverage;
+
+import com.google.common.base.Optional;
+import org.sonar.server.computation.component.CrawlerDepthLimit;
+import org.sonar.server.computation.formula.Counter;
+import org.sonar.server.computation.formula.CounterInitializationContext;
+import org.sonar.server.computation.formula.CreateMeasureContext;
+import org.sonar.server.computation.formula.Formula;
+import org.sonar.server.computation.formula.counter.DoubleVariationValue;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureVariations;
+import org.sonar.server.computation.period.Period;
+
+import static java.util.Objects.requireNonNull;
+import static org.sonar.server.computation.formula.coverage.CoverageUtils.supportedPeriods;
+import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
+
+/**
+ * A Formula which aggregates a new coverage measure by simply making the sums of its variations.
+ */
+public class NewCoverageVariationSumFormula implements Formula<NewCoverageVariationSumFormula.VariationSumCounter> {
+  private final String metricKey;
+
+  public NewCoverageVariationSumFormula(String metricKey) {
+    this.metricKey = requireNonNull(metricKey, "Metric key cannot be null");
+  }
+
+  @Override
+  public VariationSumCounter createNewCounter() {
+    return new VariationSumCounter(metricKey);
+  }
+
+  @Override
+  public Optional<Measure> createMeasure(VariationSumCounter counter, CreateMeasureContext context) {
+    if (!CrawlerDepthLimit.LEAVES.isDeeperThan(context.getComponent().getType())) {
+      return Optional.absent();
+    }
+    MeasureVariations.Builder variations = createAndPopulateBuilder(counter.array, context);
+    if (variations.isEmpty()) {
+      return Optional.absent();
+    }
+    return Optional.of(newMeasureBuilder().setVariations(variations.build()).createNoValue());
+  }
+
+  private static MeasureVariations.Builder createAndPopulateBuilder(DoubleVariationValue.Array array, CreateMeasureContext context) {
+    MeasureVariations.Builder builder = MeasureVariations.newMeasureVariationsBuilder();
+    for (Period period : supportedPeriods(context)) {
+      DoubleVariationValue elements = array.get(period);
+      if (elements.isSet()) {
+        builder.setVariation(period, elements.getValue());
+      }
+    }
+    return builder;
+  }
+
+  @Override
+  public String[] getOutputMetricKeys() {
+    return new String[] { metricKey };
+  }
+
+  public static final class VariationSumCounter implements Counter<VariationSumCounter> {
+    private final DoubleVariationValue.Array array = DoubleVariationValue.newArray();
+    private final String metricKey;
+
+    private VariationSumCounter(String metricKey) {
+      this.metricKey = metricKey;
+    }
+
+    @Override
+    public void aggregate(VariationSumCounter counter) {
+      array.incrementAll(counter.array);
+    }
+
+    @Override
+    public void initialize(CounterInitializationContext context) {
+      Optional<Measure> measure = context.getMeasure(metricKey);
+      if (!measure.isPresent() || !measure.get().hasVariations()) {
+        return;
+      }
+      MeasureVariations variations = measure.get().getVariations();
+      for (Period period : supportedPeriods(context)) {
+        if (variations.hasVariation(period.getIndex())) {
+          double variation = variations.getVariation(period.getIndex());
+          if (variation > 0) {
+            array.increment(period, variations.getVariation(period.getIndex()));
+          }
+        }
+      }
+    }
+  }
+}
index 4009600977bbe9080d4564c6682e6dbaea556800..44c850f9ffe18db30cca191beffa485eecf47e8d 100644 (file)
@@ -26,6 +26,7 @@ import org.sonar.server.computation.period.Period;
 import static java.util.Objects.requireNonNull;
 import static org.sonar.server.computation.formula.coverage.CoverageUtils.getLongVariation;
 import static org.sonar.server.computation.formula.coverage.CoverageUtils.getMeasureVariations;
+import static org.sonar.server.computation.formula.coverage.CoverageUtils.supportedPeriods;
 
 public final class SingleWithUncoveredVariationCounter extends ElementsAndCoveredElementsVariationCounter {
   private final SingleWithUncoveredMetricKeys metricKeys;
@@ -38,7 +39,7 @@ public final class SingleWithUncoveredVariationCounter extends ElementsAndCovere
   protected void initializeForSupportedLeaf(CounterInitializationContext counterContext) {
     MeasureVariations newConditions = getMeasureVariations(counterContext, metricKeys.getCovered());
     MeasureVariations uncoveredConditions = getMeasureVariations(counterContext, metricKeys.getUncovered());
-    for (Period period : counterContext.getPeriods()) {
+    for (Period period : supportedPeriods(counterContext)) {
       long elements = getLongVariation(newConditions, period);
       this.elements.increment(period, elements);
       coveredElements.increment(period, elements - getLongVariation(uncoveredConditions, period));
index 5e2de9e9525626697f84a8f9fad446e9b0bf217b..d503f5077d5f81088dc7ce6ffb18ea6bece55b48 100644 (file)
@@ -21,10 +21,12 @@ package org.sonar.server.computation.step;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
 import org.apache.commons.lang.ObjectUtils;
@@ -35,10 +37,11 @@ import org.sonar.server.computation.batch.BatchReportReader;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.PathAwareCrawler;
 import org.sonar.server.computation.component.TreeRootHolder;
-import org.sonar.server.computation.formula.CreateMeasureContext;
 import org.sonar.server.computation.formula.CounterInitializationContext;
+import org.sonar.server.computation.formula.CreateMeasureContext;
 import org.sonar.server.computation.formula.Formula;
 import org.sonar.server.computation.formula.FormulaExecutorComponentVisitor;
+import org.sonar.server.computation.formula.coverage.NewCoverageVariationSumFormula;
 import org.sonar.server.computation.formula.counter.IntVariationValue;
 import org.sonar.server.computation.formula.coverage.LinesAndConditionsWithUncoveredMetricKeys;
 import org.sonar.server.computation.formula.coverage.LinesAndConditionsWithUncoveredVariationFormula;
@@ -58,12 +61,32 @@ import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
  * Computes measures related to the New Coverage. These measures do not have values, only variations.
  */
 public class NewCoverageMeasuresStep implements ComputationStep {
+  private static final List<Formula> FORMULAS = ImmutableList.<Formula>of(
+    // UT coverage
+    new NewCoverageFormula()
+      ,
+    new NewBranchCoverageFormula(),
+    new NewLineCoverageFormula(),
+    // IT File coverage
+    new NewItCoverageFormula(),
+    new NewItBranchCoverageFormula(),
+    new NewItLinesCoverageFormula(),
+    // Overall coverage
+    new NewOverallCodeCoverageFormula(),
+    new NewOverallBranchCoverageFormula(),
+    new NewOverallLineCoverageFormula()
+  );
+
   private final TreeRootHolder treeRootHolder;
   private final PeriodsHolder periodsHolder;
+  @CheckForNull
   private final BatchReportReader batchReportReader;
   private final MetricRepository metricRepository;
   private final MeasureRepository measureRepository;
 
+  /**
+   * Constructor used when processing a Report (ie. a {@link BatchReportReader} instance is available in the container)
+   */
   public NewCoverageMeasuresStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder, BatchReportReader batchReportReader,
     MeasureRepository measureRepository, final MetricRepository metricRepository) {
     this.treeRootHolder = treeRootHolder;
@@ -73,28 +96,30 @@ public class NewCoverageMeasuresStep implements ComputationStep {
     this.measureRepository = measureRepository;
   }
 
+  /**
+   * Constructor used when processing Views (ie. no {@link BatchReportReader} instance is available in the container)
+   */
+  public NewCoverageMeasuresStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder,
+    MeasureRepository measureRepository, final MetricRepository metricRepository) {
+    this.treeRootHolder = treeRootHolder;
+    this.periodsHolder = periodsHolder;
+    this.batchReportReader = null;
+    this.metricRepository = metricRepository;
+    this.measureRepository = measureRepository;
+  }
+
   @Override
   public void execute() {
     new PathAwareCrawler<>(
       FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
         .withVariationSupport(periodsHolder)
-        .buildFor(ImmutableList.<Formula>of(
-          // UT coverage
-          new NewLinesAndConditionsCoverageFormula(batchReportReader),
-          new NewCoverageFormula(),
-          new NewBranchCoverageFormula(),
-          new NewLineCoverageFormula(),
-          // IT File coverage
-          new NewItLinesAndConditionsCoverageFormula(batchReportReader),
-          new NewItCoverageFormula(),
-          new NewItBranchCoverageFormula(),
-          new NewItLinesCoverageFormula(),
-          // Overall coverage
-          new NewOverallLinesAndConditionsCoverageFormula(batchReportReader),
-          new NewOverallCodeCoverageFormula(),
-          new NewOverallBranchCoverageFormula(),
-          new NewOverallLineCoverageFormula())))
-            .visit(treeRootHolder.getRoot());
+        .buildFor(
+          Iterables.concat(
+            NewLinesAndConditionsCoverageFormula.from(batchReportReader),
+            NewItLinesAndConditionsCoverageFormula.from(batchReportReader),
+            NewOverallLinesAndConditionsCoverageFormula.from(batchReportReader),
+            FORMULAS)))
+              .visit(treeRootHolder.getRoot());
   }
 
   @Override
@@ -103,13 +128,24 @@ public class NewCoverageMeasuresStep implements ComputationStep {
   }
 
   private static class NewLinesAndConditionsCoverageFormula extends NewLinesAndConditionsFormula {
-    public NewLinesAndConditionsCoverageFormula(BatchReportReader batchReportReader) {
+
+    private static final NewCoverageOutputMetricKeys OUTPUT_METRIC_KEYS = new NewCoverageOutputMetricKeys(
+      CoreMetrics.NEW_LINES_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_LINES_KEY,
+      CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY);
+    private static final Iterable<Formula<?>> VIEWS_FORMULAS = intSumFormulas(OUTPUT_METRIC_KEYS);
+
+    private NewLinesAndConditionsCoverageFormula(BatchReportReader batchReportReader) {
       super(batchReportReader,
         new NewCoverageInputMetricKeys(
           CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, CoreMetrics.CONDITIONS_BY_LINE_KEY, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY),
-        new NewCoverageOutputMetricKeys(
-          CoreMetrics.NEW_LINES_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_LINES_KEY,
-          CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY));
+        OUTPUT_METRIC_KEYS);
+    }
+
+    public static Iterable<Formula<?>> from(@Nullable BatchReportReader batchReportReader) {
+      if (batchReportReader == null) {
+        return VIEWS_FORMULAS;
+      }
+      return Collections.<Formula<?>>singleton(new NewLinesAndConditionsCoverageFormula(batchReportReader));
     }
   }
 
@@ -126,8 +162,7 @@ public class NewCoverageMeasuresStep implements ComputationStep {
   private static class NewBranchCoverageFormula extends SingleWithUncoveredVariationFormula {
     public NewBranchCoverageFormula() {
       super(
-        new SingleWithUncoveredMetricKeys(
-          CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY),
+        new SingleWithUncoveredMetricKeys(CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY),
         CoreMetrics.NEW_BRANCH_COVERAGE_KEY);
     }
   }
@@ -141,13 +176,24 @@ public class NewCoverageMeasuresStep implements ComputationStep {
   }
 
   private static class NewItLinesAndConditionsCoverageFormula extends NewLinesAndConditionsFormula {
-    public NewItLinesAndConditionsCoverageFormula(BatchReportReader batchReportReader) {
+
+    private static final NewCoverageOutputMetricKeys OUTPUT_METRIC_KEYS = new NewCoverageOutputMetricKeys(
+      CoreMetrics.NEW_IT_LINES_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY,
+      CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY);
+    private static final Iterable<Formula<?>> VIEWS_FORMULAS = intSumFormulas(OUTPUT_METRIC_KEYS);
+
+    private NewItLinesAndConditionsCoverageFormula(BatchReportReader batchReportReader) {
       super(batchReportReader,
         new NewCoverageInputMetricKeys(
           CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY, CoreMetrics.IT_CONDITIONS_BY_LINE_KEY, CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY),
-        new NewCoverageOutputMetricKeys(
-          CoreMetrics.NEW_IT_LINES_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY,
-          CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY));
+        OUTPUT_METRIC_KEYS);
+    }
+
+    public static Iterable<Formula<?>> from(@Nullable BatchReportReader batchReportReader) {
+      if (batchReportReader == null) {
+        return VIEWS_FORMULAS;
+      }
+      return Collections.<Formula<?>>singleton(new NewItLinesAndConditionsCoverageFormula(batchReportReader));
     }
   }
 
@@ -179,13 +225,24 @@ public class NewCoverageMeasuresStep implements ComputationStep {
   }
 
   private static class NewOverallLinesAndConditionsCoverageFormula extends NewLinesAndConditionsFormula {
-    public NewOverallLinesAndConditionsCoverageFormula(BatchReportReader batchReportReader) {
+
+    private static final NewCoverageOutputMetricKeys OUTPUT_METRIC_KEYS = new NewCoverageOutputMetricKeys(
+      CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY,
+      CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY);
+    private static final Iterable<Formula<?>> VIEWS_FORMULAS = intSumFormulas(OUTPUT_METRIC_KEYS);
+
+    private NewOverallLinesAndConditionsCoverageFormula(BatchReportReader batchReportReader) {
       super(batchReportReader,
         new NewCoverageInputMetricKeys(
           CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY, CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY),
-        new NewCoverageOutputMetricKeys(
-          CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY,
-          CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY));
+        OUTPUT_METRIC_KEYS);
+    }
+
+    public static Iterable<Formula<?>> from(@Nullable BatchReportReader batchReportReader) {
+      if (batchReportReader == null) {
+        return VIEWS_FORMULAS;
+      }
+      return Collections.<Formula<?>>singleton(new NewOverallLinesAndConditionsCoverageFormula(batchReportReader));
     }
   }
 
@@ -217,6 +274,18 @@ public class NewCoverageMeasuresStep implements ComputationStep {
     }
   }
 
+  /**
+   * Creates a List of {@link org.sonar.server.computation.formula.SumFormula.IntSumFormula} for each
+   * metric key of the specified {@link NewCoverageOutputMetricKeys} instance.
+   */
+  private static Iterable<Formula<?>> intSumFormulas(NewCoverageOutputMetricKeys outputMetricKeys) {
+    return ImmutableList.<Formula<?>>of(
+      new NewCoverageVariationSumFormula(outputMetricKeys.getNewLinesToCover()),
+      new NewCoverageVariationSumFormula(outputMetricKeys.getNewUncoveredLines()),
+      new NewCoverageVariationSumFormula(outputMetricKeys.getNewConditionsToCover()),
+      new NewCoverageVariationSumFormula(outputMetricKeys.getNewUncoveredConditions()));
+  }
+
   public static class NewLinesAndConditionsFormula implements Formula<NewCoverageCounter> {
     private final BatchReportReader batchReportReader;
     private final NewCoverageInputMetricKeys inputMetricKeys;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageMeasuresStepTest.java
deleted file mode 100644 (file)
index 3801da4..0000000
+++ /dev/null
@@ -1,605 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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.server.computation.step;
-
-import javax.annotation.Nullable;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.server.computation.batch.BatchReportReaderRule;
-import org.sonar.server.computation.batch.TreeRootHolderRule;
-import org.sonar.server.computation.component.Component;
-import org.sonar.server.computation.component.ReportComponent;
-import org.sonar.server.computation.component.FileAttributes;
-import org.sonar.server.computation.formula.coverage.LinesAndConditionsWithUncoveredMetricKeys;
-import org.sonar.server.computation.measure.Measure;
-import org.sonar.server.computation.measure.MeasureRepoEntry;
-import org.sonar.server.computation.measure.MeasureRepositoryRule;
-import org.sonar.server.computation.measure.MeasureVariations;
-import org.sonar.server.computation.metric.MetricRepositoryRule;
-import org.sonar.server.computation.period.Period;
-import org.sonar.server.computation.period.PeriodsHolderRule;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.guava.api.Assertions.assertThat;
-import static org.sonar.api.measures.CoreMetrics.CONDITIONS_BY_LINE_KEY;
-import static org.sonar.api.measures.CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY;
-import static org.sonar.api.measures.CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_LINES_TO_COVER_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_UNCOVERED_LINES_KEY;
-import static org.sonar.api.utils.DateUtils.parseDate;
-import static org.sonar.batch.protocol.output.BatchReport.Changesets;
-import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
-import static org.sonar.server.computation.component.Component.Type.FILE;
-import static org.sonar.server.computation.component.Component.Type.MODULE;
-import static org.sonar.server.computation.component.Component.Type.PROJECT;
-import static org.sonar.server.computation.component.ReportComponent.builder;
-import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
-import static org.sonar.server.computation.measure.MeasureRepoEntry.entryOf;
-import static org.sonar.server.computation.measure.MeasureRepoEntry.toEntries;
-import static org.sonar.server.computation.measure.MeasureVariations.newMeasureVariationsBuilder;
-
-public class NewCoverageMeasuresStepTest {
-
-  private static final int ROOT_REF = 1;
-  private static final int MODULE_REF = 11;
-  private static final int SUB_MODULE_REF = 111;
-  private static final int DIRECTORY_1_REF = 1111;
-  private static final int FILE_1_REF = 11111;
-  private static final int DIRECTORY_2_REF = 1112;
-  private static final int FILE_2_REF = 11121;
-  private static final int FILE_3_REF = 11122;
-
-  private static final ReportComponent MULTIPLE_FILES_TREE = builder(PROJECT, ROOT_REF)
-    .addChildren(
-      builder(MODULE, MODULE_REF)
-        .addChildren(
-          builder(MODULE, SUB_MODULE_REF)
-            .addChildren(
-              builder(DIRECTORY, DIRECTORY_1_REF)
-                .addChildren(
-                  builder(FILE, FILE_1_REF).build()
-                ).build(),
-              builder(DIRECTORY, DIRECTORY_2_REF)
-                .addChildren(
-                  builder(FILE, FILE_2_REF).build(),
-                  builder(FILE, FILE_3_REF).build()
-                ).build()
-            )
-            .build()
-        ).build()
-    ).build();
-
-  @Rule
-  public BatchReportReaderRule reportReader = new BatchReportReaderRule();
-  @Rule
-  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-  @Rule
-  public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
-  @Rule
-  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
-    .add(CoreMetrics.COVERAGE_LINE_HITS_DATA)
-    .add(CoreMetrics.CONDITIONS_BY_LINE)
-    .add(CoreMetrics.COVERED_CONDITIONS_BY_LINE)
-    .add(CoreMetrics.NEW_LINES_TO_COVER)
-    .add(CoreMetrics.NEW_UNCOVERED_LINES)
-    .add(CoreMetrics.NEW_CONDITIONS_TO_COVER)
-    .add(CoreMetrics.NEW_UNCOVERED_CONDITIONS)
-    .add(CoreMetrics.NEW_COVERAGE)
-    .add(CoreMetrics.NEW_BRANCH_COVERAGE)
-    .add(CoreMetrics.NEW_LINE_COVERAGE)
-
-    .add(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA)
-    .add(CoreMetrics.IT_CONDITIONS_BY_LINE)
-    .add(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE)
-    .add(CoreMetrics.NEW_IT_LINES_TO_COVER)
-    .add(CoreMetrics.NEW_IT_UNCOVERED_LINES)
-    .add(CoreMetrics.NEW_IT_CONDITIONS_TO_COVER)
-    .add(CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS)
-    .add(CoreMetrics.NEW_IT_COVERAGE)
-    .add(CoreMetrics.NEW_IT_BRANCH_COVERAGE)
-    .add(CoreMetrics.NEW_IT_LINE_COVERAGE)
-
-    .add(CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA)
-    .add(CoreMetrics.OVERALL_CONDITIONS_BY_LINE)
-    .add(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE)
-    .add(CoreMetrics.NEW_OVERALL_LINES_TO_COVER)
-    .add(CoreMetrics.NEW_OVERALL_UNCOVERED_LINES)
-    .add(CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER)
-    .add(CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS)
-    .add(CoreMetrics.NEW_OVERALL_COVERAGE)
-    .add(CoreMetrics.NEW_OVERALL_BRANCH_COVERAGE)
-    .add(CoreMetrics.NEW_OVERALL_LINE_COVERAGE)
-    ;
-  @Rule
-  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
-
-  private NewCoverageMeasuresStep underTest = new NewCoverageMeasuresStep(treeRootHolder, periodsHolder, reportReader,
-    measureRepository, metricRepository);
-  public static final ReportComponent FILE_COMPONENT = ReportComponent.builder(Component.Type.FILE, FILE_1_REF)
-    .setFileAttributes(new FileAttributes(false, null)).build();
-
-  @Before
-  public void setUp() {
-    periodsHolder.setPeriods(
-      new Period(2, "mode_p_1", null, parseDate("2009-12-25").getTime(), 1),
-      new Period(5, "mode_p_5", null, parseDate("2011-02-18").getTime(), 2));
-  }
-
-  @Test
-  public void no_measure_for_PROJECT_component() {
-    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, ROOT_REF).build());
-
-    underTest.execute();
-
-    assertThat(measureRepository.isEmpty()).isTrue();
-  }
-
-  @Test
-  public void no_measure_for_MODULE_component() {
-    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.MODULE, MODULE_REF).build());
-
-    underTest.execute();
-
-    assertThat(measureRepository.isEmpty()).isTrue();
-  }
-
-  @Test
-  public void no_measure_for_DIRECTORY_component() {
-    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.DIRECTORY, DIRECTORY_1_REF).build());
-
-    underTest.execute();
-
-    assertThat(measureRepository.isEmpty()).isTrue();
-  }
-
-  @Test
-  public void no_measure_for_unit_test_FILE_component() {
-    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.FILE, FILE_1_REF).setFileAttributes(new FileAttributes(true, null)).build());
-
-    underTest.execute();
-
-    assertThat(measureRepository.isEmpty()).isTrue();
-  }
-
-  @Test
-  public void no_measures_for_FILE_component_without_code() {
-    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.FILE, FILE_1_REF).setFileAttributes(new FileAttributes(false, null)).build());
-
-    underTest.execute();
-
-    assertThat(measureRepository.isEmpty()).isTrue();
-  }
-
-  @Test
-  public void verify_measure_of_condition_not_computed_if_there_is_none() {
-    treeRootHolder.setRoot(FILE_COMPONENT);
-    reportReader.putChangesets(Changesets.newBuilder()
-        .setComponentRef(FILE_COMPONENT.getReportAttributes().getRef())
-        .addChangeset(Changesets.Changeset.newBuilder().build())
-        .addChangeset(Changesets.Changeset.newBuilder()
-          .setDate(parseDate("2007-01-15").getTime())
-          .build())
-        .addChangeset(Changesets.Changeset.newBuilder()
-          .setDate(parseDate("2011-01-01").getTime())
-          .build())
-        .addChangesetIndexByLine(0)
-        .addChangesetIndexByLine(1)
-        .addChangesetIndexByLine(2)
-        .build()
-    );
-
-    underTest.execute();
-
-    assertThat(measureRepository.isEmpty()).isTrue();
-  }
-
-  @Test
-  public void verify_no_measure_when_nothing_has_changed() {
-    treeRootHolder.setRoot(FILE_COMPONENT);
-    reportReader.putChangesets(BatchReport.Changesets.newBuilder()
-      .setComponentRef(FILE_COMPONENT.getReportAttributes().getRef())
-      .addChangeset(Changesets.Changeset.newBuilder()
-        .setDate(parseDate("2008-08-02").getTime())
-        .build())
-      .addChangesetIndexByLine(0)
-      .addChangesetIndexByLine(0)
-      .addChangesetIndexByLine(0)
-      .addChangesetIndexByLine(0)
-      .build());
-    measureRepository.addRawMeasure(FILE_COMPONENT.getReportAttributes().getRef(), COVERAGE_LINE_HITS_DATA_KEY, newMeasureBuilder().create("2=1;3=1"));
-    measureRepository.addRawMeasure(FILE_COMPONENT.getReportAttributes().getRef(), CONDITIONS_BY_LINE_KEY, newMeasureBuilder().create("2=1"));
-    measureRepository.addRawMeasure(FILE_COMPONENT.getReportAttributes().getRef(), COVERED_CONDITIONS_BY_LINE_KEY, newMeasureBuilder().create("2=1"));
-
-    underTest.execute();
-
-    assertThat(measureRepository.getAddedRawMeasures(FILE_COMPONENT.getReportAttributes().getRef())).isEmpty();
-  }
-
-  @Test
-  public void no_measures_for_FILE_component_without_CoverageData() {
-    ReportComponent fileComponent = ReportComponent.builder(Component.Type.FILE, FILE_1_REF).setFileAttributes(new FileAttributes(false, null)).build();
-
-    treeRootHolder.setRoot(fileComponent);
-    reportReader.putChangesets(Changesets.newBuilder()
-      .setComponentRef(fileComponent.getReportAttributes().getRef())
-      .addChangeset(Changesets.Changeset.newBuilder()
-        .setDate(parseDate("2008-05-18").getTime())
-        .build())
-      .addChangesetIndexByLine(0)
-      .build());
-
-    underTest.execute();
-
-    assertThat(measureRepository.isEmpty()).isTrue();
-  }
-
-  @Test
-  public void verify_computation_of_measures_for_new_lines_for_FILE() {
-    String coverageLineHitsData = COVERAGE_LINE_HITS_DATA_KEY;
-    String newLinesToCover = NEW_LINES_TO_COVER_KEY;
-    String newUncoveredLines = NEW_UNCOVERED_LINES_KEY;
-    String newConditionsToCover = NEW_CONDITIONS_TO_COVER_KEY;
-    String newUncoveredConditions = NEW_UNCOVERED_CONDITIONS_KEY;
-
-    verify_computation_of_measures_for_new_lines(coverageLineHitsData,
-      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions);
-  }
-
-  @Test
-  public void verify_computation_of_measures_for_new_lines_for_IT_FILE() {
-    String coverageLineHitsData = CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY;
-    String newLinesToCover = CoreMetrics.NEW_IT_LINES_TO_COVER_KEY;
-    String newUncoveredLines = CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY;
-    String newConditionsToCover = CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY;
-    String newUncoveredConditions = CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY;
-
-    verify_computation_of_measures_for_new_lines(coverageLineHitsData,
-      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions);
-  }
-
-  @Test
-  public void verify_computation_of_measures_for_new_lines_for_Overall() {
-    String coverageLineHitsData = CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY;
-    String newLinesToCover = CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY;
-    String newUncoveredLines = CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY;
-    String newConditionsToCover = CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY;
-    String newUncoveredConditions = CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY;
-
-    verify_computation_of_measures_for_new_lines(coverageLineHitsData,
-      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions);
-  }
-
-  private void verify_computation_of_measures_for_new_lines(String coverageLineHitsData,
-                                                            String newLinesToCover, String newUncoveredLines, String newConditionsToCover, String newUncoveredConditions) {
-    treeRootHolder.setRoot(FILE_COMPONENT);
-    reportReader.putChangesets(Changesets.newBuilder()
-      .setComponentRef(FILE_COMPONENT.getReportAttributes().getRef())
-      .addChangeset(Changesets.Changeset.newBuilder().build())
-      .addChangeset(Changesets.Changeset.newBuilder()
-        .setDate(parseDate("2007-01-15").getTime())
-        .build())
-      .addChangeset(Changesets.Changeset.newBuilder()
-        .setDate(parseDate("2011-01-01").getTime())
-        .build())
-      .addChangesetIndexByLine(0)
-      .addChangesetIndexByLine(2) // line 2
-      .addChangesetIndexByLine(1) // line 3
-      .addChangesetIndexByLine(2) // line 4
-      .build());
-    measureRepository.addRawMeasure(FILE_COMPONENT.getReportAttributes().getRef(), coverageLineHitsData, newMeasureBuilder().create("2=0;3=2;4=3"));
-
-    underTest.execute();
-
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_COMPONENT.getReportAttributes().getRef()))).contains(
-      entryOf(newLinesToCover, createMeasure(2d, null)),
-      entryOf(newUncoveredLines, createMeasure(1d, null)),
-      entryOf(newConditionsToCover, createMeasure(0d, null)),
-      entryOf(newUncoveredConditions, createMeasure(0d, null))
-    );
-  }
-
-  @Test
-  public void verify_computation_of_measures_for_new_conditions_for_FILE() {
-    String coverageLineHitsData = COVERAGE_LINE_HITS_DATA_KEY;
-    String conditionsByLine = CONDITIONS_BY_LINE_KEY;
-    String coveredConditionsByLine = COVERED_CONDITIONS_BY_LINE_KEY;
-    String newLinesToCover = NEW_LINES_TO_COVER_KEY;
-    String newUncoveredLines = NEW_UNCOVERED_LINES_KEY;
-    String newConditionsToCover = NEW_CONDITIONS_TO_COVER_KEY;
-    String newUncoveredConditions = NEW_UNCOVERED_CONDITIONS_KEY;
-
-    verify_computation_of_measures_for_new_conditions(new MetricKeys(coverageLineHitsData, conditionsByLine, coveredConditionsByLine,
-      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions));
-  }
-
-  @Test
-  public void verify_computation_of_measures_for_new_conditions_for_IT_FILE() {
-    String coverageLineHitsData = CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY;
-    String conditionsByLine = CoreMetrics.IT_CONDITIONS_BY_LINE_KEY;
-    String coveredConditionsByLine = CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY;
-    String newLinesToCover = CoreMetrics.NEW_IT_LINES_TO_COVER_KEY;
-    String newUncoveredLines = CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY;
-    String newConditionsToCover = CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY;
-    String newUncoveredConditions = CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY;
-
-    verify_computation_of_measures_for_new_conditions(new MetricKeys(coverageLineHitsData, conditionsByLine, coveredConditionsByLine,
-      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions));
-  }
-
-  @Test
-  public void verify_computation_of_measures_for_new_conditions_Overall() {
-    String coverageLineHitsData = CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY;
-    String conditionsByLine = CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY;
-    String coveredConditionsByLine = CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY;
-    String newLinesToCover = CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY;
-    String newUncoveredLines = CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY;
-    String newConditionsToCover = CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY;
-    String newUncoveredConditions = CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY;
-
-    verify_computation_of_measures_for_new_conditions(new MetricKeys(coverageLineHitsData, conditionsByLine, coveredConditionsByLine,
-      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions));
-  }
-
-  @Test
-  public void verify_aggregation_of_measures_for_new_conditions() {
-    String coverageLineHitsData = CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY;
-    String conditionsByLine = CoreMetrics.IT_CONDITIONS_BY_LINE_KEY;
-    String coveredConditionsByLine = CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY;
-    String newLinesToCover = CoreMetrics.NEW_IT_LINES_TO_COVER_KEY;
-    String newUncoveredLines = CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY;
-    String newConditionsToCover = CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY;
-    String newUncoveredConditions = CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY;
-
-    MetricKeys metricKeys = new MetricKeys(coverageLineHitsData, conditionsByLine, coveredConditionsByLine,
-      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions);
-
-    treeRootHolder.setRoot(MULTIPLE_FILES_TREE);
-    defineChangeSetsAndMeasures(FILE_1_REF, metricKeys, new MeasureValues(3, 4, 1), new MeasureValues(0, 3, 2));
-    defineChangeSetsAndMeasures(FILE_2_REF, metricKeys, new MeasureValues(0, 14, 6), new MeasureValues(0, 13, 7));
-    defineChangeSetsAndMeasures(FILE_3_REF, metricKeys, new MeasureValues(3, 4, 1), new MeasureValues(1, 13, 7));
-
-    underTest.execute();
-
-    // files
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_1_REF))).contains(
-      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
-      entryOf(metricKeys.newUncoveredLines, createMeasure(3d, 2d)),
-      entryOf(metricKeys.newConditionsToCover, createMeasure(7d, 3d)),
-      entryOf(metricKeys.newUncoveredConditions, createMeasure(4d, 1d))
-    );
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_2_REF))).contains(
-      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
-      entryOf(metricKeys.newUncoveredLines, createMeasure(4d, 2d)),
-      entryOf(metricKeys.newConditionsToCover, createMeasure(27d, 13d)),
-      entryOf(metricKeys.newUncoveredConditions, createMeasure(14d, 6d))
-    );
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_3_REF))).contains(
-      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
-      entryOf(metricKeys.newUncoveredLines, createMeasure(2d, 1d)),
-      entryOf(metricKeys.newConditionsToCover, createMeasure(17d, 13d)),
-      entryOf(metricKeys.newUncoveredConditions, createMeasure(9d, 6d))
-    );
-    // directories
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(DIRECTORY_1_REF))).contains(
-      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
-      entryOf(metricKeys.newUncoveredLines, createMeasure(3d, 2d)),
-      entryOf(metricKeys.newConditionsToCover, createMeasure(7d, 3d)),
-      entryOf(metricKeys.newUncoveredConditions, createMeasure(4d, 1d))
-    );
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(DIRECTORY_2_REF))).contains(
-      entryOf(metricKeys.newLinesToCover, createMeasure(10d, 6d)),
-      entryOf(metricKeys.newUncoveredLines, createMeasure(6d, 3d)),
-      entryOf(metricKeys.newConditionsToCover, createMeasure(44d, 26d)),
-      entryOf(metricKeys.newUncoveredConditions, createMeasure(23d, 12d))
-    );
-    // submodule
-    MeasureRepoEntry[] repoEntriesFromSubModuleUp = {entryOf(metricKeys.newLinesToCover, createMeasure(15d, 9d)),
-      entryOf(metricKeys.newUncoveredLines, createMeasure(9d, 5d)),
-      entryOf(metricKeys.newConditionsToCover, createMeasure(51d, 29d)),
-      entryOf(metricKeys.newUncoveredConditions, createMeasure(27d, 13d))};
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(SUB_MODULE_REF))).contains(repoEntriesFromSubModuleUp);
-    // module
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(MODULE_REF))).contains(repoEntriesFromSubModuleUp);
-    // project
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(ROOT_REF))).contains(repoEntriesFromSubModuleUp);
-  }
-
-  @Test
-  public void verify_aggregates_variations_for_new_code_line_and_branch_Coverage() {
-    LinesAndConditionsWithUncoveredMetricKeys metricKeys = new LinesAndConditionsWithUncoveredMetricKeys(
-      CoreMetrics.NEW_LINES_TO_COVER_KEY, CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY,
-      CoreMetrics.NEW_UNCOVERED_LINES_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY
-    );
-    String codeCoverageKey = CoreMetrics.NEW_COVERAGE_KEY;
-    String lineCoverageKey = CoreMetrics.NEW_LINE_COVERAGE_KEY;
-    String branchCoverageKey = CoreMetrics.NEW_BRANCH_COVERAGE_KEY;
-
-    verify_aggregates_variations(metricKeys, codeCoverageKey, lineCoverageKey, branchCoverageKey);
-  }
-
-  @Test
-  public void verify_aggregates_variations_for_new_IT_code_line_and_branch_Coverage() {
-    LinesAndConditionsWithUncoveredMetricKeys metricKeys = new LinesAndConditionsWithUncoveredMetricKeys(
-      CoreMetrics.NEW_IT_LINES_TO_COVER_KEY, CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY,
-      CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY
-    );
-    String codeCoverageKey = CoreMetrics.NEW_IT_COVERAGE_KEY;
-    String lineCoverageKey = CoreMetrics.NEW_IT_LINE_COVERAGE_KEY;
-    String branchCoverageKey = CoreMetrics.NEW_IT_BRANCH_COVERAGE_KEY;
-
-    verify_aggregates_variations(metricKeys, codeCoverageKey, lineCoverageKey, branchCoverageKey);
-  }
-
-  @Test
-  public void verify_aggregates_variations_for_new_Overall_code_line_and_branch_Coverage() {
-    LinesAndConditionsWithUncoveredMetricKeys metricKeys = new LinesAndConditionsWithUncoveredMetricKeys(
-      CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY,
-      CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY
-    );
-    String codeCoverageKey = CoreMetrics.NEW_OVERALL_COVERAGE_KEY;
-    String lineCoverageKey = CoreMetrics.NEW_OVERALL_LINE_COVERAGE_KEY;
-    String branchCoverageKey = CoreMetrics.NEW_OVERALL_BRANCH_COVERAGE_KEY;
-
-    verify_aggregates_variations(metricKeys, codeCoverageKey, lineCoverageKey, branchCoverageKey);
-  }
-
-  private void verify_aggregates_variations(LinesAndConditionsWithUncoveredMetricKeys metricKeys, String codeCoverageKey, String lineCoverageKey, String branchCoverageKey) {
-    treeRootHolder.setRoot(MULTIPLE_FILES_TREE);
-    measureRepository
-      .addRawMeasure(FILE_1_REF, metricKeys.getLines(), createMeasure(3000d, 2000d))
-      .addRawMeasure(FILE_1_REF, metricKeys.getConditions(), createMeasure(300d, 400d))
-      .addRawMeasure(FILE_1_REF, metricKeys.getUncoveredLines(), createMeasure(30d, 200d))
-      .addRawMeasure(FILE_1_REF, metricKeys.getUncoveredConditions(), createMeasure(9d, 16d))
-
-      .addRawMeasure(FILE_2_REF, metricKeys.getLines(), createMeasure(2000d, 3000d))
-      .addRawMeasure(FILE_2_REF, metricKeys.getConditions(), createMeasure(400d, 300d))
-      .addRawMeasure(FILE_2_REF, metricKeys.getUncoveredLines(), createMeasure(200d, 30d))
-      .addRawMeasure(FILE_2_REF, metricKeys.getUncoveredConditions(), createMeasure(16d, 9d));
-
-    underTest.execute();
-
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_1_REF))).containsOnly(
-      entryOf(codeCoverageKey, createMeasure(98.8d, 91d)),
-      entryOf(lineCoverageKey, createMeasure(99d, 90d)),
-      entryOf(branchCoverageKey, createMeasure(97d, 96d))
-    );
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_2_REF))).containsOnly(
-      entryOf(codeCoverageKey, createMeasure(91d, 98.8d)),
-      entryOf(lineCoverageKey, createMeasure(90d, 99d)),
-      entryOf(branchCoverageKey, createMeasure(96d, 97d))
-    );
-    assertThat(measureRepository.getAddedRawMeasures(FILE_3_REF)).isEmpty();
-
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(DIRECTORY_1_REF))).containsOnly(
-      entryOf(codeCoverageKey, createMeasure(98.8d, 91d)),
-      entryOf(lineCoverageKey, createMeasure(99d, 90d)),
-      entryOf(branchCoverageKey, createMeasure(97d, 96d))
-    );
-
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(DIRECTORY_2_REF))).containsOnly(
-      entryOf(codeCoverageKey, createMeasure(91d, 98.8d)),
-      entryOf(lineCoverageKey, createMeasure(90d, 99d)),
-      entryOf(branchCoverageKey, createMeasure(96d, 97d))
-    );
-
-    MeasureRepoEntry[] modulesAndProjectEntries = {
-      entryOf(codeCoverageKey, createMeasure(95.5d, 95.5d)),
-      entryOf(lineCoverageKey, createMeasure(95.4d, 95.4d)),
-      entryOf(branchCoverageKey, createMeasure(96.4d, 96.4d))
-    };
-
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(SUB_MODULE_REF))).containsOnly(modulesAndProjectEntries);
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(MODULE_REF))).containsOnly(modulesAndProjectEntries);
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(ROOT_REF))).containsOnly(modulesAndProjectEntries);
-  }
-
-  private void defineChangeSetsAndMeasures(int componentRef, MetricKeys metricKeys, MeasureValues line4, MeasureValues line6) {
-    reportReader.putChangesets(Changesets.newBuilder()
-      .setComponentRef(componentRef)
-      .addChangeset(Changesets.Changeset.newBuilder().build())
-      .addChangeset(Changesets.Changeset.newBuilder()
-        .setDate(parseDate("2007-01-15").getTime())
-        .build())
-      .addChangeset(Changesets.Changeset.newBuilder()
-        .setDate(parseDate("2011-01-01").getTime())
-        .build())
-      .addChangeset(Changesets.Changeset.newBuilder()
-        .setDate(parseDate("2012-02-23").getTime())
-        .build())
-      .addChangesetIndexByLine(0)
-      .addChangesetIndexByLine(2) // line 2
-      .addChangesetIndexByLine(1) // line 3
-      .addChangesetIndexByLine(2) // line 4
-      .addChangesetIndexByLine(3) // line 5
-      .addChangesetIndexByLine(3) // line 6
-      .addChangesetIndexByLine(3) // line 7
-      .build());
-    measureRepository.addRawMeasure(componentRef, metricKeys.coverageLineHitsData, newMeasureBuilder().create("2=0;3=2;4=" + line4.lineHits + ";5=1;6=" + line6.lineHits + ";7=0"));
-    measureRepository.addRawMeasure(componentRef, metricKeys.conditionsByLine, newMeasureBuilder().create("4=" + line4.coveredConditions + ";6=" + line6.coveredConditions));
-    measureRepository.addRawMeasure(componentRef, metricKeys.coveredConditionsByLine,
-      newMeasureBuilder().create("4=" + line4.uncoveredConditions + ";6=" + line6.uncoveredConditions));
-  }
-
-  private static final class MetricKeys {
-    private final String coverageLineHitsData;
-    private final String conditionsByLine;
-    private final String coveredConditionsByLine;
-    private final String newLinesToCover;
-    private final String newUncoveredLines;
-    private final String newConditionsToCover;
-    private final String newUncoveredConditions;
-
-    public MetricKeys(String coverageLineHitsData, String conditionsByLine, String coveredConditionsByLine,
-                      String newLinesToCover, String newUncoveredLines, String newConditionsToCover, String newUncoveredConditions) {
-      this.coverageLineHitsData = coverageLineHitsData;
-      this.conditionsByLine = conditionsByLine;
-      this.coveredConditionsByLine = coveredConditionsByLine;
-      this.newLinesToCover = newLinesToCover;
-      this.newUncoveredLines = newUncoveredLines;
-      this.newConditionsToCover = newConditionsToCover;
-      this.newUncoveredConditions = newUncoveredConditions;
-    }
-  }
-
-  private static final class MeasureValues {
-    private final int lineHits;
-    private final int coveredConditions;
-    private final int uncoveredConditions;
-
-    public MeasureValues(int lineHits, int coveredConditions, int uncoveredConditions) {
-      this.lineHits = lineHits;
-      this.coveredConditions = coveredConditions;
-      this.uncoveredConditions = uncoveredConditions;
-    }
-  }
-
-  private void verify_computation_of_measures_for_new_conditions(MetricKeys metricKeys) {
-    treeRootHolder.setRoot(FILE_COMPONENT);
-    defineChangeSetsAndMeasures(FILE_COMPONENT.getReportAttributes().getRef(), metricKeys, new MeasureValues(3, 4, 1), new MeasureValues(0, 3, 2));
-
-    underTest.execute();
-
-    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_COMPONENT.getReportAttributes().getRef()))).contains(
-      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
-      entryOf(metricKeys.newUncoveredLines, createMeasure(3d, 2d)),
-      entryOf(metricKeys.newConditionsToCover, createMeasure(7d, 3d)),
-      entryOf(metricKeys.newUncoveredConditions, createMeasure(4d, 1d))
-    );
-  }
-
-  private static Measure createMeasure(@Nullable Double variationPeriod2, @Nullable Double variationPeriod5) {
-    MeasureVariations.Builder variationBuilder = newMeasureVariationsBuilder();
-    if (variationPeriod2 != null) {
-      variationBuilder.setVariation(new Period(2, "", null, 1L, 2L), variationPeriod2);
-    }
-    if (variationPeriod5 != null) {
-      variationBuilder.setVariation(new Period(5, "", null, 1L, 2L), variationPeriod5);
-    }
-    return newMeasureBuilder()
-      .setVariations(variationBuilder.build())
-      .createNoValue();
-  }
-
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportNewCoverageMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportNewCoverageMeasuresStepTest.java
new file mode 100644 (file)
index 0000000..35c66c7
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.server.computation.step;
+
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.server.computation.batch.BatchReportReaderRule;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.ReportComponent;
+import org.sonar.server.computation.component.FileAttributes;
+import org.sonar.server.computation.formula.coverage.LinesAndConditionsWithUncoveredMetricKeys;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepoEntry;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.measure.MeasureVariations;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.period.Period;
+import org.sonar.server.computation.period.PeriodsHolderRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.sonar.api.measures.CoreMetrics.CONDITIONS_BY_LINE_KEY;
+import static org.sonar.api.measures.CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY;
+import static org.sonar.api.measures.CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_LINES_TO_COVER_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_UNCOVERED_LINES_KEY;
+import static org.sonar.api.utils.DateUtils.parseDate;
+import static org.sonar.batch.protocol.output.BatchReport.Changesets;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.Component.Type.MODULE;
+import static org.sonar.server.computation.component.Component.Type.PROJECT;
+import static org.sonar.server.computation.component.ReportComponent.builder;
+import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
+import static org.sonar.server.computation.measure.MeasureRepoEntry.entryOf;
+import static org.sonar.server.computation.measure.MeasureRepoEntry.toEntries;
+import static org.sonar.server.computation.measure.MeasureVariations.newMeasureVariationsBuilder;
+
+public class ReportNewCoverageMeasuresStepTest {
+
+  private static final int ROOT_REF = 1;
+  private static final int MODULE_REF = 11;
+  private static final int SUB_MODULE_REF = 111;
+  private static final int DIRECTORY_1_REF = 1111;
+  private static final int FILE_1_REF = 11111;
+  private static final int DIRECTORY_2_REF = 1112;
+  private static final int FILE_2_REF = 11121;
+  private static final int FILE_3_REF = 11122;
+
+  private static final ReportComponent MULTIPLE_FILES_TREE = builder(PROJECT, ROOT_REF)
+    .addChildren(
+      builder(MODULE, MODULE_REF)
+        .addChildren(
+          builder(MODULE, SUB_MODULE_REF)
+            .addChildren(
+              builder(DIRECTORY, DIRECTORY_1_REF)
+                .addChildren(
+                  builder(FILE, FILE_1_REF).build()
+                ).build(),
+              builder(DIRECTORY, DIRECTORY_2_REF)
+                .addChildren(
+                  builder(FILE, FILE_2_REF).build(),
+                  builder(FILE, FILE_3_REF).build()
+                ).build()
+            )
+            .build()
+        ).build()
+    ).build();
+
+  @Rule
+  public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+  @Rule
+  public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
+  @Rule
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+    .add(CoreMetrics.COVERAGE_LINE_HITS_DATA)
+    .add(CoreMetrics.CONDITIONS_BY_LINE)
+    .add(CoreMetrics.COVERED_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.NEW_LINES_TO_COVER)
+    .add(CoreMetrics.NEW_UNCOVERED_LINES)
+    .add(CoreMetrics.NEW_CONDITIONS_TO_COVER)
+    .add(CoreMetrics.NEW_UNCOVERED_CONDITIONS)
+    .add(CoreMetrics.NEW_COVERAGE)
+    .add(CoreMetrics.NEW_BRANCH_COVERAGE)
+    .add(CoreMetrics.NEW_LINE_COVERAGE)
+
+    .add(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA)
+    .add(CoreMetrics.IT_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.NEW_IT_LINES_TO_COVER)
+    .add(CoreMetrics.NEW_IT_UNCOVERED_LINES)
+    .add(CoreMetrics.NEW_IT_CONDITIONS_TO_COVER)
+    .add(CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS)
+    .add(CoreMetrics.NEW_IT_COVERAGE)
+    .add(CoreMetrics.NEW_IT_BRANCH_COVERAGE)
+    .add(CoreMetrics.NEW_IT_LINE_COVERAGE)
+
+    .add(CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA)
+    .add(CoreMetrics.OVERALL_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.NEW_OVERALL_LINES_TO_COVER)
+    .add(CoreMetrics.NEW_OVERALL_UNCOVERED_LINES)
+    .add(CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER)
+    .add(CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS)
+    .add(CoreMetrics.NEW_OVERALL_COVERAGE)
+    .add(CoreMetrics.NEW_OVERALL_BRANCH_COVERAGE)
+    .add(CoreMetrics.NEW_OVERALL_LINE_COVERAGE)
+    ;
+  @Rule
+  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+  private NewCoverageMeasuresStep underTest = new NewCoverageMeasuresStep(treeRootHolder, periodsHolder, reportReader,
+    measureRepository, metricRepository);
+  public static final ReportComponent FILE_COMPONENT = ReportComponent.builder(Component.Type.FILE, FILE_1_REF)
+    .setFileAttributes(new FileAttributes(false, null)).build();
+
+  @Before
+  public void setUp() {
+    periodsHolder.setPeriods(
+      new Period(2, "mode_p_1", null, parseDate("2009-12-25").getTime(), 1),
+      new Period(5, "mode_p_5", null, parseDate("2011-02-18").getTime(), 2));
+  }
+
+  @Test
+  public void no_measure_for_PROJECT_component() {
+    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, ROOT_REF).build());
+
+    underTest.execute();
+
+    assertThat(measureRepository.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void no_measure_for_MODULE_component() {
+    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.MODULE, MODULE_REF).build());
+
+    underTest.execute();
+
+    assertThat(measureRepository.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void no_measure_for_DIRECTORY_component() {
+    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.DIRECTORY, DIRECTORY_1_REF).build());
+
+    underTest.execute();
+
+    assertThat(measureRepository.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void no_measure_for_unit_test_FILE_component() {
+    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.FILE, FILE_1_REF).setFileAttributes(new FileAttributes(true, null)).build());
+
+    underTest.execute();
+
+    assertThat(measureRepository.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void no_measures_for_FILE_component_without_code() {
+    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.FILE, FILE_1_REF).setFileAttributes(new FileAttributes(false, null)).build());
+
+    underTest.execute();
+
+    assertThat(measureRepository.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void verify_measure_of_condition_not_computed_if_there_is_none() {
+    treeRootHolder.setRoot(FILE_COMPONENT);
+    reportReader.putChangesets(Changesets.newBuilder()
+        .setComponentRef(FILE_COMPONENT.getReportAttributes().getRef())
+        .addChangeset(Changesets.Changeset.newBuilder().build())
+        .addChangeset(Changesets.Changeset.newBuilder()
+          .setDate(parseDate("2007-01-15").getTime())
+          .build())
+        .addChangeset(Changesets.Changeset.newBuilder()
+          .setDate(parseDate("2011-01-01").getTime())
+          .build())
+        .addChangesetIndexByLine(0)
+        .addChangesetIndexByLine(1)
+        .addChangesetIndexByLine(2)
+        .build()
+    );
+
+    underTest.execute();
+
+    assertThat(measureRepository.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void verify_no_measure_when_nothing_has_changed() {
+    treeRootHolder.setRoot(FILE_COMPONENT);
+    reportReader.putChangesets(BatchReport.Changesets.newBuilder()
+      .setComponentRef(FILE_COMPONENT.getReportAttributes().getRef())
+      .addChangeset(Changesets.Changeset.newBuilder()
+        .setDate(parseDate("2008-08-02").getTime())
+        .build())
+      .addChangesetIndexByLine(0)
+      .addChangesetIndexByLine(0)
+      .addChangesetIndexByLine(0)
+      .addChangesetIndexByLine(0)
+      .build());
+    measureRepository.addRawMeasure(FILE_COMPONENT.getReportAttributes().getRef(), COVERAGE_LINE_HITS_DATA_KEY, newMeasureBuilder().create("2=1;3=1"));
+    measureRepository.addRawMeasure(FILE_COMPONENT.getReportAttributes().getRef(), CONDITIONS_BY_LINE_KEY, newMeasureBuilder().create("2=1"));
+    measureRepository.addRawMeasure(FILE_COMPONENT.getReportAttributes().getRef(), COVERED_CONDITIONS_BY_LINE_KEY, newMeasureBuilder().create("2=1"));
+
+    underTest.execute();
+
+    assertThat(measureRepository.getAddedRawMeasures(FILE_COMPONENT.getReportAttributes().getRef())).isEmpty();
+  }
+
+  @Test
+  public void no_measures_for_FILE_component_without_CoverageData() {
+    ReportComponent fileComponent = ReportComponent.builder(Component.Type.FILE, FILE_1_REF).setFileAttributes(new FileAttributes(false, null)).build();
+
+    treeRootHolder.setRoot(fileComponent);
+    reportReader.putChangesets(Changesets.newBuilder()
+      .setComponentRef(fileComponent.getReportAttributes().getRef())
+      .addChangeset(Changesets.Changeset.newBuilder()
+        .setDate(parseDate("2008-05-18").getTime())
+        .build())
+      .addChangesetIndexByLine(0)
+      .build());
+
+    underTest.execute();
+
+    assertThat(measureRepository.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void verify_computation_of_measures_for_new_lines_for_FILE() {
+    String coverageLineHitsData = COVERAGE_LINE_HITS_DATA_KEY;
+    String newLinesToCover = NEW_LINES_TO_COVER_KEY;
+    String newUncoveredLines = NEW_UNCOVERED_LINES_KEY;
+    String newConditionsToCover = NEW_CONDITIONS_TO_COVER_KEY;
+    String newUncoveredConditions = NEW_UNCOVERED_CONDITIONS_KEY;
+
+    verify_computation_of_measures_for_new_lines(coverageLineHitsData,
+      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions);
+  }
+
+  @Test
+  public void verify_computation_of_measures_for_new_lines_for_IT_FILE() {
+    String coverageLineHitsData = CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY;
+    String newLinesToCover = CoreMetrics.NEW_IT_LINES_TO_COVER_KEY;
+    String newUncoveredLines = CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY;
+    String newConditionsToCover = CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY;
+    String newUncoveredConditions = CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY;
+
+    verify_computation_of_measures_for_new_lines(coverageLineHitsData,
+      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions);
+  }
+
+  @Test
+  public void verify_computation_of_measures_for_new_lines_for_Overall() {
+    String coverageLineHitsData = CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY;
+    String newLinesToCover = CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY;
+    String newUncoveredLines = CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY;
+    String newConditionsToCover = CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY;
+    String newUncoveredConditions = CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY;
+
+    verify_computation_of_measures_for_new_lines(coverageLineHitsData,
+      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions);
+  }
+
+  private void verify_computation_of_measures_for_new_lines(String coverageLineHitsData,
+                                                            String newLinesToCover, String newUncoveredLines, String newConditionsToCover, String newUncoveredConditions) {
+    treeRootHolder.setRoot(FILE_COMPONENT);
+    reportReader.putChangesets(Changesets.newBuilder()
+      .setComponentRef(FILE_COMPONENT.getReportAttributes().getRef())
+      .addChangeset(Changesets.Changeset.newBuilder().build())
+      .addChangeset(Changesets.Changeset.newBuilder()
+        .setDate(parseDate("2007-01-15").getTime())
+        .build())
+      .addChangeset(Changesets.Changeset.newBuilder()
+        .setDate(parseDate("2011-01-01").getTime())
+        .build())
+      .addChangesetIndexByLine(0)
+      .addChangesetIndexByLine(2) // line 2
+      .addChangesetIndexByLine(1) // line 3
+      .addChangesetIndexByLine(2) // line 4
+      .build());
+    measureRepository.addRawMeasure(FILE_COMPONENT.getReportAttributes().getRef(), coverageLineHitsData, newMeasureBuilder().create("2=0;3=2;4=3"));
+
+    underTest.execute();
+
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_COMPONENT.getReportAttributes().getRef()))).contains(
+      entryOf(newLinesToCover, createMeasure(2d, null)),
+      entryOf(newUncoveredLines, createMeasure(1d, null)),
+      entryOf(newConditionsToCover, createMeasure(0d, null)),
+      entryOf(newUncoveredConditions, createMeasure(0d, null))
+    );
+  }
+
+  @Test
+  public void verify_computation_of_measures_for_new_conditions_for_FILE() {
+    String coverageLineHitsData = COVERAGE_LINE_HITS_DATA_KEY;
+    String conditionsByLine = CONDITIONS_BY_LINE_KEY;
+    String coveredConditionsByLine = COVERED_CONDITIONS_BY_LINE_KEY;
+    String newLinesToCover = NEW_LINES_TO_COVER_KEY;
+    String newUncoveredLines = NEW_UNCOVERED_LINES_KEY;
+    String newConditionsToCover = NEW_CONDITIONS_TO_COVER_KEY;
+    String newUncoveredConditions = NEW_UNCOVERED_CONDITIONS_KEY;
+
+    verify_computation_of_measures_for_new_conditions(new MetricKeys(coverageLineHitsData, conditionsByLine, coveredConditionsByLine,
+      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions));
+  }
+
+  @Test
+  public void verify_computation_of_measures_for_new_conditions_for_IT_FILE() {
+    String coverageLineHitsData = CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY;
+    String conditionsByLine = CoreMetrics.IT_CONDITIONS_BY_LINE_KEY;
+    String coveredConditionsByLine = CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY;
+    String newLinesToCover = CoreMetrics.NEW_IT_LINES_TO_COVER_KEY;
+    String newUncoveredLines = CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY;
+    String newConditionsToCover = CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY;
+    String newUncoveredConditions = CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY;
+
+    verify_computation_of_measures_for_new_conditions(new MetricKeys(coverageLineHitsData, conditionsByLine, coveredConditionsByLine,
+      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions));
+  }
+
+  @Test
+  public void verify_computation_of_measures_for_new_conditions_Overall() {
+    String coverageLineHitsData = CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY;
+    String conditionsByLine = CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY;
+    String coveredConditionsByLine = CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY;
+    String newLinesToCover = CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY;
+    String newUncoveredLines = CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY;
+    String newConditionsToCover = CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY;
+    String newUncoveredConditions = CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY;
+
+    verify_computation_of_measures_for_new_conditions(new MetricKeys(coverageLineHitsData, conditionsByLine, coveredConditionsByLine,
+      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions));
+  }
+
+  @Test
+  public void verify_aggregation_of_measures_for_new_conditions() {
+    String coverageLineHitsData = CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY;
+    String conditionsByLine = CoreMetrics.IT_CONDITIONS_BY_LINE_KEY;
+    String coveredConditionsByLine = CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY;
+    String newLinesToCover = CoreMetrics.NEW_IT_LINES_TO_COVER_KEY;
+    String newUncoveredLines = CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY;
+    String newConditionsToCover = CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY;
+    String newUncoveredConditions = CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY;
+
+    MetricKeys metricKeys = new MetricKeys(coverageLineHitsData, conditionsByLine, coveredConditionsByLine,
+      newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions);
+
+    treeRootHolder.setRoot(MULTIPLE_FILES_TREE);
+    defineChangeSetsAndMeasures(FILE_1_REF, metricKeys, new MeasureValues(3, 4, 1), new MeasureValues(0, 3, 2));
+    defineChangeSetsAndMeasures(FILE_2_REF, metricKeys, new MeasureValues(0, 14, 6), new MeasureValues(0, 13, 7));
+    defineChangeSetsAndMeasures(FILE_3_REF, metricKeys, new MeasureValues(3, 4, 1), new MeasureValues(1, 13, 7));
+
+    underTest.execute();
+
+    // files
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_1_REF))).contains(
+      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
+      entryOf(metricKeys.newUncoveredLines, createMeasure(3d, 2d)),
+      entryOf(metricKeys.newConditionsToCover, createMeasure(7d, 3d)),
+      entryOf(metricKeys.newUncoveredConditions, createMeasure(4d, 1d))
+    );
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_2_REF))).contains(
+      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
+      entryOf(metricKeys.newUncoveredLines, createMeasure(4d, 2d)),
+      entryOf(metricKeys.newConditionsToCover, createMeasure(27d, 13d)),
+      entryOf(metricKeys.newUncoveredConditions, createMeasure(14d, 6d))
+    );
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_3_REF))).contains(
+      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
+      entryOf(metricKeys.newUncoveredLines, createMeasure(2d, 1d)),
+      entryOf(metricKeys.newConditionsToCover, createMeasure(17d, 13d)),
+      entryOf(metricKeys.newUncoveredConditions, createMeasure(9d, 6d))
+    );
+    // directories
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(DIRECTORY_1_REF))).contains(
+      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
+      entryOf(metricKeys.newUncoveredLines, createMeasure(3d, 2d)),
+      entryOf(metricKeys.newConditionsToCover, createMeasure(7d, 3d)),
+      entryOf(metricKeys.newUncoveredConditions, createMeasure(4d, 1d))
+    );
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(DIRECTORY_2_REF))).contains(
+      entryOf(metricKeys.newLinesToCover, createMeasure(10d, 6d)),
+      entryOf(metricKeys.newUncoveredLines, createMeasure(6d, 3d)),
+      entryOf(metricKeys.newConditionsToCover, createMeasure(44d, 26d)),
+      entryOf(metricKeys.newUncoveredConditions, createMeasure(23d, 12d))
+    );
+    // submodule
+    MeasureRepoEntry[] repoEntriesFromSubModuleUp = {entryOf(metricKeys.newLinesToCover, createMeasure(15d, 9d)),
+      entryOf(metricKeys.newUncoveredLines, createMeasure(9d, 5d)),
+      entryOf(metricKeys.newConditionsToCover, createMeasure(51d, 29d)),
+      entryOf(metricKeys.newUncoveredConditions, createMeasure(27d, 13d))};
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(SUB_MODULE_REF))).contains(repoEntriesFromSubModuleUp);
+    // module
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(MODULE_REF))).contains(repoEntriesFromSubModuleUp);
+    // project
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(ROOT_REF))).contains(repoEntriesFromSubModuleUp);
+  }
+
+  @Test
+  public void verify_aggregates_variations_for_new_code_line_and_branch_Coverage() {
+    LinesAndConditionsWithUncoveredMetricKeys metricKeys = new LinesAndConditionsWithUncoveredMetricKeys(
+      CoreMetrics.NEW_LINES_TO_COVER_KEY, CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY,
+      CoreMetrics.NEW_UNCOVERED_LINES_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY
+    );
+    String codeCoverageKey = CoreMetrics.NEW_COVERAGE_KEY;
+    String lineCoverageKey = CoreMetrics.NEW_LINE_COVERAGE_KEY;
+    String branchCoverageKey = CoreMetrics.NEW_BRANCH_COVERAGE_KEY;
+
+    verify_aggregates_variations(metricKeys, codeCoverageKey, lineCoverageKey, branchCoverageKey);
+  }
+
+  @Test
+  public void verify_aggregates_variations_for_new_IT_code_line_and_branch_Coverage() {
+    LinesAndConditionsWithUncoveredMetricKeys metricKeys = new LinesAndConditionsWithUncoveredMetricKeys(
+      CoreMetrics.NEW_IT_LINES_TO_COVER_KEY, CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY,
+      CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY
+    );
+    String codeCoverageKey = CoreMetrics.NEW_IT_COVERAGE_KEY;
+    String lineCoverageKey = CoreMetrics.NEW_IT_LINE_COVERAGE_KEY;
+    String branchCoverageKey = CoreMetrics.NEW_IT_BRANCH_COVERAGE_KEY;
+
+    verify_aggregates_variations(metricKeys, codeCoverageKey, lineCoverageKey, branchCoverageKey);
+  }
+
+  @Test
+  public void verify_aggregates_variations_for_new_Overall_code_line_and_branch_Coverage() {
+    LinesAndConditionsWithUncoveredMetricKeys metricKeys = new LinesAndConditionsWithUncoveredMetricKeys(
+      CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY,
+      CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY
+    );
+    String codeCoverageKey = CoreMetrics.NEW_OVERALL_COVERAGE_KEY;
+    String lineCoverageKey = CoreMetrics.NEW_OVERALL_LINE_COVERAGE_KEY;
+    String branchCoverageKey = CoreMetrics.NEW_OVERALL_BRANCH_COVERAGE_KEY;
+
+    verify_aggregates_variations(metricKeys, codeCoverageKey, lineCoverageKey, branchCoverageKey);
+  }
+
+  private void verify_aggregates_variations(LinesAndConditionsWithUncoveredMetricKeys metricKeys, String codeCoverageKey, String lineCoverageKey, String branchCoverageKey) {
+    treeRootHolder.setRoot(MULTIPLE_FILES_TREE);
+    measureRepository
+      .addRawMeasure(FILE_1_REF, metricKeys.getLines(), createMeasure(3000d, 2000d))
+      .addRawMeasure(FILE_1_REF, metricKeys.getConditions(), createMeasure(300d, 400d))
+      .addRawMeasure(FILE_1_REF, metricKeys.getUncoveredLines(), createMeasure(30d, 200d))
+      .addRawMeasure(FILE_1_REF, metricKeys.getUncoveredConditions(), createMeasure(9d, 16d))
+
+      .addRawMeasure(FILE_2_REF, metricKeys.getLines(), createMeasure(2000d, 3000d))
+      .addRawMeasure(FILE_2_REF, metricKeys.getConditions(), createMeasure(400d, 300d))
+      .addRawMeasure(FILE_2_REF, metricKeys.getUncoveredLines(), createMeasure(200d, 30d))
+      .addRawMeasure(FILE_2_REF, metricKeys.getUncoveredConditions(), createMeasure(16d, 9d));
+
+    underTest.execute();
+
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_1_REF))).containsOnly(
+      entryOf(codeCoverageKey, createMeasure(98.8d, 91d)),
+      entryOf(lineCoverageKey, createMeasure(99d, 90d)),
+      entryOf(branchCoverageKey, createMeasure(97d, 96d))
+    );
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_2_REF))).containsOnly(
+      entryOf(codeCoverageKey, createMeasure(91d, 98.8d)),
+      entryOf(lineCoverageKey, createMeasure(90d, 99d)),
+      entryOf(branchCoverageKey, createMeasure(96d, 97d))
+    );
+    assertThat(measureRepository.getAddedRawMeasures(FILE_3_REF)).isEmpty();
+
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(DIRECTORY_1_REF))).containsOnly(
+      entryOf(codeCoverageKey, createMeasure(98.8d, 91d)),
+      entryOf(lineCoverageKey, createMeasure(99d, 90d)),
+      entryOf(branchCoverageKey, createMeasure(97d, 96d))
+    );
+
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(DIRECTORY_2_REF))).containsOnly(
+      entryOf(codeCoverageKey, createMeasure(91d, 98.8d)),
+      entryOf(lineCoverageKey, createMeasure(90d, 99d)),
+      entryOf(branchCoverageKey, createMeasure(96d, 97d))
+    );
+
+    MeasureRepoEntry[] modulesAndProjectEntries = {
+      entryOf(codeCoverageKey, createMeasure(95.5d, 95.5d)),
+      entryOf(lineCoverageKey, createMeasure(95.4d, 95.4d)),
+      entryOf(branchCoverageKey, createMeasure(96.4d, 96.4d))
+    };
+
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(SUB_MODULE_REF))).containsOnly(modulesAndProjectEntries);
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(MODULE_REF))).containsOnly(modulesAndProjectEntries);
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(ROOT_REF))).containsOnly(modulesAndProjectEntries);
+  }
+
+  private void defineChangeSetsAndMeasures(int componentRef, MetricKeys metricKeys, MeasureValues line4, MeasureValues line6) {
+    reportReader.putChangesets(Changesets.newBuilder()
+      .setComponentRef(componentRef)
+      .addChangeset(Changesets.Changeset.newBuilder().build())
+      .addChangeset(Changesets.Changeset.newBuilder()
+        .setDate(parseDate("2007-01-15").getTime())
+        .build())
+      .addChangeset(Changesets.Changeset.newBuilder()
+        .setDate(parseDate("2011-01-01").getTime())
+        .build())
+      .addChangeset(Changesets.Changeset.newBuilder()
+        .setDate(parseDate("2012-02-23").getTime())
+        .build())
+      .addChangesetIndexByLine(0)
+      .addChangesetIndexByLine(2) // line 2
+      .addChangesetIndexByLine(1) // line 3
+      .addChangesetIndexByLine(2) // line 4
+      .addChangesetIndexByLine(3) // line 5
+      .addChangesetIndexByLine(3) // line 6
+      .addChangesetIndexByLine(3) // line 7
+      .build());
+    measureRepository.addRawMeasure(componentRef, metricKeys.coverageLineHitsData, newMeasureBuilder().create("2=0;3=2;4=" + line4.lineHits + ";5=1;6=" + line6.lineHits + ";7=0"));
+    measureRepository.addRawMeasure(componentRef, metricKeys.conditionsByLine, newMeasureBuilder().create("4=" + line4.coveredConditions + ";6=" + line6.coveredConditions));
+    measureRepository.addRawMeasure(componentRef, metricKeys.coveredConditionsByLine,
+      newMeasureBuilder().create("4=" + line4.uncoveredConditions + ";6=" + line6.uncoveredConditions));
+  }
+
+  private static final class MetricKeys {
+    private final String coverageLineHitsData;
+    private final String conditionsByLine;
+    private final String coveredConditionsByLine;
+    private final String newLinesToCover;
+    private final String newUncoveredLines;
+    private final String newConditionsToCover;
+    private final String newUncoveredConditions;
+
+    public MetricKeys(String coverageLineHitsData, String conditionsByLine, String coveredConditionsByLine,
+                      String newLinesToCover, String newUncoveredLines, String newConditionsToCover, String newUncoveredConditions) {
+      this.coverageLineHitsData = coverageLineHitsData;
+      this.conditionsByLine = conditionsByLine;
+      this.coveredConditionsByLine = coveredConditionsByLine;
+      this.newLinesToCover = newLinesToCover;
+      this.newUncoveredLines = newUncoveredLines;
+      this.newConditionsToCover = newConditionsToCover;
+      this.newUncoveredConditions = newUncoveredConditions;
+    }
+  }
+
+  private static final class MeasureValues {
+    private final int lineHits;
+    private final int coveredConditions;
+    private final int uncoveredConditions;
+
+    public MeasureValues(int lineHits, int coveredConditions, int uncoveredConditions) {
+      this.lineHits = lineHits;
+      this.coveredConditions = coveredConditions;
+      this.uncoveredConditions = uncoveredConditions;
+    }
+  }
+
+  private void verify_computation_of_measures_for_new_conditions(MetricKeys metricKeys) {
+    treeRootHolder.setRoot(FILE_COMPONENT);
+    defineChangeSetsAndMeasures(FILE_COMPONENT.getReportAttributes().getRef(), metricKeys, new MeasureValues(3, 4, 1), new MeasureValues(0, 3, 2));
+
+    underTest.execute();
+
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_COMPONENT.getReportAttributes().getRef()))).contains(
+      entryOf(metricKeys.newLinesToCover, createMeasure(5d, 3d)),
+      entryOf(metricKeys.newUncoveredLines, createMeasure(3d, 2d)),
+      entryOf(metricKeys.newConditionsToCover, createMeasure(7d, 3d)),
+      entryOf(metricKeys.newUncoveredConditions, createMeasure(4d, 1d))
+    );
+  }
+
+  private static Measure createMeasure(@Nullable Double variationPeriod2, @Nullable Double variationPeriod5) {
+    MeasureVariations.Builder variationBuilder = newMeasureVariationsBuilder();
+    if (variationPeriod2 != null) {
+      variationBuilder.setVariation(new Period(2, "", null, 1L, 2L), variationPeriod2);
+    }
+    if (variationPeriod5 != null) {
+      variationBuilder.setVariation(new Period(5, "", null, 1L, 2L), variationPeriod5);
+    }
+    return newMeasureBuilder()
+      .setVariations(variationBuilder.build())
+      .createNoValue();
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ViewsNewCoverageMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ViewsNewCoverageMeasuresStepTest.java
new file mode 100644 (file)
index 0000000..80ac182
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.server.computation.step;
+
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.ViewsComponent;
+import org.sonar.server.computation.formula.coverage.LinesAndConditionsWithUncoveredMetricKeys;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.measure.MeasureVariations;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.period.Period;
+import org.sonar.server.computation.period.PeriodsHolderRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.sonar.api.utils.DateUtils.parseDate;
+import static org.sonar.server.computation.component.Component.Type.PROJECT_VIEW;
+import static org.sonar.server.computation.component.Component.Type.SUBVIEW;
+import static org.sonar.server.computation.component.Component.Type.VIEW;
+import static org.sonar.server.computation.component.ViewsComponent.builder;
+import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
+import static org.sonar.server.computation.measure.MeasureRepoEntry.entryOf;
+import static org.sonar.server.computation.measure.MeasureRepoEntry.toEntries;
+import static org.sonar.server.computation.measure.MeasureVariations.newMeasureVariationsBuilder;
+
+public class ViewsNewCoverageMeasuresStepTest {
+
+  private static final int ROOT_REF = 1;
+  private static final int SUBVIEW_REF = 11;
+  private static final int SUB_SUBVIEW_1_REF = 111;
+  private static final int PROJECT_VIEW_1_REF = 1111;
+  private static final int SUB_SUBVIEW_2_REF = 112;
+  private static final int PROJECT_VIEW_2_REF = 1121;
+  private static final int PROJECT_VIEW_3_REF = 1122;
+  private static final int PROJECT_VIEW_4_REF = 12;
+
+  private static final ViewsComponent VIEWS_TREE = builder(VIEW, ROOT_REF)
+    .addChildren(
+      builder(SUBVIEW, SUBVIEW_REF)
+        .addChildren(
+          builder(SUBVIEW, SUB_SUBVIEW_1_REF)
+            .addChildren(
+              builder(PROJECT_VIEW, PROJECT_VIEW_1_REF).build())
+            .build(),
+          builder(SUBVIEW, SUB_SUBVIEW_2_REF)
+            .addChildren(
+              builder(PROJECT_VIEW, PROJECT_VIEW_2_REF).build(),
+              builder(PROJECT_VIEW, PROJECT_VIEW_3_REF).build())
+            .build(),
+          builder(PROJECT_VIEW, PROJECT_VIEW_4_REF).build())
+        .build())
+    .build();
+  private static final Double NO_PERIOD_4_OR_5_IN_VIEWS = null;
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+  @Rule
+  public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
+  @Rule
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+    .add(CoreMetrics.COVERAGE_LINE_HITS_DATA)
+    .add(CoreMetrics.CONDITIONS_BY_LINE)
+    .add(CoreMetrics.COVERED_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.NEW_LINES_TO_COVER)
+    .add(CoreMetrics.NEW_UNCOVERED_LINES)
+    .add(CoreMetrics.NEW_CONDITIONS_TO_COVER)
+    .add(CoreMetrics.NEW_UNCOVERED_CONDITIONS)
+    .add(CoreMetrics.NEW_COVERAGE)
+    .add(CoreMetrics.NEW_BRANCH_COVERAGE)
+    .add(CoreMetrics.NEW_LINE_COVERAGE)
+
+  .add(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA)
+    .add(CoreMetrics.IT_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.NEW_IT_LINES_TO_COVER)
+    .add(CoreMetrics.NEW_IT_UNCOVERED_LINES)
+    .add(CoreMetrics.NEW_IT_CONDITIONS_TO_COVER)
+    .add(CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS)
+    .add(CoreMetrics.NEW_IT_COVERAGE)
+    .add(CoreMetrics.NEW_IT_BRANCH_COVERAGE)
+    .add(CoreMetrics.NEW_IT_LINE_COVERAGE)
+
+  .add(CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA)
+    .add(CoreMetrics.OVERALL_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE)
+    .add(CoreMetrics.NEW_OVERALL_LINES_TO_COVER)
+    .add(CoreMetrics.NEW_OVERALL_UNCOVERED_LINES)
+    .add(CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER)
+    .add(CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS)
+    .add(CoreMetrics.NEW_OVERALL_COVERAGE)
+    .add(CoreMetrics.NEW_OVERALL_BRANCH_COVERAGE)
+    .add(CoreMetrics.NEW_OVERALL_LINE_COVERAGE);
+  @Rule
+  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+  private NewCoverageMeasuresStep underTest = new NewCoverageMeasuresStep(treeRootHolder, periodsHolder,
+    measureRepository, metricRepository);
+
+  @Before
+  public void setUp() {
+    periodsHolder.setPeriods(
+      new Period(2, "mode_p_1", null, parseDate("2009-12-25").getTime(), 1),
+      new Period(5, "mode_p_5", null, parseDate("2011-02-18").getTime(), 2));
+  }
+
+  @Test
+  public void verify_aggregation_of_measures_for_new_conditions() {
+    String newLinesToCover = CoreMetrics.NEW_IT_LINES_TO_COVER_KEY;
+    String newUncoveredLines = CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY;
+    String newConditionsToCover = CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY;
+    String newUncoveredConditions = CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY;
+
+    MetricKeys metricKeys = new MetricKeys(newLinesToCover, newUncoveredLines, newConditionsToCover, newUncoveredConditions);
+
+    treeRootHolder.setRoot(VIEWS_TREE);
+    // PROJECT_VIEW_1_REF has no measure
+    measureRepository.addRawMeasure(PROJECT_VIEW_2_REF, newLinesToCover, createMeasure(1d, 2d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_2_REF, newUncoveredLines, createMeasure(10d, 20d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_2_REF, newConditionsToCover, createMeasure(4d, 5d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_2_REF, newUncoveredConditions, createMeasure(40d, 50d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_3_REF, newLinesToCover, createMeasure(11d, 12d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_3_REF, newUncoveredLines, createMeasure(20d, 30d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_3_REF, newConditionsToCover, createMeasure(14d, 15d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_3_REF, newUncoveredConditions, createMeasure(50d, 60d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_4_REF, newLinesToCover, createMeasure(21d, 22d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_4_REF, newUncoveredLines, createMeasure(30d, 40d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_4_REF, newConditionsToCover, createMeasure(24d, 25d));
+    measureRepository.addRawMeasure(PROJECT_VIEW_4_REF, newUncoveredConditions, createMeasure(60d, 70d));
+
+    underTest.execute();
+
+    assertNoAddedRawMeasureOnProjectViews();
+    assertNoAddedRawMeasures(SUB_SUBVIEW_1_REF);
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(SUB_SUBVIEW_2_REF))).contains(
+      entryOf(metricKeys.newLinesToCover, createMeasure(12d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(metricKeys.newUncoveredLines, createMeasure(30d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(metricKeys.newConditionsToCover, createMeasure(18d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(metricKeys.newUncoveredConditions, createMeasure(90d, NO_PERIOD_4_OR_5_IN_VIEWS)));
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(ROOT_REF))).contains(
+      entryOf(metricKeys.newLinesToCover, createMeasure(33d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(metricKeys.newUncoveredLines, createMeasure(60d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(metricKeys.newConditionsToCover, createMeasure(42d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(metricKeys.newUncoveredConditions, createMeasure(150d, NO_PERIOD_4_OR_5_IN_VIEWS)));
+  }
+
+  @Test
+  public void verify_aggregates_variations_for_new_code_line_and_branch_Coverage() {
+    LinesAndConditionsWithUncoveredMetricKeys metricKeys = new LinesAndConditionsWithUncoveredMetricKeys(
+      CoreMetrics.NEW_LINES_TO_COVER_KEY, CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY,
+      CoreMetrics.NEW_UNCOVERED_LINES_KEY, CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY);
+    String codeCoverageKey = CoreMetrics.NEW_COVERAGE_KEY;
+    String lineCoverageKey = CoreMetrics.NEW_LINE_COVERAGE_KEY;
+    String branchCoverageKey = CoreMetrics.NEW_BRANCH_COVERAGE_KEY;
+
+    verify_aggregates_variations(metricKeys, codeCoverageKey, lineCoverageKey, branchCoverageKey);
+  }
+
+  @Test
+  public void verify_aggregates_variations_for_new_IT_code_line_and_branch_Coverage() {
+    LinesAndConditionsWithUncoveredMetricKeys metricKeys = new LinesAndConditionsWithUncoveredMetricKeys(
+      CoreMetrics.NEW_IT_LINES_TO_COVER_KEY, CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY,
+      CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY, CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY);
+    String codeCoverageKey = CoreMetrics.NEW_IT_COVERAGE_KEY;
+    String lineCoverageKey = CoreMetrics.NEW_IT_LINE_COVERAGE_KEY;
+    String branchCoverageKey = CoreMetrics.NEW_IT_BRANCH_COVERAGE_KEY;
+
+    verify_aggregates_variations(metricKeys, codeCoverageKey, lineCoverageKey, branchCoverageKey);
+  }
+
+  @Test
+  public void verify_aggregates_variations_for_new_Overall_code_line_and_branch_Coverage() {
+    LinesAndConditionsWithUncoveredMetricKeys metricKeys = new LinesAndConditionsWithUncoveredMetricKeys(
+      CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY, CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY,
+      CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY, CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY);
+    String codeCoverageKey = CoreMetrics.NEW_OVERALL_COVERAGE_KEY;
+    String lineCoverageKey = CoreMetrics.NEW_OVERALL_LINE_COVERAGE_KEY;
+    String branchCoverageKey = CoreMetrics.NEW_OVERALL_BRANCH_COVERAGE_KEY;
+
+    verify_aggregates_variations(metricKeys, codeCoverageKey, lineCoverageKey, branchCoverageKey);
+  }
+
+  private void verify_aggregates_variations(LinesAndConditionsWithUncoveredMetricKeys metricKeys, String codeCoverageKey, String lineCoverageKey, String branchCoverageKey) {
+    treeRootHolder.setRoot(VIEWS_TREE);
+    measureRepository
+      .addRawMeasure(PROJECT_VIEW_1_REF, metricKeys.getLines(), createMeasure(3000d, 2000d))
+      .addRawMeasure(PROJECT_VIEW_1_REF, metricKeys.getConditions(), createMeasure(300d, 400d))
+      .addRawMeasure(PROJECT_VIEW_1_REF, metricKeys.getUncoveredLines(), createMeasure(30d, 200d))
+      .addRawMeasure(PROJECT_VIEW_1_REF, metricKeys.getUncoveredConditions(), createMeasure(9d, 16d))
+      // PROJECT_VIEW_2_REF
+      .addRawMeasure(PROJECT_VIEW_2_REF, metricKeys.getLines(), createMeasure(2000d, 3000d))
+      .addRawMeasure(PROJECT_VIEW_2_REF, metricKeys.getConditions(), createMeasure(400d, 300d))
+      .addRawMeasure(PROJECT_VIEW_2_REF, metricKeys.getUncoveredLines(), createMeasure(200d, 30d))
+      .addRawMeasure(PROJECT_VIEW_2_REF, metricKeys.getUncoveredConditions(), createMeasure(16d, 9d))
+      // PROJECT_VIEW_3_REF has no measure
+      // PROJECT_VIEW_4_REF
+      .addRawMeasure(PROJECT_VIEW_4_REF, metricKeys.getLines(), createMeasure(1000d, 2000d))
+      .addRawMeasure(PROJECT_VIEW_4_REF, metricKeys.getConditions(), createMeasure(300d, 200d))
+      .addRawMeasure(PROJECT_VIEW_4_REF, metricKeys.getUncoveredLines(), createMeasure(100d, 20d))
+      .addRawMeasure(PROJECT_VIEW_4_REF, metricKeys.getUncoveredConditions(), createMeasure(6d, 9d));
+
+    underTest.execute();
+
+    assertNoAddedRawMeasureOnProjectViews();
+
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(SUB_SUBVIEW_1_REF))).contains(
+      entryOf(codeCoverageKey, createMeasure(98.8d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(lineCoverageKey, createMeasure(99d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(branchCoverageKey, createMeasure(97d, NO_PERIOD_4_OR_5_IN_VIEWS)));
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(SUB_SUBVIEW_2_REF))).contains(
+      entryOf(codeCoverageKey, createMeasure(91d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(lineCoverageKey, createMeasure(90d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+      entryOf(branchCoverageKey, createMeasure(96d, NO_PERIOD_4_OR_5_IN_VIEWS)));
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(SUBVIEW_REF))).contains(
+        entryOf(codeCoverageKey, createMeasure(94.8d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+        entryOf(lineCoverageKey, createMeasure(94.5d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+        entryOf(branchCoverageKey, createMeasure(96.9d, NO_PERIOD_4_OR_5_IN_VIEWS)));
+    assertThat(toEntries(measureRepository.getAddedRawMeasures(ROOT_REF))).contains(
+        entryOf(codeCoverageKey, createMeasure(94.8d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+        entryOf(lineCoverageKey, createMeasure(94.5d, NO_PERIOD_4_OR_5_IN_VIEWS)),
+        entryOf(branchCoverageKey, createMeasure(96.9d, NO_PERIOD_4_OR_5_IN_VIEWS)));
+  }
+
+  private static final class MetricKeys {
+    private final String newLinesToCover;
+    private final String newUncoveredLines;
+    private final String newConditionsToCover;
+    private final String newUncoveredConditions;
+
+    public MetricKeys(String newLinesToCover, String newUncoveredLines, String newConditionsToCover, String newUncoveredConditions) {
+      this.newLinesToCover = newLinesToCover;
+      this.newUncoveredLines = newUncoveredLines;
+      this.newConditionsToCover = newConditionsToCover;
+      this.newUncoveredConditions = newUncoveredConditions;
+    }
+  }
+
+  private static Measure createMeasure(@Nullable Double variationPeriod2, @Nullable Double variationPeriod5) {
+    MeasureVariations.Builder variationBuilder = newMeasureVariationsBuilder();
+    if (variationPeriod2 != null) {
+      variationBuilder.setVariation(new Period(2, "", null, 1L, 2L), variationPeriod2);
+    }
+    if (variationPeriod5 != null) {
+      variationBuilder.setVariation(new Period(5, "", null, 1L, 2L), variationPeriod5);
+    }
+    return newMeasureBuilder()
+      .setVariations(variationBuilder.build())
+      .createNoValue();
+  }
+
+  private void assertNoAddedRawMeasureOnProjectViews() {
+    assertNoAddedRawMeasures(PROJECT_VIEW_1_REF);
+    assertNoAddedRawMeasures(PROJECT_VIEW_2_REF);
+    assertNoAddedRawMeasures(PROJECT_VIEW_3_REF);
+  }
+
+  private void assertNoAddedRawMeasures(int componentRef) {
+    assertThat(measureRepository.getAddedRawMeasures(componentRef)).isEmpty();
+  }
+
+}