From a1a7f0062c91fc923c2e0a10f66a00d2ab4a31a4 Mon Sep 17 00:00:00 2001 From: iirina Date: Tue, 14 Aug 2018 01:00:43 -0700 Subject: Add --filter_sources flag to LcovMerger. When the `--filter_sources` flag is used `LcovMerger` excludes from the merged coverage the sources whose names match any of the regex in the flag. The value of `--filter_sources` is a list of regex separated by `,`. This flag comes as a preparation for using `gcov` instead of `lcov` for collecting coverage data for C++. The flag was not needed before because `lcov` has some functionality for excluding some files (e.g. `--no-external` to ignore coverage data for system files). Closes #5834. PiperOrigin-RevId: 208606867 --- .../java/com/google/devtools/lcovmerger/BUILD | 11 ++ .../com/google/devtools/lcovmerger/Coverage.java | 27 +++++ .../devtools/lcovmerger/LcovMergerFlags.java | 82 ++++++++++++++ .../java/com/google/devtools/lcovmerger/Main.java | 70 +++--------- .../javatests/com/google/devtools/lcovmerger/BUILD | 10 ++ .../google/devtools/lcovmerger/CoverageTest.java | 90 +++++++++++++++ .../devtools/lcovmerger/LcovMergerFlagsTest.java | 121 +++++++++++++++++++++ 7 files changed, 354 insertions(+), 57 deletions(-) create mode 100644 tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovMergerFlags.java create mode 100644 tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/LcovMergerFlagsTest.java diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD index 7af7779a78..a29d6ac5ba 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD @@ -103,6 +103,16 @@ java_library( deps = [":SourceFileCoverage"], ) +java_library( + name = "LcovMergerFlags", + srcs = ["LcovMergerFlags.java"], + deps = [ + "//third_party:auto_value", + "//third_party:guava", + "//third_party:jsr305", + ], +) + java_library( name = "MainLibrary", srcs = ["Main.java"], @@ -110,6 +120,7 @@ java_library( ":Constants", ":Coverage", ":GcovParser", + ":LcovMergerFlags", ":LcovParser", ":LcovPrinter", ":SourceFileCoverage", diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Coverage.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Coverage.java index 75eaa420cc..42e5be5ae1 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Coverage.java +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Coverage.java @@ -15,6 +15,7 @@ package com.google.devtools.lcovmerger; import java.util.Collection; +import java.util.List; import java.util.TreeMap; class Coverage { @@ -45,6 +46,32 @@ class Coverage { return merged; } + static Coverage filterOutMatchingSources(Coverage coverage, List regexes) + throws IllegalArgumentException { + if (coverage == null || regexes == null) { + throw new IllegalArgumentException("Coverage and regex should not be null."); + } + if (regexes.isEmpty()) { + return coverage; + } + Coverage filteredCoverage = new Coverage(); + for (SourceFileCoverage source : coverage.getAllSourceFiles()) { + if (!matchesAnyRegex(source.sourceFileName(), regexes)) { + filteredCoverage.add(source); + } + } + return filteredCoverage; + } + + private static boolean matchesAnyRegex(String input, List regexes) { + for (String regex : regexes) { + if (input.matches(regex)) { + return true; + } + } + return false; + } + boolean isEmpty() { return sourceFiles.isEmpty(); } diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovMergerFlags.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovMergerFlags.java new file mode 100644 index 0000000000..c218c53317 --- /dev/null +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovMergerFlags.java @@ -0,0 +1,82 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.devtools.lcovmerger; + +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableList; +import java.util.List; +import javax.annotation.Nullable; + +@AutoValue +abstract class LcovMergerFlags { + + @Nullable + abstract String coverageDir(); + + @Nullable + abstract String reportsFile(); + + abstract String outputFile(); + + abstract List filterSources(); + + /** Parse flags in the form of "--coverage_dir=... -output_file=..." */ + static LcovMergerFlags parseFlags(String[] args) { + ImmutableList.Builder filterSources = new ImmutableList.Builder<>(); + String coverageDir = null; + String reportsFile = null; + String outputFile = null; + + for (String arg : args) { + if (!arg.startsWith("--")) { + throw new IllegalArgumentException("Argument (" + arg + ") should start with --"); + } + String[] parts = arg.substring(2).split("=", 2); + if (parts.length != 2) { + throw new IllegalArgumentException("There should be = in argument (" + arg + ")"); + } + switch (parts[0]) { + case "coverage_dir": + coverageDir = parts[1]; + break; + case "reports_file": + reportsFile = parts[1]; + break; + case "output_file": + outputFile = parts[1]; + break; + case "filter_sources": + filterSources.add(parts[1]); + break; + default: + throw new IllegalArgumentException("Unknown flag " + arg); + } + } + + if (coverageDir == null && reportsFile == null) { + throw new IllegalArgumentException( + "At least one of --coverage_dir or --reports_file should be specified."); + } + if (coverageDir != null && reportsFile != null) { + throw new IllegalArgumentException( + "Only one of --coverage_dir or --reports_file must be specified."); + } + if (outputFile == null) { + throw new IllegalArgumentException("--output_file was not specified."); + } + return new AutoValue_LcovMergerFlags( + coverageDir, reportsFile, outputFile, filterSources.build()); + } +} diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Main.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Main.java index b490765383..4e3b5da7c9 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Main.java +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Main.java @@ -30,9 +30,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -45,17 +43,17 @@ public class Main { private static final Logger logger = Logger.getLogger(Main.class.getName()); public static void main(String[] args) { - Map flags = null; + LcovMergerFlags flags = null; try { - flags = parseFlags(args); + flags = LcovMergerFlags.parseFlags(args); } catch (IllegalArgumentException e) { logger.log(Level.SEVERE, e.getMessage()); System.exit(1); } List filesInCoverageDir = - flags.containsKey("coverage_dir") - ? getCoverageFilesInDir(flags.get("coverage_dir")) + flags.coverageDir() != null + ? getCoverageFilesInDir(flags.coverageDir()) : Collections.emptyList(); Coverage coverage = Coverage.merge( @@ -67,8 +65,12 @@ public class Main { System.exit(1); } + if (!flags.filterSources().isEmpty()) { + coverage = Coverage.filterOutMatchingSources(coverage, flags.filterSources()); + } + int exitStatus = 0; - String outputFile = flags.get("output_file"); + String outputFile = flags.outputFile(); try { LcovPrinter.print(new FileOutputStream(new File(outputFile)), coverage); } catch (IOException e) { @@ -90,13 +92,12 @@ public class Main { return gcovFiles; } - private static List getTracefiles( - Map flags, List filesInCoverageDir) { + private static List getTracefiles(LcovMergerFlags flags, List filesInCoverageDir) { List lcovTracefiles = new ArrayList<>(); - if (flags.containsKey("coverage_dir")) { + if (flags.coverageDir() != null) { lcovTracefiles = getFilesWithExtension(filesInCoverageDir, TRACEFILE_EXTENSION); - } else if (flags.containsKey("reports_file")) { - lcovTracefiles = getTracefilesFromFile(flags.get("reports_file")); + } else if (flags.reportsFile() != null) { + lcovTracefiles = getTracefilesFromFile(flags.reportsFile()); } if (lcovTracefiles.isEmpty()) { logger.log(Level.SEVERE, "No lcov file found."); @@ -170,49 +171,4 @@ public class Main { } return datFiles; } - - /** - * Parse flags in the form of "--coverage_dir=... -output_file=..." - */ - private static Map parseFlags(String[] args) { - Map flags = new HashMap<>(); - - for (String arg : args) { - if (!arg.startsWith("--")) { - throw new IllegalArgumentException("Argument (" + arg + ") should start with --"); - } - String[] parts = arg.substring(2).split("=", 2); - if (parts.length != 2) { - throw new IllegalArgumentException("There should be = in argument (" + arg + ")"); - } - flags.put(parts[0], parts[1]); - } - - // Validate flags - for (String flag : flags.keySet()) { - switch (flag) { - case "coverage_dir": - case "reports_file": - case "output_file": - continue; - default: - throw new IllegalArgumentException("Unknown flag --" + flag); - } - } - - if (!flags.containsKey("coverage_dir") && !flags.containsKey("reports_file")) { - throw new IllegalArgumentException( - "At least one of --coverage_dir or --reports_file should be specified."); - } - if (flags.containsKey("coverage_dir") && flags.containsKey("reports_file")) { - throw new IllegalArgumentException( - "Only one of --coverage_dir or --reports_file must be specified."); - } - if (!flags.containsKey("output_file")) { - // Different from blaze, this should be mandatory - throw new IllegalArgumentException("--output_file was not specified"); - } - - return flags; - } } diff --git a/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/BUILD b/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/BUILD index 66df9fef91..97de49e1d4 100644 --- a/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/BUILD +++ b/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/BUILD @@ -102,6 +102,16 @@ java_test( ], ) +java_test( + name = "LcovMergerFlagsTest", + srcs = ["LcovMergerFlagsTest.java"], + deps = [ + "//third_party:junit4", + "//third_party:truth", + "//tools/test/LcovMerger/java/com/google/devtools/lcovmerger:LcovMergerFlags", + ], +) + java_test( name = "MainTest", srcs = ["MainTest.java"], diff --git a/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/CoverageTest.java b/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/CoverageTest.java index 724a2095a5..56d2e6b92b 100644 --- a/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/CoverageTest.java +++ b/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/CoverageTest.java @@ -22,7 +22,9 @@ import static com.google.devtools.lcovmerger.LcovMergerTestUtils.createLinesExec import static com.google.devtools.lcovmerger.LcovMergerTestUtils.createSourceFile1; import static com.google.devtools.lcovmerger.LcovMergerTestUtils.createSourceFile2; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import java.util.Collection; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -78,4 +80,92 @@ public class CoverageTest { assertTracefile1(Iterables.get(coverage.getAllSourceFiles(), 0)); assertTracefile1(Iterables.get(coverage.getAllSourceFiles(), 1)); } + + @Test + public void testFilterSources() { + Coverage coverage = new Coverage(); + + coverage.add(new SourceFileCoverage("/filterOut/package/file1.c")); + coverage.add(new SourceFileCoverage("/filterOut/package/file2.c")); + SourceFileCoverage validSource1 = new SourceFileCoverage("/valid/package/file3.c"); + coverage.add(validSource1); + SourceFileCoverage validSource2 = new SourceFileCoverage("/valid/package/file4.c"); + coverage.add(validSource2); + Collection filteredSources = + Coverage.filterOutMatchingSources(coverage, ImmutableList.of("/filterOut/package/.+")) + .getAllSourceFiles(); + + assertThat(filteredSources).containsExactly(validSource1, validSource2); + } + + @Test + public void testFilterSourcesEmptyResult() { + Coverage coverage = new Coverage(); + + coverage.add(new SourceFileCoverage("/filterOut/package/file1.c")); + coverage.add(new SourceFileCoverage("/filterOut/package/file2.c")); + Collection filteredSources = + Coverage.filterOutMatchingSources(coverage, ImmutableList.of("/filterOut/package/.+")) + .getAllSourceFiles(); + + assertThat(filteredSources).isEmpty(); + } + + @Test + public void testFilterSourcesNoMatches() { + Coverage coverage = new Coverage(); + + SourceFileCoverage validSource1 = new SourceFileCoverage("/valid/package/file3.c"); + coverage.add(validSource1); + SourceFileCoverage validSource2 = new SourceFileCoverage("/valid/package/file4.c"); + coverage.add(validSource2); + Collection filteredSources = + Coverage.filterOutMatchingSources(coverage, ImmutableList.of("/something/else/.+")) + .getAllSourceFiles(); + + assertThat(filteredSources).containsExactly(validSource1, validSource2); + } + + @Test + public void testFilterSourcesMultipleRegex() { + Coverage coverage = new Coverage(); + + coverage.add(new SourceFileCoverage("/filterOut/package/file1.c")); + coverage.add(new SourceFileCoverage("/filterOut/package/file2.c")); + coverage.add(new SourceFileCoverage("/repo/external/p.c")); + SourceFileCoverage validSource1 = new SourceFileCoverage("/valid/package/file3.c"); + coverage.add(validSource1); + SourceFileCoverage validSource2 = new SourceFileCoverage("/valid/package/file4.c"); + coverage.add(validSource2); + Collection filteredSources = + Coverage.filterOutMatchingSources( + coverage, ImmutableList.of("/filterOut/package/.+", ".+external.+")) + .getAllSourceFiles(); + + assertThat(filteredSources).containsExactly(validSource1, validSource2); + } + + @Test + public void testFilterSourcesNoFilter() { + Coverage coverage = new Coverage(); + + SourceFileCoverage validSource1 = new SourceFileCoverage("/valid/package/file3.c"); + coverage.add(validSource1); + SourceFileCoverage validSource2 = new SourceFileCoverage("/valid/package/file4.c"); + coverage.add(validSource2); + Collection filteredSources = + Coverage.filterOutMatchingSources(coverage, ImmutableList.of()).getAllSourceFiles(); + + assertThat(filteredSources).containsExactly(validSource1, validSource2); + } + + @Test(expected = IllegalArgumentException.class) + public void testFilterSourcesNullCoverage() { + Coverage.filterOutMatchingSources(null, ImmutableList.of()); + } + + @Test(expected = IllegalArgumentException.class) + public void testFilterSourcesNullRegex() { + Coverage.filterOutMatchingSources(new Coverage(), null); + } } diff --git a/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/LcovMergerFlagsTest.java b/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/LcovMergerFlagsTest.java new file mode 100644 index 0000000000..47ff4ef0cc --- /dev/null +++ b/tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger/LcovMergerFlagsTest.java @@ -0,0 +1,121 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.devtools.lcovmerger; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; + +public class LcovMergerFlagsTest { + @Test + public void parseFlagsTestCoverageDirOutputFile() { + LcovMergerFlags flags = + LcovMergerFlags.parseFlags( + new String[] { + "--coverage_dir=my_dir", "--output_file=my_file", + }); + assertThat(flags.coverageDir()).isEqualTo("my_dir"); + assertThat(flags.outputFile()).isEqualTo("my_file"); + assertThat(flags.reportsFile()).isNull(); + assertThat(flags.filterSources()).isEmpty(); + } + + @Test + public void parseFlagsTestReportsFileOutputFile() { + LcovMergerFlags flags = + LcovMergerFlags.parseFlags( + new String[] { + "--reports_file=my_reports_file", "--output_file=my_file", + }); + assertThat(flags.reportsFile()).isEqualTo("my_reports_file"); + assertThat(flags.outputFile()).isEqualTo("my_file"); + assertThat(flags.coverageDir()).isNull(); + assertThat(flags.filterSources()).isEmpty(); + } + + @Test + public void parseFlagsTestReportsFileOutputFileFilterSources() { + LcovMergerFlags flags = + LcovMergerFlags.parseFlags( + new String[] { + "--reports_file=my_reports_file", + "--output_file=my_file", + "--filter_sources=first_filter" + }); + assertThat(flags.reportsFile()).isEqualTo("my_reports_file"); + assertThat(flags.outputFile()).isEqualTo("my_file"); + assertThat(flags.coverageDir()).isNull(); + assertThat(flags.filterSources()).containsExactly("first_filter"); + } + + @Test + public void parseFlagsTestReportsFileOutputFileMultipleFilterSources() { + LcovMergerFlags flags = + LcovMergerFlags.parseFlags( + new String[] { + "--reports_file=my_reports_file", + "--output_file=my_file", + "--filter_sources=first_filter", + "--filter_sources=second_filter" + }); + assertThat(flags.reportsFile()).isEqualTo("my_reports_file"); + assertThat(flags.outputFile()).isEqualTo("my_file"); + assertThat(flags.coverageDir()).isNull(); + assertThat(flags.filterSources()).containsExactly("first_filter", "second_filter"); + } + + @Test(expected = IllegalArgumentException.class) + public void parseFlagsTestCoverageDirAndReportsFile() { + LcovMergerFlags.parseFlags( + new String[] {"--reports_file=my_reports_file", "--coverage_dir=my_coverage_dir"}); + } + + @Test(expected = IllegalArgumentException.class) + public void parseFlagsTestEmptyFlags() { + LcovMergerFlags.parseFlags(new String[] {}); + } + + @Test(expected = IllegalArgumentException.class) + public void parseFlagsTestNoOutputFile() { + LcovMergerFlags.parseFlags( + new String[] { + "--reports_file=my_reports_file", + }); + } + + @Test(expected = IllegalArgumentException.class) + public void parseFlagsTestUnknownFlag() { + LcovMergerFlags.parseFlags( + new String[] { + "--fake_flag=my_reports_file", + }); + } + + @Test(expected = IllegalArgumentException.class) + public void parseFlagsTestInvalidFlagValue() { + LcovMergerFlags.parseFlags( + new String[] { + "--reports_file", "--output_file=my_file", + }); + } + + @Test(expected = IllegalArgumentException.class) + public void parseFlagsTestInvalidFlagValueWithoutDashes() { + LcovMergerFlags.parseFlags( + new String[] { + "reports_file", "--output_file=my_file", + }); + } +} -- cgit v1.2.3