Browse Source

SONAR-5389 Add replacement for CoverageMeasureBuilder

tags/5.0-RC1
Julien HENRY 9 years ago
parent
commit
0b2a624ab6

+ 7
- 0
sonar-batch/src/main/java/org/sonar/batch/scan2/BaseSensorContext.java View 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);

+ 8
- 0
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java View 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.

+ 1
- 1
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/DefaultStorable.java View 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;
}

+ 103
- 0
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/Coverage.java View File

@@ -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();

}

+ 147
- 0
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultCoverage.java View File

@@ -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();
}
}

}

+ 94
- 0
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultCoverageTest.java View File

@@ -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"));
}

}

+ 4
- 4
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseCoverageTest.java View 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)

Loading…
Cancel
Save