]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5389 Add replacement for CoverageMeasureBuilder
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 15 Oct 2014 08:10:40 +0000 (10:10 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Mon, 20 Oct 2014 09:45:31 +0000 (11:45 +0200)
sonar-batch/src/main/java/org/sonar/batch/scan2/BaseSensorContext.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/DefaultStorable.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/Coverage.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultCoverage.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultCoverageTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseCoverageTest.java

index c925f400100fb6ad8994631766edcedb77fa5fe9..09b9e5b5f3ebd485d5173e5f62e3149468ccfc85 100644 (file)
@@ -38,8 +38,10 @@ import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
 import org.sonar.api.batch.sensor.measure.Measure;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
+import org.sonar.api.batch.sensor.test.Coverage;
 import org.sonar.api.batch.sensor.test.TestCaseCoverage;
 import org.sonar.api.batch.sensor.test.TestCaseExecution;
+import org.sonar.api.batch.sensor.test.internal.DefaultCoverage;
 import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseCoverage;
 import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseExecution;
 import org.sonar.api.config.Settings;
@@ -154,6 +156,11 @@ public abstract class BaseSensorContext implements SensorContext, SensorStorage
     }
   }
 
+  @Override
+  public Coverage newCoverage() {
+    return new DefaultCoverage(this);
+  }
+
   @Override
   public TestCaseExecution newTestCaseExecution() {
     return new DefaultTestCaseExecution(this);
index 865fff64c9e401ac1e1b82ffe4f9b9095336f117..805d9db24177fa800bbfa44521398722ee176690 100644 (file)
@@ -30,6 +30,7 @@ import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
 import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.measure.Measure;
 import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
+import org.sonar.api.batch.sensor.test.Coverage;
 import org.sonar.api.batch.sensor.test.TestCaseCoverage;
 import org.sonar.api.batch.sensor.test.TestCaseExecution;
 import org.sonar.api.config.Settings;
@@ -112,6 +113,13 @@ public interface SensorContext {
 
   // ------------ TESTS ------------
 
+  /**
+   * Create a new coverage report.
+   * Don't forget to call {@link Coverage#save()} once all parameters are provided.
+   * @since 5.0
+   */
+  Coverage newCoverage();
+
   /**
    * Create a new test case execution report.
    * Don't forget to call {@link TestCaseExecution#save()} once all parameters are provided.
index 9b1c9fa113c0fb1887d8399879701c9e50629d4c..587ee6cd3d6347548d9a1a5e1fd4744bf7ece3fa 100644 (file)
@@ -41,7 +41,7 @@ public abstract class DefaultStorable {
 
   public final void save() {
     Preconditions.checkNotNull(this.storage, "No persister on this object");
-    Preconditions.checkState(!saved, "This measure was already saved");
+    Preconditions.checkState(!saved, "This object was already saved");
     doSave();
     this.saved = true;
   }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/Coverage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/Coverage.java
new file mode 100644 (file)
index 0000000..9181754
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.api.batch.sensor.test;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.measure.Metric;
+import org.sonar.api.measures.CoreMetrics;
+
+/**
+ * @since 5.0
+ */
+public interface Coverage {
+
+  public enum CoverageType {
+    UNIT(CoreMetrics.LINES_TO_COVER, CoreMetrics.UNCOVERED_LINES, CoreMetrics.COVERAGE_LINE_HITS_DATA, CoreMetrics.CONDITIONS_TO_COVER, CoreMetrics.UNCOVERED_CONDITIONS,
+      CoreMetrics.CONDITIONS_BY_LINE, CoreMetrics.COVERED_CONDITIONS_BY_LINE),
+    INTEGRATION(CoreMetrics.IT_LINES_TO_COVER, CoreMetrics.IT_UNCOVERED_LINES, CoreMetrics.IT_COVERAGE_LINE_HITS_DATA, CoreMetrics.IT_CONDITIONS_TO_COVER,
+      CoreMetrics.IT_UNCOVERED_CONDITIONS, CoreMetrics.IT_CONDITIONS_BY_LINE, CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE),
+    OVERALL(CoreMetrics.OVERALL_LINES_TO_COVER, CoreMetrics.OVERALL_UNCOVERED_LINES, CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA, CoreMetrics.OVERALL_CONDITIONS_TO_COVER,
+      CoreMetrics.OVERALL_UNCOVERED_CONDITIONS, CoreMetrics.OVERALL_CONDITIONS_BY_LINE, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE);
+
+    private Metric<Integer> linesToCover;
+    private Metric<Integer> uncoveredLines;
+    private Metric<String> lineHitsData;
+    private Metric<Integer> conditionsToCover;
+    private Metric<Integer> uncoveredConditions;
+    private Metric<String> conditionsByLine;
+    private Metric<String> coveredConditionsByLine;
+
+    private CoverageType(Metric<Integer> linesToCover, Metric<Integer> uncoveredLines, Metric<String> lineHitsData, Metric<Integer> conditionsToCover,
+      Metric<Integer> uncoveredConditions, Metric<String> conditionsByLine, Metric<String> coveredConditionsByLine) {
+      this.linesToCover = linesToCover;
+      this.uncoveredLines = uncoveredLines;
+      this.lineHitsData = lineHitsData;
+      this.conditionsToCover = conditionsToCover;
+      this.uncoveredConditions = uncoveredConditions;
+      this.conditionsByLine = conditionsByLine;
+      this.coveredConditionsByLine = coveredConditionsByLine;
+    }
+
+    public Metric<Integer> linesToCover() {
+      return linesToCover;
+    }
+
+    public Metric<Integer> uncoveredLines() {
+      return uncoveredLines;
+    }
+
+    public Metric<String> lineHitsData() {
+      return lineHitsData;
+    }
+
+    public Metric<Integer> conditionsToCover() {
+      return conditionsToCover;
+    }
+
+    public Metric<Integer> uncoveredConditions() {
+      return uncoveredConditions;
+    }
+
+    public Metric<String> conditionsByLine() {
+      return conditionsByLine;
+    }
+
+    public Metric<String> coveredConditionsByLine() {
+      return coveredConditionsByLine;
+    }
+  }
+
+  /**
+   * The file you are storing coverage on.
+   */
+  Coverage onFile(InputFile inputFile);
+
+  Coverage ofType(CoverageType type);
+
+  Coverage lineHits(int line, int hits);
+
+  Coverage conditions(int line, int conditions, int coveredConditions);
+
+  /**
+   * Call this method only once when your are done with defining the test case coverage.
+   */
+  void save();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultCoverage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultCoverage.java
new file mode 100644 (file)
index 0000000..4671dab
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * 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.api.batch.sensor.test.internal;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputFile.Type;
+import org.sonar.api.batch.sensor.SensorStorage;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
+import org.sonar.api.batch.sensor.test.Coverage;
+import org.sonar.api.utils.KeyValueFormat;
+
+import java.util.SortedMap;
+
+public final class DefaultCoverage implements Coverage {
+
+  private static final String INPUT_FILE_SHOULD_BE_NON_NULL = "InputFile should be non null";
+
+  private InputFile file;
+  private CoverageType type;
+  private int totalCoveredLines = 0, totalConditions = 0, totalCoveredConditions = 0;
+  private SortedMap<Integer, Integer> hitsByLine = Maps.newTreeMap();
+  private SortedMap<Integer, Integer> conditionsByLine = Maps.newTreeMap();
+  private SortedMap<Integer, Integer> coveredConditionsByLine = Maps.newTreeMap();
+
+  protected transient final SensorStorage storage;
+  private transient boolean saved = false;
+
+  public DefaultCoverage() {
+    this.storage = null;
+  }
+
+  public DefaultCoverage(SensorStorage storage) {
+    this.storage = storage;
+  }
+
+  @Override
+  public DefaultCoverage lineHits(int lineId, int hits) {
+    if (!hitsByLine.containsKey(lineId)) {
+      hitsByLine.put(lineId, hits);
+      if (hits > 0) {
+        totalCoveredLines += 1;
+      }
+    }
+    return this;
+  }
+
+  @Override
+  public DefaultCoverage conditions(int lineId, int conditions, int coveredConditions) {
+    if (conditions > 0 && !conditionsByLine.containsKey(lineId)) {
+      totalConditions += conditions;
+      totalCoveredConditions += coveredConditions;
+      conditionsByLine.put(lineId, conditions);
+      coveredConditionsByLine.put(lineId, coveredConditions);
+    }
+    return this;
+  }
+
+  public InputFile file() {
+    return file;
+  }
+
+  @Override
+  public DefaultCoverage onFile(InputFile inputFile) {
+    Preconditions.checkNotNull(inputFile, INPUT_FILE_SHOULD_BE_NON_NULL);
+    Preconditions.checkArgument(inputFile.type() == Type.MAIN, "Coverage is only supported on main files");
+    this.file = inputFile;
+    return this;
+  }
+
+  public CoverageType type() {
+    return type;
+  }
+
+  @Override
+  public DefaultCoverage ofType(CoverageType type) {
+    Preconditions.checkNotNull(type);
+    this.type = type;
+    return this;
+  }
+
+  public void save() {
+    Preconditions.checkNotNull(this.storage, "No persister on this object");
+    Preconditions.checkState(!saved, "This object was already saved");
+    Preconditions.checkNotNull(this.file, "File is mandatory on Coverage");
+    Preconditions.checkNotNull(this.type, "Type is mandatory on Coverage");
+
+    if (hitsByLine.size() > 0) {
+      new DefaultMeasure<Integer>(storage)
+        .onFile(file)
+        .forMetric(type.linesToCover())
+        .withValue(hitsByLine.size())
+        .save();
+      new DefaultMeasure<Integer>(storage)
+        .onFile(file)
+        .forMetric(type.uncoveredLines())
+        .withValue(hitsByLine.size() - totalCoveredLines)
+        .save();
+      new DefaultMeasure<String>(storage)
+        .onFile(file)
+        .forMetric(type.lineHitsData())
+        .withValue(KeyValueFormat.format(hitsByLine))
+        .save();
+    }
+    if (totalConditions > 0) {
+      new DefaultMeasure<Integer>(storage)
+        .onFile(file)
+        .forMetric(type.conditionsToCover())
+        .withValue(totalConditions)
+        .save();
+      new DefaultMeasure<Integer>(storage)
+        .onFile(file)
+        .forMetric(type.uncoveredConditions())
+        .withValue(totalConditions - totalCoveredConditions)
+        .save();
+      new DefaultMeasure<String>(storage)
+        .onFile(file)
+        .forMetric(type.coveredConditionsByLine())
+        .withValue(KeyValueFormat.format(coveredConditionsByLine))
+        .save();
+      new DefaultMeasure<String>(storage)
+        .onFile(file)
+        .forMetric(type.conditionsByLine())
+        .withValue(KeyValueFormat.format(conditionsByLine))
+        .save();
+    }
+  }
+
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultCoverageTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultCoverageTest.java
new file mode 100644 (file)
index 0000000..d89707b
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.api.batch.sensor.test.internal;
+
+import org.junit.Test;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.SensorStorage;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
+import org.sonar.api.batch.sensor.test.Coverage.CoverageType;
+import org.sonar.api.measures.CoreMetrics;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class DefaultCoverageTest {
+
+  private InputFile main = new DefaultInputFile("foo", "src/Foo.php").setType(InputFile.Type.MAIN);
+
+  @Test
+  public void testCreation() {
+    DefaultCoverage coverage = new DefaultCoverage()
+      .onFile(main)
+      .ofType(CoverageType.UNIT)
+      .lineHits(1, 2)
+      .lineHits(2, 5)
+      .conditions(1, 2, 1);
+
+    assertThat(coverage.file()).isEqualTo(main);
+    assertThat(coverage.type()).isEqualTo(CoverageType.UNIT);
+  }
+
+  @Test
+  public void testSaveUnitTests() {
+    SensorStorage storage = mock(SensorStorage.class);
+    new DefaultCoverage(storage)
+      .onFile(main)
+      .ofType(CoverageType.UNIT)
+      .lineHits(1, 2)
+      .lineHits(2, 5)
+      .lineHits(3, 0)
+      .lineHits(4, 0)
+      .conditions(1, 2, 1)
+      .save();
+
+    verify(storage).store(new DefaultMeasure<Integer>()
+      .onFile(main)
+      .forMetric(CoreMetrics.LINES_TO_COVER)
+      .withValue(4));
+    verify(storage).store(new DefaultMeasure<Integer>()
+      .onFile(main)
+      .forMetric(CoreMetrics.UNCOVERED_LINES)
+      .withValue(2));
+    verify(storage).store(new DefaultMeasure<String>()
+      .onFile(main)
+      .forMetric(CoreMetrics.COVERAGE_LINE_HITS_DATA)
+      .withValue("1=2;2=5;3=0;4=0"));
+    verify(storage).store(new DefaultMeasure<Integer>()
+      .onFile(main)
+      .forMetric(CoreMetrics.CONDITIONS_TO_COVER)
+      .withValue(2));
+    verify(storage).store(new DefaultMeasure<Integer>()
+      .onFile(main)
+      .forMetric(CoreMetrics.UNCOVERED_CONDITIONS)
+      .withValue(1));
+    verify(storage).store(new DefaultMeasure<String>()
+      .onFile(main)
+      .forMetric(CoreMetrics.COVERED_CONDITIONS_BY_LINE)
+      .withValue("1=1"));
+    verify(storage).store(new DefaultMeasure<String>()
+      .onFile(main)
+      .forMetric(CoreMetrics.CONDITIONS_BY_LINE)
+      .withValue("1=2"));
+  }
+
+}
index 2daa6e5d34ecfd48f7d6af5f177f69eccc725545..7f4d8001df7b517898b66e160b16c802739988d2 100644 (file)
@@ -39,7 +39,7 @@ public class DefaultTestCaseCoverageTest {
 
   @Test
   public void testCreation() throws Exception {
-    DefaultTestCaseCoverage testCaseCoverage = new DefaultTestCaseCoverage(null)
+    DefaultTestCaseCoverage testCaseCoverage = new DefaultTestCaseCoverage()
       .testFile(testFile)
       .testName("myTest")
       .cover(mainFile)
@@ -53,17 +53,17 @@ public class DefaultTestCaseCoverageTest {
 
   @Test
   public void testEqualsHashCodeToString() {
-    DefaultTestCaseCoverage testCaseCoverage1 = new DefaultTestCaseCoverage(null)
+    DefaultTestCaseCoverage testCaseCoverage1 = new DefaultTestCaseCoverage()
       .testFile(testFile)
       .testName("myTest")
       .cover(mainFile)
       .onLines(Arrays.asList(1, 2, 3));
-    DefaultTestCaseCoverage testCaseCoverage1a = new DefaultTestCaseCoverage(null)
+    DefaultTestCaseCoverage testCaseCoverage1a = new DefaultTestCaseCoverage()
       .testFile(testFile)
       .testName("myTest")
       .cover(mainFile)
       .onLines(Arrays.asList(1, 2, 3));
-    DefaultTestCaseCoverage testCaseCoverage2 = new DefaultTestCaseCoverage(null)
+    DefaultTestCaseCoverage testCaseCoverage2 = new DefaultTestCaseCoverage()
       .testFile(testFile)
       .testName("myTest2")
       .cover(mainFile)