diff --git a/its/plugin/projects/lcov-wildcard/bar/bar.js b/its/plugin/projects/lcov-wildcard/bar/bar.js new file mode 100644 index 00000000000..4bad53da13b --- /dev/null +++ b/its/plugin/projects/lcov-wildcard/bar/bar.js @@ -0,0 +1,12 @@ +function isArrayLike (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; +} + +function size (obj) { + if (obj == null) { + return 0; + } + return isArrayLike(obj) ? obj.length : Object.keys(obj).length; +} + +module.exports = size; diff --git a/its/plugin/projects/lcov-wildcard/bar/bar.lcov b/its/plugin/projects/lcov-wildcard/bar/bar.lcov new file mode 100644 index 00000000000..8d13373be45 --- /dev/null +++ b/its/plugin/projects/lcov-wildcard/bar/bar.lcov @@ -0,0 +1,24 @@ +TN: +SF:./bar.js +FN:1,isArrayLike +FN:5,size +FNF:2 +FNH:2 +FNDA:2,isArrayLike +FNDA:2,size +DA:1,1 +DA:2,2 +DA:5,1 +DA:6,2 +DA:7,0 +DA:9,2 +DA:12,1 +LF:7 +LH:6 +BRDA:6,1,0,0 +BRDA:6,1,1,2 +BRDA:9,2,0,1 +BRDA:9,2,1,1 +BRF:4 +BRH:3 +end_of_record diff --git a/its/plugin/projects/lcov-wildcard/baz/baz.js b/its/plugin/projects/lcov-wildcard/baz/baz.js new file mode 100644 index 00000000000..4bad53da13b --- /dev/null +++ b/its/plugin/projects/lcov-wildcard/baz/baz.js @@ -0,0 +1,12 @@ +function isArrayLike (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; +} + +function size (obj) { + if (obj == null) { + return 0; + } + return isArrayLike(obj) ? obj.length : Object.keys(obj).length; +} + +module.exports = size; diff --git a/its/plugin/projects/lcov-wildcard/baz/baz.lcov b/its/plugin/projects/lcov-wildcard/baz/baz.lcov new file mode 100644 index 00000000000..87bd27d861c --- /dev/null +++ b/its/plugin/projects/lcov-wildcard/baz/baz.lcov @@ -0,0 +1,24 @@ +TN: +SF:./baz.js +FN:1,isArrayLike +FN:5,size +FNF:2 +FNH:2 +FNDA:2,isArrayLike +FNDA:2,size +DA:1,1 +DA:2,2 +DA:5,1 +DA:6,2 +DA:7,0 +DA:9,2 +DA:12,1 +LF:7 +LH:6 +BRDA:6,1,0,0 +BRDA:6,1,1,2 +BRDA:9,2,0,1 +BRDA:9,2,1,1 +BRF:4 +BRH:3 +end_of_record diff --git a/its/plugin/projects/lcov-wildcard/baz/qux/qux.js b/its/plugin/projects/lcov-wildcard/baz/qux/qux.js new file mode 100644 index 00000000000..4bad53da13b --- /dev/null +++ b/its/plugin/projects/lcov-wildcard/baz/qux/qux.js @@ -0,0 +1,12 @@ +function isArrayLike (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; +} + +function size (obj) { + if (obj == null) { + return 0; + } + return isArrayLike(obj) ? obj.length : Object.keys(obj).length; +} + +module.exports = size; diff --git a/its/plugin/projects/lcov-wildcard/baz/qux/qux.lcov b/its/plugin/projects/lcov-wildcard/baz/qux/qux.lcov new file mode 100644 index 00000000000..234a6029812 --- /dev/null +++ b/its/plugin/projects/lcov-wildcard/baz/qux/qux.lcov @@ -0,0 +1,24 @@ +TN: +SF:./qux.js +FN:1,isArrayLike +FN:5,size +FNF:2 +FNH:2 +FNDA:2,isArrayLike +FNDA:2,size +DA:1,1 +DA:2,2 +DA:5,1 +DA:6,2 +DA:7,0 +DA:9,2 +DA:12,1 +LF:7 +LH:6 +BRDA:6,1,0,0 +BRDA:6,1,1,2 +BRDA:9,2,0,1 +BRDA:9,2,1,1 +BRF:4 +BRH:3 +end_of_record diff --git a/its/plugin/projects/lcov-wildcard/foo.js b/its/plugin/projects/lcov-wildcard/foo.js new file mode 100644 index 00000000000..4bad53da13b --- /dev/null +++ b/its/plugin/projects/lcov-wildcard/foo.js @@ -0,0 +1,12 @@ +function isArrayLike (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; +} + +function size (obj) { + if (obj == null) { + return 0; + } + return isArrayLike(obj) ? obj.length : Object.keys(obj).length; +} + +module.exports = size; diff --git a/its/plugin/projects/lcov-wildcard/foo.lcov b/its/plugin/projects/lcov-wildcard/foo.lcov new file mode 100644 index 00000000000..b362930e291 --- /dev/null +++ b/its/plugin/projects/lcov-wildcard/foo.lcov @@ -0,0 +1,24 @@ +TN: +SF:./foo.js +FN:1,isArrayLike +FN:5,size +FNF:2 +FNH:2 +FNDA:2,isArrayLike +FNDA:2,size +DA:1,1 +DA:2,2 +DA:5,1 +DA:6,2 +DA:7,0 +DA:9,2 +DA:12,1 +LF:7 +LH:6 +BRDA:6,1,0,0 +BRDA:6,1,1,2 +BRDA:9,2,0,1 +BRDA:9,2,1,1 +BRF:4 +BRH:3 +end_of_record diff --git a/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/CoverageTest.java b/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/CoverageTest.java index 0e39791effe..06437b5eefc 100644 --- a/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/CoverageTest.java +++ b/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/CoverageTest.java @@ -218,4 +218,23 @@ public void conditions_on_non_executable_lines() { assertThat(getMeasureAsInt(projectKey, "conditions_to_cover")).isEqualTo(2); assertThat(getMeasureAsInt(projectKey, "uncovered_conditions")).isEqualTo(1); } + + @Test + public void wildcard_LCOV_report_paths() { + final String projectKey = "LcovWildcardReportPaths"; + SonarScanner build = Tests.createScanner() + .setProjectDir(TestUtils.projectDir("lcov-wildcard")) + .setProjectKey(projectKey) + .setProjectName(projectKey) + .setProjectVersion("1.0") + .setSourceDirs(".") + .setProperty("sonar.javascript.lcov.reportPaths", "foo.lcov,bar/*.lcov,**/qux/*.lcov"); + Tests.setEmptyProfile(projectKey); + orchestrator.executeBuild(build); + + assertThat(getMeasureAsInt(projectKey + ":foo.js", "uncovered_lines")).isEqualTo(1); + assertThat(getMeasureAsInt(projectKey + ":bar/bar.js", "uncovered_lines")).isEqualTo(1); + assertThat(getMeasureAsInt(projectKey + ":baz/baz.js", "uncovered_lines")).isEqualTo(5); + assertThat(getMeasureAsInt(projectKey + ":baz/qux/qux.js", "uncovered_lines")).isEqualTo(1); + } } diff --git a/pom.xml b/pom.xml index 676b0005cca..9bcd6a29b07 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 3.35.0.2707 4.3.1.2486 2.8.6 - 1.14.1.690 + 1.16.0.719 ${project.groupId}:sonar-javascript-plugin:jar true diff --git a/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/lcov/CoverageSensor.java b/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/lcov/CoverageSensor.java index a05229caae6..0cff7ce0b6e 100644 --- a/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/lcov/CoverageSensor.java +++ b/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/lcov/CoverageSensor.java @@ -20,14 +20,12 @@ package org.sonar.plugins.javascript.lcov; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.CheckForNull; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; @@ -41,6 +39,7 @@ import org.sonar.plugins.javascript.JavaScriptLanguage; import org.sonar.plugins.javascript.JavaScriptPlugin; import org.sonar.plugins.javascript.TypeScriptLanguage; +import org.sonarsource.analyzer.commons.FileProvider; public class CoverageSensor implements Sensor { private static final Logger LOG = Loggers.get(CoverageSensor.class); @@ -64,11 +63,7 @@ public void execute(SensorContext context) { reports.addAll(Arrays.asList(context.config().getStringArray(JavaScriptPlugin.TS_LCOV_REPORT_PATHS))); } - List lcovFiles = reports.stream() - .map(report -> getIOFile(context.fileSystem().baseDir(), report)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - + List lcovFiles = getLcovFiles(context.fileSystem().baseDir(), reports); if (lcovFiles.isEmpty()) { LOG.warn("No coverage information will be saved because all LCOV files cannot be found."); return; @@ -76,6 +71,25 @@ public void execute(SensorContext context) { saveCoverageFromLcovFiles(context, lcovFiles); } + private static List getLcovFiles(File baseDir, Set reportPaths) { + List lcovFiles = new ArrayList<>(); + for (String reportPath : reportPaths) { + LOG.debug("Using '{}' to resolve LCOV files", reportPath); + FileProvider fileProvider = new FileProvider(baseDir, reportPath); + List matchingFiles = fileProvider.getMatchingFiles(); + if (matchingFiles.isEmpty()) { + File file = new File(reportPath); + if (!file.exists()) { + LOG.info("No LCOV files were found using {}", reportPath); + } else { + matchingFiles.add(file); + } + } + lcovFiles.addAll(matchingFiles); + } + return lcovFiles; + } + private static void saveCoverageFromLcovFiles(SensorContext context, List lcovFiles) { LOG.info("Analysing {}", lcovFiles); @@ -111,22 +125,4 @@ private static void saveCoverageFromLcovFiles(SensorContext context, List LOG.warn("Found {} inconsistencies in coverage report. Re-run analyse in debug mode to see details.", inconsistenciesNumber); } } - - /** - * Returns a java.io.File for the given path. - * If path is not absolute, returns a File with module base directory as parent path. - */ - @CheckForNull - private static File getIOFile(File baseDir, String path) { - File file = new File(path); - if (!file.isAbsolute()) { - file = new File(baseDir, path); - } - if (!file.isFile()) { - LOG.warn("No coverage information will be saved because LCOV file cannot be found."); - LOG.warn("Provided LCOV file path: {}. Seek file with path: {}", path, file.getAbsolutePath()); - return null; - } - return file; - } } diff --git a/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/lcov/CoverageSensorTest.java b/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/lcov/CoverageSensorTest.java index 4022d4ab13a..e41c8bb6d04 100644 --- a/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/lcov/CoverageSensorTest.java +++ b/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/lcov/CoverageSensorTest.java @@ -173,7 +173,7 @@ public void test_unresolved_path() { .contains("Could not resolve 2 file paths in [" + moduleBaseDir.getAbsolutePath() + fileName + "]") .contains("First unresolved path: unresolved/file1.js (Run in DEBUG mode to get full list of unresolved paths)"); assertThat(logTester.logs(LoggerLevel.DEBUG)) - .isEmpty(); + .contains("Using 'reports/report_with_unresolved_path.lcov' to resolve LCOV files"); } @Test @@ -202,7 +202,7 @@ public void should_log_warning_when_wrong_data() throws Exception { assertThat(context.coveredConditions("moduleKey:file1.js", 2)).isEqualTo(2); assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Problem during processing LCOV report: can't save DA data for line 3 of coverage report file (java.lang.NumberFormatException: For input string: \"1.\")."); - String stringIndexOutOfBoundLogMessage = logTester.logs(LoggerLevel.DEBUG).get(1); + String stringIndexOutOfBoundLogMessage = logTester.logs(LoggerLevel.DEBUG).get(2); assertThat(stringIndexOutOfBoundLogMessage).startsWith("Problem during processing LCOV report: can't save DA data for line 4 of coverage report file (java.lang.StringIndexOutOfBoundsException:"); assertThat(logTester.logs(LoggerLevel.DEBUG).get(logTester.logs(LoggerLevel.DEBUG).size() - 1)).startsWith("Problem during processing LCOV report: can't save BRDA data for line 6 of coverage report file (java.lang.ArrayIndexOutOfBoundsException: "); assertThat(logTester.logs(LoggerLevel.WARN)).contains("Found 3 inconsistencies in coverage report. Re-run analyse in debug mode to see details."); @@ -285,6 +285,24 @@ public void should_resolve_absolute_path() throws Exception { assertThat(context.lineHits(file2Key, 2)).isEqualTo(5); } + @Test + public void should_resolve_wildcard_report_paths() throws Exception { + settings.setProperty(JavaScriptPlugin.LCOV_REPORT_PATHS, "**/wildcard/**/*.lcov"); + inputFile("file1.js", Type.MAIN); + inputFile("file2.js", Type.MAIN); // not referenced in any '**/wildcard/**/*.lcov' files + inputFile("tests/file1.js", Type.MAIN); + coverageSensor.execute(context); + + String file1Key = "moduleKey:file1.js"; + assertThat(context.lineHits(file1Key, 2)).isEqualTo(1); + + String file2Key = "moduleKey:file2.js"; + assertThat(context.lineHits(file2Key, 2)).isNull(); + + String nestedFileKey = "moduleKey:tests/file1.js"; + assertThat(context.lineHits(nestedFileKey, 2)).isEqualTo(1); + } + @Test public void should_import_coverage_for_ts() throws Exception { DefaultInputFile inputFile = new TestInputFileBuilder("moduleKey", "src/file1.ts") diff --git a/sonar-javascript-plugin/src/test/resources/coverage/wildcard/nested/report.lcov b/sonar-javascript-plugin/src/test/resources/coverage/wildcard/nested/report.lcov new file mode 100644 index 00000000000..9784ef69022 --- /dev/null +++ b/sonar-javascript-plugin/src/test/resources/coverage/wildcard/nested/report.lcov @@ -0,0 +1,4 @@ +TN: +SF:tests/file1.js +DA:2,1 +end_of_record diff --git a/sonar-javascript-plugin/src/test/resources/coverage/wildcard/report.info b/sonar-javascript-plugin/src/test/resources/coverage/wildcard/report.info new file mode 100644 index 00000000000..a012d395a3e --- /dev/null +++ b/sonar-javascript-plugin/src/test/resources/coverage/wildcard/report.info @@ -0,0 +1,4 @@ +TN: +SF:file2.js +DA:2,1 +end_of_record diff --git a/sonar-javascript-plugin/src/test/resources/coverage/wildcard/report.lcov b/sonar-javascript-plugin/src/test/resources/coverage/wildcard/report.lcov new file mode 100644 index 00000000000..dc9b48a2bca --- /dev/null +++ b/sonar-javascript-plugin/src/test/resources/coverage/wildcard/report.lcov @@ -0,0 +1,4 @@ +TN: +SF:file1.js +DA:2,1 +end_of_record