Don't skip filtering when manifest of source-file paths is empty
When a coverage entry doesn't correspond to a file listed in execPathsOfUninstrumentedFiles, it's discarded. However, since this class has a constructor which takes no arguments and sets execPathsOfUninstrumentedFiles to an empty-set. But this leads to those same no-filtering behavior when the constructor is passed an empty list of valid source files.
Instead, the "leave as is" behavior should be conditioned on `execPathsOfUninstrumentedFiles == null`, and an empty set should be treated consistently with other sets with no matching items.
RELNOTES: None.
PiperOrigin-RevId: 445497757
diff --git a/src/java_tools/junitrunner/java/com/google/testing/coverage/JacocoLCOVFormatter.java b/src/java_tools/junitrunner/java/com/google/testing/coverage/JacocoLCOVFormatter.java
index ae9f058..b2c2bc5 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/coverage/JacocoLCOVFormatter.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/coverage/JacocoLCOVFormatter.java
@@ -20,6 +20,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.TreeMap;
import org.jacoco.core.analysis.IBundleCoverage;
import org.jacoco.core.analysis.IClassCoverage;
@@ -44,17 +45,21 @@
// jars passed through java_import or some custom rule where blaze doesn't have enough context to
// compute the right paths, but relies on these pre-computed exec paths.
// Exec paths can be provided in two formats, either as a plain string or as a delimited
- // string mapping source file paths to class paths.
- private final ImmutableSet<String> execPathsOfUninstrumentedFiles;
+ // string mapping source file paths to class paths. Coverage entries whose class-paths are not the
+ // suffix of any file in this list are discarded. If not provided (as is
+ // the case when class is initialized with the zero-argument constructor), the entries are
+ // returned unchanged (but note this may result in LCOV output which do not reference actual
+ // file-paths).
+ private final Optional<ImmutableSet<String>> execPathsOfUninstrumentedFiles;
private static final String EXEC_PATH_DELIMITER = "///";
public JacocoLCOVFormatter(ImmutableSet<String> execPathsOfUninstrumentedFiles) {
- this.execPathsOfUninstrumentedFiles = execPathsOfUninstrumentedFiles;
+ this.execPathsOfUninstrumentedFiles = Optional.of(execPathsOfUninstrumentedFiles);
}
public JacocoLCOVFormatter() {
- this.execPathsOfUninstrumentedFiles = ImmutableSet.of();
+ this.execPathsOfUninstrumentedFiles = Optional.empty();
}
public IReportVisitor createVisitor(
@@ -64,13 +69,13 @@
private Map<String, Map<String, IClassCoverage>> sourceToClassCoverage = new TreeMap<>();
private Map<String, ISourceFileCoverage> sourceToFileCoverage = new TreeMap<>();
- private String getExecPathForEntryName(String fileName) {
+ private String getExecPathForEntryName(String classPath) {
if (execPathsOfUninstrumentedFiles.isEmpty()) {
- return fileName;
+ return classPath;
}
- String matchingFileName = fileName.startsWith("/") ? fileName : "/" + fileName;
- for (String execPath : execPathsOfUninstrumentedFiles) {
+ String matchingFileName = classPath.startsWith("/") ? classPath : "/" + classPath;
+ for (String execPath : execPathsOfUninstrumentedFiles.get()) {
if (execPath.contains(EXEC_PATH_DELIMITER)) {
String[] parts = execPath.split(EXEC_PATH_DELIMITER, 2);
if (parts.length != 2) {
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/coverage/JacocoLCOVFormatterUninstrumentedTest.java b/src/java_tools/junitrunner/javatests/com/google/testing/coverage/JacocoLCOVFormatterUninstrumentedTest.java
index 0e6aab1..d56d33e 100644
--- a/src/java_tools/junitrunner/javatests/com/google/testing/coverage/JacocoLCOVFormatterUninstrumentedTest.java
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/coverage/JacocoLCOVFormatterUninstrumentedTest.java
@@ -146,7 +146,7 @@
@Test
public void testVisitBundleWithNoMatchHasEmptyOutput() throws IOException {
- // Paths
+ // Non-matching path
ImmutableSet<String> execPaths = ImmutableSet.of("/path/does/not/match/anything.txt");
JacocoLCOVFormatter formatter = new JacocoLCOVFormatter(execPaths);
IReportVisitor visitor =
@@ -159,4 +159,36 @@
String coverageOutput = writer.toString();
assertThat(coverageOutput).isEmpty();
}
+
+ @Test
+ public void testVisitBundleWithNoExecPathsHasEmptyOutput() throws IOException {
+ // Empty list of exec paths
+ ImmutableSet<String> execPaths = ImmutableSet.of();
+ JacocoLCOVFormatter formatter = new JacocoLCOVFormatter(execPaths);
+ IReportVisitor visitor =
+ formatter.createVisitor(
+ new PrintWriter(writer), new TreeMap<String, BranchCoverageDetail>());
+
+ visitor.visitBundle(mockBundle, mock(ISourceFileLocator.class));
+ visitor.visitEnd();
+
+ String coverageOutput = writer.toString();
+ assertThat(coverageOutput).isEmpty();
+ }
+
+ @Test
+ public void testVisitBundleWithoutExecPathsDoesNotPruneOutput() throws IOException {
+ // No paths, don't attempt to demangle paths and prune the output, just output with
+ // class-paths as is.
+ JacocoLCOVFormatter formatter = new JacocoLCOVFormatter();
+ IReportVisitor visitor =
+ formatter.createVisitor(
+ new PrintWriter(writer), new TreeMap<String, BranchCoverageDetail>());
+
+ visitor.visitBundle(mockBundle, mock(ISourceFileLocator.class));
+ visitor.visitEnd();
+
+ String coverageOutput = writer.toString();
+ assertThat(coverageOutput).isNotEmpty();
+ }
}