diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2015-04-01 21:02:38 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2015-04-07 17:20:46 +0200 |
commit | ca8ad6b8b61305bd336325d05789da57b76efd7c (patch) | |
tree | 8391791a24c49a022d5b62f6d1e17165f4e21504 /sonar-batch | |
parent | fa3100e005c2fc3884ae6cce708129b71830fe91 (diff) | |
download | sonarqube-ca8ad6b8b61305bd336325d05789da57b76efd7c.tar.gz sonarqube-ca8ad6b8b61305bd336325d05789da57b76efd7c.zip |
SONAR-6339 Feed Coverage in compute report
Diffstat (limited to 'sonar-batch')
6 files changed, 155 insertions, 46 deletions
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 new file mode 100644 index 00000000000..b2b4eaf5442 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/CoveragePublisher.java @@ -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); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java index 0295e13bb2b..9b0dde87911 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java @@ -44,6 +44,9 @@ public class DuplicationsPublisher implements ReportPublisherStep { @Override public void publish(BatchReportWriter writer) { for (final BatchResource resource : resourceCache.all()) { + if (!resource.isFile()) { + continue; + } Iterable<DefaultDuplication> dups = duplicationCache.byComponent(resource.resource().getEffectiveKey()); if (dups.iterator().hasNext()) { Iterable<org.sonar.batch.protocol.output.BatchReport.Duplication> reportDuplications = Iterables.transform(dups, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index 6e724866c67..d9a67454ad0 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -57,7 +57,6 @@ import org.sonar.batch.language.LanguageDistributionDecorator; import org.sonar.batch.phases.*; import org.sonar.batch.qualitygate.GenerateQualityGateEvents; import org.sonar.batch.qualitygate.QualityGateVerifier; -import org.sonar.batch.report.*; import org.sonar.batch.rule.*; import org.sonar.batch.scan.filesystem.*; import org.sonar.batch.scan.report.IssuesReports; @@ -114,11 +113,6 @@ public class ModuleScanContainer extends ComponentContainer { SensorsExecutor.class, InitializersExecutor.class, ProjectInitializer.class, - ReportPublisher.class, - ComponentsPublisher.class, - IssuesPublisher.class, - MeasuresPublisher.class, - DuplicationsPublisher.class, moduleDefinition.getContainerExtensions(), // file system 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 30bfafa9793..43ba3500126 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 @@ -186,6 +186,8 @@ public class ProjectScanContainer extends ComponentContainer { ComponentsPublisher.class, IssuesPublisher.class, MeasuresPublisher.class, + DuplicationsPublisher.class, + CoveragePublisher.class, ScanTaskObservers.class); } 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 724c900176e..5aec595a9f7 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 @@ -22,8 +22,10 @@ package org.sonar.batch.sensor; import com.google.common.base.Function; import com.google.common.base.Preconditions; 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.InputPath; +import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.rule.ActiveRules; @@ -43,7 +45,10 @@ import org.sonar.api.measures.Formula; import org.sonar.api.measures.Metric; import org.sonar.api.measures.PersistenceMode; 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.source.Symbol; import org.sonar.batch.duplication.DuplicationCache; @@ -151,18 +156,14 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(Issue issue) { - Resource r; + String componentKey; InputPath inputPath = issue.inputPath(); if (inputPath != null) { - if (inputPath instanceof InputDir) { - r = Directory.create(inputPath.relativePath()); - } else { - r = File.create(inputPath.relativePath()); - } + componentKey = ComponentKeys.createEffectiveKey(project.getKey(), inputPath); } 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) { @@ -178,33 +179,12 @@ public class DefaultSensorStorage implements SensorStorage { .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) { - if (file.type() == InputFile.Type.MAIN) { - return getMainResource(file); - } else { - return getTestResource(file); + BatchResource r = resourceCache.get(file); + if (r == null) { + throw new IllegalStateException("Provided input file is not indexed"); } + return (File) r.resource(); } @Override diff --git a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java index 60f04a9f626..3114e054718 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java @@ -117,9 +117,9 @@ public class DefaultSensorStorageTest { InputFile file = new DefaultInputFile("foo", "src/Foo.php"); 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.getResource(sonarFile)).thenReturn(sonarFile); sensorStorage.store(new DefaultMeasure() .onFile(file) .forMetric(CoreMetrics.NCLOC) @@ -137,12 +137,11 @@ public class DefaultSensorStorageTest { InputFile file = new DefaultInputFile("foo", "src/Foo.php"); 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.getResource(sonarFile)).thenReturn(sonarFile); - sensorStorage.store(new DefaultMeasure() .onFile(file) .forMetric(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION) |