Skip to content

Commit

Permalink
SONAR-6339 Feed Coverage in compute report
Browse files Browse the repository at this point in the history
  • Loading branch information
henryju committed Apr 7, 2015
1 parent fa3100e commit ca8ad6b
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 46 deletions.
@@ -0,0 +1,131 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.report;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.batch.index.BatchResource;
import org.sonar.batch.index.ResourceCache;
import org.sonar.batch.protocol.output.BatchReport.Coverage;
import org.sonar.batch.protocol.output.BatchReport.Coverage.Builder;
import org.sonar.batch.protocol.output.BatchReportWriter;
import org.sonar.batch.scan.measure.MeasureCache;

import java.util.LinkedHashMap;
import java.util.Map;

public class CoveragePublisher implements ReportPublisherStep {

private final ResourceCache resourceCache;
private final MeasureCache measureCache;

public CoveragePublisher(ResourceCache resourceCache, MeasureCache measureCache) {
this.resourceCache = resourceCache;
this.measureCache = measureCache;
}

@Override
public void publish(BatchReportWriter writer) {
for (final BatchResource resource : resourceCache.all()) {
if (!resource.isFile()) {
continue;
}
Map<Integer, Coverage.Builder> coveragePerLine = new LinkedHashMap<>();

applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, new MeasureOperation() {
@Override
public void apply(String value, Coverage.Builder builder) {
builder.setUtHits(Integer.parseInt(value) > 0);
builder.setConditions(1);
}
});
applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), 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.inputPath()).lines(), 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.inputPath()).lines(), 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.inputPath()).lines(), 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.inputPath()).lines(), CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, new MeasureOperation() {
@Override
public void apply(String value, Coverage.Builder builder) {
builder.setItCoveredConditions(Integer.parseInt(value));
}
});

writer.writeFileCoverage(resource.batchId(), Iterables.transform(coveragePerLine.values(), new Function<Coverage.Builder, Coverage>() {
@Override
public Coverage apply(Builder input) {
return input.build();
}
}));
}
}

void applyLineMeasure(String inputFileKey, int lineCount, String metricKey, Map<Integer, Coverage.Builder> coveragePerLine, MeasureOperation op) {
Iterable<Measure> measures = measureCache.byMetric(inputFileKey, metricKey);
if (measures.iterator().hasNext()) {
Measure measure = measures.iterator().next();
Map<Integer, String> lineMeasures = KeyValueFormat.parseIntString((String) measure.value());
for (Map.Entry<Integer, String> lineMeasure : lineMeasures.entrySet()) {
int lineIdx = lineMeasure.getKey();
if (lineIdx <= lineCount) {
String value = lineMeasure.getValue();
if (StringUtils.isNotEmpty(value)) {
Coverage.Builder coverageBuilder = coveragePerLine.get(lineIdx);
if (coverageBuilder == null) {
coverageBuilder = Coverage.newBuilder();
coverageBuilder.setLine(lineIdx);
coveragePerLine.put(lineIdx, coverageBuilder);
}
op.apply(value, coverageBuilder);
}
}
}
}
}

static interface MeasureOperation {
void apply(String value, Coverage.Builder builder);
}

}
Expand Up @@ -44,6 +44,9 @@ public DuplicationsPublisher(ResourceCache resourceCache, DuplicationCache dupli
@Override @Override
public void publish(BatchReportWriter writer) { public void publish(BatchReportWriter writer) {
for (final BatchResource resource : resourceCache.all()) { for (final BatchResource resource : resourceCache.all()) {
if (!resource.isFile()) {
continue;
}
Iterable<DefaultDuplication> dups = duplicationCache.byComponent(resource.resource().getEffectiveKey()); Iterable<DefaultDuplication> dups = duplicationCache.byComponent(resource.resource().getEffectiveKey());
if (dups.iterator().hasNext()) { if (dups.iterator().hasNext()) {
Iterable<org.sonar.batch.protocol.output.BatchReport.Duplication> reportDuplications = Iterables.transform(dups, Iterable<org.sonar.batch.protocol.output.BatchReport.Duplication> reportDuplications = Iterables.transform(dups,
Expand Down
Expand Up @@ -57,7 +57,6 @@
import org.sonar.batch.phases.*; import org.sonar.batch.phases.*;
import org.sonar.batch.qualitygate.GenerateQualityGateEvents; import org.sonar.batch.qualitygate.GenerateQualityGateEvents;
import org.sonar.batch.qualitygate.QualityGateVerifier; import org.sonar.batch.qualitygate.QualityGateVerifier;
import org.sonar.batch.report.*;
import org.sonar.batch.rule.*; import org.sonar.batch.rule.*;
import org.sonar.batch.scan.filesystem.*; import org.sonar.batch.scan.filesystem.*;
import org.sonar.batch.scan.report.IssuesReports; import org.sonar.batch.scan.report.IssuesReports;
Expand Down Expand Up @@ -114,11 +113,6 @@ private void addCoreComponents() {
SensorsExecutor.class, SensorsExecutor.class,
InitializersExecutor.class, InitializersExecutor.class,
ProjectInitializer.class, ProjectInitializer.class,
ReportPublisher.class,
ComponentsPublisher.class,
IssuesPublisher.class,
MeasuresPublisher.class,
DuplicationsPublisher.class,
moduleDefinition.getContainerExtensions(), moduleDefinition.getContainerExtensions(),


// file system // file system
Expand Down
Expand Up @@ -186,6 +186,8 @@ private void addBatchComponents() {
ComponentsPublisher.class, ComponentsPublisher.class,
IssuesPublisher.class, IssuesPublisher.class,
MeasuresPublisher.class, MeasuresPublisher.class,
DuplicationsPublisher.class,
CoveragePublisher.class,


ScanTaskObservers.class); ScanTaskObservers.class);
} }
Expand Down
Expand Up @@ -22,8 +22,10 @@
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import org.sonar.api.batch.fs.*; import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputPath;
import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.measure.MetricFinder;
import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.ActiveRules;
Expand All @@ -43,7 +45,10 @@
import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric;
import org.sonar.api.measures.PersistenceMode; import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.measures.SumChildDistributionFormula; import org.sonar.api.measures.SumChildDistributionFormula;
import org.sonar.api.resources.*; import org.sonar.api.resources.Directory;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Scopes;
import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleKey;
import org.sonar.api.source.Symbol; import org.sonar.api.source.Symbol;
import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.duplication.DuplicationCache;
Expand Down Expand Up @@ -151,18 +156,14 @@ private void setValueAccordingToMetricType(Measure<?> measure, org.sonar.api.mea


@Override @Override
public void store(Issue issue) { public void store(Issue issue) {
Resource r; String componentKey;
InputPath inputPath = issue.inputPath(); InputPath inputPath = issue.inputPath();
if (inputPath != null) { if (inputPath != null) {
if (inputPath instanceof InputDir) { componentKey = ComponentKeys.createEffectiveKey(project.getKey(), inputPath);
r = Directory.create(inputPath.relativePath());
} else {
r = File.create(inputPath.relativePath());
}
} else { } else {
r = project; componentKey = project.getKey();
} }
moduleIssues.initAndAddIssue(toDefaultIssue(project.getKey(), ComponentKeys.createEffectiveKey(project, r), issue)); moduleIssues.initAndAddIssue(toDefaultIssue(project.getKey(), componentKey, issue));
} }


public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) { public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) {
Expand All @@ -178,33 +179,12 @@ public static DefaultIssue toDefaultIssue(String projectKey, String componentKey
.build(); .build();
} }


private File getTestResource(InputFile testFile) {
File testRes = File.create(testFile.relativePath());
testRes.setQualifier(Qualifiers.UNIT_TEST_FILE);
// Reload
testRes = sonarIndex.getResource(testRes);
if (testRes == null) {
throw new IllegalArgumentException("Provided input file is not indexed or not a test file: " + testFile);
}
return testRes;
}

private File getMainResource(InputFile mainFile) {
File mainRes = File.create(mainFile.relativePath());
// Reload
mainRes = sonarIndex.getResource(mainRes);
if (mainRes == null) {
throw new IllegalArgumentException("Provided input file is not indexed or not a main file: " + mainRes);
}
return mainRes;
}

private File getFile(InputFile file) { private File getFile(InputFile file) {
if (file.type() == InputFile.Type.MAIN) { BatchResource r = resourceCache.get(file);
return getMainResource(file); if (r == null) {
} else { throw new IllegalStateException("Provided input file is not indexed");
return getTestResource(file);
} }
return (File) r.resource();
} }


@Override @Override
Expand Down
Expand Up @@ -117,9 +117,9 @@ public void shouldSaveFileMeasureToSensorContext() {
InputFile file = new DefaultInputFile("foo", "src/Foo.php"); InputFile file = new DefaultInputFile("foo", "src/Foo.php");


ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class); ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class);
File sonarFile = File.create("src/Foo.php"); Resource sonarFile = File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
resourceCache.add(sonarFile, null).setInputPath(file);
when(sonarIndex.addMeasure(eq(sonarFile), argumentCaptor.capture())).thenReturn(null); when(sonarIndex.addMeasure(eq(sonarFile), argumentCaptor.capture())).thenReturn(null);
when(sonarIndex.getResource(sonarFile)).thenReturn(sonarFile);
sensorStorage.store(new DefaultMeasure() sensorStorage.store(new DefaultMeasure()
.onFile(file) .onFile(file)
.forMetric(CoreMetrics.NCLOC) .forMetric(CoreMetrics.NCLOC)
Expand All @@ -137,12 +137,11 @@ public void shouldSetAppropriatePersistenceMode() {
InputFile file = new DefaultInputFile("foo", "src/Foo.php"); InputFile file = new DefaultInputFile("foo", "src/Foo.php");


ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class); ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class);
File sonarFile = File.create("src/Foo.php"); Resource sonarFile = File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
resourceCache.add(sonarFile, null).setInputPath(file);


when(sonarIndex.addMeasure(eq(sonarFile), argumentCaptor.capture())).thenReturn(null); when(sonarIndex.addMeasure(eq(sonarFile), argumentCaptor.capture())).thenReturn(null);


when(sonarIndex.getResource(sonarFile)).thenReturn(sonarFile);

sensorStorage.store(new DefaultMeasure() sensorStorage.store(new DefaultMeasure()
.onFile(file) .onFile(file)
.forMetric(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION) .forMetric(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION)
Expand Down

0 comments on commit ca8ad6b

Please sign in to comment.