aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2015-07-08 16:41:48 +0200
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>2015-07-09 09:24:48 +0200
commitd603b0e328f689e8094cbd42656b588be3d75d07 (patch)
tree98ea0899754b6bc50f33b9b57d35abf84c948308 /server
parent8f502d25123940c965e3d58418695a8d45a0e5e7 (diff)
downloadsonarqube-d603b0e328f689e8094cbd42656b588be3d75d07.tar.gz
sonarqube-d603b0e328f689e8094cbd42656b588be3d75d07.zip
add FormulaExecutorComponentVisitor + variation aggregation support
FormulaExecutorComponentVisitor allows having any step execute Formula Formula can now do aggregation of MeasureVariation values also renamed FormulaRepository to CoreFormulaRepository and ComputeFormulaMeasuresStep to CoreMetricFormulaExecutorStep
Diffstat (limited to 'server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepository.java (renamed from server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaRepository.java)2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepositoryImpl.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/formula/CounterContext.java7
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitor.java192
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/DoubleVariationValue.java118
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/IntVariationValue.java118
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/LongVariationValue.java117
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStep.java176
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStep.java57
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaExecutionTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaStepTest.java)31
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaExecutionTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaStepTest.java)18
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitorTest.java261
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaExecutionTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaStepTest.java)18
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsHolderRule.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStepTest.java222
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStepTest.java51
17 files changed, 952 insertions, 443 deletions
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/CoreFormulaRepository.java
index a59334abb7a..4629cba61d3 100644
--- 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/CoreFormulaRepository.java
@@ -22,7 +22,7 @@ package org.sonar.server.computation.formula;
import java.util.List;
-public interface FormulaRepository {
+public interface CoreFormulaRepository {
List<Formula> getFormulas();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepositoryImpl.java
index 9b729e2bd64..46dc36ad830 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepositoryImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepositoryImpl.java
@@ -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
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CounterContext.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CounterContext.java
index c34ffdac8ff..0da22ee9d24 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CounterContext.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CounterContext.java
@@ -21,10 +21,17 @@
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
index 00000000000..94a26817791
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitor.java
@@ -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/counter/DoubleVariationValue.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/DoubleVariationValue.java
new file mode 100644
index 00000000000..b2b39340fd9
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/DoubleVariationValue.java
@@ -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
index 00000000000..8bcb5bf4d41
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/IntVariationValue.java
@@ -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
index 00000000000..a79706b4bcf
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/counter/LongVariationValue.java
@@ -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;
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
index 46c8c4b9241..b1ba6eeec24 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
@@ -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
index e1d81e03866..00000000000
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStep.java
+++ /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
index 00000000000..851e3e1b3d3
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStep.java
@@ -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/AverageFormulaStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaExecutionTest.java
index 27d7b0196ad..9ce12223fe5 100644
--- 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/AverageFormulaExecutionTest.java
@@ -30,11 +30,9 @@ 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 org.sonar.server.computation.period.PeriodsHolderRule;
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;
@@ -46,32 +44,31 @@ 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 {
+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();
- ComputeFormulaMeasuresStep sut;
+ FormulaExecutorComponentVisitor 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);
+ 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
@@ -106,7 +103,7 @@ public class AverageFormulaStepTest {
measureRepository.addRawMeasure(1211, COMPLEXITY_IN_FUNCTIONS_KEY, newMeasureBuilder().create(9));
measureRepository.addRawMeasure(1211, FUNCTIONS_KEY, newMeasureBuilder().create(2));
- sut.execute();
+ 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)));
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/DistributionFormulaExecutionTest.java
index a38efcf0cf6..1df455ff261 100644
--- 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/DistributionFormulaExecutionTest.java
@@ -30,12 +30,9 @@ 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;
@@ -45,24 +42,21 @@ 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 {
+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);
- ComputeFormulaMeasuresStep sut;
+ FormulaExecutorComponentVisitor 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);
+ public void setUp() throws Exception {
+ sut = FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
+ .buildFor(Lists.<Formula>newArrayList(new DistributionFormula(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)));
}
@Test
@@ -92,7 +86,7 @@ public class DistributionFormulaStepTest {
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();
+ 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")));
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
index 00000000000..5936f1aad9d
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/FormulaExecutorComponentVisitorTest.java
@@ -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/SumFormulaStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaExecutionTest.java
index 074bcc58ee8..01c8c0ef265 100644
--- 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/SumFormulaExecutionTest.java
@@ -30,12 +30,9 @@ 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;
@@ -45,24 +42,21 @@ 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 {
+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);
- ComputeFormulaMeasuresStep sut;
+ FormulaExecutorComponentVisitor 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);
+ public void setUp() throws Exception {
+ sut = FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
+ .buildFor(Lists.<Formula>newArrayList(new SumFormula(LINES_KEY)));
}
@Test
@@ -92,7 +86,7 @@ public class SumFormulaStepTest {
measureRepository.addRawMeasure(1112, LINES_KEY, newMeasureBuilder().create(8));
measureRepository.addRawMeasure(1211, LINES_KEY, newMeasureBuilder().create(2));
- sut.execute();
+ 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)));
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsHolderRule.java
index 0df6577d6c7..8e7179443ac 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsHolderRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsHolderRule.java
@@ -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
index 5fb4c2df4ad..00000000000
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeFormulaMeasuresStepTest.java
+++ /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
index 00000000000..a38b736ac1c
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/CoreMetricFormulaExecutorStepTest.java
@@ -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();
+ }
+}