From 7722e7a1940a2ca9182fc3cc5195aeff75e5d106 Mon Sep 17 00:00:00 2001 From: Paul O'Neill Date: Thu, 29 Dec 2016 20:36:04 +0000 Subject: [PATCH] Fix #48 - Angular CLI-generated LCOV files fail to map to source TypeScript (#80) - Fix Angular CLI LCOV parser issue #48 - Extend code coverage of LCOV parser --- pom.xml | 11 +- .../com/pablissimo/sonar/LCOVParserImpl.java | 9 ++ .../pablissimo/sonar/LCOVParserImplTest.java | 144 ++++++++++++++++++ src/test/resources/lcov/angular.lcov | 5 + .../resources/lcov/angularendswithbang.lcov | 3 + src/test/resources/lcov/basic.lcov | 5 + src/test/resources/lcov/blank.lcov | 0 src/test/resources/lcov/existingandnot.lcov | 8 + src/test/resources/lcov/nolinehits.lcov | 2 + src/test/resources/lcov/outofrangelines.lcov | 5 + 10 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/pablissimo/sonar/LCOVParserImplTest.java create mode 100644 src/test/resources/lcov/angular.lcov create mode 100644 src/test/resources/lcov/angularendswithbang.lcov create mode 100644 src/test/resources/lcov/basic.lcov create mode 100644 src/test/resources/lcov/blank.lcov create mode 100644 src/test/resources/lcov/existingandnot.lcov create mode 100644 src/test/resources/lcov/nolinehits.lcov create mode 100644 src/test/resources/lcov/outofrangelines.lcov diff --git a/pom.xml b/pom.xml index faec2b4..dc82d8e 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.pablissimo.sonar sonar-typescript-plugin sonar-plugin - 0.97-SNAPSHOT + 0.98-SNAPSHOT TypeScript Analyse TypeScript projects @@ -117,6 +117,15 @@ + + + ${project.basedir}/src/test/resources + + **/* + + + + org.sonarsource.sonar-packaging-maven-plugin diff --git a/src/main/java/com/pablissimo/sonar/LCOVParserImpl.java b/src/main/java/com/pablissimo/sonar/LCOVParserImpl.java index d743f96..7648560 100644 --- a/src/main/java/com/pablissimo/sonar/LCOVParserImpl.java +++ b/src/main/java/com/pablissimo/sonar/LCOVParserImpl.java @@ -150,6 +150,15 @@ private FileData loadCurrentFileData(final Map files, Strin FileData fileData = null; // some tools (like Istanbul, Karma) provide relative paths, so let's consider them relative to project directory InputFile inputFile = context.fileSystem().inputFile(context.fileSystem().predicates().hasPath(filePath)); + + // Try to accommodate Angular projects that, when the angular template loader's used + // by checking for a ! in the filepath if the path isn't found - have a bash at seeking + // everything after the ! as a second fallback pass + if (inputFile == null && filePath.contains("!") && (filePath.indexOf("!") + 1) < filePath.length()) { + String amendedPath = filePath.substring(filePath.indexOf("!") + 1); + inputFile = context.fileSystem().inputFile(context.fileSystem().predicates().hasPath(amendedPath)); + } + if (inputFile != null) { fileData = files.get(inputFile); if (fileData == null) { diff --git a/src/test/java/com/pablissimo/sonar/LCOVParserImplTest.java b/src/test/java/com/pablissimo/sonar/LCOVParserImplTest.java new file mode 100644 index 0000000..f793ce2 --- /dev/null +++ b/src/test/java/com/pablissimo/sonar/LCOVParserImplTest.java @@ -0,0 +1,144 @@ +package com.pablissimo.sonar; + +import static org.junit.Assert.*; +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Map; + +import org.junit.Before; +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.coverage.NewCoverage; +import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; +import org.sonar.api.batch.sensor.internal.SensorContextTester; + +public class LCOVParserImplTest { + LOCSensor sensor; + + SensorContextTester sensorContext; + DefaultInputFile inputFile; + + @Before + public void setUp() throws Exception { + this.sensorContext = SensorContextTester.create(new File("")); + + this.inputFile = + new DefaultInputFile("", "path/to/file.ts") + .setLanguage(TypeScriptLanguage.LANGUAGE_KEY) + .setLines(3); + + this.sensorContext.fileSystem().add(this.inputFile); + } + + @Test + public void parsesBasicLcovFiles() { + Map coverage = executeForTestCase("basic"); + DefaultCoverage c = (DefaultCoverage) coverage.get(this.inputFile); + + assertEquals((Integer) 3, c.hitsByLine().get(1)); + assertEquals((Integer) 0, c.hitsByLine().get(2)); + assertEquals((Integer) 1, c.hitsByLine().get(3)); + + assertEquals(3, c.linesToCover()); + } + + @Test + public void parsesAngularTemplateLoaderOutput() { + Map coverage = executeForTestCase("angular"); + DefaultCoverage c = (DefaultCoverage) coverage.get(this.inputFile); + + assertEquals((Integer) 3, c.hitsByLine().get(1)); + assertEquals((Integer) 0, c.hitsByLine().get(2)); + assertEquals((Integer) 1, c.hitsByLine().get(3)); + + assertEquals(3, c.linesToCover()); + } + + @Test + public void handlesNoContent() { + Map coverage = executeForTestCase("blank"); + + assertNotNull(coverage); + assertEquals(0, coverage.size()); + } + + @Test + public void handlesNoLineHitsForASingleFile() { + Map coverage = executeForTestCase("nolinehits"); + DefaultCoverage c = (DefaultCoverage) coverage.get(this.inputFile); + + assertEquals(1, coverage.size()); + + assertNotNull(c); + assertNull(c.hitsByLine().get(1)); + assertNull(c.hitsByLine().get(2)); + assertNull(c.hitsByLine().get(3)); + } + + @Test + public void ignoresFilesNotPartOfAnalysisSet() { + Map coverage = executeForTestCase("existingandnot"); + DefaultCoverage c = (DefaultCoverage) coverage.get(this.inputFile); + + assertNotNull(c); + assertEquals(1, coverage.size()); + } + + @Test + public void handlesFilesEndingWithExclamationMarkIfNotPartOfSet() { + Map coverage = executeForTestCase("angularendswithbang"); + + assertNotNull(coverage); + assertEquals(0, coverage.size()); + } + + @Test + public void handlesOutOfRangeLineNumbers() { + Map coverage = executeForTestCase("outofrangelines"); + DefaultCoverage c = (DefaultCoverage) coverage.get(this.inputFile); + + assertNotNull(c); + assertEquals(1, coverage.size()); + + assertEquals((Integer) 3, c.hitsByLine().get(1)); + } + + @Test(expected = IllegalArgumentException.class) + public void createThrowsWhenFileDoesNotExist() { + File nonExistent = new File("whatever"); + LCOVParserImpl.create(this.sensorContext, nonExistent); + } + + @Test(expected = IllegalArgumentException.class) + public void parseFileThrowsWhenFileDoesNotExist() { + File nonExistent = new File("whatever"); + LCOVParser parser = getParser(resource("basic")); + + parser.parseFile(nonExistent); + } + + private Map executeForTestCase(String testName) { + File lcovFile = resource(testName); + LCOVParser parser = getParser(lcovFile); + + return parser.parseFile(lcovFile); + } + + private LCOVParser getParser(File lcovFile) { + return LCOVParserImpl.create(this.sensorContext, lcovFile); + } + + private File resource(String testName) { + URL lcovUrl = LCOVParserImplTest.class.getClassLoader().getResource("./lcov/" + testName + ".lcov"); + + try { + File lcovFile = new File(lcovUrl.toURI()); + return lcovFile; + } + catch (URISyntaxException e) { + return null; + } + } +} diff --git a/src/test/resources/lcov/angular.lcov b/src/test/resources/lcov/angular.lcov new file mode 100644 index 0000000..cd83c05 --- /dev/null +++ b/src/test/resources/lcov/angular.lcov @@ -0,0 +1,5 @@ +SF:path/to/template/loader.js!path/to/file.ts +DA:1,3 +DA:2,0 +DA:3,1 +end_of_record \ No newline at end of file diff --git a/src/test/resources/lcov/angularendswithbang.lcov b/src/test/resources/lcov/angularendswithbang.lcov new file mode 100644 index 0000000..3258517 --- /dev/null +++ b/src/test/resources/lcov/angularendswithbang.lcov @@ -0,0 +1,3 @@ +SF:path/to/template/loader.js! +DA:1,3 +end_of_record \ No newline at end of file diff --git a/src/test/resources/lcov/basic.lcov b/src/test/resources/lcov/basic.lcov new file mode 100644 index 0000000..226cbd3 --- /dev/null +++ b/src/test/resources/lcov/basic.lcov @@ -0,0 +1,5 @@ +SF:path/to/file.ts +DA:1,3 +DA:2,0 +DA:3,1 +end_of_record \ No newline at end of file diff --git a/src/test/resources/lcov/blank.lcov b/src/test/resources/lcov/blank.lcov new file mode 100644 index 0000000..e69de29 diff --git a/src/test/resources/lcov/existingandnot.lcov b/src/test/resources/lcov/existingandnot.lcov new file mode 100644 index 0000000..78d460a --- /dev/null +++ b/src/test/resources/lcov/existingandnot.lcov @@ -0,0 +1,8 @@ +SF:path/to/file.ts +DA:1,3 +DA:2,0 +DA:3,1 +end_of_record +SF:path/to/nonexistent/file.ts +DA:1,1 +end_of_record \ No newline at end of file diff --git a/src/test/resources/lcov/nolinehits.lcov b/src/test/resources/lcov/nolinehits.lcov new file mode 100644 index 0000000..3bd5861 --- /dev/null +++ b/src/test/resources/lcov/nolinehits.lcov @@ -0,0 +1,2 @@ +SF:path/to/file.ts +end_of_record \ No newline at end of file diff --git a/src/test/resources/lcov/outofrangelines.lcov b/src/test/resources/lcov/outofrangelines.lcov new file mode 100644 index 0000000..2bc107e --- /dev/null +++ b/src/test/resources/lcov/outofrangelines.lcov @@ -0,0 +1,5 @@ +SF:path/to/file.ts +DA:0,3 +DA:1,3 +DA:4,3 +end_of_record \ No newline at end of file