From: Sébastien Lesaint Date: Wed, 8 Jul 2015 14:41:48 +0000 (+0200) Subject: add FormulaExecutorComponentVisitor + variation aggregation support X-Git-Tag: 5.2-RC1~1160 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d603b0e328f689e8094cbd42656b588be3d75d07;p=sonarqube.git 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 --- 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 index 00000000000..4629cba61d3 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/CoreFormulaRepository.java @@ -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 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 FORMULAS = ImmutableList.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 getMeasure(String metricKey); + /** + * Lists of Periods defined for the current project. They can be used to aggregate values of MeasureVariations. + */ + List 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 { + private static final PathAwareVisitor.SimpleStackElementFactory COUNTERS_FACTORY = new PathAwareVisitor.SimpleStackElementFactory() { + + @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 formulas; + + private FormulaExecutorComponentVisitor(Builder builder, List 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 formulas) { + return new FormulaExecutorComponentVisitor(this, formulas); + } + } + + + + @Override + protected void visitProject(Component project, Path path) { + processNotFile(project, path); + } + + @Override + protected void visitModule(Component module, Path path) { + processNotFile(module, path); + } + + @Override + protected void visitDirectory(Component directory, Path path) { + processNotFile(directory, path); + } + + @Override + protected void visitFile(Component file, Path path) { + processFile(file, path); + } + + private void processNotFile(Component component, Path 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 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 = formula.createMeasure(counter, component.getType()); + if (measure.isPresent()) { + measureRepository.add(component, metric, measure.get()); + } + } + + private void aggregateToParent(Path 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 getMeasure(String metricKey) { + return measureRepository.getRawMeasure(file, metricRepository.getByKey(metricKey)); + } + + @Override + public List getPeriods() { + return periodsHolder.getPeriods(); + } + } + + public static class Counters { + Map 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 index a59334abb7a..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/FormulaRepository.java +++ /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 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 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. + *

+ * Basically, this class will be used in a {@link Counter} implementation as an array property which can be easily + * creating using method {@link #newArray()}. + *

+ */ +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 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. + *

+ * Basically, this class will be used in a {@link Counter} implementation as an array property which can be easily + * creating using method {@link #newArray()}. + *

+ */ +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 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. + *

+ * Basically, this class will be used in a {@link Counter} implementation as an array property which can be easily + * creating using method {@link #newArray()}. + *

+ */ +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 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 { - - public ComputeFormulaMeasureVisitor() { - super(Component.Type.FILE, ComponentVisitor.Order.POST_ORDER, new SimpleStackElementFactory() { - - @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 path) { - processNotFile(project, path); - } - - @Override - protected void visitModule(Component module, Path path) { - processNotFile(module, path); - } - - @Override - protected void visitDirectory(Component directory, Path path) { - processNotFile(directory, path); - } - - @Override - protected void visitFile(Component file, Path path) { - processFile(file, path); - } - - private void processNotFile(Component component, PathAwareVisitor.Path 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 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 = formula.createMeasure(counter, component.getType()); - if (measure.isPresent()) { - measureRepository.add(component, metric, measure.get()); - } - } - - private void aggregateToParent(PathAwareVisitor.Path 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 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 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/AverageFormulaExecutionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaExecutionTest.java new file mode 100644 index 00000000000..9ce12223fe5 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaExecutionTest.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; + +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.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 index 27d7b0196ad..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/AverageFormulaStepTest.java +++ /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.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 index 00000000000..1df455ff261 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaExecutionTest.java @@ -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.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 index a38efcf0cf6..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/DistributionFormulaStepTest.java +++ /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.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 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.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 { + + @Override + public FakeCounter createNewCounter() { + return new FakeCounter(); + } + + @Override + public Optional 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 { + private int value = 0; + + @Override + public void aggregate(FakeCounter counter) { + this.value += counter.value; + } + + @Override + public void aggregate(CounterContext counterContext) { + Optional measureOptional = counterContext.getMeasure(LINES_KEY); + if (measureOptional.isPresent()) { + value += measureOptional.get().getIntValue(); + } + } + } + + private static class FakeVariationFormula implements Formula { + + @Override + public FakeVariationCounter createNewCounter() { + return new FakeVariationCounter(); + } + + @Override + public Optional createMeasure(FakeVariationCounter counter, Component.Type componentType) { + Optional 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 { + private final IntVariationValue.Array values = IntVariationValue.newArray(); + + @Override + public void aggregate(FakeVariationCounter counter) { + values.incrementAll(counter.values); + } + + @Override + public void aggregate(CounterContext counterContext) { + Optional 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 index 00000000000..01c8c0ef265 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaExecutionTest.java @@ -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.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 index 074bcc58ee8..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/SumFormulaStepTest.java +++ /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.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(); - } - -} 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.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 { - - @Override - public FakeCounter createNewCounter() { - return new FakeCounter(); - } - - @Override - public Optional 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 { - - private int value = 0; - - @Override - public void aggregate(FakeCounter counter) { - this.value += counter.value; - } - - @Override - public void aggregate(CounterContext counterContext) { - Optional 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(); + } +}