]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6339 Feed Coverage in compute report
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 1 Apr 2015 19:02:38 +0000 (21:02 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 7 Apr 2015 15:20:46 +0000 (17:20 +0200)
sonar-batch/src/main/java/org/sonar/batch/report/CoveragePublisher.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java

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 (file)
index 0000000..b2b4eaf
--- /dev/null
@@ -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);
+  }
+
+}
index 0295e13bb2b78ac2d88092c75dcf3dc62cdfd5d4..9b0dde8791185419f718de65587fcf89b0c96718 100644 (file)
@@ -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,
index 6e724866c674f67da049f438fbf9c305771822e1..d9a67454ad0e2bcb43254ba4f8445eef6455c92e 100644 (file)
@@ -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
index 30bfafa97931b2f43210d74beafac022c618e7a0..43ba3500126cc292bbdb97571f21f307871db6b6 100644 (file)
@@ -186,6 +186,8 @@ public class ProjectScanContainer extends ComponentContainer {
       ComponentsPublisher.class,
       IssuesPublisher.class,
       MeasuresPublisher.class,
+      DuplicationsPublisher.class,
+      CoveragePublisher.class,
 
       ScanTaskObservers.class);
   }
index 724c900176e7d3f304c2ec9ecfef148136cf7b42..5aec595a9f74aae82c019c625f5c324cd353ed42 100644 (file)
@@ -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
index 60f04a9f6266dc34083b026a7c4b8d4a28c6264a..3114e054718de061c659baf6e8efb3080eee35c6 100644 (file)
@@ -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)
index 33dc4b35b71035f2e359ae5d62cc2d81d07d2336..bebf743c01c83baa84d764cf6d236702d23681d8 100644 (file)
@@ -24,6 +24,7 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import org.sonar.api.resources.Scopes;
+import org.sonar.api.test.MutableTestPlan;
 import org.sonar.api.utils.SonarException;
 
 import javax.annotation.Nullable;
@@ -485,7 +486,15 @@ public final class CoreMetrics {
     .setOptimizedBestValue(true)
     .create();
 
+  /**
+   * @deprecated since 5.2 use {@link MutableTestPlan}
+   */
+  @Deprecated
   public static final String TEST_DATA_KEY = "test_data";
+  /**
+   * @deprecated since 5.2 use {@link MutableTestPlan}
+   */
+  @Deprecated
   public static final Metric<String> TEST_DATA = new Metric.Builder(TEST_DATA_KEY, "Unit tests details", Metric.ValueType.DATA)
     .setDescription("Unit tests details")
     .setDirection(Metric.DIRECTION_WORST)
@@ -580,12 +589,19 @@ public final class CoreMetrics {
     .setDeleteHistoricalData(true)
     .create();
 
+  /**
+   * 
+   * @deprecated since 5.2 soon to be removed
+   */
+  @Deprecated
   public static final String COVERAGE_LINE_HITS_DATA_KEY = "coverage_line_hits_data";
 
   /**
    * Key-value pairs, where key - is a number of line, and value - is a number of hits for this line.
    * Use {@link CoverageMeasuresBuilder} to build measure for this metric.
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final Metric<String> COVERAGE_LINE_HITS_DATA = new Metric.Builder(COVERAGE_LINE_HITS_DATA_KEY, "Coverage hits by line", Metric.ValueType.DATA)
     .setDomain(DOMAIN_TESTS)
     .setDeleteHistoricalData(true)
@@ -656,25 +672,37 @@ public final class CoreMetrics {
     .setDeleteHistoricalData(true)
     .create();
 
+  /**
+   * @deprecated since 5.2 soon to be removed
+   */
+  @Deprecated
   public static final String CONDITIONS_BY_LINE_KEY = "conditions_by_line";
 
   /**
    * Use {@link CoverageMeasuresBuilder} to build measure for this metric.
    *
    * @since 2.7
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final Metric<String> CONDITIONS_BY_LINE = new Metric.Builder(CONDITIONS_BY_LINE_KEY, "Conditions by line", Metric.ValueType.DATA)
     .setDomain(DOMAIN_TESTS)
     .setDeleteHistoricalData(true)
     .create();
 
+  /**
+   * @deprecated since 5.2 soon to be removed
+   */
+  @Deprecated
   public static final String COVERED_CONDITIONS_BY_LINE_KEY = "covered_conditions_by_line";
 
   /**
    * Use {@link CoverageMeasuresBuilder} to build measure for this metric.
    *
    * @since 2.7
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final Metric<String> COVERED_CONDITIONS_BY_LINE = new Metric.Builder(COVERED_CONDITIONS_BY_LINE_KEY, "Covered conditions by line", Metric.ValueType.DATA)
     .setDomain(DOMAIN_TESTS)
     .setDeleteHistoricalData(true)
@@ -823,12 +851,16 @@ public final class CoreMetrics {
 
   /**
    * @since 2.12
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final String IT_COVERAGE_LINE_HITS_DATA_KEY = "it_coverage_line_hits_data";
 
   /**
    * @since 2.12
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final Metric<String> IT_COVERAGE_LINE_HITS_DATA = new Metric.Builder(IT_COVERAGE_LINE_HITS_DATA_KEY, "IT coverage hits data", Metric.ValueType.DATA)
     .setDescription("Integration Tests Code coverage line hits data")
     .setDirection(Metric.DIRECTION_NONE)
@@ -940,12 +972,16 @@ public final class CoreMetrics {
 
   /**
    * @since 2.12
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final String IT_CONDITIONS_BY_LINE_KEY = "it_conditions_by_line";
 
   /**
    * @since 2.12
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final Metric<String> IT_CONDITIONS_BY_LINE = new Metric.Builder(IT_CONDITIONS_BY_LINE_KEY, "IT conditions by line", Metric.ValueType.DATA)
     .setDomain(DOMAIN_INTEGRATION_TESTS)
     .setDeleteHistoricalData(true)
@@ -953,12 +989,16 @@ public final class CoreMetrics {
 
   /**
    * @since 2.12
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final String IT_COVERED_CONDITIONS_BY_LINE_KEY = "it_covered_conditions_by_line";
 
   /**
    * @since 2.12
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final Metric<String> IT_COVERED_CONDITIONS_BY_LINE = new Metric.Builder(IT_COVERED_CONDITIONS_BY_LINE_KEY, "IT covered conditions by line", Metric.ValueType.DATA)
     .setDomain(DOMAIN_INTEGRATION_TESTS)
     .setDeleteHistoricalData(true)
@@ -1107,12 +1147,16 @@ public final class CoreMetrics {
 
   /**
    * @since 3.3
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final String OVERALL_COVERAGE_LINE_HITS_DATA_KEY = "overall_coverage_line_hits_data";
 
   /**
    * @since 3.3
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final Metric<String> OVERALL_COVERAGE_LINE_HITS_DATA = new Metric.Builder(OVERALL_COVERAGE_LINE_HITS_DATA_KEY, "Overall coverage hits by line",
     Metric.ValueType.DATA)
     .setDescription("Coverage hits by all tests and by line")
@@ -1227,12 +1271,16 @@ public final class CoreMetrics {
 
   /**
    * @since 3.3
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final String OVERALL_CONDITIONS_BY_LINE_KEY = "overall_conditions_by_line";
 
   /**
    * @since 3.3
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final Metric<String> OVERALL_CONDITIONS_BY_LINE = new Metric.Builder(OVERALL_CONDITIONS_BY_LINE_KEY, "Overall conditions by line", Metric.ValueType.DATA)
     .setDescription("Overall conditions by all tests and by line")
     .setDomain(DOMAIN_OVERALL_TESTS)
@@ -1241,12 +1289,16 @@ public final class CoreMetrics {
 
   /**
    * @since 3.3
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final String OVERALL_COVERED_CONDITIONS_BY_LINE_KEY = "overall_covered_conditions_by_line";
 
   /**
    * @since 3.3
+   * @deprecated since 5.2 soon to be removed
    */
+  @Deprecated
   public static final Metric<String> OVERALL_COVERED_CONDITIONS_BY_LINE = new Metric.Builder(OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, "Overall covered branches by line",
     Metric.ValueType.DATA)
     .setDescription("Overall covered branches by all tests and by line")