|
|
@@ -25,11 +25,11 @@ import java.util.Collections; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
import java.util.stream.StreamSupport; |
|
|
|
|
|
|
|
import org.sonar.api.batch.fs.InputComponent; |
|
|
|
import org.sonar.api.batch.fs.InputFile; |
|
|
|
import org.sonar.api.batch.fs.InputFile.Type; |
|
|
|
import org.sonar.api.batch.fs.internal.DefaultInputComponent; |
|
|
|
import org.sonar.api.batch.fs.internal.DefaultInputFile; |
|
|
|
import org.sonar.api.batch.measure.Metric; |
|
|
|
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; |
|
|
|
import org.sonar.api.measures.CoreMetrics; |
|
|
@@ -82,25 +82,30 @@ public class MeasuresPublisher implements ReportPublisherStep { |
|
|
|
public void publish(ScannerReportWriter writer) { |
|
|
|
final ScannerReport.Measure.Builder builder = ScannerReport.Measure.newBuilder(); |
|
|
|
|
|
|
|
for (final InputComponent c : componentStore.allFilesToPublish()) { |
|
|
|
for (final InputComponent c : componentStore.all()) { |
|
|
|
DefaultInputComponent component = (DefaultInputComponent) c; |
|
|
|
// Recompute all coverage measures from line data to take into account the possible merge of several reports |
|
|
|
updateCoverageFromLineData(component); |
|
|
|
// Recompute test execution measures from MutableTestPlan to take into account the possible merge of several reports |
|
|
|
updateTestExecutionFromTestPlan(component); |
|
|
|
if (component.isFile()) { |
|
|
|
DefaultInputFile file = (DefaultInputFile) component; |
|
|
|
// Recompute all coverage measures from line data to take into account the possible merge of several reports |
|
|
|
updateCoverageFromLineData(file); |
|
|
|
// Recompute test execution measures from MutableTestPlan to take into account the possible merge of several reports |
|
|
|
updateTestExecutionFromTestPlan(file); |
|
|
|
} |
|
|
|
|
|
|
|
Iterable<DefaultMeasure<?>> scannerMeasures = measureCache.byComponentKey(component.key()); |
|
|
|
writer.writeComponentMeasures(component.batchId(), StreamSupport.stream(scannerMeasures.spliterator(), false) |
|
|
|
.map(input -> { |
|
|
|
if (input.value() == null) { |
|
|
|
throw new IllegalArgumentException( |
|
|
|
String.format("Measure on metric '%s' and component '%s' has no value, but it's not allowed", input.metric().key(), component.key())); |
|
|
|
} |
|
|
|
builder.clear(); |
|
|
|
builder.setMetricKey(input.metric().key()); |
|
|
|
setValueAccordingToType(builder, input); |
|
|
|
return builder.build(); |
|
|
|
}).collect(Collectors.toList())); |
|
|
|
if (scannerMeasures.iterator().hasNext()) { |
|
|
|
writer.writeComponentMeasures(component.batchId(), StreamSupport.stream(scannerMeasures.spliterator(), false) |
|
|
|
.map(input -> { |
|
|
|
if (input.value() == null) { |
|
|
|
throw new IllegalArgumentException( |
|
|
|
String.format("Measure on metric '%s' and component '%s' has no value, but it's not allowed", input.metric().key(), component.key())); |
|
|
|
} |
|
|
|
builder.clear(); |
|
|
|
builder.setMetricKey(input.metric().key()); |
|
|
|
setValueAccordingToType(builder, input); |
|
|
|
return builder.build(); |
|
|
|
}).collect(Collectors.toList())); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -122,48 +127,48 @@ public class MeasuresPublisher implements ReportPublisherStep { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void updateTestExecutionFromTestPlan(final InputComponent component) { |
|
|
|
final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component); |
|
|
|
private void updateTestExecutionFromTestPlan(final InputFile inputFile) { |
|
|
|
final MutableTestPlan testPlan = testPlanBuilder.getTestPlanByFile(inputFile); |
|
|
|
if (testPlan == null || Iterables.isEmpty(testPlan.testCases())) { |
|
|
|
return; |
|
|
|
} |
|
|
|
long nonSkippedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() != Status.SKIPPED).count(); |
|
|
|
measureCache.put(component.key(), TESTS_KEY, new DefaultMeasure<Integer>().forMetric(TESTS).withValue((int) nonSkippedTests)); |
|
|
|
measureCache.put(inputFile.key(), TESTS_KEY, new DefaultMeasure<Integer>().forMetric(TESTS).withValue((int) nonSkippedTests)); |
|
|
|
long executionTime = StreamSupport.stream(testPlan.testCases().spliterator(), false).mapToLong(t -> t.durationInMs() != null ? t.durationInMs().longValue() : 0L).sum(); |
|
|
|
measureCache.put(component.key(), TEST_EXECUTION_TIME_KEY, new DefaultMeasure<Long>().forMetric(TEST_EXECUTION_TIME).withValue(executionTime)); |
|
|
|
measureCache.put(inputFile.key(), TEST_EXECUTION_TIME_KEY, new DefaultMeasure<Long>().forMetric(TEST_EXECUTION_TIME).withValue(executionTime)); |
|
|
|
long errorTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.ERROR).count(); |
|
|
|
measureCache.put(component.key(), TEST_ERRORS_KEY, new DefaultMeasure<Integer>().forMetric(TEST_ERRORS).withValue((int) errorTests)); |
|
|
|
measureCache.put(inputFile.key(), TEST_ERRORS_KEY, new DefaultMeasure<Integer>().forMetric(TEST_ERRORS).withValue((int) errorTests)); |
|
|
|
long skippedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.SKIPPED).count(); |
|
|
|
measureCache.put(component.key(), SKIPPED_TESTS_KEY, new DefaultMeasure<Integer>().forMetric(SKIPPED_TESTS).withValue((int) skippedTests)); |
|
|
|
measureCache.put(inputFile.key(), SKIPPED_TESTS_KEY, new DefaultMeasure<Integer>().forMetric(SKIPPED_TESTS).withValue((int) skippedTests)); |
|
|
|
long failedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.FAILURE).count(); |
|
|
|
measureCache.put(component.key(), TEST_FAILURES_KEY, new DefaultMeasure<Integer>().forMetric(TEST_FAILURES).withValue((int) failedTests)); |
|
|
|
measureCache.put(inputFile.key(), TEST_FAILURES_KEY, new DefaultMeasure<Integer>().forMetric(TEST_FAILURES).withValue((int) failedTests)); |
|
|
|
} |
|
|
|
|
|
|
|
private void updateCoverageFromLineData(final InputComponent component) { |
|
|
|
if (!component.isFile() || ((InputFile) component).type() != Type.MAIN) { |
|
|
|
private void updateCoverageFromLineData(final InputFile inputFile) { |
|
|
|
if (inputFile.type() != Type.MAIN) { |
|
|
|
return; |
|
|
|
} |
|
|
|
DefaultMeasure<String> lineHitsMeasure = (DefaultMeasure<String>) measureCache.byMetric(component.key(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY); |
|
|
|
DefaultMeasure<String> lineHitsMeasure = (DefaultMeasure<String>) measureCache.byMetric(inputFile.key(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY); |
|
|
|
if (lineHitsMeasure != null) { |
|
|
|
Map<Integer, Integer> lineHits = KeyValueFormat.parseIntInt(lineHitsMeasure.value()); |
|
|
|
measureCache.put(component.key(), LINES_TO_COVER_KEY, new DefaultMeasure<Integer>().forMetric(LINES_TO_COVER).withValue(lineHits.keySet().size())); |
|
|
|
measureCache.put(component.key(), UNCOVERED_LINES_KEY, |
|
|
|
measureCache.put(inputFile.key(), LINES_TO_COVER_KEY, new DefaultMeasure<Integer>().forMetric(LINES_TO_COVER).withValue(lineHits.keySet().size())); |
|
|
|
measureCache.put(inputFile.key(), UNCOVERED_LINES_KEY, |
|
|
|
new DefaultMeasure<Integer>().forMetric(UNCOVERED_LINES).withValue((int) lineHits.values() |
|
|
|
.stream() |
|
|
|
.filter(hit -> hit == 0) |
|
|
|
.count())); |
|
|
|
} |
|
|
|
DefaultMeasure<String> conditionsMeasure = (DefaultMeasure<String>) measureCache.byMetric(component.key(), CoreMetrics.CONDITIONS_BY_LINE_KEY); |
|
|
|
DefaultMeasure<String> coveredConditionsMeasure = (DefaultMeasure<String>) measureCache.byMetric(component.key(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY); |
|
|
|
DefaultMeasure<String> conditionsMeasure = (DefaultMeasure<String>) measureCache.byMetric(inputFile.key(), CoreMetrics.CONDITIONS_BY_LINE_KEY); |
|
|
|
DefaultMeasure<String> coveredConditionsMeasure = (DefaultMeasure<String>) measureCache.byMetric(inputFile.key(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY); |
|
|
|
if (conditionsMeasure != null) { |
|
|
|
Map<Integer, Integer> conditions = KeyValueFormat.parseIntInt(conditionsMeasure.value()); |
|
|
|
Map<Integer, Integer> coveredConditions = coveredConditionsMeasure != null ? KeyValueFormat.parseIntInt(coveredConditionsMeasure.value()) : Collections.emptyMap(); |
|
|
|
measureCache.put(component.key(), CONDITIONS_TO_COVER_KEY, new DefaultMeasure<Integer>().forMetric(CONDITIONS_TO_COVER).withValue(conditions |
|
|
|
measureCache.put(inputFile.key(), CONDITIONS_TO_COVER_KEY, new DefaultMeasure<Integer>().forMetric(CONDITIONS_TO_COVER).withValue(conditions |
|
|
|
.values() |
|
|
|
.stream() |
|
|
|
.mapToInt(Integer::intValue) |
|
|
|
.sum())); |
|
|
|
measureCache.put(component.key(), UNCOVERED_CONDITIONS_KEY, |
|
|
|
measureCache.put(inputFile.key(), UNCOVERED_CONDITIONS_KEY, |
|
|
|
new DefaultMeasure<Integer>().forMetric(UNCOVERED_CONDITIONS) |
|
|
|
.withValue((int) conditions.keySet() |
|
|
|
.stream() |