]> source.dussan.org Git - sonarqube.git/commitdiff
add FormulaExecutorComponentVisitor + variation aggregation support
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 8 Jul 2015 14:41:48 +0000 (16:41 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 9 Jul 2015 07:24:48 +0000 (09:24 +0200)
FormulaExecutorComponentVisitor allows having any step execute Formula
Formula can now do aggregation of MeasureVariation values
also renamed FormulaRepository to CoreFormulaRepository and ComputeFormulaMeasuresStep to CoreMetricFormulaExecutorStep

21 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepository.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepositoryImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/formula/CounterContext.java
server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitor.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaRepository.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/DoubleVariationValue.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/IntVariationValue.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/LongVariationValue.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStep.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStep.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaExecutionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaStepTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaExecutionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaStepTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitorTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaExecutionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaStepTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsHolderRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStepTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStepTest.java [new file with mode: 0644]

diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepository.java
new file mode 100644 (file)
index 0000000..4629cba
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import java.util.List;
+
+public interface CoreFormulaRepository {
+
+  List<Formula> getFormulas();
+
+}
index 9b729e2bd64302ce91f99c97934ed0c9930d7b77..46dc36ad8301c3a856d3aa2c4e09373ff54bbc4c 100644 (file)
@@ -38,7 +38,7 @@ import static org.sonar.api.measures.CoreMetrics.GENERATED_LINES_KEY;
 import static org.sonar.api.measures.CoreMetrics.GENERATED_NCLOC_KEY;
 import static org.sonar.api.measures.CoreMetrics.STATEMENTS_KEY;
 
-public class CoreFormulaRepositoryImpl implements FormulaRepository {
+public class CoreFormulaRepositoryImpl implements CoreFormulaRepository {
 
   private static final List<Formula> FORMULAS = ImmutableList.<Formula>of(
     // TODO When all decorators will be moved to CE, uncomment commented lines to activate all formulas and remove formulas declaration in
index c34ffdac8ff0d38ead26caaecbd6204a56ee6460..0da22ee9d2473bd939bce986e0eb613d3818564a 100644 (file)
 package org.sonar.server.computation.formula;
 
 import com.google.common.base.Optional;
+import java.util.List;
 import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.period.Period;
 
 public interface CounterContext {
 
   Optional<Measure> getMeasure(String metricKey);
 
+  /**
+   * Lists of Periods defined for the current project. They can be used to aggregate values of MeasureVariations.
+   */
+  List<Period> getPeriods();
+
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitor.java
new file mode 100644 (file)
index 0000000..94a2681
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * 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;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.CheckForNull;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.PathAwareVisitor;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.period.Period;
+import org.sonar.server.computation.period.PeriodsHolder;
+
+import static java.util.Objects.requireNonNull;
+
+public class FormulaExecutorComponentVisitor extends PathAwareVisitor<FormulaExecutorComponentVisitor.Counters> {
+  private static final PathAwareVisitor.SimpleStackElementFactory<Counters> COUNTERS_FACTORY = new PathAwareVisitor.SimpleStackElementFactory<Counters>() {
+
+    @Override
+    public Counters createForAny(Component component) {
+      return new Counters();
+    }
+
+    @Override
+    public Counters createForFile(Component component) {
+      // No need to create a counter on file levels
+      return null;
+    }
+  };
+
+  @CheckForNull
+  private final PeriodsHolder periodsHolder;
+  private final MetricRepository metricRepository;
+  private final MeasureRepository measureRepository;
+  private final List<Formula> formulas;
+
+  private FormulaExecutorComponentVisitor(Builder builder, List<Formula> formulas) {
+    super(Component.Type.FILE, Order.POST_ORDER, COUNTERS_FACTORY);
+    this.periodsHolder = builder.periodsHolder;
+    this.measureRepository = builder.measureRepository;
+    this.metricRepository = builder.metricRepository;
+    this.formulas = ImmutableList.copyOf(formulas);
+  }
+
+  public static Builder newBuilder(MetricRepository metricRepository, MeasureRepository measureRepository) {
+    return new Builder(metricRepository, measureRepository);
+  }
+
+  public static class Builder {
+    private final MetricRepository metricRepository;
+    private final MeasureRepository measureRepository;
+    @CheckForNull
+    private PeriodsHolder periodsHolder;
+
+    private Builder(MetricRepository metricRepository, MeasureRepository measureRepository) {
+      this.metricRepository = requireNonNull(metricRepository);
+      this.measureRepository = requireNonNull(measureRepository);
+    }
+
+    public Builder create(MetricRepository metricRepository, MeasureRepository measureRepository) {
+      return new Builder(metricRepository, measureRepository);
+    }
+
+    public Builder withVariationSupport(PeriodsHolder periodsHolder) {
+      this.periodsHolder = requireNonNull(periodsHolder);
+      return this;
+    }
+
+    public FormulaExecutorComponentVisitor buildFor(List<Formula> formulas) {
+      return new FormulaExecutorComponentVisitor(this, formulas);
+    }
+  }
+
+
+
+  @Override
+  protected void visitProject(Component project, Path<FormulaExecutorComponentVisitor.Counters> path) {
+    processNotFile(project, path);
+  }
+
+  @Override
+  protected void visitModule(Component module, Path<FormulaExecutorComponentVisitor.Counters> path) {
+    processNotFile(module, path);
+  }
+
+  @Override
+  protected void visitDirectory(Component directory, Path<FormulaExecutorComponentVisitor.Counters> path) {
+    processNotFile(directory, path);
+  }
+
+  @Override
+  protected void visitFile(Component file, Path<FormulaExecutorComponentVisitor.Counters> path) {
+    processFile(file, path);
+  }
+
+  private void processNotFile(Component component, Path<FormulaExecutorComponentVisitor.Counters> path) {
+    for (Formula formula : formulas) {
+      Counter counter = path.current().getCounter(formula.getOutputMetricKey());
+      // If there were no file under this node, the counter won't be initialized
+      if (counter != null) {
+        addNewMeasure(component, formula, counter);
+        aggregateToParent(path, formula, counter);
+      }
+    }
+  }
+
+  private void processFile(Component file, Path<FormulaExecutorComponentVisitor.Counters> path) {
+    CounterContext counterContext = new CounterContextImpl(file);
+    for (Formula formula : formulas) {
+      Counter counter = formula.createNewCounter();
+      counter.aggregate(counterContext);
+      addNewMeasure(file, formula, counter);
+      aggregateToParent(path, formula, counter);
+    }
+  }
+
+  private void addNewMeasure(Component component, Formula formula, Counter counter) {
+    Metric metric = metricRepository.getByKey(formula.getOutputMetricKey());
+    Optional<Measure> measure = formula.createMeasure(counter, component.getType());
+    if (measure.isPresent()) {
+      measureRepository.add(component, metric, measure.get());
+    }
+  }
+
+  private void aggregateToParent(Path<FormulaExecutorComponentVisitor.Counters> path, Formula formula, Counter currentCounter) {
+    if (!path.isRoot()) {
+      path.parent().aggregate(formula.getOutputMetricKey(), currentCounter);
+    }
+  }
+
+  private class CounterContextImpl implements CounterContext {
+    private final Component file;
+
+    public CounterContextImpl(Component file) {
+      this.file = file;
+    }
+
+    @Override
+    public Optional<Measure> getMeasure(String metricKey) {
+      return measureRepository.getRawMeasure(file, metricRepository.getByKey(metricKey));
+    }
+
+    @Override
+    public List<Period> getPeriods() {
+      return periodsHolder.getPeriods();
+    }
+  }
+
+  public static class Counters {
+    Map<String, Counter> countersByFormula = new HashMap<>();
+
+    public void aggregate(String metricKey, Counter childCounter) {
+      Counter counter = countersByFormula.get(metricKey);
+      if (counter == null) {
+        countersByFormula.put(metricKey, childCounter);
+      } else {
+        counter.aggregate(childCounter);
+      }
+    }
+
+    /**
+     * Counter can be null on a level when it has not been fed by children levels
+     */
+    @CheckForNull
+    public Counter getCounter(String metricKey) {
+      return countersByFormula.get(metricKey);
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaRepository.java
deleted file mode 100644 (file)
index a59334a..0000000
+++ /dev/null
@@ -1,29 +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.formula;
-
-import java.util.List;
-
-public interface FormulaRepository {
-
-  List<Formula> getFormulas();
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/DoubleVariationValue.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/DoubleVariationValue.java
new file mode 100644 (file)
index 0000000..b2b3934
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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.counter;
+
+import com.google.common.base.Optional;
+import javax.annotation.Nullable;
+import org.sonar.server.computation.formula.Counter;
+import org.sonar.server.computation.measure.MeasureVariations;
+import org.sonar.server.computation.period.PeriodsHolder;
+
+import static org.sonar.server.computation.period.PeriodsHolder.MAX_NUMBER_OF_PERIODS;
+
+/**
+ * Convenience class wrapping a double to compute the value of a MeasureVariation as an double and know it is has ever
+ * been set.
+ * <p>
+ * Basically, this class will be used in a {@link Counter} implementation as an array property which can be easily
+ * creating using method {@link #newArray()}.
+ * </p>
+ */
+public class DoubleVariationValue {
+  private boolean set = false;
+  private double value = 0L;
+
+  public void increment(double increment) {
+    this.value += increment;
+    this.set = true;
+  }
+
+  public void increment(@Nullable DoubleVariationValue value) {
+    if (value != null) {
+      increment(value.value);
+    }
+  }
+
+  public boolean isSet() {
+    return set;
+  }
+
+  public double getValue() {
+    return value;
+  }
+
+  /**
+   * Creates a new Array of {@link DoubleVariationValue} of size {@link PeriodsHolder#MAX_NUMBER_OF_PERIODS},
+   * initialized with newly creates {@link DoubleVariationValue} instances.
+   */
+  public static Array newArray() {
+    return new Array();
+  }
+
+  public static class Array {
+    private final DoubleVariationValue[] values;
+
+    public Array() {
+      this.values = new DoubleVariationValue[MAX_NUMBER_OF_PERIODS];
+      for (int i = 0; i < MAX_NUMBER_OF_PERIODS; i++) {
+        this.values[i] = new DoubleVariationValue();
+      }
+    }
+
+    public void increment(int index, double value) {
+      this.values[index].increment(value);
+    }
+
+    public void incrementAll(Array source) {
+      for (int i = 0; i < this.values.length; i++) {
+        if (source.values[i].isSet()) {
+          this.values[i].increment(source.values[i]);
+        }
+      }
+    }
+
+    /**
+     * Creates a new MeasureVariations from the current array.
+     *
+     * @throws IllegalArgumentException if none of the {@link DoubleVariationValue} in the array is set
+     */
+    public Optional<MeasureVariations> toMeasureVariations() {
+      if (!isAnySet()) {
+        return Optional.absent();
+      }
+      Double[] variations = new Double[values.length];
+      for (int i = 0; i < values.length; i++) {
+        if (values[i].isSet()) {
+          variations[i] = (double) values[i].getValue();
+        }
+      }
+      return Optional.of(new MeasureVariations(variations));
+    }
+
+    private boolean isAnySet() {
+      for (DoubleVariationValue value : values) {
+        if (value.isSet()) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/IntVariationValue.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/IntVariationValue.java
new file mode 100644 (file)
index 0000000..8bcb5bf
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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.counter;
+
+import com.google.common.base.Optional;
+import javax.annotation.Nullable;
+import org.sonar.server.computation.formula.Counter;
+import org.sonar.server.computation.measure.MeasureVariations;
+import org.sonar.server.computation.period.PeriodsHolder;
+
+import static org.sonar.server.computation.period.PeriodsHolder.MAX_NUMBER_OF_PERIODS;
+
+/**
+ * Convenience class wrapping a int to compute the value of a MeasureVariation as an int and know it is has ever been
+ * set.
+ * <p>
+ * Basically, this class will be used in a {@link Counter} implementation as an array property which can be easily
+ * creating using method {@link #newArray()}.
+ * </p>
+ */
+public class IntVariationValue {
+  private boolean set = false;
+  private int value = 0;
+
+  public void increment(int increment) {
+    this.value += increment;
+    this.set = true;
+  }
+
+  public void increment(@Nullable IntVariationValue value) {
+    if (value != null) {
+      increment(value.value);
+    }
+  }
+
+  public boolean isSet() {
+    return set;
+  }
+
+  public int getValue() {
+    return value;
+  }
+
+  /**
+   * Creates a new Array of {@link IntVariationValue} of size {@link PeriodsHolder#MAX_NUMBER_OF_PERIODS},
+   * initialized with newly creates {@link IntVariationValue} instances.
+   */
+  public static Array newArray() {
+    return new Array();
+  }
+
+  public static class Array {
+    private final IntVariationValue[] values;
+
+    public Array() {
+      this.values = new IntVariationValue[MAX_NUMBER_OF_PERIODS];
+      for (int i = 0; i < MAX_NUMBER_OF_PERIODS; i++) {
+        this.values[i] = new IntVariationValue();
+      }
+    }
+
+    public void increment(int index, int value) {
+      this.values[index].increment(value);
+    }
+
+    public void incrementAll(Array source) {
+      for (int i = 0; i < this.values.length; i++) {
+        if (source.values[i].isSet()) {
+          this.values[i].increment(source.values[i]);
+        }
+      }
+    }
+
+    /**
+     * Creates a new MeasureVariations from the current array.
+     *
+     * @throws IllegalArgumentException if none of the {@link IntVariationValue} in the array is set
+     */
+    public Optional<MeasureVariations> toMeasureVariations() {
+      if (!isAnySet()) {
+        return Optional.absent();
+      }
+      Double[] variations = new Double[values.length];
+      for (int i = 0; i < values.length; i++) {
+        if (values[i].isSet()) {
+          variations[i] = (double) values[i].getValue();
+        }
+      }
+      return Optional.of(new MeasureVariations(variations));
+    }
+
+    private boolean isAnySet() {
+      for (IntVariationValue value : values) {
+        if (value.isSet()) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/LongVariationValue.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/LongVariationValue.java
new file mode 100644 (file)
index 0000000..a79706b
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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.counter;
+
+import com.google.common.base.Optional;
+import javax.annotation.Nullable;
+import org.sonar.server.computation.measure.MeasureVariations;
+import org.sonar.server.computation.period.PeriodsHolder;
+
+/**
+ * Convenience class wrapping a long to compute the value of a MeasureVariation as an long and know it is has ever been
+ * set.
+ * <p>
+ * Basically, this class will be used in a {@link Counter} implementation as an array property which can be easily
+ * creating using method {@link #newArray()}.
+ * </p>
+ */
+import static org.sonar.server.computation.period.PeriodsHolder.MAX_NUMBER_OF_PERIODS;
+
+public class LongVariationValue {
+  private boolean set = false;
+  private long value = 0L;
+
+  public void increment(long increment) {
+    this.value += increment;
+    this.set = true;
+  }
+
+  public void increment(@Nullable LongVariationValue value) {
+    if (value != null) {
+      increment(value.value);
+    }
+  }
+
+  public boolean isSet() {
+    return set;
+  }
+
+  public long getValue() {
+    return value;
+  }
+
+  /**
+   * Creates a new Array of {@link LongVariationValue} of size {@link PeriodsHolder#MAX_NUMBER_OF_PERIODS},
+   * initialized with newly creates {@link LongVariationValue} instances.
+   */
+  public static Array newArray() {
+    return new Array();
+  }
+
+  public static class Array {
+    private final LongVariationValue[] values;
+
+    public Array() {
+      this.values = new LongVariationValue[MAX_NUMBER_OF_PERIODS];
+      for (int i = 0; i < MAX_NUMBER_OF_PERIODS; i++) {
+        this.values[i] = new LongVariationValue();
+      }
+    }
+
+    public void increment(int index, long value) {
+      this.values[index].increment(value);
+    }
+
+    public void incrementAll(Array source) {
+      for (int i = 0; i < this.values.length; i++) {
+        if (source.values[i].isSet()) {
+          this.values[i].increment(source.values[i]);
+        }
+      }
+    }
+
+    /**
+     * Creates a new MeasureVariations from the current array.
+     *
+     * @throws IllegalArgumentException if none of the {@link LongVariationValue} in the array is set
+     */
+    public Optional<MeasureVariations> toMeasureVariations() {
+      if (!isAnySet()) {
+        return Optional.absent();
+      }
+      Double[] variations = new Double[values.length];
+      for (int i = 0; i < values.length; i++) {
+        if (values[i].isSet()) {
+          variations[i] = (double) values[i].getValue();
+        }
+      }
+      return Optional.of(new MeasureVariations(variations));
+    }
+
+    private boolean isAnySet() {
+      for (LongVariationValue value : values) {
+        if (value.isSet()) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+}
index 46c8c4b924194fd9c8cc5d4e7df3c58da86a3ec5..b1ba6eeec24f53c3294f38919d0da56a7f509464 100644 (file)
@@ -52,8 +52,8 @@ public class ComputationSteps {
 
       // data computation
       IntegrateIssuesStep.class,
+      CoreMetricFormulaExecutorStep.class,
       CustomMeasuresCopyStep.class,
-      ComputeFormulaMeasuresStep.class,      
 
       // SQALE measures depend on issues
       SqaleMeasuresStep.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStep.java
deleted file mode 100644 (file)
index e1d81e0..0000000
+++ /dev/null
@@ -1,176 +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 com.google.common.base.Optional;
-import java.util.HashMap;
-import java.util.Map;
-import javax.annotation.CheckForNull;
-import org.sonar.server.computation.component.Component;
-import org.sonar.server.computation.component.ComponentVisitor;
-import org.sonar.server.computation.component.PathAwareVisitor;
-import org.sonar.server.computation.component.TreeRootHolder;
-import org.sonar.server.computation.formula.Counter;
-import org.sonar.server.computation.formula.CounterContext;
-import org.sonar.server.computation.formula.Formula;
-import org.sonar.server.computation.formula.FormulaRepository;
-import org.sonar.server.computation.measure.Measure;
-import org.sonar.server.computation.measure.MeasureRepository;
-import org.sonar.server.computation.metric.Metric;
-import org.sonar.server.computation.metric.MetricRepository;
-
-public class ComputeFormulaMeasuresStep implements ComputationStep {
-
-  private final TreeRootHolder treeRootHolder;
-  private final MeasureRepository measureRepository;
-  private final MetricRepository metricRepository;
-  private final FormulaRepository formulaRepository;
-
-  public ComputeFormulaMeasuresStep(TreeRootHolder treeRootHolder, MeasureRepository measureRepository, MetricRepository metricRepository, FormulaRepository formulaRepository) {
-    this.treeRootHolder = treeRootHolder;
-    this.measureRepository = measureRepository;
-    this.metricRepository = metricRepository;
-    this.formulaRepository = formulaRepository;
-  }
-
-  @Override
-  public void execute() {
-    new ComputeFormulaMeasureVisitor().visit(treeRootHolder.getRoot());
-  }
-
-  private class ComputeFormulaMeasureVisitor extends PathAwareVisitor<Counters> {
-
-    public ComputeFormulaMeasureVisitor() {
-      super(Component.Type.FILE, ComponentVisitor.Order.POST_ORDER, new SimpleStackElementFactory<Counters>() {
-
-        @Override
-        public Counters createForAny(Component component) {
-          return new Counters();
-        }
-
-        @Override
-        public Counters createForFile(Component component) {
-          // No need to create a counter on file levels
-          return null;
-        }
-      });
-    }
-
-    @Override
-    protected void visitProject(Component project, Path<Counters> path) {
-      processNotFile(project, path);
-    }
-
-    @Override
-    protected void visitModule(Component module, Path<Counters> path) {
-      processNotFile(module, path);
-    }
-
-    @Override
-    protected void visitDirectory(Component directory, Path<Counters> path) {
-      processNotFile(directory, path);
-    }
-
-    @Override
-    protected void visitFile(Component file, Path<Counters> path) {
-      processFile(file, path);
-    }
-
-    private void processNotFile(Component component, PathAwareVisitor.Path<Counters> path) {
-      for (Formula formula : formulaRepository.getFormulas()) {
-        Counter counter = path.current().getCounter(formula.getOutputMetricKey());
-        // If there were no file under this node, the counter won't be initialized
-        if (counter != null) {
-          addNewMeasure(component, formula, counter);
-          aggregateToParent(path, formula, counter);
-        }
-      }
-    }
-
-    private void processFile(Component file, PathAwareVisitor.Path<Counters> path) {
-      CounterContext counterContext = new CounterContextImpl(file);
-      for (Formula formula : formulaRepository.getFormulas()) {
-        Counter counter = newCounter(formula);
-        counter.aggregate(counterContext);
-        addNewMeasure(file, formula, counter);
-        aggregateToParent(path, formula, counter);
-      }
-    }
-
-    private void addNewMeasure(Component component, Formula formula, Counter counter) {
-      Metric metric = metricRepository.getByKey(formula.getOutputMetricKey());
-      Optional<Measure> measure = formula.createMeasure(counter, component.getType());
-      if (measure.isPresent()) {
-        measureRepository.add(component, metric, measure.get());
-      }
-    }
-
-    private void aggregateToParent(PathAwareVisitor.Path<Counters> path, Formula formula, Counter currentCounter) {
-      if (!path.isRoot()) {
-        path.parent().aggregate(formula.getOutputMetricKey(), currentCounter);
-      }
-    }
-  }
-
-  private static Counter newCounter(Formula formula) {
-    return formula.createNewCounter();
-  }
-
-  @Override
-  public String getDescription() {
-    return "Compute formula measures";
-  }
-
-  private static class Counters {
-    Map<String, Counter> countersByMetricKey = new HashMap<>();
-
-    public void aggregate(String metricKey, Counter childCounter) {
-      Counter counter = countersByMetricKey.get(metricKey);
-      if (counter == null) {
-        countersByMetricKey.put(metricKey, childCounter);
-      } else {
-        counter.aggregate(childCounter);
-      }
-    }
-
-    /**
-     * Counter can be null on a level when it has not been fed by children levels
-     */
-    @CheckForNull
-    public Counter getCounter(String metricKey) {
-      return countersByMetricKey.get(metricKey);
-    }
-  }
-
-  private class CounterContextImpl implements CounterContext {
-
-    private final Component file;
-
-    public CounterContextImpl(Component file) {
-      this.file = file;
-    }
-
-    @Override
-    public Optional<Measure> getMeasure(String metricKey) {
-      return measureRepository.getRawMeasure(file, metricRepository.getByKey(metricKey));
-    }
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStep.java
new file mode 100644 (file)
index 0000000..851e3e1
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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 org.sonar.server.computation.component.TreeRootHolder;
+import org.sonar.server.computation.formula.CoreFormulaRepository;
+import org.sonar.server.computation.formula.FormulaExecutorComponentVisitor;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.MetricRepository;
+
+public class CoreMetricFormulaExecutorStep implements ComputationStep {
+
+  private final TreeRootHolder treeRootHolder;
+  private final MetricRepository metricRepository;
+  private final MeasureRepository measureRepository;
+  private final CoreFormulaRepository coreFormulaRepository;
+
+  public CoreMetricFormulaExecutorStep(TreeRootHolder treeRootHolder,
+    MetricRepository metricRepository, MeasureRepository measureRepository,
+    CoreFormulaRepository coreFormulaRepository) {
+    this.treeRootHolder = treeRootHolder;
+    this.metricRepository = metricRepository;
+    this.measureRepository = measureRepository;
+    this.coreFormulaRepository = coreFormulaRepository;
+  }
+
+  @Override
+  public void execute() {
+    FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
+      .buildFor(coreFormulaRepository.getFormulas())
+      .visit(treeRootHolder.getRoot());
+  }
+
+  @Override
+  public String getDescription() {
+    return "Execute formula to compute measures of Core metrics";
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaExecutionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaExecutionTest.java
new file mode 100644 (file)
index 0000000..9ce1222
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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;
+
+import com.google.common.collect.Lists;
+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.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.period.PeriodsHolderRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.measures.CoreMetrics.COMPLEXITY_IN_FUNCTIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.FUNCTIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_KEY;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+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.DumbComponent.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;
+
+public class AverageFormulaExecutionTest {
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+  @Rule
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+    .add(CoreMetrics.FUNCTION_COMPLEXITY)
+    .add(CoreMetrics.COMPLEXITY_IN_FUNCTIONS)
+    .add(CoreMetrics.FUNCTIONS);
+  @Rule
+  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+  @Rule
+  public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
+
+  FormulaExecutorComponentVisitor sut;
+
+  @Before
+  public void setUp() throws Exception {
+    sut = FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
+      .buildFor(Lists.<Formula>newArrayList(
+        AverageFormula.Builder.newBuilder()
+          .setOutputMetricKey(FUNCTION_COMPLEXITY_KEY)
+          .setMainMetricKey(COMPLEXITY_IN_FUNCTIONS_KEY)
+          .setByMetricKey(FUNCTIONS_KEY)
+          .build()));
+  }
+
+  @Test
+  public void add_measures() {
+    DumbComponent project = builder(PROJECT, 1)
+      .addChildren(
+        builder(MODULE, 11)
+          .addChildren(
+            builder(DIRECTORY, 111)
+              .addChildren(
+                builder(Component.Type.FILE, 1111).build(),
+                builder(Component.Type.FILE, 1112).build()
+              ).build()
+          ).build(),
+        builder(MODULE, 12)
+          .addChildren(
+            builder(DIRECTORY, 121)
+              .addChildren(
+                builder(Component.Type.FILE, 1211).build()
+              ).build()
+          ).build()
+      ).build();
+
+    treeRootHolder.setRoot(project);
+
+    measureRepository.addRawMeasure(1111, COMPLEXITY_IN_FUNCTIONS_KEY, newMeasureBuilder().create(5));
+    measureRepository.addRawMeasure(1111, FUNCTIONS_KEY, newMeasureBuilder().create(2));
+
+    measureRepository.addRawMeasure(1112, COMPLEXITY_IN_FUNCTIONS_KEY, newMeasureBuilder().create(1));
+    measureRepository.addRawMeasure(1112, FUNCTIONS_KEY, newMeasureBuilder().create(1));
+
+    measureRepository.addRawMeasure(1211, COMPLEXITY_IN_FUNCTIONS_KEY, newMeasureBuilder().create(9));
+    measureRepository.addRawMeasure(1211, FUNCTIONS_KEY, newMeasureBuilder().create(2));
+
+    sut.visit(project);
+
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(3d)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(2d)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(2d)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1111))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(2.5d)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1112))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(1d)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(4.5d)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(4.5d)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1211))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(4.5d)));
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaStepTest.java
deleted file mode 100644 (file)
index 27d7b01..0000000
+++ /dev/null
@@ -1,121 +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.formula;
-
-import com.google.common.collect.Lists;
-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.Component;
-import org.sonar.server.computation.component.DumbComponent;
-import org.sonar.server.computation.measure.MeasureRepositoryRule;
-import org.sonar.server.computation.metric.MetricRepositoryRule;
-import org.sonar.server.computation.step.ComputeFormulaMeasuresStep;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.measures.CoreMetrics.COMPLEXITY_IN_FUNCTIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.FUNCTIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_KEY;
-import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
-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.DumbComponent.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;
-
-public class AverageFormulaStepTest {
-
-  @Rule
-  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
-  @Rule
-  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
-    .add(CoreMetrics.FUNCTION_COMPLEXITY)
-    .add(CoreMetrics.COMPLEXITY_IN_FUNCTIONS)
-    .add(CoreMetrics.FUNCTIONS);
-
-  @Rule
-  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
-
-  ComputeFormulaMeasuresStep sut;
-
-  @Before
-  public void setUp() {
-    FormulaRepository formulaRepository = mock(FormulaRepository.class);
-    when(formulaRepository.getFormulas()).thenReturn(Lists.<Formula>newArrayList(
-      AverageFormula.Builder.newBuilder()
-        .setOutputMetricKey(FUNCTION_COMPLEXITY_KEY)
-        .setMainMetricKey(COMPLEXITY_IN_FUNCTIONS_KEY)
-        .setByMetricKey(FUNCTIONS_KEY)
-        .build()));
-    sut = new ComputeFormulaMeasuresStep(treeRootHolder, measureRepository, metricRepository, formulaRepository);
-  }
-
-  @Test
-  public void add_measures() {
-    DumbComponent project = builder(PROJECT, 1)
-      .addChildren(
-        builder(MODULE, 11)
-          .addChildren(
-            builder(DIRECTORY, 111)
-              .addChildren(
-                builder(Component.Type.FILE, 1111).build(),
-                builder(Component.Type.FILE, 1112).build()
-              ).build()
-          ).build(),
-        builder(MODULE, 12)
-          .addChildren(
-            builder(DIRECTORY, 121)
-              .addChildren(
-                builder(Component.Type.FILE, 1211).build()
-              ).build()
-          ).build()
-      ).build();
-
-    treeRootHolder.setRoot(project);
-
-    measureRepository.addRawMeasure(1111, COMPLEXITY_IN_FUNCTIONS_KEY, newMeasureBuilder().create(5));
-    measureRepository.addRawMeasure(1111, FUNCTIONS_KEY, newMeasureBuilder().create(2));
-
-    measureRepository.addRawMeasure(1112, COMPLEXITY_IN_FUNCTIONS_KEY, newMeasureBuilder().create(1));
-    measureRepository.addRawMeasure(1112, FUNCTIONS_KEY, newMeasureBuilder().create(1));
-
-    measureRepository.addRawMeasure(1211, COMPLEXITY_IN_FUNCTIONS_KEY, newMeasureBuilder().create(9));
-    measureRepository.addRawMeasure(1211, FUNCTIONS_KEY, newMeasureBuilder().create(2));
-
-    sut.execute();
-
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(3d)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(2d)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(2d)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1111))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(2.5d)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1112))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(1d)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(4.5d)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(4.5d)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1211))).containsOnly(entryOf(FUNCTION_COMPLEXITY_KEY, newMeasureBuilder().create(4.5d)));
-  }
-
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaExecutionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaExecutionTest.java
new file mode 100644 (file)
index 0000000..1df455f
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import com.google.common.collect.Lists;
+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.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+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.DumbComponent.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;
+
+public class DistributionFormulaExecutionTest {
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+  @Rule
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
+  @Rule
+  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+  FormulaExecutorComponentVisitor sut;
+
+  @Before
+  public void setUp() throws Exception {
+    sut = FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
+      .buildFor(Lists.<Formula>newArrayList(new DistributionFormula(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)));
+  }
+
+  @Test
+  public void add_measures() {
+    DumbComponent project = builder(PROJECT, 1)
+      .addChildren(
+        builder(MODULE, 11)
+          .addChildren(
+            builder(DIRECTORY, 111)
+              .addChildren(
+                builder(Component.Type.FILE, 1111).build(),
+                builder(Component.Type.FILE, 1112).build()
+              ).build()
+          ).build(),
+        builder(MODULE, 12)
+          .addChildren(
+            builder(DIRECTORY, 121)
+              .addChildren(
+                builder(Component.Type.FILE, 1211).build()
+              ).build()
+          ).build()
+      ).build();
+
+    treeRootHolder.setRoot(project);
+
+    measureRepository.addRawMeasure(1111, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=3;3.5=5;6.5=9"));
+    measureRepository.addRawMeasure(1112, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=0;3.5=2;6.5=1"));
+    measureRepository.addRawMeasure(1211, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=1;3.5=3;6.5=2"));
+
+    sut.visit(project);
+
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=4;3.5=10;6.5=12")));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=3;3.5=7;6.5=10")));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=3;3.5=7;6.5=10")));
+    assertThat(measureRepository.getNewRawMeasures(1111)).isEmpty();
+    assertThat(measureRepository.getNewRawMeasures(1112)).isEmpty();
+    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=1;3.5=3;6.5=2")));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=1;3.5=3;6.5=2")));
+    assertThat(measureRepository.getNewRawMeasures(1211)).isEmpty();
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaStepTest.java
deleted file mode 100644 (file)
index a38efcf..0000000
+++ /dev/null
@@ -1,107 +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.formula;
-
-import com.google.common.collect.Lists;
-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.Component;
-import org.sonar.server.computation.component.DumbComponent;
-import org.sonar.server.computation.measure.MeasureRepositoryRule;
-import org.sonar.server.computation.metric.MetricRepositoryRule;
-import org.sonar.server.computation.step.ComputeFormulaMeasuresStep;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.guava.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY;
-import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
-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.DumbComponent.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;
-
-public class DistributionFormulaStepTest {
-
-  @Rule
-  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
-  @Rule
-  public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
-
-  @Rule
-  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
-
-  ComputeFormulaMeasuresStep sut;
-
-  @Before
-  public void setUp() {
-    FormulaRepository formulaRepository = mock(FormulaRepository.class);
-    when(formulaRepository.getFormulas()).thenReturn(Lists.<Formula>newArrayList(new DistributionFormula(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)));
-    sut = new ComputeFormulaMeasuresStep(treeRootHolder, measureRepository, metricRepository, formulaRepository);
-  }
-
-  @Test
-  public void add_measures() {
-    DumbComponent project = builder(PROJECT, 1)
-      .addChildren(
-        builder(MODULE, 11)
-          .addChildren(
-            builder(DIRECTORY, 111)
-              .addChildren(
-                builder(Component.Type.FILE, 1111).build(),
-                builder(Component.Type.FILE, 1112).build()
-              ).build()
-          ).build(),
-        builder(MODULE, 12)
-          .addChildren(
-            builder(DIRECTORY, 121)
-              .addChildren(
-                builder(Component.Type.FILE, 1211).build()
-              ).build()
-          ).build()
-      ).build();
-
-    treeRootHolder.setRoot(project);
-
-    measureRepository.addRawMeasure(1111, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=3;3.5=5;6.5=9"));
-    measureRepository.addRawMeasure(1112, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=0;3.5=2;6.5=1"));
-    measureRepository.addRawMeasure(1211, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=1;3.5=3;6.5=2"));
-
-    sut.execute();
-
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=4;3.5=10;6.5=12")));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=3;3.5=7;6.5=10")));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=3;3.5=7;6.5=10")));
-    assertThat(measureRepository.getNewRawMeasures(1111)).isEmpty();
-    assertThat(measureRepository.getNewRawMeasures(1112)).isEmpty();
-    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=1;3.5=3;6.5=2")));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0.5=1;3.5=3;6.5=2")));
-    assertThat(measureRepository.getNewRawMeasures(1211)).isEmpty();
-  }
-
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitorTest.java
new file mode 100644 (file)
index 0000000..5936f1a
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * 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;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import org.assertj.guava.api.Assertions;
+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.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.formula.counter.IntVariationValue;
+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.measures.CoreMetrics.LINES_KEY;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_IT_COVERAGE_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_LINES_TO_COVER_KEY;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+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.DumbComponent.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;
+
+public class FormulaExecutorComponentVisitorTest {
+  public static final DumbComponent BALANCED_COMPONENT_TREE = DumbComponent.builder(PROJECT, 1)
+    .addChildren(
+      DumbComponent.builder(MODULE, 11)
+        .addChildren(
+          DumbComponent.builder(DIRECTORY, 111)
+            .addChildren(
+              builder(Component.Type.FILE, 1111).build(),
+              builder(Component.Type.FILE, 1112).build()
+            ).build()
+        ).build(),
+      DumbComponent.builder(MODULE, 12)
+        .addChildren(
+          DumbComponent.builder(DIRECTORY, 121)
+            .addChildren(
+              builder(Component.Type.FILE, 1211).build()
+            ).build()
+        ).build()
+    ).build();
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+  @Rule
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+    .add(CoreMetrics.LINES)
+    .add(CoreMetrics.NCLOC)
+    .add(CoreMetrics.NEW_LINES_TO_COVER)
+    .add(CoreMetrics.NEW_IT_COVERAGE);
+  @Rule
+  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+  @Rule
+  public PeriodsHolderRule periodsHolder = new PeriodsHolderRule()
+    .setPeriods(new Period(2, "some mode", null, 95l, 756l));
+
+  FormulaExecutorComponentVisitor underTest = FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
+    .withVariationSupport(periodsHolder)
+    .buildFor(ImmutableList.<Formula>of(new FakeFormula(), new FakeVariationFormula()));
+
+  @Test
+  public void verify_aggregation_on_value() throws Exception {
+    treeRootHolder.setRoot(BALANCED_COMPONENT_TREE);
+
+    measureRepository.addRawMeasure(1111, LINES_KEY, newMeasureBuilder().create(10));
+    measureRepository.addRawMeasure(1112, LINES_KEY, newMeasureBuilder().create(8));
+    measureRepository.addRawMeasure(1211, LINES_KEY, newMeasureBuilder().create(2));
+
+    underTest.visit(BALANCED_COMPONENT_TREE);
+
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(20)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(18)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(18)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1111))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(10)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1112))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(8)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(2)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(2)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1211))).containsOnly(entryOf(NCLOC_KEY, newMeasureBuilder().create(2)));
+  }
+
+  @Test
+  public void verify_aggregation_on_variations() throws Exception {
+    treeRootHolder.setRoot(BALANCED_COMPONENT_TREE);
+
+    measureRepository.addRawMeasure(1111, NEW_LINES_TO_COVER_KEY, createMeasureWithVariation(10));
+    measureRepository.addRawMeasure(1112, NEW_LINES_TO_COVER_KEY, createMeasureWithVariation(8));
+    measureRepository.addRawMeasure(1211, NEW_LINES_TO_COVER_KEY, createMeasureWithVariation(2));
+
+    underTest.visit(BALANCED_COMPONENT_TREE);
+
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(NEW_IT_COVERAGE_KEY, createMeasureWithVariation(20)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(entryOf(NEW_IT_COVERAGE_KEY, createMeasureWithVariation(18)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(entryOf(NEW_IT_COVERAGE_KEY, createMeasureWithVariation(18)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1111))).containsOnly(entryOf(NEW_IT_COVERAGE_KEY, createMeasureWithVariation(10)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1112))).containsOnly(entryOf(NEW_IT_COVERAGE_KEY, createMeasureWithVariation(8)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(NEW_IT_COVERAGE_KEY, createMeasureWithVariation(2)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(NEW_IT_COVERAGE_KEY, createMeasureWithVariation(2)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1211))).containsOnly(entryOf(NEW_IT_COVERAGE_KEY, createMeasureWithVariation(2)));
+  }
+
+  private static Measure createMeasureWithVariation(double variation2Value) {
+    return newMeasureBuilder().setVariations(new MeasureVariations(null, null, variation2Value)).createNoValue();
+  }
+
+  @Test
+  public void add_no_measure() throws Exception {
+    DumbComponent project = DumbComponent.builder(PROJECT, 1)
+      .addChildren(
+        DumbComponent.builder(MODULE, 11)
+          .addChildren(
+            DumbComponent.builder(DIRECTORY, 111)
+              .addChildren(
+                builder(Component.Type.FILE, 1111).build()
+              ).build()
+          ).build()
+      ).build();
+    treeRootHolder.setRoot(project);
+
+    underTest.visit(project);
+
+    Assertions.assertThat(measureRepository.getNewRawMeasures(1)).isEmpty();
+    Assertions.assertThat(measureRepository.getNewRawMeasures(11)).isEmpty();
+    Assertions.assertThat(measureRepository.getNewRawMeasures(111)).isEmpty();
+    Assertions.assertThat(measureRepository.getNewRawMeasures(1111)).isEmpty();
+  }
+
+  @Test
+  public void add_no_measure_when_no_file() throws Exception {
+    DumbComponent project = DumbComponent.builder(PROJECT, 1)
+      .addChildren(
+        DumbComponent.builder(MODULE, 11)
+          .addChildren(
+            DumbComponent.builder(DIRECTORY, 111).build()
+          ).build()
+      ).build();
+    treeRootHolder.setRoot(project);
+
+    underTest.visit(project);
+
+    assertThat(measureRepository.getNewRawMeasures(1)).isEmpty();
+    assertThat(measureRepository.getNewRawMeasures(11)).isEmpty();
+    assertThat(measureRepository.getNewRawMeasures(111)).isEmpty();
+  }
+
+  private static class FakeFormula implements Formula<FakeCounter> {
+
+    @Override
+    public FakeCounter createNewCounter() {
+      return new FakeCounter();
+    }
+
+    @Override
+    public Optional<Measure> createMeasure(FakeCounter counter, Component.Type componentType) {
+      if (counter.value <= 0) {
+        return Optional.absent();
+      }
+      return Optional.of(Measure.newMeasureBuilder().create(counter.value));
+    }
+
+    @Override
+    public String getOutputMetricKey() {
+      return NCLOC_KEY;
+    }
+  }
+
+  private static class FakeCounter implements Counter<FakeCounter> {
+    private int value = 0;
+
+    @Override
+    public void aggregate(FakeCounter counter) {
+      this.value += counter.value;
+    }
+
+    @Override
+    public void aggregate(CounterContext counterContext) {
+      Optional<Measure> measureOptional = counterContext.getMeasure(LINES_KEY);
+      if (measureOptional.isPresent()) {
+        value += measureOptional.get().getIntValue();
+      }
+    }
+  }
+
+  private static class FakeVariationFormula implements Formula<FakeVariationCounter> {
+
+    @Override
+    public FakeVariationCounter createNewCounter() {
+      return new FakeVariationCounter();
+    }
+
+    @Override
+    public Optional<Measure> createMeasure(FakeVariationCounter counter, Component.Type componentType) {
+      Optional<MeasureVariations> measureVariations = counter.values.toMeasureVariations();
+      if (measureVariations.isPresent()) {
+        return Optional.of(
+          newMeasureBuilder()
+            .setVariations(measureVariations.get())
+            .createNoValue()
+          );
+      }
+      return Optional.absent();
+    }
+
+    @Override
+    public String getOutputMetricKey() {
+      return NEW_IT_COVERAGE_KEY;
+    }
+  }
+
+  private static class FakeVariationCounter implements Counter<FakeVariationCounter> {
+    private final IntVariationValue.Array values = IntVariationValue.newArray();
+
+    @Override
+    public void aggregate(FakeVariationCounter counter) {
+      values.incrementAll(counter.values);
+    }
+
+    @Override
+    public void aggregate(CounterContext counterContext) {
+      Optional<Measure> measureOptional = counterContext.getMeasure(NEW_LINES_TO_COVER_KEY);
+      if (!measureOptional.isPresent()) {
+        return;
+      }
+      for (Period period : counterContext.getPeriods()) {
+        this.values.increment(
+          period.getIndex(),
+          (int) measureOptional.get().getVariations().getVariation(period.getIndex() + 1));
+      }
+    }
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaExecutionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaExecutionTest.java
new file mode 100644 (file)
index 0000000..01c8c0e
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import com.google.common.collect.Lists;
+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.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+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.DumbComponent.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;
+
+public class SumFormulaExecutionTest {
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+  @Rule
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(CoreMetrics.LINES);
+  @Rule
+  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+  FormulaExecutorComponentVisitor sut;
+
+  @Before
+  public void setUp() throws Exception {
+    sut = FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
+      .buildFor(Lists.<Formula>newArrayList(new SumFormula(LINES_KEY)));
+  }
+
+  @Test
+  public void add_measures() {
+    DumbComponent project = builder(PROJECT, 1)
+      .addChildren(
+        builder(MODULE, 11)
+          .addChildren(
+            builder(DIRECTORY, 111)
+              .addChildren(
+                builder(Component.Type.FILE, 1111).build(),
+                builder(Component.Type.FILE, 1112).build()
+              ).build()
+          ).build(),
+        builder(MODULE, 12)
+          .addChildren(
+            builder(DIRECTORY, 121)
+              .addChildren(
+                builder(Component.Type.FILE, 1211).build()
+              ).build()
+          ).build()
+      ).build();
+
+    treeRootHolder.setRoot(project);
+
+    measureRepository.addRawMeasure(1111, LINES_KEY, newMeasureBuilder().create(10));
+    measureRepository.addRawMeasure(1112, LINES_KEY, newMeasureBuilder().create(8));
+    measureRepository.addRawMeasure(1211, LINES_KEY, newMeasureBuilder().create(2));
+
+    sut.visit(project);
+
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(20)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(18)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(18)));
+    assertThat(measureRepository.getNewRawMeasures(1111)).isEmpty();
+    assertThat(measureRepository.getNewRawMeasures(1112)).isEmpty();
+    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(2)));
+    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(2)));
+    assertThat(measureRepository.getNewRawMeasures(1211)).isEmpty();
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaStepTest.java
deleted file mode 100644 (file)
index 074bcc5..0000000
+++ /dev/null
@@ -1,107 +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.formula;
-
-import com.google.common.collect.Lists;
-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.Component;
-import org.sonar.server.computation.component.DumbComponent;
-import org.sonar.server.computation.measure.MeasureRepositoryRule;
-import org.sonar.server.computation.metric.MetricRepositoryRule;
-import org.sonar.server.computation.step.ComputeFormulaMeasuresStep;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.guava.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
-import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
-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.DumbComponent.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;
-
-public class SumFormulaStepTest {
-
-  @Rule
-  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
-  @Rule
-  public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(CoreMetrics.LINES);
-
-  @Rule
-  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
-
-  ComputeFormulaMeasuresStep sut;
-
-  @Before
-  public void setUp() {
-    FormulaRepository formulaRepository = mock(FormulaRepository.class);
-    when(formulaRepository.getFormulas()).thenReturn(Lists.<Formula>newArrayList(new SumFormula(LINES_KEY)));
-    sut = new ComputeFormulaMeasuresStep(treeRootHolder, measureRepository, metricRepository, formulaRepository);
-  }
-
-  @Test
-  public void add_measures() {
-    DumbComponent project = builder(PROJECT, 1)
-      .addChildren(
-        builder(MODULE, 11)
-          .addChildren(
-            builder(DIRECTORY, 111)
-              .addChildren(
-                builder(Component.Type.FILE, 1111).build(),
-                builder(Component.Type.FILE, 1112).build()
-              ).build()
-          ).build(),
-        builder(MODULE, 12)
-          .addChildren(
-            builder(DIRECTORY, 121)
-              .addChildren(
-                builder(Component.Type.FILE, 1211).build()
-              ).build()
-          ).build()
-      ).build();
-
-    treeRootHolder.setRoot(project);
-
-    measureRepository.addRawMeasure(1111, LINES_KEY, newMeasureBuilder().create(10));
-    measureRepository.addRawMeasure(1112, LINES_KEY, newMeasureBuilder().create(8));
-    measureRepository.addRawMeasure(1211, LINES_KEY, newMeasureBuilder().create(2));
-
-    sut.execute();
-
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(20)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(18)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(18)));
-    assertThat(measureRepository.getNewRawMeasures(1111)).isEmpty();
-    assertThat(measureRepository.getNewRawMeasures(1112)).isEmpty();
-    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(2)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(LINES_KEY, newMeasureBuilder().create(2)));
-    assertThat(measureRepository.getNewRawMeasures(1211)).isEmpty();
-  }
-
-}
index 0df6577d6c7572555ae22d8135f5bdfb03307acb..8e7179443ac57542e4bf5716cdc3f25cac8ac409 100644 (file)
@@ -47,9 +47,10 @@ public class PeriodsHolderRule implements TestRule, PeriodsHolder {
     this.delegate = new PeriodsHolderImpl();
   }
 
-  public void setPeriods(Period... periods) {
+  public PeriodsHolderRule setPeriods(Period... periods) {
     delegate = new PeriodsHolderImpl();
     delegate.setPeriods(Arrays.asList(periods));
+    return this;
   }
 
   @Override
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStepTest.java
deleted file mode 100644 (file)
index 5fb4c2d..0000000
+++ /dev/null
@@ -1,222 +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 com.google.common.base.Optional;
-import com.google.common.collect.Lists;
-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.Component;
-import org.sonar.server.computation.component.DumbComponent;
-import org.sonar.server.computation.formula.Counter;
-import org.sonar.server.computation.formula.CounterContext;
-import org.sonar.server.computation.formula.Formula;
-import org.sonar.server.computation.formula.FormulaRepository;
-import org.sonar.server.computation.measure.Measure;
-import org.sonar.server.computation.measure.MeasureRepositoryRule;
-import org.sonar.server.computation.metric.MetricRepositoryRule;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.guava.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
-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.DumbComponent.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;
-
-public class ComputeFormulaMeasuresStepTest {
-
-  @Rule
-  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
-  @Rule
-  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
-    .add(CoreMetrics.LINES)
-    .add(CoreMetrics.NCLOC);
-
-  @Rule
-  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
-
-  ComputeFormulaMeasuresStep sut;
-
-  @Before
-  public void setUp() {
-    FormulaRepository formulaRepository = mock(FormulaRepository.class);
-    when(formulaRepository.getFormulas()).thenReturn(Lists.<Formula>newArrayList(new FakeFormula()));
-    sut = new ComputeFormulaMeasuresStep(treeRootHolder, measureRepository, metricRepository, formulaRepository);
-  }
-
-  @Test
-  public void add_measures() {
-    DumbComponent project = DumbComponent.builder(PROJECT, 1)
-      .addChildren(
-        DumbComponent.builder(MODULE, 11)
-          .addChildren(
-            DumbComponent.builder(DIRECTORY, 111)
-              .addChildren(
-                builder(Component.Type.FILE, 1111).build(),
-                builder(Component.Type.FILE, 1112).build()
-              ).build()
-          ).build(),
-        DumbComponent.builder(MODULE, 12)
-          .addChildren(
-            DumbComponent.builder(DIRECTORY, 121)
-              .addChildren(
-                builder(Component.Type.FILE, 1211).build()
-              ).build()
-          ).build()
-      ).build();
-
-    treeRootHolder.setRoot(project);
-
-    measureRepository.addRawMeasure(1111, CoreMetrics.LINES_KEY, newMeasureBuilder().create(10));
-    measureRepository.addRawMeasure(1112, CoreMetrics.LINES_KEY, newMeasureBuilder().create(8));
-    measureRepository.addRawMeasure(1211, CoreMetrics.LINES_KEY, newMeasureBuilder().create(2));
-
-    sut.execute();
-
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(20)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(18)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(18)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1111))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(10)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1112))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(8)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(2)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(2)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1211))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(2)));
-  }
-
-  @Test
-  public void add_no_measure() {
-    DumbComponent project = DumbComponent.builder(PROJECT, 1)
-      .addChildren(
-        DumbComponent.builder(MODULE, 11)
-          .addChildren(
-            DumbComponent.builder(DIRECTORY, 111)
-              .addChildren(
-                builder(Component.Type.FILE, 1111).build()
-              ).build()
-          ).build()
-      ).build();
-
-    treeRootHolder.setRoot(project);
-
-    sut.execute();
-
-    assertThat(measureRepository.getNewRawMeasures(1)).isEmpty();
-    assertThat(measureRepository.getNewRawMeasures(11)).isEmpty();
-    assertThat(measureRepository.getNewRawMeasures(111)).isEmpty();
-    assertThat(measureRepository.getNewRawMeasures(1111)).isEmpty();
-  }
-
-  @Test
-  public void add_no_measure_when_no_file() {
-    DumbComponent project = DumbComponent.builder(PROJECT, 1)
-      .addChildren(
-        DumbComponent.builder(MODULE, 11)
-          .addChildren(
-            DumbComponent.builder(DIRECTORY, 111).build()
-          ).build()
-      ).build();
-
-    treeRootHolder.setRoot(project);
-
-    sut.execute();
-
-    assertThat(measureRepository.getNewRawMeasures(1)).isEmpty();
-    assertThat(measureRepository.getNewRawMeasures(11)).isEmpty();
-    assertThat(measureRepository.getNewRawMeasures(111)).isEmpty();
-  }
-
-  @Test
-  public void add_no_measure_on_module_without_file() {
-    DumbComponent project = DumbComponent.builder(PROJECT, 1)
-      .addChildren(
-        DumbComponent.builder(MODULE, 11)
-          .addChildren(
-            DumbComponent.builder(DIRECTORY, 111).build()
-          ).build(),
-        DumbComponent.builder(MODULE, 12)
-          .addChildren(
-            DumbComponent.builder(DIRECTORY, 121)
-              .addChildren(
-                builder(Component.Type.FILE, 1211).build()
-              ).build()
-          ).build()
-      ).build();
-    treeRootHolder.setRoot(project);
-    measureRepository.addRawMeasure(1211, CoreMetrics.LINES_KEY, newMeasureBuilder().create(10));
-
-    sut.execute();
-
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(10)));
-    assertThat(measureRepository.getNewRawMeasures(11)).isEmpty();
-    assertThat(measureRepository.getNewRawMeasures(111)).isEmpty();
-    assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(10)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(10)));
-    assertThat(toEntries(measureRepository.getNewRawMeasures(1211))).containsOnly(entryOf(CoreMetrics.NCLOC_KEY, newMeasureBuilder().create(10)));
-  }
-
-  private static class FakeFormula implements Formula<FakeCounter> {
-
-    @Override
-    public FakeCounter createNewCounter() {
-      return new FakeCounter();
-    }
-
-    @Override
-    public Optional<Measure> createMeasure(FakeCounter counter, Component.Type componentType) {
-      if (counter.value <= 0) {
-        return Optional.absent();
-      }
-      return Optional.of(Measure.newMeasureBuilder().create(counter.value));
-    }
-
-    @Override
-    public String getOutputMetricKey() {
-      return CoreMetrics.NCLOC_KEY;
-    }
-  }
-
-  private static class FakeCounter implements Counter<FakeCounter> {
-
-    private int value = 0;
-
-    @Override
-    public void aggregate(FakeCounter counter) {
-      this.value += counter.value;
-    }
-
-    @Override
-    public void aggregate(CounterContext counterContext) {
-      Optional<Measure> measureOptional = counterContext.getMeasure(CoreMetrics.LINES_KEY);
-      if (measureOptional.isPresent()) {
-        value += measureOptional.get().getIntValue();
-      }
-    }
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStepTest.java
new file mode 100644 (file)
index 0000000..a38b736
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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 org.junit.Rule;
+import org.junit.Test;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.formula.CoreFormulaRepository;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.MetricRepository;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class CoreMetricFormulaExecutorStepTest {
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+  @Test
+  public void verify_execute_formulas_from_CoreFormulaRepository() {
+    CoreFormulaRepository coreFormulaRepository = mock(CoreFormulaRepository.class);
+    treeRootHolder.setRoot(DumbComponent.builder(Component.Type.PROJECT, 1).build());
+
+    CoreMetricFormulaExecutorStep underTest = new CoreMetricFormulaExecutorStep(treeRootHolder,
+      mock(MetricRepository.class), mock(MeasureRepository.class), coreFormulaRepository);
+
+    underTest.execute();
+
+    verify(coreFormulaRepository, times(1)).getFormulas();
+  }
+}