diff options
Diffstat (limited to 'sonar-batch/src/main/java')
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(); + } + +} |