// Since SQ 6.2 all coverage reports are merged as coverage
Resource project = orchestrator.getServer().getWsClient().find(ResourceQuery.createForMetrics("sample-overall-coverage", ALL_COVERAGE_METRICS));
- assertThat(project.getMeasureValue("line_coverage")).isEqualTo(50.0);
+ assertThat(project.getMeasureValue("line_coverage")).isEqualTo(75.0);
assertThat(project.getMeasureValue("lines_to_cover")).isEqualTo(4);
- assertThat(project.getMeasureValue("uncovered_lines")).isEqualTo(2);
- assertThat(project.getMeasureValue("branch_coverage")).isEqualTo(25.0);
+ assertThat(project.getMeasureValue("uncovered_lines")).isEqualTo(1);
+ assertThat(project.getMeasureValue("branch_coverage")).isEqualTo(50.0);
assertThat(project.getMeasureValue("conditions_to_cover")).isEqualTo(4);
- assertThat(project.getMeasureValue("uncovered_conditions")).isEqualTo(3);
- assertThat(project.getMeasureValue("coverage")).isEqualTo(37.5);
+ assertThat(project.getMeasureValue("uncovered_conditions")).isEqualTo(2);
+ assertThat(project.getMeasureValue("coverage")).isEqualTo(62.5);
assertThat(project.getMeasureValue("it_coverage")).isNull();
private static final Offset<Double> DEFAULT_OFFSET = Offset.offset(0.1d);
private static final String[] ALL_NEW_COVERAGE_METRICS = new String[] {
- "new_coverage", "new_line_coverage", "new_branch_coverage",
- "new_it_coverage", "new_it_line_coverage", "new_it_branch_coverage",
- "new_overall_coverage", "new_overall_line_coverage", "new_overall_branch_coverage"
+ "new_coverage", "new_line_coverage", "new_branch_coverage"
};
@BeforeClass
orchestrator.executeBuilds(SonarScanner.create(projectDir("testing/xoo-sample-new-coverage-v1"))
.setProperty("sonar.projectDate", "2015-02-01")
- .setProperty("sonar.scm.disabled", "false")
- );
+ .setProperty("sonar.scm.disabled", "false"));
orchestrator.executeBuilds(SonarScanner.create(projectDir("testing/xoo-sample-new-coverage-v2"))
.setProperty("sonar.scm.disabled", "false"));
}
@Test
- public void new_unit_test_coverage() throws Exception {
- Resource project = orchestrator.getServer().getWsClient().find(ResourceQuery.createForMetrics(PROJECT_KEY, ALL_NEW_COVERAGE_METRICS).setIncludeTrends(true));
- assertThat(project.getMeasure("new_coverage").getVariation1()).isEqualTo(62.5d, DEFAULT_OFFSET);
- assertThat(project.getMeasure("new_line_coverage").getVariation1()).isEqualTo(80d, DEFAULT_OFFSET);
- assertThat(project.getMeasure("new_branch_coverage").getVariation1()).isEqualTo(33.3, DEFAULT_OFFSET);
- }
-
- @Test
- public void new_integration_test_coverage() throws Exception {
- Resource project = orchestrator.getServer().getWsClient().find(ResourceQuery.createForMetrics(PROJECT_KEY, ALL_NEW_COVERAGE_METRICS).setIncludeTrends(true));
- assertThat(project.getMeasure("new_it_coverage").getVariation1()).isEqualTo(85.7, DEFAULT_OFFSET);
- assertThat(project.getMeasure("new_it_line_coverage").getVariation1()).isEqualTo(100d, DEFAULT_OFFSET);
- assertThat(project.getMeasure("new_it_branch_coverage").getVariation1()).isEqualTo(66.7, DEFAULT_OFFSET);
- }
-
- @Test
- public void new_overall_coverage() throws Exception {
+ public void new_coverage() throws Exception {
Resource project = orchestrator.getServer().getWsClient().find(ResourceQuery.createForMetrics(PROJECT_KEY, ALL_NEW_COVERAGE_METRICS).setIncludeTrends(true));
- assertThat(project.getMeasure("new_overall_coverage").getVariation1()).isEqualTo(44.4d, DEFAULT_OFFSET);
- assertThat(project.getMeasure("new_overall_line_coverage").getVariation1()).isEqualTo(50d, DEFAULT_OFFSET);
- assertThat(project.getMeasure("new_overall_branch_coverage").getVariation1()).isEqualTo(42.85, DEFAULT_OFFSET);
+ assertThat(project.getMeasure("new_coverage").getVariation1()).isEqualTo(66.6d, DEFAULT_OFFSET);
+ assertThat(project.getMeasure("new_line_coverage").getVariation1()).isEqualTo(100d, DEFAULT_OFFSET);
+ assertThat(project.getMeasure("new_branch_coverage").getVariation1()).isEqualTo(42.8, DEFAULT_OFFSET);
}
}
"code": "\t\tif (foo == bar && biz > 1) {",
"utLineHits": 0,
"utConditions": 4,
- "utCoveredConditions": 1
+ "utCoveredConditions": 2
},
{
"line": 11,
import com.google.common.base.Function;
import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
import javax.annotation.Nonnull;
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
-import org.sonar.core.metric.ScannerMetrics;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.utils.KeyValueFormat;
import org.sonar.scanner.index.BatchComponent;
import org.sonar.scanner.index.BatchComponentCache;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.scan.measure.MeasureCache;
import static com.google.common.collect.Iterables.transform;
+import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER_KEY;
+import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER_KEY;
+import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS;
+import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES;
+import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES_KEY;
public class MeasuresPublisher implements ReportPublisherStep {
}
- private static final class MetricToKey implements Function<Metric, String> {
- @Override
- public String apply(Metric input) {
- return input.key();
- }
- }
-
- private final BatchComponentCache resourceCache;
+ private final BatchComponentCache componentCache;
private final MeasureCache measureCache;
- private final ScannerMetrics scannerMetrics;
- public MeasuresPublisher(BatchComponentCache resourceCache, MeasureCache measureCache, ScannerMetrics scannerMetrics) {
- this.resourceCache = resourceCache;
+ public MeasuresPublisher(BatchComponentCache resourceCache, MeasureCache measureCache) {
+ this.componentCache = resourceCache;
this.measureCache = measureCache;
- this.scannerMetrics = scannerMetrics;
}
@Override
public void publish(ScannerReportWriter writer) {
- for (final BatchComponent resource : resourceCache.all()) {
- Iterable<DefaultMeasure<?>> scannerMeasures = measureCache.byComponentKey(resource.key());
- Iterable<ScannerReport.Measure> reportMeasures = transform(scannerMeasures, new MeasureToReportMeasure(resource));
- writer.writeComponentMeasures(resource.batchId(), reportMeasures);
+ for (final BatchComponent component : componentCache.all()) {
+ // Recompute all coverage measures from line data to take into account the possible merge of several reports
+ DefaultMeasure<String> lineHitsMeasure = (DefaultMeasure<String>) measureCache.byMetric(component.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,
+ 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);
+ 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
+ .values()
+ .stream()
+ .mapToInt(Integer::intValue)
+ .sum()));
+ measureCache.put(component.key(), UNCOVERED_CONDITIONS_KEY,
+ new DefaultMeasure<Integer>().forMetric(UNCOVERED_CONDITIONS)
+ .withValue((int) conditions.keySet()
+ .stream()
+ .mapToInt(line -> conditions.get(line) - coveredConditions.get(line))
+ .sum()));
+ }
+ Iterable<DefaultMeasure<?>> scannerMeasures = measureCache.byComponentKey(component.key());
+ Iterable<ScannerReport.Measure> reportMeasures = transform(scannerMeasures, new MeasureToReportMeasure(component));
+ writer.writeComponentMeasures(component.batchId(), reportMeasures);
}
}
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
import org.sonar.api.config.Settings;
+import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.core.metric.ScannerMetrics;
import org.sonar.duplications.block.Block;
FILE_FEEDBACK_EDGES_KEY,
FILE_TANGLE_INDEX_KEY,
FILE_TANGLES_KEY,
+ // SONARPHP-621
COMMENTED_OUT_CODE_LINES_KEY);
+ // Some Sensors still save those metrics
private static final List<String> COMPUTED_ON_CE_SIDE_METRICS_KEYS = Arrays.asList(
TEST_SUCCESS_DENSITY_KEY,
PUBLIC_DOCUMENTED_API_DENSITY_KEY);
if (previousMeasure != null) {
measureCache.put(file.key(), metric.key(), new DefaultMeasure<String>()
.forMetric((Metric<String>) metric)
- .withValue(KeyValueFormat.format(mergeLineMetric((String) previousMeasure.value(), (String) measure.value()))));
+ .withValue(KeyValueFormat.format(mergeCoverageLineMetric(metric, (String) previousMeasure.value(), (String) measure.value()))));
} else {
measureCache.put(file.key(), metric.key(), measure);
}
} else {
- // Other coverage metrics are all integer values
- DefaultMeasure<?> previousMeasure = measureCache.byMetric(file.key(), metric.key());
- if (previousMeasure != null) {
- measureCache.put(file.key(), metric.key(), new DefaultMeasure<Integer>()
- .forMetric((Metric<Integer>) metric)
- .withValue(Math.max((Integer) previousMeasure.value(), (Integer) measure.value())));
- } else {
- measureCache.put(file.key(), metric.key(), measure);
- }
+ // Other coverage metrics are all integer values. Just erase value, it will be recomputed at the end anyway
+ measureCache.put(file.key(), metric.key(), measure);
}
}
/**
- * Merge the two line data measures, keeping max value in case they both contains a value for the same line.
+ * Merge the two line coverage data measures. For lines hits use the sum, and for conditions
+ * keep max value in case they both contains a value for the same line.
*/
- private Map<Integer, Integer> mergeLineMetric(String value1, String value2) {
+ private static Map<Integer, Integer> mergeCoverageLineMetric(Metric<?> metric, String value1, String value2) {
Map<Integer, Integer> data1 = KeyValueFormat.parseIntInt(value1);
Map<Integer, Integer> data2 = KeyValueFormat.parseIntInt(value2);
- return Stream.of(data1, data2)
- .map(Map::entrySet)
- .flatMap(Collection::stream)
- .collect(
- Collectors.toMap(
- Map.Entry::getKey,
- Map.Entry::getValue,
- Integer::max));
+ if (metric.key().equals(CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY)) {
+ return Stream.of(data1, data2)
+ .map(Map::entrySet)
+ .flatMap(Collection::stream)
+ .collect(
+ Collectors.toMap(
+ Map.Entry::getKey,
+ Map.Entry::getValue,
+ Integer::sum));
+ } else {
+ return Stream.of(data1, data2)
+ .map(Map::entrySet)
+ .flatMap(Collection::stream)
+ .collect(
+ Collectors.toMap(
+ Map.Entry::getKey,
+ Map.Entry::getValue,
+ Integer::max));
+ }
}
public boolean isDeprecatedMetric(String metricKey) {
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.measures.CoreMetrics;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.scanner.scan.measure.MeasureCache;
+import org.sonar.scanner.sensor.coverage.CoverageExclusions;
-import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Sets.newHashSet;
}
private final MeasureCache measureCache;
+ private final CoverageExclusions coverageExclusions;
- public ZeroCoverageSensor(MeasureCache measureCache) {
+ public ZeroCoverageSensor(MeasureCache measureCache, CoverageExclusions exclusions) {
this.measureCache = measureCache;
+ this.coverageExclusions = exclusions;
}
@Override
public void execute(final SensorContext context) {
FileSystem fs = context.fileSystem();
for (InputFile f : fs.inputFiles(fs.predicates().hasType(Type.MAIN))) {
+ if (coverageExclusions.isExcluded(f)) {
+ continue;
+ }
if (!isCoverageMeasuresAlreadyDefined(f)) {
DefaultMeasure<String> execLines = (DefaultMeasure<String>) 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, DefaultMeasure<String> execLines) {
NewCoverage newCoverage = context.newCoverage().onFile(f);
- Map<Integer, String> lineMeasures = KeyValueFormat.parseIntString((String) execLines.value());
- for (Map.Entry<Integer, String> lineMeasure : lineMeasures.entrySet()) {
+ Map<Integer, Integer> lineMeasures = KeyValueFormat.parseIntInt((String) execLines.value());
+ for (Map.Entry<Integer, Integer> 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);
- }
+ if (lineIdx <= f.lines() && lineMeasure.getValue() > 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)));
+ Set<String> allCoverageMetricKeys = newHashSet(transform(CoverageType.UNIT.allMetrics(), metricToKey));
return !Sets.intersection(metricKeys, allCoverageMetricKeys).isEmpty();
}
File xooFile = new File(srcDir, "sample.xoo");
FileUtils.write(xooFile, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}");
File xooUtCoverageFile = new File(srcDir, "sample.xoo.coverage");
- FileUtils.write(xooUtCoverageFile, "2:2:2:2");
+ FileUtils.write(xooUtCoverageFile, "2:2:2:2\n4:0");
File xooItCoverageFile = new File(srcDir, "sample.xoo.itcoverage");
- FileUtils.write(xooItCoverageFile, "2:2:2:1\n3:1");
+ FileUtils.write(xooItCoverageFile, "2:2:2:1\n3:1\n5:0");
TaskResult result = tester.newTask()
.properties(ImmutableMap.<String, String>builder()
assertThat(result.coverageFor(file, 3).getHits()).isTrue();
Map<String, List<org.sonar.scanner.protocol.output.ScannerReport.Measure>> allMeasures = result.allMeasures();
- assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue.value")
- .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));
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue.value", "stringValue.value")
+ .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 4, ""), // 2, 3, 4, 5
+ tuple(CoreMetrics.UNCOVERED_LINES_KEY, 2, ""), // 4, 5
+ tuple(CoreMetrics.CONDITIONS_TO_COVER_KEY, 2, ""), // 2 x 2
+ tuple(CoreMetrics.UNCOVERED_CONDITIONS_KEY, 0, ""),
+ tuple(CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, 0, "2=4;3=1;4=0;5=0"),
+ tuple(CoreMetrics.CONDITIONS_BY_LINE_KEY, 0, "2=2"),
+ tuple(CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, 0, "2=2"));
}
@Test
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.resources.Project;
-import org.sonar.core.metric.ScannerMetrics;
import org.sonar.core.util.CloseableIterator;
import org.sonar.scanner.index.BatchComponentCache;
import org.sonar.scanner.protocol.output.ScannerReport;
resourceCache.add(sampleFile, null);
measureCache = mock(MeasureCache.class);
when(measureCache.byComponentKey(anyString())).thenReturn(Collections.<DefaultMeasure<?>>emptyList());
- publisher = new MeasuresPublisher(resourceCache, measureCache, new ScannerMetrics());
+ publisher = new MeasuresPublisher(resourceCache, measureCache);
}
@Test