summaryrefslogtreecommitdiffstats
path: root/sonar-batch/src/main/java
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2016-02-19 10:39:17 +0100
committerJulien HENRY <julien.henry@sonarsource.com>2016-02-22 10:57:26 +0100
commitaccc8fc25dbdddb7fc6fbc5fa171ce29929d01d9 (patch)
tree0b4daa853beea7ec734f8b52df0cf126f4e7efe8 /sonar-batch/src/main/java
parentcef567021858b0e7239ac80d4512172ef7b70dbd (diff)
downloadsonarqube-accc8fc25dbdddb7fc6fbc5fa171ce29929d01d9.tar.gz
sonarqube-accc8fc25dbdddb7fc6fbc5fa171ce29929d01d9.zip
SONAR-5772 New executable_lines_data metric
Diffstat (limited to 'sonar-batch/src/main/java')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/report/CoveragePublisher.java14
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java190
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java6
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/source/ZeroCoverageSensor.java112
7 files changed, 244 insertions, 88 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
index 319b8292ba3..4b7c4b61f57 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
@@ -35,6 +35,7 @@ import org.sonar.batch.scm.ScmConfiguration;
import org.sonar.batch.scm.ScmSensor;
import org.sonar.batch.source.CodeColorizerSensor;
import org.sonar.batch.source.LinesSensor;
+import org.sonar.batch.source.ZeroCoverageSensor;
import org.sonar.batch.task.ListTask;
import org.sonar.batch.task.ScanTask;
import org.sonar.batch.task.Tasks;
@@ -55,6 +56,7 @@ public class BatchComponents {
ScmSensor.class,
LinesSensor.class,
+ ZeroCoverageSensor.class,
CodeColorizerSensor.class,
// Issues tracking
diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/CoveragePublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/CoveragePublisher.java
index dbfe3b4d03b..921c37f5a83 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/report/CoveragePublisher.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/report/CoveragePublisher.java
@@ -54,49 +54,49 @@ public class CoveragePublisher implements ReportPublisherStep {
}
Map<Integer, Coverage.Builder> coveragePerLine = new LinkedHashMap<>();
- applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine,
+ int lineCount = ((InputFile) resource.inputComponent()).lines();
+ applyLineMeasure(resource.key(), lineCount, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine,
new MeasureOperation() {
@Override
public void apply(String value, Coverage.Builder builder) {
builder.setUtHits(Integer.parseInt(value) > 0);
}
});
- applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.CONDITIONS_BY_LINE_KEY, coveragePerLine,
+ applyLineMeasure(resource.key(), lineCount, CoreMetrics.CONDITIONS_BY_LINE_KEY, coveragePerLine,
new MeasureOperation() {
@Override
public void apply(String value, Coverage.Builder builder) {
builder.setConditions(Integer.parseInt(value));
}
});
- applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine,
+ applyLineMeasure(resource.key(), lineCount, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine,
new MeasureOperation() {
@Override
public void apply(String value, Coverage.Builder builder) {
builder.setUtCoveredConditions(Integer.parseInt(value));
}
});
- applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine,
+ applyLineMeasure(resource.key(), lineCount, CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine,
new MeasureOperation() {
@Override
public void apply(String value, Coverage.Builder builder) {
builder.setItHits(Integer.parseInt(value) > 0);
}
});
- applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine,
+ applyLineMeasure(resource.key(), lineCount, CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine,
new MeasureOperation() {
@Override
public void apply(String value, Coverage.Builder builder) {
builder.setItCoveredConditions(Integer.parseInt(value));
}
});
- applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine,
+ applyLineMeasure(resource.key(), lineCount, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine,
new MeasureOperation() {
@Override
public void apply(String value, Coverage.Builder builder) {
builder.setOverallCoveredConditions(Integer.parseInt(value));
}
});
-
writer.writeComponentCoverage(resource.batchId(), Iterables.transform(coveragePerLine.values(), BuildCoverage.INSTANCE));
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java
index 9f7ed5657af..5561f456043 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java
@@ -20,9 +20,11 @@
package org.sonar.batch.report;
import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
+import com.google.common.base.Predicate;
import java.io.Serializable;
+import java.util.Set;
import javax.annotation.Nonnull;
+import org.sonar.api.batch.measure.Metric;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric.ValueType;
import org.sonar.batch.index.BatchComponent;
@@ -31,97 +33,135 @@ import org.sonar.batch.protocol.Constants.MeasureValueType;
import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.batch.protocol.output.BatchReportWriter;
import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.core.metric.BatchMetrics;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Sets.newHashSet;
public class MeasuresPublisher implements ReportPublisherStep {
- private final BatchComponentCache resourceCache;
- private final MeasureCache measureCache;
+ private static final class MeasureToReportMeasure implements Function<Measure, BatchReport.Measure> {
+ private final BatchComponent resource;
+ private final BatchReport.Measure.Builder builder = BatchReport.Measure.newBuilder();
- public MeasuresPublisher(BatchComponentCache resourceCache, MeasureCache measureCache) {
- this.resourceCache = resourceCache;
- this.measureCache = measureCache;
- }
+ private MeasureToReportMeasure(BatchComponent resource) {
+ this.resource = resource;
+ }
- @Override
- public void publish(BatchReportWriter writer) {
- for (final BatchComponent resource : resourceCache.all()) {
- Iterable<Measure> batchMeasures = measureCache.byResource(resource.resource());
- Iterable<org.sonar.batch.protocol.output.BatchReport.Measure> reportMeasures = Iterables.transform(batchMeasures, new Function<Measure, BatchReport.Measure>() {
- private final BatchReport.Measure.Builder builder = BatchReport.Measure.newBuilder();
-
- @Override
- public BatchReport.Measure apply(@Nonnull Measure input) {
- validateMeasure(input, resource.key());
- return toReportMeasure(builder, input);
- }
- });
- writer.writeComponentMeasures(resource.batchId(), reportMeasures);
+ @Override
+ public BatchReport.Measure apply(@Nonnull Measure input) {
+ validateMeasure(input, resource.key());
+ return toReportMeasure(builder, input);
+ }
+
+ private static void validateMeasure(Measure measure, String componentKey) {
+ if (measure.getValue() == null && measure.getData() == null) {
+ throw new IllegalArgumentException(String.format("Measure on metric '%s' and component '%s' has no value, but it's not allowed", measure.getMetricKey(), componentKey));
+ }
+ }
+
+ private BatchReport.Measure toReportMeasure(BatchReport.Measure.Builder builder, Measure measure) {
+ builder.clear();
+
+ builder.setValueType(getMeasureValueType(measure.getMetric().getType()));
+ setValueAccordingToType(builder, measure);
+ // Because some numeric measures also have a data (like Sqale rating)
+ String data = measure.getData();
+ if (data != null) {
+ builder.setStringValue(data);
+ }
+ builder.setMetricKey(measure.getMetricKey());
+ return builder.build();
}
- }
- private static void validateMeasure(Measure measure, String componentKey) {
- if (measure.getValue() == null && measure.getData() == null) {
- throw new IllegalArgumentException(String.format("Measure on metric '%s' and component '%s' has no value, but it's not allowed", measure.getMetricKey(), componentKey));
+ private void setValueAccordingToType(BatchReport.Measure.Builder builder, Measure measure) {
+ Serializable value = measure.value();
+ switch (builder.getValueType()) {
+ case BOOLEAN:
+ builder.setBooleanValue((Boolean) value);
+ break;
+ case DOUBLE:
+ builder.setDoubleValue(((Number) value).doubleValue());
+ break;
+ case INT:
+ builder.setIntValue(((Number) value).intValue());
+ break;
+ case LONG:
+ builder.setLongValue(((Number) value).longValue());
+ break;
+ case STRING:
+ builder.setStringValue((String) value);
+ break;
+ default:
+ throw new IllegalStateException("Unknown value type: " + builder.getValueType());
+ }
}
+
+ private MeasureValueType getMeasureValueType(ValueType type) {
+ switch (type) {
+ case INT:
+ case RATING:
+ return MeasureValueType.INT;
+ case FLOAT:
+ case PERCENT:
+ return MeasureValueType.DOUBLE;
+ case BOOL:
+ return MeasureValueType.BOOLEAN;
+ case STRING:
+ case DATA:
+ case LEVEL:
+ case DISTRIB:
+ return MeasureValueType.STRING;
+ case WORK_DUR:
+ case MILLISEC:
+ return MeasureValueType.LONG;
+ default:
+ throw new IllegalStateException("Unknown value type: " + type);
+ }
+ }
+
}
- private BatchReport.Measure toReportMeasure(BatchReport.Measure.Builder builder, Measure measure) {
- builder.clear();
+ private static final class IsMetricAllowed implements Predicate<Measure> {
+ private final Set<String> allowedMetricKeys;
+
+ private IsMetricAllowed(Set<String> allowedMetricKeys) {
+ this.allowedMetricKeys = allowedMetricKeys;
+ }
- builder.setValueType(getMeasureValueType(measure.getMetric().getType()));
- setValueAccordingToType(builder, measure);
- // Because some numeric measures also have a data (like Sqale rating)
- String data = measure.getData();
- if (data != null) {
- builder.setStringValue(data);
+ @Override
+ public boolean apply(Measure input) {
+ return allowedMetricKeys.contains(input.getMetricKey());
}
- builder.setMetricKey(measure.getMetricKey());
- return builder.build();
}
- private void setValueAccordingToType(BatchReport.Measure.Builder builder, Measure measure) {
- Serializable value = measure.value();
- switch (builder.getValueType()) {
- case BOOLEAN:
- builder.setBooleanValue((Boolean) value);
- break;
- case DOUBLE:
- builder.setDoubleValue(((Number) value).doubleValue());
- break;
- case INT:
- builder.setIntValue(((Number) value).intValue());
- break;
- case LONG:
- builder.setLongValue(((Number) value).longValue());
- break;
- case STRING:
- builder.setStringValue((String) value);
- break;
- default:
- throw new IllegalStateException("Unknown value type: " + builder.getValueType());
+ private static final class MetricToKey implements Function<Metric, String> {
+ @Override
+ public String apply(Metric input) {
+ return input.key();
}
}
- private MeasureValueType getMeasureValueType(ValueType type) {
- switch (type) {
- case INT:
- case RATING:
- return MeasureValueType.INT;
- case FLOAT:
- case PERCENT:
- return MeasureValueType.DOUBLE;
- case BOOL:
- return MeasureValueType.BOOLEAN;
- case STRING:
- case DATA:
- case LEVEL:
- case DISTRIB:
- return MeasureValueType.STRING;
- case WORK_DUR:
- case MILLISEC:
- return MeasureValueType.LONG;
- default:
- throw new IllegalStateException("Unknown value type: " + type);
+ private final BatchComponentCache resourceCache;
+ private final MeasureCache measureCache;
+ private final BatchMetrics batchMetrics;
+
+ public MeasuresPublisher(BatchComponentCache resourceCache, MeasureCache measureCache, BatchMetrics batchMetrics) {
+ this.resourceCache = resourceCache;
+ this.measureCache = measureCache;
+ this.batchMetrics = batchMetrics;
+ }
+
+ @Override
+ public void publish(BatchReportWriter writer) {
+ final Set<String> allowedMetricKeys = newHashSet(transform(batchMetrics.getMetrics(), new MetricToKey()));
+ for (final BatchComponent resource : resourceCache.all()) {
+ Iterable<Measure> batchMeasures = measureCache.byResource(resource.resource());
+ Iterable<org.sonar.batch.protocol.output.BatchReport.Measure> reportMeasures = transform(
+ filter(batchMeasures, new IsMetricAllowed(allowedMetricKeys)),
+ new MeasureToReportMeasure(resource));
+ writer.writeComponentMeasures(resource.batchId(), reportMeasures);
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
index a567cd2e48b..2e265e2640d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
@@ -92,6 +92,7 @@ import org.sonar.batch.source.CodeColorizers;
import org.sonar.batch.test.TestPlanBuilder;
import org.sonar.batch.test.TestableBuilder;
import org.sonar.core.issue.workflow.FunctionExecutor;
+import org.sonar.core.metric.BatchMetrics;
import org.sonar.core.platform.ComponentContainer;
public class ProjectScanContainer extends ComponentContainer {
@@ -197,6 +198,7 @@ public class ProjectScanContainer extends ComponentContainer {
ProjectSettings.class,
// Report
+ BatchMetrics.class,
ReportPublisher.class,
AnalysisContextReportPublisher.class,
MetadataPublisher.class,
@@ -206,7 +208,7 @@ public class ProjectScanContainer extends ComponentContainer {
CoveragePublisher.class,
SourcePublisher.class,
TestExecutionAndCoveragePublisher.class,
-
+
// Cpd
CpdExecutor.class,
SonarDuplicationsIndex.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java
index c0adb26f4b6..963f7e14b91 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java
@@ -51,7 +51,11 @@ public class MeasureCache {
}
public Iterable<Measure> byResource(Resource r) {
- return cache.values(r.getEffectiveKey());
+ return byComponentKey(r.getEffectiveKey());
+ }
+
+ public Iterable<Measure> byComponentKey(String effectiveKey) {
+ return cache.values(effectiveKey);
}
@CheckForNull
diff --git a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
index 1d7221b8d96..09604877532 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
@@ -67,10 +67,6 @@ public class DefaultSensorStorage implements SensorStorage {
private static final Logger LOG = LoggerFactory.getLogger(DefaultSensorStorage.class);
private static final List<Metric> INTERNAL_METRICS = Arrays.<Metric>asList(
- // Computed by CpdSensor
- CoreMetrics.DUPLICATED_FILES,
- CoreMetrics.DUPLICATED_LINES,
- CoreMetrics.DUPLICATED_BLOCKS,
// Computed by LinesSensor
CoreMetrics.LINES);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/ZeroCoverageSensor.java b/sonar-batch/src/main/java/org/sonar/batch/source/ZeroCoverageSensor.java
new file mode 100644
index 00000000000..f211050dabc
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/source/ZeroCoverageSensor.java
@@ -0,0 +1,112 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.batch.source;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Sets;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputFile.Type;
+import org.sonar.api.batch.measure.Metric;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.batch.sensor.coverage.CoverageType;
+import org.sonar.api.batch.sensor.coverage.NewCoverage;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.batch.scan.measure.MeasureCache;
+
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Sets.newHashSet;
+
+@Phase(name = Phase.Name.POST)
+public final class ZeroCoverageSensor implements Sensor {
+
+ private static final class MeasureToMetricKey implements Function<Measure, String> {
+ @Override
+ public String apply(Measure input) {
+ return input.getMetricKey();
+ }
+ }
+
+ private static final class MetricToKey implements Function<Metric, String> {
+ @Override
+ public String apply(Metric input) {
+ return input.key();
+ }
+ }
+
+ private final MeasureCache measureCache;
+
+ public ZeroCoverageSensor(MeasureCache measureCache) {
+ this.measureCache = measureCache;
+ }
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor.name("Zero Coverage Sensor");
+ }
+
+ @Override
+ public void execute(final SensorContext context) {
+ FileSystem fs = context.fileSystem();
+ for (InputFile f : fs.inputFiles(fs.predicates().hasType(Type.MAIN))) {
+ if (!isCoverageMeasuresAlreadyDefined(f)) {
+ Measure execLines = measureCache.byMetric(f.key(), CoreMetrics.EXECUTABLE_LINES_DATA_KEY);
+ if (execLines != null) {
+ storeZeroCoverageForEachExecutableLine(context, f, execLines);
+ }
+
+ }
+ }
+ }
+
+ private static void storeZeroCoverageForEachExecutableLine(final SensorContext context, InputFile f, Measure execLines) {
+ NewCoverage newCoverage = context.newCoverage().ofType(CoverageType.UNIT).onFile(f);
+ Map<Integer, String> lineMeasures = KeyValueFormat.parseIntString((String) execLines.value());
+ for (Map.Entry<Integer, String> lineMeasure : lineMeasures.entrySet()) {
+ int lineIdx = lineMeasure.getKey();
+ if (lineIdx <= f.lines()) {
+ String value = lineMeasure.getValue();
+ if (StringUtils.isNotEmpty(value) && Integer.parseInt(value) > 0) {
+ newCoverage.lineHits(lineIdx, 0);
+ }
+ }
+ }
+ newCoverage.save();
+ }
+
+ private boolean isCoverageMeasuresAlreadyDefined(InputFile f) {
+ Set<String> metricKeys = newHashSet(transform(measureCache.byComponentKey(f.key()), new MeasureToMetricKey()));
+ Function<Metric, String> metricToKey = new MetricToKey();
+ Set<String> allCoverageMetricKeys = newHashSet(concat(transform(CoverageType.UNIT.allMetrics(), metricToKey),
+ transform(CoverageType.IT.allMetrics(), metricToKey),
+ transform(CoverageType.OVERALL.allMetrics(), metricToKey)));
+ return !Sets.intersection(metricKeys, allCoverageMetricKeys).isEmpty();
+ }
+
+}