aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2015-06-26 18:56:40 +0200
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>2015-07-01 11:07:22 +0200
commit14a416be493074ac409e3d753b56fb06048624e1 (patch)
treed00ba0bb093d241d723c1fdf55973c55bdba63ee /server/sonar-server
parent63cccef9e2f7e297fc5423f7dd8fa8f3508b265b (diff)
downloadsonarqube-14a416be493074ac409e3d753b56fb06048624e1.tar.gz
sonarqube-14a416be493074ac409e3d753b56fb06048624e1.zip
SONAR-6645 move New Coverage computation to Compute Engine
Diffstat (limited to 'server/sonar-server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/FileCoverageMetricKeys.java59
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/ItFileCoverageMetricKeys.java59
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/NewCoverageMetricKeys.java41
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/NewCoverageMetricKeysModule.java32
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/OverallFileCoverageMetricKeys.java59
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/package-info.java24
-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/NewCoverageAggregationStep.java249
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/NewCoverageMeasuresStep.java352
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageAggregationStepTest.java293
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageMeasuresStepTest.java334
13 files changed, 1510 insertions, 2 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
index 2c7e62a7c88..dad331ca5ff 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
@@ -51,6 +51,7 @@ import org.sonar.server.computation.issue.ScmAccountCacheLoader;
import org.sonar.server.computation.issue.SourceLinesCache;
import org.sonar.server.computation.language.LanguageRepositoryImpl;
import org.sonar.server.computation.measure.MeasureRepositoryImpl;
+import org.sonar.server.computation.measure.newcoverage.NewCoverageMetricKeysModule;
import org.sonar.server.computation.metric.MetricRepositoryImpl;
import org.sonar.server.computation.period.PeriodsHolderImpl;
import org.sonar.server.computation.qualitygate.EvaluationResultTextConverterImpl;
@@ -149,6 +150,9 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co
QualityGateServiceImpl.class,
EvaluationResultTextConverterImpl.class,
+ // new coverage measures
+ NewCoverageMetricKeysModule.class,
+
// issues
ScmAccountCacheLoader.class,
ScmAccountCache.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/FileCoverageMetricKeys.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/FileCoverageMetricKeys.java
new file mode 100644
index 00000000000..004abe6573e
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/FileCoverageMetricKeys.java
@@ -0,0 +1,59 @@
+/*
+ * 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.measure.newcoverage;
+
+import org.sonar.api.measures.CoreMetrics;
+
+public class FileCoverageMetricKeys implements NewCoverageMetricKeys {
+ @Override
+ public String coverageLineHitsData() {
+ return CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY;
+ }
+
+ @Override
+ public String conditionsByLine() {
+ return CoreMetrics.CONDITIONS_BY_LINE_KEY;
+ }
+
+ @Override
+ public String coveredConditionsByLine() {
+ return CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY;
+ }
+
+ @Override
+ public String newLinesToCover() {
+ return CoreMetrics.NEW_LINES_TO_COVER_KEY;
+ }
+
+ @Override
+ public String newUncoveredLines() {
+ return CoreMetrics.NEW_UNCOVERED_LINES_KEY;
+ }
+
+ @Override
+ public String newConditionsToCover() {
+ return CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY;
+ }
+
+ @Override
+ public String newUncoveredConditions() {
+ return CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/ItFileCoverageMetricKeys.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/ItFileCoverageMetricKeys.java
new file mode 100644
index 00000000000..e6676fac7fc
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/ItFileCoverageMetricKeys.java
@@ -0,0 +1,59 @@
+/*
+ * 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.measure.newcoverage;
+
+import org.sonar.api.measures.CoreMetrics;
+
+public class ItFileCoverageMetricKeys implements NewCoverageMetricKeys {
+ @Override
+ public String coverageLineHitsData() {
+ return CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY;
+ }
+
+ @Override
+ public String conditionsByLine() {
+ return CoreMetrics.IT_CONDITIONS_BY_LINE_KEY;
+ }
+
+ @Override
+ public String coveredConditionsByLine() {
+ return CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY;
+ }
+
+ @Override
+ public String newLinesToCover() {
+ return CoreMetrics.NEW_IT_LINES_TO_COVER_KEY;
+ }
+
+ @Override
+ public String newUncoveredLines() {
+ return CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY;
+ }
+
+ @Override
+ public String newConditionsToCover() {
+ return CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY;
+ }
+
+ @Override
+ public String newUncoveredConditions() {
+ return CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/NewCoverageMetricKeys.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/NewCoverageMetricKeys.java
new file mode 100644
index 00000000000..7aa98942aaa
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/NewCoverageMetricKeys.java
@@ -0,0 +1,41 @@
+/*
+ * 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.measure.newcoverage;
+
+/**
+ * Holds the keys of the 3 metrics from which are computed 4 new coverage metrics.
+ */
+public interface NewCoverageMetricKeys {
+ /* input metrics */
+ String coverageLineHitsData();
+
+ String conditionsByLine();
+
+ String coveredConditionsByLine();
+
+ /* output metrics */
+ String newLinesToCover();
+
+ String newUncoveredLines();
+
+ String newConditionsToCover();
+
+ String newUncoveredConditions();
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/NewCoverageMetricKeysModule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/NewCoverageMetricKeysModule.java
new file mode 100644
index 00000000000..35014bb71ce
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/NewCoverageMetricKeysModule.java
@@ -0,0 +1,32 @@
+/*
+ * 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.measure.newcoverage;
+
+import org.sonar.core.component.Module;
+
+public class NewCoverageMetricKeysModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ FileCoverageMetricKeys.class,
+ ItFileCoverageMetricKeys.class,
+ OverallFileCoverageMetricKeys.class);
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/OverallFileCoverageMetricKeys.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/OverallFileCoverageMetricKeys.java
new file mode 100644
index 00000000000..9297fa7572f
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/OverallFileCoverageMetricKeys.java
@@ -0,0 +1,59 @@
+/*
+ * 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.measure.newcoverage;
+
+import org.sonar.api.measures.CoreMetrics;
+
+public class OverallFileCoverageMetricKeys implements NewCoverageMetricKeys {
+ @Override
+ public String coverageLineHitsData() {
+ return CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY;
+ }
+
+ @Override
+ public String conditionsByLine() {
+ return CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY;
+ }
+
+ @Override
+ public String coveredConditionsByLine() {
+ return CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY;
+ }
+
+ @Override
+ public String newLinesToCover() {
+ return CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY;
+ }
+
+ @Override
+ public String newUncoveredLines() {
+ return CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY;
+ }
+
+ @Override
+ public String newConditionsToCover() {
+ return CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY;
+ }
+
+ @Override
+ public String newUncoveredConditions() {
+ return CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/package-info.java
new file mode 100644
index 00000000000..656c487e8bd
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/newcoverage/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation.measure.newcoverage;
+
+import javax.annotation.ParametersAreNonnullByDefault;
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 d4e6d6fa0ff..a40b9e94fda 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
@@ -57,6 +57,8 @@ public class ComputationSteps {
CustomMeasuresCopyStep.class,
ComputeIssueMeasuresStep.class,
SqaleMeasuresStep.class,
+ NewCoverageMeasuresStep.class,
+ NewCoverageAggregationStep.class,
// Must be executed after computation of all measures
FillMeasuresWithVariationsStep.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/NewCoverageAggregationStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/NewCoverageAggregationStep.java
new file mode 100644
index 00000000000..91f878d99c5
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/NewCoverageAggregationStep.java
@@ -0,0 +1,249 @@
+/*
+ * 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.Function;
+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.Nonnull;
+import javax.annotation.Nullable;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.PathAwareVisitor;
+import org.sonar.server.computation.component.TreeRootHolder;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.measure.MeasureVariations;
+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 com.google.common.collect.FluentIterable.from;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER;
+import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
+
+public class NewCoverageAggregationStep implements ComputationStep {
+ private static final List<String> AGGREGATED_METRIC_KEYS = ImmutableList.of(
+ CoreMetrics.NEW_LINES_TO_COVER_KEY,
+ CoreMetrics.NEW_UNCOVERED_LINES_KEY,
+ CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY,
+ CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY,
+ CoreMetrics.NEW_IT_LINES_TO_COVER_KEY,
+ CoreMetrics.NEW_IT_UNCOVERED_LINES_KEY,
+ CoreMetrics.NEW_IT_CONDITIONS_TO_COVER_KEY,
+ CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS_KEY,
+ CoreMetrics.NEW_OVERALL_LINES_TO_COVER_KEY,
+ CoreMetrics.NEW_OVERALL_UNCOVERED_LINES_KEY,
+ CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER_KEY,
+ CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS_KEY
+ );
+ private static final VariationBuildersFactory VARIATION_BUILDERS_FACTORY = new VariationBuildersFactory();
+
+ private final TreeRootHolder treeRootHolder;
+ private final PeriodsHolder periodsHolder;
+ private final MeasureRepository measureRepository;
+ private final List<Metric> metrics;
+
+ public NewCoverageAggregationStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder,
+ final MetricRepository metricRepository, MeasureRepository measureRepository) {
+ this.treeRootHolder = treeRootHolder;
+ this.periodsHolder = periodsHolder;
+ this.measureRepository = measureRepository;
+ this.metrics = from(AGGREGATED_METRIC_KEYS)
+ .transform(new Function<String, Metric>() {
+ @Override
+ @Nonnull
+ public Metric apply(@Nonnull String input) {
+ return metricRepository.getByKey(input);
+ }
+ }).toList();
+ }
+
+ @Override
+ public void execute() {
+ new NewCoverageAggregationComponentVisitor()
+ .visit(treeRootHolder.getRoot());
+ }
+
+ private final class NewCoverageAggregationComponentVisitor extends PathAwareVisitor<VariationBuilders> {
+ public NewCoverageAggregationComponentVisitor() {
+ super(FILE, POST_ORDER, VARIATION_BUILDERS_FACTORY);
+ }
+
+ @Override
+ protected void visitProject(Component project, Path<VariationBuilders> path) {
+ addNewMeasures(project, path.current());
+ }
+
+ @Override
+ protected void visitModule(Component module, Path<VariationBuilders> path) {
+ addNewMeasures(module, path.current());
+ updateParentVariations(path);
+ }
+
+ @Override
+ protected void visitDirectory(Component directory, Path<VariationBuilders> path) {
+ addNewMeasures(directory, path.current());
+ updateParentVariations(path);
+ }
+
+ @Override
+ protected void visitFile(Component file, Path<VariationBuilders> path) {
+ initVariationBuilders(file, path.parent());
+ }
+ }
+
+ /**
+ * Creates a new VariationBuilders instance for all Component types except FILE.
+ */
+ private static class VariationBuildersFactory extends PathAwareVisitor.SimpleStackElementFactory<VariationBuilders> {
+ @Override
+ public VariationBuilders createForAny(Component component) {
+ return new VariationBuilders();
+ }
+
+ @Override
+ public VariationBuilders createForFile(Component file) {
+ return null;
+ }
+ }
+
+ private void initVariationBuilders(Component file, VariationBuilders builders) {
+ for (Metric metric : metrics) {
+ Optional<Measure> measureOptional = measureRepository.getRawMeasure(file, metric);
+ if (!measureOptional.isPresent()) {
+ continue;
+ }
+ Measure measure = measureOptional.get();
+ if (!measure.hasVariations() || measure.getValueType() != Measure.ValueType.NO_VALUE) {
+ continue;
+ }
+
+ builders.getOrCreateBuilder(metric.getKey()).incrementVariationsBy(measure.getVariations(), periodsHolder);
+ }
+ }
+
+ private void addNewMeasures(Component component, VariationBuilders current) {
+ for (Metric metric : metrics) {
+ Optional<VariationBuilder> builder = current.getBuilder(metric.getKey());
+ if (builder.isPresent() && builder.get().hasVariation()) {
+ measureRepository.add(component, metric, newMeasureBuilder().setVariations(builder.get().build()).createNoValue());
+ }
+ }
+ }
+
+ private void updateParentVariations(PathAwareVisitor.Path<VariationBuilders> path) {
+ path.parent().add(path.current());
+ }
+
+ /**
+ * Holds a VariationBuilder instance for each Metric for which the aggregation is computed in this step
+ */
+ private static final class VariationBuilders {
+ private final Map<String, VariationBuilder> builders;
+
+ public VariationBuilders() {
+ this.builders = new HashMap<>(AGGREGATED_METRIC_KEYS.size());
+ }
+
+ public VariationBuilder getOrCreateBuilder(String metricKey) {
+ VariationBuilder builder = this.builders.get(metricKey);
+ if (builder == null) {
+ builder = new VariationBuilder();
+ this.builders.put(metricKey, builder);
+ }
+ return builder;
+ }
+
+ public Optional<VariationBuilder> getBuilder(String metricKey) {
+ return Optional.fromNullable(this.builders.get(metricKey));
+ }
+
+ public void add(VariationBuilders current) {
+ for (Map.Entry<String, VariationBuilder> entry : current.builders.entrySet()) {
+ VariationBuilder builder = getOrCreateBuilder(entry.getKey());
+ builder.incrementVariationsBy(entry.getValue());
+ }
+ }
+
+ }
+
+ /**
+ * This class holds the variations for a specific Component in the tree and for a specific Metric.
+ * It is mutable and exposes methods to easily increment variations from either a specific
+ * MeasureVariations instance.
+ * This class stores variations as int and makes calculations on int values because all metrics actually have int
+ * values in their variations.
+ */
+ private static final class VariationBuilder {
+ private final Integer[] variations = new Integer[5];
+
+ public void incrementVariationsBy(MeasureVariations variations, PeriodsHolder periodsHolder) {
+ for (Period period : periodsHolder.getPeriods()) {
+ if (variations.hasVariation(period.getIndex())) {
+ increment(period.getIndex() - 1, (int) variations.getVariation(period.getIndex()));
+ }
+ }
+ }
+
+ public boolean hasVariation() {
+ for (@Nullable Integer variation : variations) {
+ if (variation != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void incrementVariationsBy(VariationBuilder variationBuilder) {
+ for (int i = 0; i < variationBuilder.variations.length; i++) {
+ increment(i, variationBuilder.variations[i]);
+ }
+ }
+
+ private void increment(int arrayIndex, @Nullable Integer value) {
+ if (value == null) {
+ return;
+ }
+ if (variations[arrayIndex] == null) {
+ variations[arrayIndex] = 0;
+ }
+ variations[arrayIndex] += value;
+ }
+
+ public MeasureVariations build() {
+ Double[] res = new Double[variations.length];
+ for (int i = 0; i < variations.length; i++) {
+ res[i] = variations[i] == null ? null : (double) variations[i];
+ }
+ return new MeasureVariations(res);
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return "Aggregates New Coverage measures";
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/NewCoverageMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/NewCoverageMeasuresStep.java
new file mode 100644
index 00000000000..de709598a7c
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/NewCoverageMeasuresStep.java
@@ -0,0 +1,352 @@
+/*
+ * 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.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.ObjectUtils;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.server.computation.batch.BatchReportReader;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor;
+import org.sonar.server.computation.component.TreeRootHolder;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.measure.MeasureVariations;
+import org.sonar.server.computation.measure.newcoverage.NewCoverageMetricKeys;
+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 com.google.common.collect.FluentIterable.from;
+import static java.util.Arrays.asList;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER;
+import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
+
+/**
+ * Computes measures related to the New Coverage. These measures do not have values, only variations.
+ */
+public class NewCoverageMeasuresStep implements ComputationStep {
+ private final TreeRootHolder treeRootHolder;
+ private final PeriodsHolder periodsHolder;
+ private final BatchReportReader batchReportReader;
+ private final MeasureRepository measureRepository;
+
+ private final List<NewCoverageMetrics> newCoverageMetricsList;
+
+ public NewCoverageMeasuresStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder, BatchReportReader batchReportReader,
+ MeasureRepository measureRepository, final MetricRepository metricRepository, NewCoverageMetricKeys... newCoverageMetricsList) {
+ this.treeRootHolder = treeRootHolder;
+ this.periodsHolder = periodsHolder;
+ this.batchReportReader = batchReportReader;
+ this.measureRepository = measureRepository;
+ this.newCoverageMetricsList = from(asList(newCoverageMetricsList))
+ .transform(new Function<NewCoverageMetricKeys, NewCoverageMetrics>() {
+ @Nullable
+ @Override
+ public NewCoverageMetrics apply(@Nullable NewCoverageMetricKeys input) {
+ return new NewCoverageMetrics(metricRepository, input);
+ }
+ })
+ .toList();
+ }
+
+ @Override
+ public void execute() {
+ new DepthTraversalTypeAwareVisitor(FILE, POST_ORDER) {
+ @Override
+ public void visitFile(Component file) {
+ if (!file.getFileAttributes().isUnitTest()) {
+ processFileComponent(file);
+ }
+ }
+ }.visit(treeRootHolder.getRoot());
+ }
+
+ private void processFileComponent(Component file) {
+ for (NewCoverageMetrics newCoverageMetrics : newCoverageMetricsList) {
+ Counters counters = new Counters(file, periodsHolder);
+ if (populateCounters(newCoverageMetrics, counters)) {
+ compute(newCoverageMetrics, counters);
+ }
+ }
+ }
+
+ private boolean populateCounters(NewCoverageMetrics newCoverageMetrics, Counters counters) {
+ Component fileComponent = counters.getComponent();
+ BatchReport.Changesets componentScm = batchReportReader.readChangesets(fileComponent.getRef());
+ if (componentScm == null) {
+ return false;
+ }
+
+ Optional<Measure> hitsByLineMeasure = measureRepository.getRawMeasure(fileComponent, newCoverageMetrics.coverageLineHitsData);
+ if (!hitsByLineMeasure.isPresent() || hitsByLineMeasure.get().getValueType() == Measure.ValueType.NO_VALUE) {
+ return false;
+ }
+
+ Map<Integer, Integer> hitsByLine = parseCountByLine(hitsByLineMeasure);
+ Map<Integer, Integer> conditionsByLine = parseCountByLine(measureRepository.getRawMeasure(fileComponent, newCoverageMetrics.conditionsByLine));
+ Map<Integer, Integer> coveredConditionsByLine = parseCountByLine(measureRepository.getRawMeasure(fileComponent, newCoverageMetrics.coveredConditionsByLine));
+
+ for (Map.Entry<Integer, Integer> entry : hitsByLine.entrySet()) {
+ int lineId = entry.getKey();
+ int hits = entry.getValue();
+ int conditions = (Integer) ObjectUtils.defaultIfNull(conditionsByLine.get(lineId), 0);
+ int coveredConditions = (Integer) ObjectUtils.defaultIfNull(coveredConditionsByLine.get(lineId), 0);
+ BatchReport.Changesets.Changeset changeset = componentScm.getChangeset(componentScm.getChangesetIndexByLine(lineId - 1));
+ Date date = changeset.hasDate() ? new Date(changeset.getDate()) : null;
+
+ counters.analyze(date, hits, conditions, coveredConditions);
+ }
+
+ return true;
+ }
+
+ private static Map<Integer, Integer> parseCountByLine(Optional<Measure> measure) {
+ if (measure.isPresent() && measure.get().getValueType() != Measure.ValueType.NO_VALUE) {
+ return KeyValueFormat.parseIntInt(measure.get().getStringValue());
+ }
+ return Collections.emptyMap();
+ }
+
+ private void compute(NewCoverageMetrics newCoverageMetrics, Counters counters) {
+ computeMeasure(counters, newCoverageMetrics.newLinesToCover, NewLinesToCoverComputer.INSTANCE);
+ computeMeasure(counters, newCoverageMetrics.newUncoveredLines, NewUncoveredLinesComputer.INSTANCE);
+ computeMeasure(counters, newCoverageMetrics.newConditionsToCover, NewConditionsToCoverComputer.INSTANCE);
+ computeMeasure(counters, newCoverageMetrics.newUncoveredConditions, NewUncoveredConditionsComputer.INSTANCE);
+ }
+
+ private void computeMeasure(final Counters counters, Metric metric, NewCoverageMeasureComputer measureComputer) {
+ List<Counter> nonEmptyCounters = from(counters.getCounters()).filter(CounterHasNewCode.INSTANCE).toList();
+ if (nonEmptyCounters.isEmpty()) {
+ measureRepository.add(counters.getComponent(), metric, newMeasureBuilder().createNoValue());
+ return;
+ }
+
+ MeasureVariations.Builder variationsBuilder = MeasureVariations.newMeasureVarationsBuilder();
+ for (Counter counter : nonEmptyCounters) {
+ variationsBuilder.setVariation(counter.getPeriodIndex(), measureComputer.compute(counter));
+ }
+ measureRepository.add(counters.getComponent(), metric, newMeasureBuilder().setVariations(variationsBuilder.build()).createNoValue());
+ }
+
+ @Override
+ public String getDescription() {
+ return "Computation of New Coverage measures";
+ }
+
+ /**
+ * Internal class storing the Metric objects for each property of a {@link NewCoverageMetricKeys} instance
+ */
+ private static final class NewCoverageMetrics {
+ private final Metric coverageLineHitsData;
+ private final Metric conditionsByLine;
+ private final Metric coveredConditionsByLine;
+ private final Metric newLinesToCover;
+ private final Metric newUncoveredLines;
+ private final Metric newConditionsToCover;
+ private final Metric newUncoveredConditions;
+
+ public NewCoverageMetrics(MetricRepository metricRepository, NewCoverageMetricKeys keys) {
+ this.coverageLineHitsData = metricRepository.getByKey(keys.coverageLineHitsData());
+ this.conditionsByLine = metricRepository.getByKey(keys.conditionsByLine());
+ this.coveredConditionsByLine = metricRepository.getByKey(keys.coveredConditionsByLine());
+ this.newLinesToCover = metricRepository.getByKey(keys.newLinesToCover());
+ this.newUncoveredLines = metricRepository.getByKey(keys.newUncoveredLines());
+ this.newConditionsToCover = metricRepository.getByKey(keys.newConditionsToCover());
+ this.newUncoveredConditions = metricRepository.getByKey(keys.newUncoveredConditions());
+ }
+
+ }
+
+ /**
+ * Holds the {@link Counter}s (one for each Period in a specific {@link PeriodsHolder}) for a specific Component.
+ */
+ private static final class Counters {
+ private final Component component;
+ private final List<Counter> counters;
+
+ public Counters(Component component, PeriodsHolder periodsHolder) {
+ this.component = component;
+ this.counters = from(periodsHolder.getPeriods()).transform(PeriodToCounter.INSTANCE).toList();
+ }
+
+ public void analyze(@Nullable Date lineDate, int hits, int conditions, int coveredConditions) {
+ if (lineDate == null) {
+ return;
+ }
+ for (Counter counter : getCounters()) {
+ counter.analyze(lineDate, hits, conditions, coveredConditions);
+ }
+ }
+
+ public Component getComponent() {
+ return component;
+ }
+
+ private Iterable<Counter> getCounters() {
+ return this.counters;
+ }
+
+ }
+
+ public static final class Counter {
+ private final int periodIndex;
+ private final Date date;
+ private Integer newLines;
+ private Integer newCoveredLines;
+ private Integer newConditions;
+ private Integer newCoveredConditions;
+
+ private Counter(int periodIndex, Date date) {
+ this.periodIndex = periodIndex;
+ this.date = date;
+ }
+
+ public int getPeriodIndex() {
+ return periodIndex;
+ }
+
+ void analyze(Date lineDate, int hits, int conditions, int coveredConditions) {
+ if (lineDate.after(date)) {
+ addLine(hits > 0);
+ addConditions(conditions, coveredConditions);
+ }
+ }
+
+ void addLine(boolean covered) {
+ if (newLines == null) {
+ newLines = 0;
+ }
+ newLines += 1;
+ if (covered) {
+ if (newCoveredLines == null) {
+ newCoveredLines = 0;
+ }
+ newCoveredLines += 1;
+ }
+ }
+
+ void addConditions(int count, int countCovered) {
+ if (newConditions == null) {
+ newConditions = 0;
+ }
+ newConditions += count;
+ if (count > 0) {
+ if (newCoveredConditions == null) {
+ newCoveredConditions = 0;
+ }
+ newCoveredConditions += countCovered;
+ }
+ }
+
+ boolean hasNewCode() {
+ return newLines != null;
+ }
+
+ public int getNewLines() {
+ return newLines != null ? newLines : 0;
+ }
+
+ public int getNewCoveredLines() {
+ return newCoveredLines != null ? newCoveredLines : 0;
+ }
+
+ public int getNewConditions() {
+ return newConditions != null ? newConditions : 0;
+ }
+
+ public int getNewCoveredConditions() {
+ return newCoveredConditions != null ? newCoveredConditions : 0;
+ }
+ }
+
+ private enum PeriodToCounter implements Function<Period, Counter> {
+ INSTANCE;
+
+ @Override
+ @Nonnull
+ public Counter apply(@Nonnull Period input) {
+ return new Counter(input.getIndex(), new Date(input.getSnapshotDate()));
+ }
+ }
+
+ private enum CounterHasNewCode implements Predicate<Counter> {
+ INSTANCE;
+
+ @Override
+ public boolean apply(@Nonnull Counter input) {
+ return input.hasNewCode();
+ }
+ }
+
+ /**
+ * Represents a way of computing a measure value from a given Counter.
+ */
+ private interface NewCoverageMeasureComputer {
+ int compute(Counter counter);
+ }
+
+ private enum NewLinesToCoverComputer implements NewCoverageMeasureComputer {
+ INSTANCE;
+
+ @Override
+ public int compute(Counter counter) {
+ return counter.getNewLines();
+ }
+ }
+
+ private enum NewUncoveredLinesComputer implements NewCoverageMeasureComputer {
+ INSTANCE;
+
+ @Override
+ public int compute(Counter counter) {
+ return counter.getNewLines() - counter.getNewCoveredLines();
+ }
+ }
+
+ private enum NewConditionsToCoverComputer implements NewCoverageMeasureComputer {
+ INSTANCE;
+
+ @Override
+ public int compute(Counter counter) {
+ return counter.getNewConditions();
+ }
+ }
+
+ private enum NewUncoveredConditionsComputer implements NewCoverageMeasureComputer {
+ INSTANCE;
+
+ @Override
+ public int compute(Counter counter) {
+ return counter.getNewConditions() - counter.getNewCoveredConditions();
+ }
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java
index cd050090bdc..381579a9e1e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java
@@ -21,7 +21,6 @@ package org.sonar.server.computation.measure;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import java.util.HashMap;
@@ -42,6 +41,7 @@ import org.sonar.server.computation.metric.MetricRepositoryRule;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Maps.filterKeys;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
@@ -133,7 +133,7 @@ public class MeasureRepositoryRule extends ExternalResource implements MeasureRe
checkAndInitProvidersState();
ImmutableSetMultimap.Builder<String, Measure> builder = ImmutableSetMultimap.builder();
- for (Map.Entry<InternalKey, Measure> entry : FluentIterable.from(filterKeys(rawMeasures, hasComponentRef(component)).entrySet()).filter(isNewMeasure)) {
+ for (Map.Entry<InternalKey, Measure> entry : from(filterKeys(rawMeasures, hasComponentRef(component)).entrySet()).filter(isNewMeasure)) {
builder.put(entry.getKey().getMetricKey(), entry.getValue());
}
return builder.build();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageAggregationStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageAggregationStepTest.java
new file mode 100644
index 00000000000..1305e2ad3ff
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageAggregationStepTest.java
@@ -0,0 +1,293 @@
+/*
+ * 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.Function;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import javax.annotation.Nonnull;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+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.Measure;
+import org.sonar.server.computation.measure.MeasureRepoEntry;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.measure.MeasureVariations;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.period.Period;
+import org.sonar.server.computation.period.PeriodsHolderRule;
+
+import static com.google.common.collect.FluentIterable.from;
+import static com.google.common.collect.ImmutableList.of;
+import static org.assertj.core.api.Assertions.assertThat;
+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.FILE;
+import static org.sonar.server.computation.component.Component.Type.MODULE;
+import static org.sonar.server.computation.component.Component.Type.PROJECT;
+import static org.sonar.server.computation.component.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;
+
+@RunWith(DataProviderRunner.class)
+public class NewCoverageAggregationStepTest {
+
+ private static final DumbComponent MOST_SIMPLE_ONE_FILE_TREE = builder(PROJECT, 1)
+ .addChildren(
+ builder(MODULE, 2)
+ .addChildren(
+ builder(DIRECTORY, 3)
+ .addChildren(
+ builder(FILE, 4).build())
+ .build()
+ ).build()
+ ).build();
+
+ private static final DumbComponent MANY_FILES_TREE = builder(PROJECT, 1)
+ .addChildren(
+ builder(MODULE, 11)
+ .addChildren(
+ builder(DIRECTORY, 111)
+ .addChildren(
+ builder(FILE, 1111).build(),
+ builder(FILE, 1112).build()
+ ).build(),
+ builder(DIRECTORY, 112)
+ .addChildren(
+ builder(FILE, 1121).build(),
+ builder(FILE, 1122).build(),
+ builder(FILE, 1123).build()
+ ).build()
+ ).build(),
+ builder(MODULE, 12)
+ .addChildren(
+ builder(DIRECTORY, 121)
+ .addChildren(
+ builder(FILE, 1211).build(),
+ builder(FILE, 1212).build()
+ ).build(),
+ builder(DIRECTORY, 122).build()
+ ).build(),
+ builder(MODULE, 13).build()
+ ).build();
+
+ private static final String SOME_MODE = "some mode";
+ private static final long SOME_SNAPSHOT_DATE = 1234L;
+ private static final long SOME_SNAPSHOT_ID = 876L;
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+ @Rule
+ public PeriodsHolderRule periodHolder = new PeriodsHolderRule();
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(CoreMetrics.NEW_LINES_TO_COVER)
+ .add(CoreMetrics.NEW_UNCOVERED_LINES)
+ .add(CoreMetrics.NEW_CONDITIONS_TO_COVER)
+ .add(CoreMetrics.NEW_UNCOVERED_CONDITIONS)
+ .add(CoreMetrics.NEW_IT_LINES_TO_COVER)
+ .add(CoreMetrics.NEW_IT_UNCOVERED_LINES)
+ .add(CoreMetrics.NEW_IT_CONDITIONS_TO_COVER)
+ .add(CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS)
+ .add(CoreMetrics.NEW_OVERALL_LINES_TO_COVER)
+ .add(CoreMetrics.NEW_OVERALL_UNCOVERED_LINES)
+ .add(CoreMetrics.NEW_OVERALL_CONDITIONS_TO_COVER)
+ .add(CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS);
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ private NewCoverageAggregationStep underTest = new NewCoverageAggregationStep(treeRootHolder, periodHolder, metricRepository, measureRepository);
+
+ @DataProvider
+ public static Object[][] trees_without_FILE_COMPONENT() {
+ return new Object[][] {
+ {builder(PROJECT, 1).build()},
+ {builder(PROJECT, 1).addChildren(builder(MODULE, 2).build()).build()},
+ {builder(PROJECT, 1).addChildren(builder(MODULE, 2).addChildren(builder(DIRECTORY, 3).build()).build()).build()},
+ };
+ }
+
+ @Test
+ @UseDataProvider("trees_without_FILE_COMPONENT")
+ public void no_measures_added_if_there_is_only_PROJECT_component(Component root) {
+ treeRootHolder.setRoot(root);
+
+ underTest.execute();
+
+ assertThat(measureRepository.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void no_measures_added_if_there_is_no_rawMeasure_on_FILE_component() {
+ treeRootHolder.setRoot(MOST_SIMPLE_ONE_FILE_TREE);
+
+ underTest.execute();
+
+ assertThat(measureRepository.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void no_measures_added_if_there_is_rawMeasure_has_no_variation_on_FILE_component() {
+ treeRootHolder.setRoot(MOST_SIMPLE_ONE_FILE_TREE);
+ measureRepository.addRawMeasure(4, NEW_LINES_TO_COVER_KEY, newMeasureBuilder().createNoValue());
+
+ underTest.execute();
+
+ assertThat(measureRepository.getNewRawMeasures(1).isEmpty()).isTrue();
+ assertThat(measureRepository.getNewRawMeasures(2).isEmpty()).isTrue();
+ assertThat(measureRepository.getNewRawMeasures(3).isEmpty()).isTrue();
+ assertThat(measureRepository.getNewRawMeasures(4).isEmpty()).isTrue();
+ }
+
+ @Test
+ public void no_measures_added_if_there_is_rawMeasure_is_not_NOVALUE_on_FILE_component() {
+ treeRootHolder.setRoot(MOST_SIMPLE_ONE_FILE_TREE);
+ measureRepository.addRawMeasure(4, NEW_LINES_TO_COVER_KEY, newMeasureBuilder().setVariations(new MeasureVariations(1d)).create("some value"));
+
+ underTest.execute();
+
+ assertThat(measureRepository.getNewRawMeasures(1).isEmpty()).isTrue();
+ assertThat(measureRepository.getNewRawMeasures(2).isEmpty()).isTrue();
+ assertThat(measureRepository.getNewRawMeasures(3).isEmpty()).isTrue();
+ assertThat(measureRepository.getNewRawMeasures(4).isEmpty()).isTrue();
+ }
+
+ @Test
+ public void no_measures_added_if_there_is_no_period() {
+ treeRootHolder.setRoot(MOST_SIMPLE_ONE_FILE_TREE);
+ periodHolder.setPeriods();
+ measureRepository.addRawMeasure(4, NEW_LINES_TO_COVER_KEY, createMeasure(2d));
+
+ underTest.execute();
+
+ assertThat(measureRepository.getNewRawMeasures(1).isEmpty()).isTrue();
+ assertThat(measureRepository.getNewRawMeasures(2).isEmpty()).isTrue();
+ assertThat(measureRepository.getNewRawMeasures(3).isEmpty()).isTrue();
+ assertThat(measureRepository.getNewRawMeasures(4).isEmpty()).isTrue();
+ }
+
+ @Test
+ public void measures_added_even_if_rawMeasure_has_variation_0_on_FILE_component() {
+ treeRootHolder.setRoot(MOST_SIMPLE_ONE_FILE_TREE);
+ periodHolder.setPeriods(createPeriod(2));
+ measureRepository.addRawMeasure(4, NEW_LINES_TO_COVER_KEY, createMeasure(0d));
+
+ underTest.execute();
+
+ MeasureRepoEntry expectedEntry = entryOf(NEW_LINES_TO_COVER_KEY, createMeasure(0d));
+ assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(expectedEntry);
+ assertThat(toEntries(measureRepository.getNewRawMeasures(2))).containsOnly(expectedEntry);
+ assertThat(toEntries(measureRepository.getNewRawMeasures(3))).containsOnly(expectedEntry);
+ assertThat(measureRepository.getNewRawMeasures(4).isEmpty()).isTrue();
+ }
+
+ @Test
+ public void measures_added_on_all_components_but_FILE_and_are_sum_of_childrens_value() {
+ treeRootHolder.setRoot(MANY_FILES_TREE);
+ periodHolder.setPeriods(createPeriod(2));
+ for (Integer fileComponentRef : of(1111, 1112, 1121, 1122, 1123, 1211, 1212)) {
+ measureRepository.addRawMeasure(fileComponentRef, NEW_LINES_TO_COVER_KEY, createMeasure((double) fileComponentRef));
+ }
+
+ underTest.execute();
+
+ assertThat(toEntries(measureRepository.getNewRawMeasures(111))).containsOnly(
+ entryOf(NEW_LINES_TO_COVER_KEY, createMeasure(1111 + 1112))
+ );
+ assertThat(toEntries(measureRepository.getNewRawMeasures(112))).containsOnly(
+ entryOf(NEW_LINES_TO_COVER_KEY, createMeasure(1121 + 1122 + 1123))
+ );
+ assertThat(toEntries(measureRepository.getNewRawMeasures(121))).containsOnly(
+ entryOf(NEW_LINES_TO_COVER_KEY, createMeasure(1211 + 1212))
+ );
+ assertThat(measureRepository.getNewRawMeasures(122).isEmpty()).isTrue();
+ assertThat(toEntries(measureRepository.getNewRawMeasures(11))).containsOnly(
+ entryOf(NEW_LINES_TO_COVER_KEY, createMeasure(1111 + 1112 + 1121 + 1122 + 1123))
+ );
+ assertThat(toEntries(measureRepository.getNewRawMeasures(12))).containsOnly(
+ entryOf(NEW_LINES_TO_COVER_KEY, createMeasure(1211 + 1212))
+ );
+ assertThat(measureRepository.getNewRawMeasures(13).isEmpty()).isTrue();
+ assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(
+ entryOf(NEW_LINES_TO_COVER_KEY, createMeasure(1111 + 1112 + 1121 + 1122 + 1123 + 1211 + 1212))
+ );
+ }
+
+ @Test
+ public void verify_measures_are_created_for_all_metrics() {
+ treeRootHolder.setRoot(MOST_SIMPLE_ONE_FILE_TREE);
+ periodHolder.setPeriods(createPeriod(2));
+ for (Metric metric : metricRepository.getAll()) {
+ measureRepository.addRawMeasure(4, metric.getKey(), createMeasure(metric.getKey().hashCode()));
+ }
+
+ underTest.execute();
+
+ MeasureRepoEntry[] expectedEntries = from(metricRepository.getAll())
+ .transform(new Function<Metric, MeasureRepoEntry>() {
+ @Override
+ @Nonnull
+ public MeasureRepoEntry apply(@Nonnull Metric input) {
+ return entryOf(input.getKey(), createMeasure(input.getKey().hashCode()));
+ }
+ }).toArray(MeasureRepoEntry.class);
+
+ assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(expectedEntries);
+ assertThat(toEntries(measureRepository.getNewRawMeasures(2))).containsOnly(expectedEntries);
+ assertThat(toEntries(measureRepository.getNewRawMeasures(3))).containsOnly(expectedEntries);
+ assertThat(measureRepository.getNewRawMeasures(4).isEmpty()).isTrue();
+ }
+
+ @Test
+ public void verify_measures_are_created_for_all_periods() {
+ treeRootHolder.setRoot(MOST_SIMPLE_ONE_FILE_TREE);
+ periodHolder.setPeriods(createPeriod(1), createPeriod(2), createPeriod(3), createPeriod(4), createPeriod(5));
+ Measure measure = newMeasureBuilder().setVariations(new MeasureVariations(5d, 4d, 3d, 2d, 1d)).createNoValue();
+ measureRepository.addRawMeasure(4, NEW_LINES_TO_COVER_KEY, measure);
+
+ underTest.execute();
+
+ assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(NEW_LINES_TO_COVER_KEY, measure));
+ assertThat(toEntries(measureRepository.getNewRawMeasures(2))).containsOnly(entryOf(NEW_LINES_TO_COVER_KEY, measure));
+ assertThat(toEntries(measureRepository.getNewRawMeasures(3))).containsOnly(entryOf(NEW_LINES_TO_COVER_KEY, measure));
+ assertThat(measureRepository.getNewRawMeasures(4).isEmpty()).isTrue();
+ }
+
+ @Test
+ public void verify_description() {
+ assertThat(underTest.getDescription()).isEqualTo("Aggregates New Coverage measures");
+
+ }
+
+ private Measure createMeasure(double variation2) {
+ return newMeasureBuilder().setVariations(new MeasureVariations(null, variation2)).createNoValue();
+ }
+
+ private static Period createPeriod(int periodIndex) {
+ return new Period(periodIndex, SOME_MODE, null, SOME_SNAPSHOT_DATE, SOME_SNAPSHOT_ID);
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageMeasuresStepTest.java
new file mode 100644
index 00000000000..8dfe0b9f2a8
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/NewCoverageMeasuresStepTest.java
@@ -0,0 +1,334 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.step;
+
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.server.computation.batch.BatchReportReaderRule;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.component.FileAttributes;
+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.measure.newcoverage.NewCoverageMetricKeys;
+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.sonar.api.measures.CoreMetrics.CONDITIONS_BY_LINE_KEY;
+import static org.sonar.api.measures.CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY;
+import static org.sonar.api.measures.CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_LINES_TO_COVER_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_UNCOVERED_LINES_KEY;
+import static org.sonar.api.utils.DateUtils.parseDate;
+import static org.sonar.batch.protocol.output.BatchReport.Changesets;
+import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
+import static org.sonar.server.computation.measure.MeasureRepoEntry.entryOf;
+import static org.sonar.server.computation.measure.MeasureRepoEntry.toEntries;
+import static org.sonar.server.computation.measure.MeasureVariations.newMeasureVarationsBuilder;
+
+public class NewCoverageMeasuresStepTest {
+ private static final NewCoverageMetricKeys SOME_COVERAGE_METRIC_KEYS = new SomeNewCoverageMetricKeys();
+
+ @Rule
+ public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+ @Rule
+ public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ // adds metrics referenced by SomeNewCoverageMetricKeys
+ .add(CoreMetrics.COVERAGE_LINE_HITS_DATA)
+ .add(CoreMetrics.CONDITIONS_BY_LINE)
+ .add(CoreMetrics.COVERED_CONDITIONS_BY_LINE)
+ .add(CoreMetrics.NEW_LINES_TO_COVER)
+ .add(CoreMetrics.NEW_UNCOVERED_LINES)
+ .add(CoreMetrics.NEW_CONDITIONS_TO_COVER)
+ .add(CoreMetrics.NEW_UNCOVERED_CONDITIONS);
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ private NewCoverageMeasuresStep underTest = new NewCoverageMeasuresStep(treeRootHolder, periodsHolder, reportReader, measureRepository, metricRepository,
+ SOME_COVERAGE_METRIC_KEYS);
+ public static final DumbComponent FILE_COMPONENT = DumbComponent.builder(Component.Type.FILE, 1).setFileAttributes(new FileAttributes(false, null)).build();
+
+ @Before
+ public void setUp() throws Exception {
+ periodsHolder.setPeriods(
+ new Period(2, "mode_p_1", null, parseDate("2009-12-25").getTime(), 1),
+ new Period(5, "mode_p_5", null, parseDate("2011-02-18").getTime(), 2));
+ }
+
+ @Test
+ public void no_measure_for_PROJECT_component() {
+ treeRootHolder.setRoot(DumbComponent.builder(Component.Type.PROJECT, 1).build());
+
+ underTest.execute();
+
+ assertThat(measureRepository.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void no_measure_for_MODULE_component() {
+ treeRootHolder.setRoot(DumbComponent.builder(Component.Type.MODULE, 1).build());
+
+ underTest.execute();
+
+ assertThat(measureRepository.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void no_measure_for_DIRECTORY_component() {
+ treeRootHolder.setRoot(DumbComponent.builder(Component.Type.DIRECTORY, 1).build());
+
+ underTest.execute();
+
+ assertThat(measureRepository.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void no_measure_for_unit_test_FILE_component() {
+ treeRootHolder.setRoot(DumbComponent.builder(Component.Type.FILE, 1).setFileAttributes(new FileAttributes(true, null)).build());
+
+ underTest.execute();
+
+ assertThat(measureRepository.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void no_measures_for_FILE_component_without_code() {
+ treeRootHolder.setRoot(DumbComponent.builder(Component.Type.FILE, 1).setFileAttributes(new FileAttributes(false, null)).build());
+
+ underTest.execute();
+
+ assertThat(measureRepository.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void no_measures_for_FILE_component_without_CoverageData() {
+ DumbComponent fileComponent = DumbComponent.builder(Component.Type.FILE, 1).setFileAttributes(new FileAttributes(false, null)).build();
+
+ treeRootHolder.setRoot(fileComponent);
+ reportReader.putChangesets(Changesets.newBuilder()
+ .setComponentRef(fileComponent.getRef())
+ .addChangeset(Changesets.Changeset.newBuilder()
+ .setDate(parseDate("2008-05-18").getTime())
+ .build())
+ .addChangesetIndexByLine(0)
+ .build());
+
+ underTest.execute();
+
+ assertThat(measureRepository.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void verify_computation_of_measures_for_new_lines() {
+ treeRootHolder.setRoot(FILE_COMPONENT);
+ reportReader.putChangesets(BatchReport.Changesets.newBuilder()
+ .setComponentRef(1)
+ .addChangeset(Changesets.Changeset.newBuilder().build())
+ .addChangeset(Changesets.Changeset.newBuilder()
+ .setDate(parseDate("2007-01-15").getTime())
+ .build())
+ .addChangeset(Changesets.Changeset.newBuilder()
+ .setDate(parseDate("2011-01-01").getTime())
+ .build())
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(1)
+ .addChangesetIndexByLine(2)
+ .build());
+ measureRepository.addRawMeasure(FILE_COMPONENT.getRef(), COVERAGE_LINE_HITS_DATA_KEY, newMeasureBuilder().create("10=2;11=3"));
+
+ underTest.execute();
+
+ assertThat(toEntries(measureRepository.getNewRawMeasures(FILE_COMPONENT.getRef()))).containsOnly(
+ entryOf(NEW_LINES_TO_COVER_KEY, createMeasure(1d, null)),
+ entryOf(NEW_UNCOVERED_LINES_KEY, createMeasure(0d, null)),
+ entryOf(NEW_CONDITIONS_TO_COVER_KEY, createMeasure(0d, null)),
+ entryOf(NEW_UNCOVERED_CONDITIONS_KEY, createMeasure(0d, null))
+ );
+ }
+
+ @Test
+ public void verify_computation_of_measures_for_new_conditions() {
+ treeRootHolder.setRoot(FILE_COMPONENT);
+ reportReader.putChangesets(Changesets.newBuilder()
+ .setComponentRef(1)
+ .addChangeset(Changesets.Changeset.newBuilder().build())
+ .addChangeset(Changesets.Changeset.newBuilder()
+ .setDate(parseDate("2007-01-15").getTime())
+ .build())
+ .addChangeset(Changesets.Changeset.newBuilder()
+ .setDate(parseDate("2011-01-01").getTime())
+ .build())
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(1)
+ .addChangesetIndexByLine(2)
+ .build()
+ );
+ measureRepository.addRawMeasure(FILE_COMPONENT.getRef(), COVERAGE_LINE_HITS_DATA_KEY, newMeasureBuilder().create("10=2;11=3"));
+ measureRepository.addRawMeasure(FILE_COMPONENT.getRef(), CONDITIONS_BY_LINE_KEY, newMeasureBuilder().create("11=4"));
+ measureRepository.addRawMeasure(FILE_COMPONENT.getRef(), COVERED_CONDITIONS_BY_LINE_KEY, newMeasureBuilder().create("11=1"));
+
+ underTest.execute();
+
+ assertThat(toEntries(measureRepository.getNewRawMeasures(FILE_COMPONENT.getRef()))).containsOnly(
+ entryOf(NEW_LINES_TO_COVER_KEY, createMeasure(1d, null)),
+ entryOf(NEW_UNCOVERED_LINES_KEY, createMeasure(0d, null)),
+ entryOf(NEW_CONDITIONS_TO_COVER_KEY, createMeasure(4d, null)),
+ entryOf(NEW_UNCOVERED_CONDITIONS_KEY, createMeasure(3d, null))
+ );
+ }
+
+ @Test
+ public void verify_measure_of_condition_not_computed_if_there_is_none() {
+ treeRootHolder.setRoot(FILE_COMPONENT);
+ reportReader.putChangesets(Changesets.newBuilder()
+ .setComponentRef(1)
+ .addChangeset(Changesets.Changeset.newBuilder().build())
+ .addChangeset(Changesets.Changeset.newBuilder()
+ .setDate(parseDate("2007-01-15").getTime())
+ .build())
+ .addChangeset(Changesets.Changeset.newBuilder()
+ .setDate(parseDate("2011-01-01").getTime())
+ .build())
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(1)
+ .addChangesetIndexByLine(2)
+ .build()
+ );
+
+ underTest.execute();
+
+ assertThat(measureRepository.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void verify_no_measure_when_nothing_has_changed() {
+ treeRootHolder.setRoot(FILE_COMPONENT);
+ reportReader.putChangesets(BatchReport.Changesets.newBuilder()
+ .setComponentRef(1)
+ .addChangeset(Changesets.Changeset.newBuilder()
+ .setDate(parseDate("2008-08-02").getTime())
+ .build())
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .build());
+ measureRepository.addRawMeasure(FILE_COMPONENT.getRef(), COVERAGE_LINE_HITS_DATA_KEY, newMeasureBuilder().create("2=1;3=1"));
+ measureRepository.addRawMeasure(FILE_COMPONENT.getRef(), CONDITIONS_BY_LINE_KEY, newMeasureBuilder().create("2=1"));
+ measureRepository.addRawMeasure(FILE_COMPONENT.getRef(), COVERED_CONDITIONS_BY_LINE_KEY, newMeasureBuilder().create("2=1"));
+
+ underTest.execute();
+
+ assertThat(toEntries(measureRepository.getNewRawMeasures(FILE_COMPONENT.getRef()))).containsOnly(
+ entryOf(NEW_LINES_TO_COVER_KEY, newMeasureBuilder().createNoValue()),
+ entryOf(NEW_UNCOVERED_LINES_KEY, newMeasureBuilder().createNoValue()),
+ entryOf(NEW_CONDITIONS_TO_COVER_KEY, newMeasureBuilder().createNoValue()),
+ entryOf(NEW_UNCOVERED_CONDITIONS_KEY, newMeasureBuilder().createNoValue())
+ );
+ }
+
+ private static Measure createMeasure(@Nullable Double variationPeriod2, @Nullable Double variationPeriod5) {
+ MeasureVariations.Builder variationBuilder = newMeasureVarationsBuilder();
+ if (variationPeriod2 != null) {
+ variationBuilder.setVariation(2, variationPeriod2);
+ }
+ if (variationPeriod5 != null) {
+ variationBuilder.setVariation(5, variationPeriod5);
+ }
+ return newMeasureBuilder()
+ .setVariations(variationBuilder.build())
+ .createNoValue();
+ }
+
+ private static class SomeNewCoverageMetricKeys implements NewCoverageMetricKeys {
+ @Override
+ public String coverageLineHitsData() {
+ return COVERAGE_LINE_HITS_DATA_KEY;
+ }
+
+ @Override
+ public String conditionsByLine() {
+ return CONDITIONS_BY_LINE_KEY;
+ }
+
+ @Override
+ public String coveredConditionsByLine() {
+ return COVERED_CONDITIONS_BY_LINE_KEY;
+ }
+
+ @Override
+ public String newLinesToCover() {
+ return NEW_LINES_TO_COVER_KEY;
+ }
+
+ @Override
+ public String newUncoveredLines() {
+ return NEW_UNCOVERED_LINES_KEY;
+ }
+
+ @Override
+ public String newConditionsToCover() {
+ return NEW_CONDITIONS_TO_COVER_KEY;
+ }
+
+ @Override
+ public String newUncoveredConditions() {
+ return NEW_UNCOVERED_CONDITIONS_KEY;
+ }
+ }
+}