aboutsummaryrefslogtreecommitdiffstats
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
parentcef567021858b0e7239ac80d4512172ef7b70dbd (diff)
downloadsonarqube-accc8fc25dbdddb7fc6fbc5fa171ce29929d01d9.tar.gz
sonarqube-accc8fc25dbdddb7fc6fbc5fa171ce29929d01d9.zip
SONAR-5772 New executable_lines_data metric
-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
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java55
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java28
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java16
-rw-r--r--sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java21
-rw-r--r--sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java9
-rw-r--r--sonar-db/pom.xml4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/CoverageType.java30
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/internal/DefaultCoverage.java24
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java22
17 files changed, 368 insertions, 175 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();
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java
index 9c9fa85b624..d693527df47 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java
@@ -91,11 +91,11 @@ public class CoverageMediumTest {
assertThat(result.coverageFor(file, 2).getOverallCoveredConditions()).isEqualTo(0);
Map<String, List<org.sonar.batch.protocol.output.BatchReport.Measure>> allMeasures = result.allMeasures();
- assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue", "stringValue")
- .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 2, ""),
- tuple(CoreMetrics.UNCOVERED_LINES_KEY, 0, ""),
- tuple(CoreMetrics.CONDITIONS_TO_COVER_KEY, 2, ""),
- tuple(CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, 0, "2=1"));
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue")
+ .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 2),
+ tuple(CoreMetrics.UNCOVERED_LINES_KEY, 0),
+ tuple(CoreMetrics.CONDITIONS_TO_COVER_KEY, 2),
+ tuple(CoreMetrics.UNCOVERED_CONDITIONS_KEY, 1));
}
@Test
@@ -132,4 +132,49 @@ public class CoverageMediumTest {
CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY);
}
+ @Test
+ public void fallbackOnExecutableLines() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File measuresFile = new File(srcDir, "sample.xoo.measures");
+ FileUtils.write(xooFile, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}");
+ FileUtils.write(measuresFile, "executable_lines_data:2=1;3=1;4=0");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ InputFile file = result.inputFile("src/sample.xoo");
+ assertThat(result.coverageFor(file, 1)).isNull();
+
+ assertThat(result.coverageFor(file, 2).getUtHits()).isFalse();
+ assertThat(result.coverageFor(file, 2).getItHits()).isFalse();
+ assertThat(result.coverageFor(file, 2).getConditions()).isEqualTo(0);
+ assertThat(result.coverageFor(file, 2).getUtCoveredConditions()).isEqualTo(0);
+ assertThat(result.coverageFor(file, 2).getItCoveredConditions()).isEqualTo(0);
+ assertThat(result.coverageFor(file, 2).getOverallCoveredConditions()).isEqualTo(0);
+
+ assertThat(result.coverageFor(file, 3).getUtHits()).isFalse();
+ assertThat(result.coverageFor(file, 4)).isNull();
+
+ Map<String, List<org.sonar.batch.protocol.output.BatchReport.Measure>> allMeasures = result.allMeasures();
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue")
+ .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 2),
+ tuple(CoreMetrics.UNCOVERED_LINES_KEY, 2));
+
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey").doesNotContain(CoreMetrics.CONDITIONS_TO_COVER_KEY, CoreMetrics.UNCOVERED_CONDITIONS_KEY);
+ }
+
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java
index 3399ab52604..51830c30d1e 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java
@@ -30,8 +30,6 @@ import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.measures.Metric.ValueType;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.batch.index.BatchComponentCache;
@@ -39,6 +37,7 @@ import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.batch.protocol.output.BatchReportReader;
import org.sonar.batch.protocol.output.BatchReportWriter;
import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.core.metric.BatchMetrics;
import org.sonar.core.util.CloseableIterator;
import static java.util.Arrays.asList;
@@ -70,28 +69,17 @@ public class MeasuresPublisherTest {
resourceCache.add(sampleFile, null);
measureCache = mock(MeasureCache.class);
when(measureCache.byResource(any(Resource.class))).thenReturn(Collections.<Measure>emptyList());
- publisher = new MeasuresPublisher(resourceCache, measureCache);
+ publisher = new MeasuresPublisher(resourceCache, measureCache, new BatchMetrics());
}
@Test
public void publishMeasures() throws Exception {
- Measure measure = new Measure<>(CoreMetrics.COVERAGE)
- .setValue(2.0)
- .setPersonId(2);
- // Manual measure
- Measure manual = new Measure<>(new Metric<>("manual_metric", ValueType.BOOL))
- .setValue(1.0);
- // Sqale rating have both a value and a data
- Measure rating = new Measure<>(CoreMetrics.SQALE_RATING)
- .setValue(2.0)
- .setData("A");
- // Long measure
- Measure longMeasure = new Measure<>(CoreMetrics.TECHNICAL_DEBT)
- .setValue(1.0);
+ Measure measure = new Measure<>(CoreMetrics.LINES_TO_COVER)
+ .setValue(2.0);
// String value
Measure stringMeasure = new Measure<>(CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION)
.setData("foo bar");
- when(measureCache.byResource(sampleFile)).thenReturn(asList(measure, manual, rating, longMeasure, stringMeasure));
+ when(measureCache.byResource(sampleFile)).thenReturn(asList(measure, stringMeasure));
File outputDir = temp.newFolder();
BatchReportWriter writer = new BatchReportWriter(outputDir);
@@ -102,13 +90,13 @@ public class MeasuresPublisherTest {
assertThat(reader.readComponentMeasures(1)).hasSize(0);
try (CloseableIterator<BatchReport.Measure> componentMeasures = reader.readComponentMeasures(2)) {
- assertThat(componentMeasures).hasSize(5);
+ assertThat(componentMeasures).hasSize(2);
}
}
@Test
public void fail_with_IAE_when_measure_has_no_value() throws Exception {
- Measure measure = new Measure<>(CoreMetrics.COVERAGE);
+ Measure measure = new Measure<>(CoreMetrics.LINES_TO_COVER);
when(measureCache.byResource(sampleFile)).thenReturn(Collections.singletonList(measure));
File outputDir = temp.newFolder();
@@ -118,7 +106,7 @@ public class MeasuresPublisherTest {
publisher.publish(writer);
fail();
} catch (RuntimeException e) {
- assertThat(ExceptionUtils.getFullStackTrace(e)).contains("Measure on metric 'coverage' and component 'foo:src/Foo.php' has no value, but it's not allowed");
+ assertThat(ExceptionUtils.getFullStackTrace(e)).contains("Measure on metric 'lines_to_cover' and component 'foo:src/Foo.php' has no value, but it's not allowed");
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
index 043ae5eb107..61f21529547 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
@@ -166,22 +166,6 @@ public class MeasureCacheTest extends AbstractCachesTest {
}
@Test
- public void should_add_measure_with_same_metric() {
- Project p = new Project("struts");
-
- assertThat(measureCache.entries()).hasSize(0);
- assertThat(measureCache.byResource(p)).hasSize(0);
-
- Measure m1 = new Measure(CoreMetrics.NCLOC, 1.0);
- Measure m2 = new Measure(CoreMetrics.NCLOC, 1.0).setPersonId(2);
- measureCache.put(p, m1);
- measureCache.put(p, m2);
-
- assertThat(measureCache.entries()).hasSize(2);
- assertThat(measureCache.byResource(p)).hasSize(2);
- }
-
- @Test
public void should_get_measures() {
Project p = new Project("struts");
Resource dir = Directory.create("foo/bar").setEffectiveKey("struts:foo/bar");
diff --git a/sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java b/sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java
index 0fcaf3bee79..f93d5faa92d 100644
--- a/sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java
+++ b/sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java
@@ -20,10 +20,8 @@
package org.sonar.core.metric;
import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
@@ -32,6 +30,8 @@ import org.sonar.api.measures.Metric;
import org.sonar.api.measures.Metrics;
import org.sonar.api.server.ServerSide;
+import static com.google.common.collect.FluentIterable.from;
+import static java.util.Arrays.asList;
import static org.sonar.api.measures.CoreMetrics.ACCESSORS;
import static org.sonar.api.measures.CoreMetrics.CLASSES;
import static org.sonar.api.measures.CoreMetrics.COMMENTED_OUT_CODE_LINES;
@@ -45,9 +45,6 @@ import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER;
import static org.sonar.api.measures.CoreMetrics.COVERAGE_LINE_HITS_DATA;
import static org.sonar.api.measures.CoreMetrics.COVERED_CONDITIONS_BY_LINE;
import static org.sonar.api.measures.CoreMetrics.DIRECTORIES;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES;
import static org.sonar.api.measures.CoreMetrics.FILES;
import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION;
import static org.sonar.api.measures.CoreMetrics.FUNCTIONS;
@@ -115,10 +112,6 @@ public class BatchMetrics {
STATEMENTS,
ACCESSORS,
- DUPLICATED_LINES,
- DUPLICATED_BLOCKS,
- DUPLICATED_FILES,
-
COMPLEXITY,
COMPLEXITY_IN_CLASSES,
COMPLEXITY_IN_FUNCTIONS,
@@ -155,11 +148,14 @@ public class BatchMetrics {
OVERALL_COVERED_CONDITIONS_BY_LINE,
OVERALL_CONDITIONS_BY_LINE,
- QUALITY_PROFILES
- );
+ QUALITY_PROFILES);
private final Set<Metric> metrics;
+ public BatchMetrics() {
+ this.metrics = ALLOWED_CORE_METRICS;
+ }
+
public BatchMetrics(Metrics[] metricsRepositories) {
this.metrics = ImmutableSet.copyOf(Iterables.concat(getPluginMetrics(metricsRepositories), ALLOWED_CORE_METRICS));
}
@@ -169,8 +165,7 @@ public class BatchMetrics {
}
private static Iterable<Metric> getPluginMetrics(Metrics[] metricsRepositories) {
- return FluentIterable.from(Arrays.asList(metricsRepositories))
- .transformAndConcat(FlattenMetrics.INSTANCE);
+ return from(asList(metricsRepositories)).transformAndConcat(FlattenMetrics.INSTANCE);
}
private enum FlattenMetrics implements Function<Metrics, List<Metric>> {
diff --git a/sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java b/sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java
index 1af8d06e155..c1e2e5e2fb3 100644
--- a/sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java
@@ -31,12 +31,12 @@ import static org.assertj.core.api.Assertions.assertThat;
public class BatchMetricsTest {
- static final BatchMetrics SENSOR_METRICS_WITHOUT_METRIC_PLUGIN = new BatchMetrics(new Metrics[]{});
- static final BatchMetrics SENSOR_METRICS_WITH_PLUGIN = new BatchMetrics(new Metrics[]{new FakeMetrics()});
+ static final BatchMetrics SENSOR_METRICS_WITHOUT_METRIC_PLUGIN = new BatchMetrics(new Metrics[] {});
+ static final BatchMetrics SENSOR_METRICS_WITH_PLUGIN = new BatchMetrics(new Metrics[] {new FakeMetrics()});
@Test
public void check_number_of_allowed_core_metrics() throws Exception {
- assertThat(SENSOR_METRICS_WITHOUT_METRIC_PLUGIN.getMetrics()).hasSize(52);
+ assertThat(SENSOR_METRICS_WITHOUT_METRIC_PLUGIN.getMetrics()).hasSize(49);
}
@Test
@@ -52,8 +52,7 @@ public class BatchMetricsTest {
public List<Metric> getMetrics() {
return ImmutableList.<Metric>of(
new Metric.Builder("key1", "name1", Metric.ValueType.INT).create(),
- new Metric.Builder("key2", "name2", Metric.ValueType.FLOAT).create()
- );
+ new Metric.Builder("key2", "name2", Metric.ValueType.FLOAT).create());
}
}
}
diff --git a/sonar-db/pom.xml b/sonar-db/pom.xml
index 86339eed76d..9cb8dc2b64c 100644
--- a/sonar-db/pom.xml
+++ b/sonar-db/pom.xml
@@ -13,6 +13,10 @@
<name>SonarQube :: Database</name>
<description>Create and request SonarQube schema</description>
+
+ <properties>
+ <sonar.exclusions>target/generated-sources/**</sonar.exclusions>
+ </properties>
<dependencies>
<dependency>
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
index 66734f4026f..228dab0a51c 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
@@ -96,7 +96,7 @@ public interface SensorContext {
/**
* Builder to define coverage in a file.
- * Don't forget to call {@link NewDuplication#save()}.
+ * Don't forget to call {@link NewCoverage#save()}.
*/
NewCoverage newCoverage();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/CoverageType.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/CoverageType.java
index ceef90f9788..cdab75f44c4 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/CoverageType.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/CoverageType.java
@@ -20,9 +20,31 @@
package org.sonar.api.batch.sensor.coverage;
import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
import org.sonar.api.measures.Metric;
-import static org.sonar.api.measures.CoreMetrics.*;
+import static org.sonar.api.measures.CoreMetrics.CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.COVERAGE_LINE_HITS_DATA;
+import static org.sonar.api.measures.CoreMetrics.COVERED_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.IT_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.IT_CONDITIONS_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.IT_COVERAGE_LINE_HITS_DATA;
+import static org.sonar.api.measures.CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.IT_LINES_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.IT_UNCOVERED_CONDITIONS;
+import static org.sonar.api.measures.CoreMetrics.IT_UNCOVERED_LINES;
+import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_CONDITIONS_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_LINES_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_CONDITIONS;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_LINES;
+import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS;
+import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES;
/**
* Different coverage categories.
@@ -43,6 +65,7 @@ public enum CoverageType {
private final Metric uncoveredConditions;
private final Metric coveredConditionsByLine;
private final Metric conditionsByLine;
+ private final Set<Metric> all;
private CoverageType(Metric linesToCover, Metric uncoveredLines, Metric lineHitsData, Metric conditionsToCover, Metric uncoveredConditions, Metric coveredConditionsByLine,
Metric conditionsByLine) {
@@ -53,6 +76,11 @@ public enum CoverageType {
this.uncoveredConditions = uncoveredConditions;
this.coveredConditionsByLine = coveredConditionsByLine;
this.conditionsByLine = conditionsByLine;
+ this.all = ImmutableSet.of(linesToCover, uncoveredLines, lineHitsData, conditionsToCover, uncoveredConditions, coveredConditionsByLine, conditionsByLine);
+ }
+
+ public Set<Metric> allMetrics() {
+ return all;
}
public Metric linesToCover() {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/internal/DefaultCoverage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/internal/DefaultCoverage.java
index 7f4ca3ec676..96e5f9737ba 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/internal/DefaultCoverage.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/internal/DefaultCoverage.java
@@ -21,6 +21,9 @@ package org.sonar.api.batch.sensor.coverage.internal;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
+import java.util.Collections;
+import java.util.SortedMap;
+import javax.annotation.Nullable;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.sensor.coverage.CoverageType;
@@ -28,11 +31,6 @@ import org.sonar.api.batch.sensor.coverage.NewCoverage;
import org.sonar.api.batch.sensor.internal.DefaultStorable;
import org.sonar.api.batch.sensor.internal.SensorStorage;
-import javax.annotation.Nullable;
-
-import java.util.Collections;
-import java.util.SortedMap;
-
public class DefaultCoverage extends DefaultStorable implements NewCoverage {
private DefaultInputFile inputFile;
@@ -64,7 +62,6 @@ public class DefaultCoverage extends DefaultStorable implements NewCoverage {
@Override
public NewCoverage ofType(CoverageType type) {
- validateFile();
Preconditions.checkNotNull(type, "type can't be null");
this.type = type;
return this;
@@ -93,20 +90,6 @@ public class DefaultCoverage extends DefaultStorable implements NewCoverage {
Preconditions.checkState(line > 0, "Line number must be strictly positive: " + line);
}
- private void validateLines() {
- for (int l : hitsByLine.keySet()) {
- validateLine(l);
- }
-
- for (int l : conditionsByLine.keySet()) {
- validateLine(l);
- }
-
- for (int l : coveredConditionsByLine.keySet()) {
- validateLine(l);
- }
- }
-
private void validateFile() {
Preconditions.checkNotNull(inputFile, "Call onFile() first");
}
@@ -157,7 +140,6 @@ public class DefaultCoverage extends DefaultStorable implements NewCoverage {
public void doSave() {
validateFile();
Preconditions.checkNotNull(type, "Call ofType() first");
- validateLines();
storage.store(this);
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
index da186c87254..796c66d1a15 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
@@ -19,7 +19,6 @@
*/
package org.sonar.api.measures;
-import com.google.common.annotations.Beta;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import java.lang.reflect.Field;
@@ -2123,7 +2122,6 @@ public final class CoreMetrics {
/**
* @since 2.14
*/
- @Beta
public static final String NCLOC_DATA_KEY = "ncloc_data";
/**
@@ -2133,7 +2131,6 @@ public final class CoreMetrics {
* @see org.sonar.api.measures.FileLinesContext
* @since 2.14
*/
- @Beta
public static final Metric<String> NCLOC_DATA = new Metric.Builder(NCLOC_DATA_KEY, "ncloc_data", Metric.ValueType.DATA)
.setHidden(true)
.setDomain(DOMAIN_SIZE)
@@ -2142,7 +2139,6 @@ public final class CoreMetrics {
/**
* @since 2.14
*/
- @Beta
public static final String COMMENT_LINES_DATA_KEY = "comment_lines_data";
/**
@@ -2152,12 +2148,28 @@ public final class CoreMetrics {
* @see org.sonar.api.measures.FileLinesContext
* @since 2.14
*/
- @Beta
public static final Metric<String> COMMENT_LINES_DATA = new Metric.Builder(COMMENT_LINES_DATA_KEY, "comment_lines_data", Metric.ValueType.DATA)
.setHidden(true)
.setDomain(DOMAIN_DOCUMENTATION)
.create();
+ /**
+ * @since 5.5
+ */
+ public static final String EXECUTABLE_LINES_DATA_KEY = "executable_lines_data";
+
+ /**
+ * Information about executable lines of code in file.
+ * Key-value pairs, where key - is a number of line, and value - is an indicator of whether line contains executable code (1) or not (0).
+ *
+ * @see org.sonar.api.measures.FileLinesContext
+ * @since 5.5
+ */
+ public static final Metric<String> EXECUTABLE_LINES_DATA = new Metric.Builder(EXECUTABLE_LINES_DATA_KEY, "executable_lines_data", Metric.ValueType.DATA)
+ .setHidden(true)
+ .setDomain(DOMAIN_TESTS)
+ .create();
+
// --------------------------------------------------------------------------------------------------------------------
//
// OTHERS