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;
}
}
+ @Override
+ public Coverage newCoverage() {
+ return new DefaultCoverage(this);
+ }
+
@Override
public TestCaseExecution newTestCaseExecution() {
return new DefaultTestCaseExecution(this);
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;
// ------------ 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.
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;
}
--- /dev/null
+/*
+ * 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();
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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"));
+ }
+
+}
@Test
public void testCreation() throws Exception {
- DefaultTestCaseCoverage testCaseCoverage = new DefaultTestCaseCoverage(null)
+ DefaultTestCaseCoverage testCaseCoverage = new DefaultTestCaseCoverage()
.testFile(testFile)
.testName("myTest")
.cover(mainFile)
@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)