import com.sonar.orchestrator.Orchestrator;
import com.sonar.orchestrator.build.SonarRunner;
import it.Category2Suite;
+
import java.io.File;
+
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.skyscreamer.jsonassert.JSONAssert;
import org.sonar.wsclient.services.Resource;
import org.sonar.wsclient.services.ResourceQuery;
-
import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.projectDir;
public void testLineHitNoConditions() throws IOException {
File coverage = new File(baseDir, "src/foo.xoo.itcoverage");
FileUtils.write(coverage, "1:3\n\n#comment");
- DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo");
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo").setLines(10);
context.fileSystem().add(inputFile);
sensor.execute(context);
public void testLineHitAndConditions() throws IOException {
File coverage = new File(baseDir, "src/foo.xoo.itcoverage");
FileUtils.write(coverage, "1:3:4:2");
- DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo");
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo").setLines(10);
context.fileSystem().add(inputFile);
sensor.execute(context);
public void testLineHitNoConditions() throws IOException {
File coverage = new File(baseDir, "src/foo.xoo.overallcoverage");
FileUtils.write(coverage, "1:3\n\n#comment");
- DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo");
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo").setLines(10);
context.fileSystem().add(inputFile);
sensor.execute(context);
public void testLineHitAndConditions() throws IOException {
File coverage = new File(baseDir, "src/foo.xoo.overallcoverage");
FileUtils.write(coverage, "1:3:4:2");
- DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo");
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo").setLines(10);
context.fileSystem().add(inputFile);
sensor.execute(context);
public void testLineHitNoConditions() throws IOException {
File coverage = new File(baseDir, "src/foo.xoo.coverage");
FileUtils.write(coverage, "1:3\n\n#comment");
- DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo");
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo").setLines(10);
context.fileSystem().add(inputFile);
sensor.execute(context);
public void testLineHitAndConditions() throws IOException {
File coverage = new File(baseDir, "src/foo.xoo.coverage");
FileUtils.write(coverage, "1:3:4:2");
- DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo");
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo").setLines(10);
context.fileSystem().add(inputFile);
sensor.execute(context);
this.index = index;
this.project = project;
this.coverageFilter = coverageFilter;
+
}
public Project getProject() {
@Override
public Measure saveMeasure(Resource resource, Metric metric, Double value) {
- return saveMeasure(resource, new Measure(metric, value));
+ Measure<?> measure = new Measure(metric, value);
+ coverageFilter.validate(measure, resource.getPath());
+ return saveMeasure(resource, measure);
}
@Override
public Measure saveMeasure(Resource resource, Measure measure) {
Resource resourceOrProject = resourceOrProject(resource);
+
if (coverageFilter.accept(resourceOrProject, measure)) {
return index.addMeasure(resourceOrProject, measure);
} else {
@Override
public Measure saveMeasure(InputFile inputFile, Metric metric, Double value) {
- return saveMeasure(getResource(inputFile), metric, value);
+ Measure<?> measure = new Measure(metric, value);
+ coverageFilter.validate(measure, inputFile);
+ return saveMeasure(getResource(inputFile), measure);
}
@Override
public Measure saveMeasure(InputFile inputFile, Measure measure) {
+ coverageFilter.validate(measure, inputFile);
return saveMeasure(getResource(inputFile), measure);
}
*/
package org.sonar.batch.sensor.coverage;
+import org.sonar.api.batch.fs.FileSystem;
+
+import javax.annotation.CheckForNull;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.utils.KeyValueFormat;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
+
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
import java.util.Set;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
private final Settings settings;
private final Set<Metric> coverageMetrics;
+ private final Set<Metric> byLineMetrics;
private Collection<WildcardPattern> resourcePatterns;
- public CoverageExclusions(Settings settings) {
+ private final FileSystem fs;
+
+ public CoverageExclusions(Settings settings, FileSystem fs) {
this.settings = settings;
+ this.fs = fs;
this.coverageMetrics = new HashSet<>();
+ this.byLineMetrics = new HashSet<>();
// UT
coverageMetrics.add(CoreMetrics.COVERAGE);
coverageMetrics.add(CoreMetrics.LINE_COVERAGE);
coverageMetrics.add(CoreMetrics.NEW_OVERALL_UNCOVERED_LINES);
coverageMetrics.add(CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS);
+ byLineMetrics.add(CoreMetrics.OVERALL_CONDITIONS_BY_LINE);
+ byLineMetrics.add(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE);
+ byLineMetrics.add(CoreMetrics.COVERED_CONDITIONS_BY_LINE);
+ byLineMetrics.add(CoreMetrics.CONDITIONS_BY_LINE);
+ byLineMetrics.add(CoreMetrics.IT_CONDITIONS_BY_LINE);
+ byLineMetrics.add(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE);
+
initPatterns();
}
- public boolean accept(Resource resource, Measure measure) {
+ private boolean isLineMetrics(Metric<?> metric) {
+ return this.byLineMetrics.contains(metric);
+ }
+
+ public void validate(Measure<?> measure, InputFile inputFile) {
+ Metric<?> metric = measure.getMetric();
+
+ if (!isLineMetrics(metric)) {
+ return;
+ }
+
+ Map<Integer, Integer> m = KeyValueFormat.parseIntInt(measure.getData());
+ validatePositiveLine(m, inputFile.absolutePath());
+ validateMaxLine(m, inputFile);
+ }
+
+ @CheckForNull
+ private InputFile getInputFile(String filePath) {
+ return fs.inputFile(fs.predicates().hasRelativePath(filePath));
+ }
+
+ public void validate(Measure<?> measure, String filePath) {
+ Metric<?> metric = measure.getMetric();
+
+ if (!isLineMetrics(metric)) {
+ return;
+ }
+
+ InputFile inputFile = getInputFile(filePath);
+
+ if (inputFile == null) {
+ throw new IllegalStateException(String.format("Can't create measure for resource '%s': resource is not indexed as a file", filePath));
+ }
+
+ validate(measure, inputFile);
+ }
+
+ private static void validateMaxLine(Map<Integer, Integer> m, InputFile inputFile) {
+ int maxLine = inputFile.lines();
+
+ for (int l : m.keySet()) {
+ if (l > maxLine) {
+ throw new IllegalStateException(String.format("Can't create measure for line %d for file '%s' with %d lines", l, inputFile.absolutePath(), maxLine));
+ }
+ }
+ }
+
+ private static void validatePositiveLine(Map<Integer, Integer> m, String filePath) {
+ for (int l : m.keySet()) {
+ if (l <= 0) {
+ throw new IllegalStateException(String.format("Measure with line %d for file '%s' must be > 0", l, filePath));
+ }
+ }
+ }
+
+ public boolean accept(Resource resource, Measure<?> measure) {
if (isCoverageMetric(measure.getMetric())) {
return !hasMatchingPattern(resource);
} else {
}
}
- private boolean isCoverageMetric(Metric metric) {
+ private boolean isCoverageMetric(Metric<?> metric) {
return this.coverageMetrics.contains(metric);
}
assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey")
.doesNotContain(CoreMetrics.LINES_TO_COVER_KEY, CoreMetrics.UNCOVERED_LINES_KEY, CoreMetrics.CONDITIONS_TO_COVER_KEY,
CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY);
-
}
}
*/
package org.sonar.batch.mediumtest.tests;
+import org.hamcrest.Description;
+
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.io.FileUtils;
import org.junit.After;
public class CoveragePerTestMediumTest {
- @org.junit.Rule
+ @Rule
public TemporaryFolder temp = new TemporaryFolder();
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
public BatchMediumTester tester = BatchMediumTester.builder()
.registerPlugin("xoo", new XooPlugin())
.addDefaultQProfile("xoo", "Sonar Way")
}
@Test
- public void coveragePerTestInReport() throws IOException {
-
- File baseDir = temp.getRoot();
+ // SONAR-6183
+ public void invalidCoverage() throws IOException {
+ File baseDir = createTestFiles();
File srcDir = new File(baseDir, "src");
- srcDir.mkdir();
- File testDir = new File(baseDir, "test");
- testDir.mkdir();
- File xooFile = new File(srcDir, "sample.xoo");
- FileUtils.write(xooFile, "foo");
+ File coverageFile = new File(srcDir, "sample.xoo.coverage");
+ FileUtils.write(coverageFile, "0:2\n");
- File xooFile2 = new File(srcDir, "sample2.xoo");
- FileUtils.write(xooFile2, "foo");
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Error processing line 1 of file");
+ exception.expectCause(new TypeSafeMatcher<Throwable>() {
- File xooTestFile = new File(testDir, "sampleTest.xoo");
- FileUtils.write(xooTestFile, "failure\nerror\nok\nskipped");
+ @Override
+ public void describeTo(Description description) {
+ // nothing to do
+ }
- File xooTestFile2 = new File(testDir, "sample2Test.xoo");
- FileUtils.write(xooTestFile2, "test file tests");
+ @Override
+ protected boolean matchesSafely(Throwable item) {
+ return item.getMessage().contains("Line number must be strictly positive");
+ }
+ });
+ runTask(baseDir);
+
+ }
+
+ @Test
+ public void coveragePerTestInReport() throws IOException {
+ File baseDir = createTestFiles();
+ File testDir = new File(baseDir, "test");
File xooTestExecutionFile = new File(testDir, "sampleTest.xoo.test");
FileUtils.write(xooTestExecutionFile, "some test:4:::OK:UNIT\n" +
FileUtils.write(xooCoveragePerTestFile, "some test;src/sample.xoo,10,11;src/sample2.xoo,1,2\n" +
"another test;src/sample.xoo,10,20\n");
- TaskResult result = tester.newTask()
- .properties(ImmutableMap.<String, String>builder()
- .put("sonar.task", "scan")
- .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
- .put("sonar.projectKey", "com.foo.project")
- .put("sonar.projectName", "Foo Project")
- .put("sonar.projectVersion", "1.0-SNAPSHOT")
- .put("sonar.projectDescription", "Description of Foo Project")
- .put("sonar.sources", "src")
- .put("sonar.tests", "test")
- .build())
- .start();
+ TaskResult result = runTask(baseDir);
InputFile file = result.inputFile("test/sampleTest.xoo");
org.sonar.batch.protocol.output.BatchReport.CoverageDetail someTest = result.coveragePerTestFor(file, "some test");
assertThat(anotherTest.getCoveredFile(0).getCoveredLineList()).containsExactly(10, 20);
}
+ private TaskResult runTask(File baseDir) {
+ return tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.tests", "test")
+ .build())
+ .start();
+ }
+
+ private File createTestFiles() throws IOException {
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+ File testDir = new File(baseDir, "test");
+ testDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "foo");
+
+ File xooFile2 = new File(srcDir, "sample2.xoo");
+ FileUtils.write(xooFile2, "foo");
+
+ File xooTestFile = new File(testDir, "sampleTest.xoo");
+ FileUtils.write(xooTestFile, "failure\nerror\nok\nskipped");
+
+ File xooTestFile2 = new File(testDir, "sample2Test.xoo");
+ FileUtils.write(xooTestFile2, "test file tests");
+
+ return baseDir;
+ }
+
}
*/
package org.sonar.batch.sensor.coverage;
+import org.junit.rules.TemporaryFolder;
+
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import com.google.common.collect.ImmutableMap;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.KeyValueFormat;
import org.sonar.core.config.ExclusionProperties;
+import java.util.Map;
+
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class CoverageExclusionsTest {
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
private Settings settings;
+ private DefaultFileSystem fs;
private CoverageExclusions filter;
@Before
public void createFilter() {
settings = new Settings(new PropertyDefinitions(ExclusionProperties.all()));
- filter = new CoverageExclusions(settings);
+ fs = new DefaultFileSystem(temp.getRoot());
+ filter = new CoverageExclusions(settings, fs);
+ }
+
+ @Test
+ public void shouldValidateStrictlyPositiveLine() {
+ DefaultInputFile file = new DefaultInputFile("module", "testfile");
+ Measure measure = mock(Measure.class);
+ Map<Integer, Integer> map = ImmutableMap.of(0, 3);
+
+ String data = KeyValueFormat.format(map);
+ when(measure.getMetric()).thenReturn(CoreMetrics.IT_CONDITIONS_BY_LINE);
+ when(measure.getData()).thenReturn(data);
+
+ fs.add(file);
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("must be > 0");
+ filter.validate(measure, "testfile");
+ }
+
+ @Test
+ public void shouldValidateFileExists() {
+ DefaultInputFile file = new DefaultInputFile("module", "testfile");
+ Measure measure = mock(Measure.class);
+ Map<Integer, Integer> map = ImmutableMap.of(0, 3);
+
+ String data = KeyValueFormat.format(map);
+ when(measure.getMetric()).thenReturn(CoreMetrics.IT_CONDITIONS_BY_LINE);
+ when(measure.getData()).thenReturn(data);
+
+ fs.add(file);
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("resource is not indexed as a file");
+ filter.validate(measure, "dummy");
+ }
+
+ @Test
+ public void shouldValidateMaxLine() {
+ DefaultInputFile file = new DefaultInputFile("module", "testfile");
+ file.setLines(10);
+ Measure measure = mock(Measure.class);
+ Map<Integer, Integer> map = ImmutableMap.of(11, 3);
+
+ String data = KeyValueFormat.format(map);
+ when(measure.getMetric()).thenReturn(CoreMetrics.COVERED_CONDITIONS_BY_LINE);
+ when(measure.getData()).thenReturn(data);
+
+ exception.expect(IllegalStateException.class);
+ filter.validate(measure, file);
}
@Test
@Override
public DefaultCoverage onFile(InputFile inputFile) {
- Preconditions.checkNotNull(inputFile, "file can't be null");
this.inputFile = (DefaultInputFile) inputFile;
return this;
}
@Override
public NewCoverage ofType(CoverageType type) {
+ validateFile();
Preconditions.checkNotNull(type, "type can't be null");
this.type = type;
return this;
@Override
public NewCoverage lineHits(int line, int hits) {
+ validateFile();
+ validateLine(line);
+
if (!hitsByLine.containsKey(line)) {
hitsByLine.put(line, hits);
if (hits > 0) {
return this;
}
+ private void validateLine(int line) {
+ Preconditions.checkState(line <= inputFile.lines(), String.format("Line %d is out of range in the file %s (lines: %d)", line, inputFile.relativePath(), inputFile.lines()));
+ Preconditions.checkState(line > 0, "Line number must be strictly positive: " + line);
+ }
+
+ private void validateLines() {
+ for (int l : hitsByLine.keySet()) {
+ validateLine(l);
+ }
+
+ for (int l : conditionsByLine.keySet()) {
+ validateLine(l);
+ }
+
+ for (int l : coveredConditionsByLine.keySet()) {
+ validateLine(l);
+ }
+ }
+
+ private void validateFile() {
+ Preconditions.checkNotNull(inputFile, "Call onFile() first");
+ }
+
@Override
public NewCoverage conditions(int line, int conditions, int coveredConditions) {
+ validateFile();
+ validateLine(line);
+
if (conditions > 0 && !conditionsByLine.containsKey(line)) {
totalConditions += conditions;
totalCoveredConditions += coveredConditions;
@Override
public void doSave() {
- Preconditions.checkNotNull(inputFile, "Call onFile() first");
+ validateFile();
Preconditions.checkNotNull(type, "Call ofType() first");
+ validateLines();
storage.store(this);
}
*/
package org.sonar.api.batch.sensor.internal;
+import org.sonar.api.batch.sensor.coverage.NewCoverage;
+
+import org.junit.rules.ExpectedException;
+
import java.io.File;
import java.io.StringReader;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.config.Settings;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.rule.RuleKey;
-
import static org.assertj.core.api.Assertions.assertThat;
public class SensorContextTesterTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
private SensorContextTester tester;
private File baseDir;
assertThat(tester.duplications()).hasSize(1);
}
+ @Test
+ public void testCoverageAtLineZero() {
+ assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.UNIT, 1)).isNull();
+ assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.UNIT, 4)).isNull();
+
+ exception.expect(IllegalStateException.class);
+ NewCoverage coverage = tester.newCoverage()
+ .onFile(new DefaultInputFile("foo", "src/Foo.java").initMetadata(new FileMetadata().readMetadata(new StringReader("annot dsf fds foo bar"))))
+ .ofType(CoverageType.UNIT)
+ .lineHits(0, 3);
+ }
+
+ @Test
+ public void testCoverageAtLineOutOfRange() {
+ assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.UNIT, 1)).isNull();
+ assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.UNIT, 4)).isNull();
+ exception.expect(IllegalStateException.class);
+
+ NewCoverage coverage = tester.newCoverage()
+ .onFile(new DefaultInputFile("foo", "src/Foo.java").initMetadata(new FileMetadata().readMetadata(new StringReader("annot dsf fds foo bar"))))
+ .ofType(CoverageType.UNIT)
+ .lineHits(4, 3);
+ }
+
@Test
public void testLineHits() {
assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.UNIT, 1)).isNull();
assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.UNIT, 4)).isNull();
tester.newCoverage()
- .onFile(new DefaultInputFile("foo", "src/Foo.java").initMetadata(new FileMetadata().readMetadata(new StringReader("annot dsf fds foo bar"))))
+ .onFile(new DefaultInputFile("foo", "src/Foo.java").initMetadata(new FileMetadata().readMetadata(new StringReader("annot dsf fds foo bar\nasdas"))))
.ofType(CoverageType.UNIT)
.lineHits(1, 2)
- .lineHits(4, 3)
+ .lineHits(2, 3)
.save();
assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.UNIT, 1)).isEqualTo(2);
assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.IT, 1)).isNull();
- assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.UNIT, 4)).isEqualTo(3);
+ assertThat(tester.lineHits("foo:src/Foo.java", CoverageType.UNIT, 2)).isEqualTo(3);
}
@Test
assertThat(tester.conditions("foo:src/Foo.java", CoverageType.UNIT, 1)).isNull();
assertThat(tester.coveredConditions("foo:src/Foo.java", CoverageType.UNIT, 1)).isNull();
tester.newCoverage()
- .onFile(new DefaultInputFile("foo", "src/Foo.java").initMetadata(new FileMetadata().readMetadata(new StringReader("annot dsf fds foo bar"))))
+ .onFile(new DefaultInputFile("foo", "src/Foo.java").initMetadata(new FileMetadata().readMetadata(new StringReader("annot dsf fds foo bar\nasd\nasdas\nasdfas"))))
.ofType(CoverageType.UNIT)
.conditions(1, 4, 2)
.save();