diff options
Diffstat (limited to 'tools/test/LcovMerger/java')
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD | 30 | ||||
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BranchCoverage.java | 23 | ||||
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Constants.java (renamed from tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovConstants.java) | 20 | ||||
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Coverage.java | 15 | ||||
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/GcovParser.java | 196 | ||||
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovParser.java | 66 | ||||
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovPrinter.java | 44 | ||||
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Main.java | 112 | ||||
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Parser.java | 23 | ||||
-rw-r--r-- | tools/test/LcovMerger/java/com/google/devtools/lcovmerger/SourceFileCoverage.java | 8 |
10 files changed, 423 insertions, 114 deletions
diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD index 38e0816a95..7af7779a78 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD @@ -56,8 +56,8 @@ java_library( srcs = ["LcovPrinter.java"], deps = [ ":BranchCoverage", + ":Constants", ":Coverage", - ":LcovConstants", ":LineCoverage", ":SourceFileCoverage", "//third_party:guava", @@ -65,16 +65,33 @@ java_library( ) java_library( - name = "LcovConstants", - srcs = ["LcovConstants.java"], + name = "Constants", + srcs = ["Constants.java"], +) + +java_library( + name = "GcovParser", + srcs = [ + "GcovParser.java", + "Parser.java", + ], + deps = [ + ":BranchCoverage", + ":Constants", + ":LineCoverage", + ":SourceFileCoverage", + ], ) java_library( name = "LcovParser", - srcs = ["LcovParser.java"], + srcs = [ + "LcovParser.java", + "Parser.java", + ], deps = [ ":BranchCoverage", - ":LcovConstants", + ":Constants", ":LineCoverage", ":SourceFileCoverage", ], @@ -90,8 +107,9 @@ java_library( name = "MainLibrary", srcs = ["Main.java"], deps = [ + ":Constants", ":Coverage", - ":LcovConstants", + ":GcovParser", ":LcovParser", ":LcovPrinter", ":SourceFileCoverage", diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BranchCoverage.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BranchCoverage.java index 0fbe78311a..bc12dc6098 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BranchCoverage.java +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BranchCoverage.java @@ -22,15 +22,14 @@ import com.google.auto.value.AutoValue; @AutoValue abstract class BranchCoverage { - static BranchCoverage create( - int lineNumber, - String blockNumber, - String branchNumber, - boolean wasExecuted, - int nrOfExecutions) { - assert (wasExecuted && nrOfExecutions > 0) || (!wasExecuted && nrOfExecutions == 0); + static BranchCoverage create(int lineNumber, int nrOfExecutions) { return new AutoValue_BranchCoverage( - lineNumber, blockNumber, branchNumber, wasExecuted, nrOfExecutions); + lineNumber, /*blockNumber=*/ "", /*branchNumber=*/ "", nrOfExecutions); + } + + static BranchCoverage createWithBlockAndBranch( + int lineNumber, String blockNumber, String branchNumber, int nrOfExecutions) { + return new AutoValue_BranchCoverage(lineNumber, blockNumber, branchNumber, nrOfExecutions); } /** @@ -44,11 +43,10 @@ abstract class BranchCoverage { assert first.blockNumber().equals(second.blockNumber()); assert first.branchNumber().equals(second.branchNumber()); - return create( + return createWithBlockAndBranch( first.lineNumber(), first.blockNumber(), first.branchNumber(), - first.wasExecuted() || second.wasExecuted(), first.nrOfExecutions() + second.nrOfExecutions()); } @@ -56,6 +54,9 @@ abstract class BranchCoverage { // The two numbers below should be -1 for non-gcc emitted coverage (e.g. Java). abstract String blockNumber(); // internal gcc internal ID for the branch abstract String branchNumber(); // internal gcc internal ID for the branch - abstract boolean wasExecuted(); abstract int nrOfExecutions(); + + boolean wasExecuted() { + return nrOfExecutions() > 0; + } } diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovConstants.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Constants.java index 83c6ce5f85..9a8fe88850 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovConstants.java +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Constants.java @@ -15,10 +15,12 @@ package com.google.devtools.lcovmerger; /** - * Stores markers used by the lcov tracefile. See - * <a href="http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php"> lcov documentation</a> + * Stores markers used by the lcov tracefile and gcov intermediate format file. See <a + * href="http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php">lcov documentation</a> and the flag + * {@code --intermediate-format} in <a href="https://gcc.gnu.org/onlinedocs/gcc/Invoking-Gcov.html"> + * gcov documentation</a>. */ -class LcovConstants { +class Constants { static final String SF_MARKER = "SF:"; static final String FN_MARKER = "FN:"; static final String FNDA_MARKER = "FNDA:"; @@ -32,7 +34,17 @@ class LcovConstants { static final String LH_MARKER = "LH:"; static final String LF_MARKER = "LF:"; static final String END_OF_RECORD_MARKER = "end_of_record"; - static final String LCOV_DELIMITER = ","; static final String TAKEN = "-"; static final String TRACEFILE_EXTENSION = ".dat"; + static final String DELIMITER = ","; + static final String GCOV_EXTENSION = ".gcov"; + static final String GCOV_VERSION_MARKER = "version:"; + static final String GCOV_CWD_MARKER = "cwd:"; + static final String GCOV_FILE_MARKER = "file:"; + static final String GCOV_FUNCTION_MARKER = "function:"; + static final String GCOV_LINE_MARKER = "lcount:"; + static final String GCOV_BRANCH_MARKER = "branch:"; + static final String GCOV_BRANCH_NOTEXEC = "notexec"; + static final String GCOV_BRANCH_NOTTAKEN = "taken"; + static final String GCOV_BRANCH_TAKEN = "nottaken"; } 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 82733aa84a..75eaa420cc 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Coverage.java +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Coverage.java @@ -34,6 +34,21 @@ class Coverage { } } + static Coverage merge(Coverage c1, Coverage c2) { + Coverage merged = new Coverage(); + for (SourceFileCoverage sourceFile : c1.getAllSourceFiles()) { + merged.add(sourceFile); + } + for (SourceFileCoverage sourceFile : c2.getAllSourceFiles()) { + merged.add(sourceFile); + } + return merged; + } + + boolean isEmpty() { + return sourceFiles.isEmpty(); + } + Collection<SourceFileCoverage> getAllSourceFiles() { return sourceFiles.values(); } diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/GcovParser.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/GcovParser.java new file mode 100644 index 0000000000..25592192b2 --- /dev/null +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/GcovParser.java @@ -0,0 +1,196 @@ +// 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.devtools.lcovmerger.Constants.DELIMITER; +import static com.google.devtools.lcovmerger.Constants.GCOV_BRANCH_MARKER; +import static com.google.devtools.lcovmerger.Constants.GCOV_BRANCH_NOTEXEC; +import static com.google.devtools.lcovmerger.Constants.GCOV_BRANCH_NOTTAKEN; +import static com.google.devtools.lcovmerger.Constants.GCOV_BRANCH_TAKEN; +import static com.google.devtools.lcovmerger.Constants.GCOV_CWD_MARKER; +import static com.google.devtools.lcovmerger.Constants.GCOV_FILE_MARKER; +import static com.google.devtools.lcovmerger.Constants.GCOV_FUNCTION_MARKER; +import static com.google.devtools.lcovmerger.Constants.GCOV_LINE_MARKER; +import static com.google.devtools.lcovmerger.Constants.GCOV_VERSION_MARKER; +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A {@link Parser} for gcov intermediate format. See the flag {@code --intermediate-format} in <a + * href="https://gcc.gnu.org/onlinedocs/gcc/Invoking-gcov.html">gcov documentation</a>. + */ +public class GcovParser { + + private static final Logger logger = Logger.getLogger(GcovParser.class.getName()); + private List<SourceFileCoverage> allSourceFiles; + private final InputStream inputStream; + private SourceFileCoverage currentSourceFileCoverage; + + private GcovParser(InputStream inputStream) { + this.inputStream = inputStream; + } + + public static List<SourceFileCoverage> parse(InputStream inputStream) throws IOException { + return new GcovParser(inputStream).parse(); + } + + private List<SourceFileCoverage> parse() throws IOException { + allSourceFiles = new ArrayList<>(); + boolean malformedInput = false; + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(inputStream, UTF_8))) { + String line; + // TODO(bazel-team): This is susceptible to OOM if the input file is too large and doesn't + // contain any newlines. + while ((line = bufferedReader.readLine()) != null) { + if (!parseLine(line)) { + malformedInput = true; + } + } + bufferedReader.close(); + } + endSourceFile(); + if (malformedInput) { + throw new IOException("gcov intermediate input is malformed."); + } + return allSourceFiles; + } + + /** + * Merges {@code currentSourceFileCoverage} into {@code allSourceFilesCoverageData} and resets + * {@code currentSourceFileCoverage} to null. + */ + private void endSourceFile() { + if (currentSourceFileCoverage == null) { + return; + } + allSourceFiles.add(currentSourceFileCoverage); + currentSourceFileCoverage = null; + } + + private boolean parseLine(String line) { + if (line.startsWith(GCOV_FILE_MARKER)) { + endSourceFile(); + return parseSource(line); + } + if (line.startsWith(GCOV_FUNCTION_MARKER)) { + return parseFunction(line); + } + if (line.startsWith(GCOV_LINE_MARKER)) { + return parseLCount(line); + } + if (line.startsWith(GCOV_BRANCH_MARKER)) { + return parseBranch(line); + } + if (line.startsWith(GCOV_VERSION_MARKER) || line.startsWith(GCOV_CWD_MARKER)) { + // Ignore these fields for now as they are not necessary. + return true; + } + return false; + } + + private boolean parseSource(String line) { + String sourcefile = line.substring(GCOV_FILE_MARKER.length()); + if (sourcefile.isEmpty()) { + logger.log(Level.WARNING, "gcov info doesn't contain source file name on line: " + line); + return false; + } + currentSourceFileCoverage = new SourceFileCoverage(sourcefile); + return true; + } + + // function:start_line_number,end_line_number,execution_count,function_name + private boolean parseFunction(String line) { + String lineContent = line.substring(GCOV_FUNCTION_MARKER.length()); + String[] items = lineContent.split(DELIMITER, -1); + if (items.length != 4) { + logger.log(Level.WARNING, "gcov info contains invalid line " + line); + return false; + } + try { + // Ignore end_line_number since it's redundant information. + int startLine = Integer.parseInt(items[0]); + int execCount = Integer.parseInt(items[2]); + String functionName = items[3]; + currentSourceFileCoverage.addLineNumber(functionName, startLine); + currentSourceFileCoverage.addFunctionExecution(functionName, execCount); + } catch (NumberFormatException e) { + logger.log(Level.WARNING, "gcov info contains invalid line " + line); + return false; + } + return true; + } + + // lcount:line number,execution_count,has_unexecuted_block + private boolean parseLCount(String line) { + String lineContent = line.substring(GCOV_LINE_MARKER.length()); + String[] items = lineContent.split(DELIMITER, -1); + if (items.length != 3) { + logger.log(Level.WARNING, "gcov info contains invalid line " + line); + return false; + } + try { + // Ignore has_unexecuted_block since it's not used. + int lineNr = Integer.parseInt(items[0]); + int execCount = Integer.parseInt(items[1]); + currentSourceFileCoverage.addLine(lineNr, LineCoverage.create(lineNr, execCount, null)); + } catch (NumberFormatException e) { + logger.log(Level.WARNING, "gcov info contains invalid line " + line); + return false; + } + return true; + } + + // branch:line_number,branch_coverage_type + private boolean parseBranch(String line) { + String lineContent = line.substring(GCOV_BRANCH_MARKER.length()); + String[] items = lineContent.split(DELIMITER, -1); + if (items.length != 2) { + logger.log(Level.WARNING, "gcov info contains invalid line " + line); + return false; + } + try { + // Ignore has_unexecuted_block since it's not used. + int lineNr = Integer.parseInt(items[0]); + String type = items[1]; + int execCount; + switch (type) { + case GCOV_BRANCH_NOTEXEC: + execCount = 0; + break; + case GCOV_BRANCH_NOTTAKEN: + case GCOV_BRANCH_TAKEN: + execCount = 1; + break; + default: + logger.log(Level.WARNING, "gcov info contains invalid line " + line); + return false; + } + currentSourceFileCoverage.addBranch(lineNr, BranchCoverage.create(lineNr, execCount)); + } catch (NumberFormatException e) { + logger.log(Level.WARNING, "gcov info contains invalid line " + line); + return false; + } + return true; + } +} diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovParser.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovParser.java index b441b8761e..64062db60b 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovParser.java +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovParser.java @@ -14,21 +14,21 @@ package com.google.devtools.lcovmerger; -import static com.google.devtools.lcovmerger.LcovConstants.BA_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.BRDA_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.BRF_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.BRH_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.DA_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.END_OF_RECORD_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.FNDA_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.FNF_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.FNH_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.FN_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.LCOV_DELIMITER; -import static com.google.devtools.lcovmerger.LcovConstants.LF_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.LH_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.SF_MARKER; -import static com.google.devtools.lcovmerger.LcovConstants.TAKEN; +import static com.google.devtools.lcovmerger.Constants.BA_MARKER; +import static com.google.devtools.lcovmerger.Constants.BRDA_MARKER; +import static com.google.devtools.lcovmerger.Constants.BRF_MARKER; +import static com.google.devtools.lcovmerger.Constants.BRH_MARKER; +import static com.google.devtools.lcovmerger.Constants.DA_MARKER; +import static com.google.devtools.lcovmerger.Constants.DELIMITER; +import static com.google.devtools.lcovmerger.Constants.END_OF_RECORD_MARKER; +import static com.google.devtools.lcovmerger.Constants.FNDA_MARKER; +import static com.google.devtools.lcovmerger.Constants.FNF_MARKER; +import static com.google.devtools.lcovmerger.Constants.FNH_MARKER; +import static com.google.devtools.lcovmerger.Constants.FN_MARKER; +import static com.google.devtools.lcovmerger.Constants.LF_MARKER; +import static com.google.devtools.lcovmerger.Constants.LH_MARKER; +import static com.google.devtools.lcovmerger.Constants.SF_MARKER; +import static com.google.devtools.lcovmerger.Constants.TAKEN; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.BufferedReader; @@ -47,19 +47,15 @@ import java.util.logging.Logger; class LcovParser { private static final Logger logger = Logger.getLogger(LcovParser.class.getName()); + private final InputStream inputStream; private SourceFileCoverage currentSourceFileCoverage; - private final InputStream tracefileStream; - private LcovParser(InputStream tracefileStream) { - this.tracefileStream = tracefileStream; + private LcovParser(InputStream inputStream) { + this.inputStream = inputStream; } - /** - * Returns a list of the source files found in the given tracefile. - */ - public static List<SourceFileCoverage> parse(InputStream tracefileStream) throws IOException { - LcovParser lcovParser = new LcovParser(tracefileStream); - return lcovParser.parse(); + public static List<SourceFileCoverage> parse(InputStream inputStream) throws IOException { + return new LcovParser(inputStream).parse(); } /** @@ -72,7 +68,7 @@ class LcovParser { private List<SourceFileCoverage> parse() throws IOException { List<SourceFileCoverage> allSourceFiles = new ArrayList<>(); try (BufferedReader bufferedReader = - new BufferedReader(new InputStreamReader(tracefileStream, UTF_8))) { + new BufferedReader(new InputStreamReader(inputStream, UTF_8))) { String line; while ((line = bufferedReader.readLine()) != null) { parseLine(line, allSourceFiles); @@ -164,7 +160,7 @@ class LcovParser { // FN:<line number of function start>,<function name> private boolean parseFNLine(String line) { String lineContent = line.substring(FN_MARKER.length()); - String[] funcData = lineContent.split(LCOV_DELIMITER, -1); + String[] funcData = lineContent.split(DELIMITER, -1); if (funcData.length != 2 || funcData[0].isEmpty() || funcData[1].isEmpty()) { logger.log(Level.WARNING, "Tracefile contains invalid FN line " + line); return false; @@ -183,7 +179,7 @@ class LcovParser { // FNDA:<execution count>,<function name> private boolean parseFNDALine(String line) { String lineContent = line.substring(FNDA_MARKER.length()); - String[] funcData = lineContent.split(LCOV_DELIMITER, -1); + String[] funcData = lineContent.split(DELIMITER, -1); if (funcData.length != 2 || funcData[0].isEmpty() || funcData[1].isEmpty()) { logger.log(Level.WARNING, "Tracefile contains invalid FNDA line " + line); return false; @@ -238,7 +234,7 @@ class LcovParser { // BA:<line number>,<taken> private boolean parseBALine(String line) { String lineContent = line.substring(BA_MARKER.length()); - String[] lineData = lineContent.split(LCOV_DELIMITER, -1); + String[] lineData = lineContent.split(DELIMITER, -1); if (lineData.length != 2) { logger.log(Level.WARNING, "Tracefile contains invalid BRDA line " + line); return false; @@ -253,12 +249,7 @@ class LcovParser { int lineNumber = Integer.parseInt(lineData[0]); int taken = Integer.parseInt(lineData[1]); - boolean wasExecuted = false; - if (taken == 1 || taken == 2) { - wasExecuted = true; - } - BranchCoverage branchCoverage = - BranchCoverage.create(lineNumber, "", "", wasExecuted, taken); + BranchCoverage branchCoverage = BranchCoverage.create(lineNumber, taken); currentSourceFileCoverage.addBranch(lineNumber, branchCoverage); } catch (NumberFormatException e) { @@ -271,7 +262,7 @@ class LcovParser { // BRDA:<line number>,<block number>,<branch number>,<taken> private boolean parseBRDALine(String line) { String lineContent = line.substring(BRDA_MARKER.length()); - String[] lineData = lineContent.split(LCOV_DELIMITER, -1); + String[] lineData = lineContent.split(DELIMITER, -1); if (lineData.length != 4) { logger.log(Level.WARNING, "Tracefile contains invalid BRDA line " + line); return false; @@ -295,7 +286,8 @@ class LcovParser { wasExecuted = true; } BranchCoverage branchCoverage = - BranchCoverage.create(lineNumber, blockNumber, branchNumber, wasExecuted, executionCount); + BranchCoverage.createWithBlockAndBranch( + lineNumber, blockNumber, branchNumber, executionCount); currentSourceFileCoverage.addBranch(lineNumber, branchCoverage); } catch (NumberFormatException e) { @@ -344,7 +336,7 @@ class LcovParser { // DA:<line number>,<execution count>,[,<checksum>] private boolean parseDALine(String line) { String lineContent = line.substring(DA_MARKER.length()); - String[] lineData = lineContent.split(LCOV_DELIMITER, -1); + String[] lineData = lineContent.split(DELIMITER, -1); if (lineData.length != 2 && lineData.length != 3) { logger.log(Level.WARNING, "Tracefile contains invalid DA line " + line); return false; diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovPrinter.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovPrinter.java index ef5f9e16ad..7fa6ccc38b 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovPrinter.java +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/LcovPrinter.java @@ -88,7 +88,7 @@ class LcovPrinter { // SF:<absolute path to the source file> private void printSFLine(SourceFileCoverage sourceFile) throws IOException { - bufferedWriter.write(LcovConstants.SF_MARKER); + bufferedWriter.write(Constants.SF_MARKER); bufferedWriter.write(sourceFile.sourceFileName()); bufferedWriter.newLine(); } @@ -97,9 +97,9 @@ class LcovPrinter { private void printFNLines(SourceFileCoverage sourceFile) throws IOException { for (Entry<String, Integer> entry : sourceFile.getAllLineNumbers()) { - bufferedWriter.write(LcovConstants.FN_MARKER); + bufferedWriter.write(Constants.FN_MARKER); bufferedWriter.write(Integer.toString(entry.getValue())); // line number of function start - bufferedWriter.write(LcovConstants.LCOV_DELIMITER); + bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(entry.getKey()); // function name bufferedWriter.newLine(); } @@ -109,9 +109,9 @@ class LcovPrinter { private void printFNDALines(SourceFileCoverage sourceFile) throws IOException { for (Entry<String, Integer> entry : sourceFile.getAllExecutionCount()) { - bufferedWriter.write(LcovConstants.FNDA_MARKER); + bufferedWriter.write(Constants.FNDA_MARKER); bufferedWriter.write(Integer.toString(entry.getValue())); // execution count - bufferedWriter.write(LcovConstants.LCOV_DELIMITER); + bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(entry.getKey()); // function name bufferedWriter.newLine(); } @@ -119,14 +119,14 @@ class LcovPrinter { // FNF:<number of functions found> private void printFNFLine(SourceFileCoverage sourceFile) throws IOException { - bufferedWriter.write(LcovConstants.FNF_MARKER); + bufferedWriter.write(Constants.FNF_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrFunctionsFound())); bufferedWriter.newLine(); } // FNH:<number of functions hit> private void printFNHLine(SourceFileCoverage sourceFile) throws IOException { - bufferedWriter.write(LcovConstants.FNH_MARKER); + bufferedWriter.write(Constants.FNH_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrFunctionsHit())); bufferedWriter.newLine(); } @@ -138,17 +138,17 @@ class LcovPrinter { // We skip printing this as a BRDA line and print it later as a BA line. continue; } - bufferedWriter.write(LcovConstants.BRDA_MARKER); + bufferedWriter.write(Constants.BRDA_MARKER); bufferedWriter.write(Integer.toString(branch.lineNumber())); - bufferedWriter.write(LcovConstants.LCOV_DELIMITER); + bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(branch.blockNumber()); - bufferedWriter.write(LcovConstants.LCOV_DELIMITER); + bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(branch.branchNumber()); - bufferedWriter.write(LcovConstants.LCOV_DELIMITER); + bufferedWriter.write(Constants.DELIMITER); if (branch.wasExecuted()) { bufferedWriter.write(Integer.toString(branch.nrOfExecutions())); } else { - bufferedWriter.write(LcovConstants.TAKEN); + bufferedWriter.write(Constants.TAKEN); } bufferedWriter.newLine(); } @@ -161,9 +161,9 @@ class LcovPrinter { // This branch was already printed with more information as a BRDA line. continue; } - bufferedWriter.write(LcovConstants.BA_MARKER); + bufferedWriter.write(Constants.BA_MARKER); bufferedWriter.write(Integer.toString(branch.lineNumber())); - bufferedWriter.write(LcovConstants.LCOV_DELIMITER); + bufferedWriter.write(Constants.DELIMITER); // 0 = branch was not executed // 1 = branch was executed but not taken // 2 = branch was executed and taken @@ -175,7 +175,7 @@ class LcovPrinter { // BRF:<number of branches found> private void printBRFLine(SourceFileCoverage sourceFile) throws IOException { if (sourceFile.nrBranchesFound() > 0) { - bufferedWriter.write(LcovConstants.BRF_MARKER); + bufferedWriter.write(Constants.BRF_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrBranchesFound())); bufferedWriter.newLine(); } @@ -185,7 +185,7 @@ class LcovPrinter { private void printBRHLine(SourceFileCoverage sourceFile) throws IOException { // Only print if there were any branches found. if (sourceFile.nrBranchesFound() > 0) { - bufferedWriter.write(LcovConstants.BRH_MARKER); + bufferedWriter.write(Constants.BRH_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrBranchesHit())); bufferedWriter.newLine(); } @@ -195,12 +195,12 @@ class LcovPrinter { private void printDALines(SourceFileCoverage sourceFile) throws IOException { for (LineCoverage lineExecution : sourceFile.getAllLineExecution()) { - bufferedWriter.write(LcovConstants.DA_MARKER); + bufferedWriter.write(Constants.DA_MARKER); bufferedWriter.write(Integer.toString(lineExecution.lineNumber())); - bufferedWriter.write(LcovConstants.LCOV_DELIMITER); + bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(Integer.toString(lineExecution.executionCount())); if (lineExecution.checksum() != null) { - bufferedWriter.write(LcovConstants.LCOV_DELIMITER); + bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(lineExecution.checksum()); } bufferedWriter.newLine(); @@ -209,21 +209,21 @@ class LcovPrinter { // LH:<number of lines with a non-zero execution count> private void printLHLine(SourceFileCoverage sourceFile) throws IOException { - bufferedWriter.write(LcovConstants.LH_MARKER); + bufferedWriter.write(Constants.LH_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrOfLinesWithNonZeroExecution())); bufferedWriter.newLine(); } // LF:<number of instrumented lines> private void printLFLine(SourceFileCoverage sourceFile) throws IOException { - bufferedWriter.write(LcovConstants.LF_MARKER); + bufferedWriter.write(Constants.LF_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrOfInstrumentedLines())); bufferedWriter.newLine(); } // end_of_record private void printEndOfRecordLine() throws IOException { - bufferedWriter.write(LcovConstants.END_OF_RECORD_MARKER); + bufferedWriter.write(Constants.END_OF_RECORD_MARKER); bufferedWriter.newLine(); } } 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 2984d7b274..b490765383 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Main.java +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Main.java @@ -14,7 +14,8 @@ package com.google.devtools.lcovmerger; -import static com.google.devtools.lcovmerger.LcovConstants.TRACEFILE_EXTENSION; +import static com.google.devtools.lcovmerger.Constants.GCOV_EXTENSION; +import static com.google.devtools.lcovmerger.Constants.TRACEFILE_EXTENSION; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.VisibleForTesting; @@ -28,6 +29,7 @@ import java.nio.file.Files; 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; @@ -51,65 +53,107 @@ public class Main { System.exit(1); } + List<File> filesInCoverageDir = + flags.containsKey("coverage_dir") + ? getCoverageFilesInDir(flags.get("coverage_dir")) + : Collections.emptyList(); + Coverage coverage = + Coverage.merge( + parseFiles(getTracefiles(flags, filesInCoverageDir), LcovParser::parse), + parseFiles(getGcovInfoFiles(filesInCoverageDir), GcovParser::parse)); + + if (coverage.isEmpty()) { + logger.log(Level.SEVERE, "There was no coverage found."); + System.exit(1); + } + + int exitStatus = 0; + String outputFile = flags.get("output_file"); + try { + LcovPrinter.print(new FileOutputStream(new File(outputFile)), coverage); + } catch (IOException e) { + logger.log( + Level.SEVERE, + "Could not write to output file " + outputFile + " due to " + e.getMessage()); + exitStatus = 1; + } + System.exit(exitStatus); + } + + private static List<File> getGcovInfoFiles(List<File> filesInCoverageDir) { + List<File> gcovFiles = getFilesWithExtension(filesInCoverageDir, GCOV_EXTENSION); + if (gcovFiles.isEmpty()) { + logger.log(Level.SEVERE, "No gcov info file found."); + } else { + logger.log(Level.INFO, "Found " + gcovFiles.size() + " gcov info files."); + } + return gcovFiles; + } + + private static List<File> getTracefiles( + Map<String, String> flags, List<File> filesInCoverageDir) { List<File> lcovTracefiles = new ArrayList<>(); if (flags.containsKey("coverage_dir")) { - logger.log(Level.SEVERE, "Retrieving tracefiles from coverage_dir."); - lcovTracefiles = getLcovTracefilesFromDir(flags.get("coverage_dir")); + lcovTracefiles = getFilesWithExtension(filesInCoverageDir, TRACEFILE_EXTENSION); } else if (flags.containsKey("reports_file")) { - logger.log(Level.SEVERE, "Retrieving tracefiles from reports_file."); - lcovTracefiles = getLcovTracefilesFromFile(flags.get("reports_file")); + lcovTracefiles = getTracefilesFromFile(flags.get("reports_file")); } if (lcovTracefiles.isEmpty()) { logger.log(Level.SEVERE, "No lcov file found."); - System.exit(1); + } else { + logger.log(Level.INFO, "Found " + lcovTracefiles.size() + " tracefiles."); } - logger.log(Level.SEVERE, "Found " + lcovTracefiles.size() + " tracefiles."); + return lcovTracefiles; + } + + private static Coverage parseFiles(List<File> files, Parser parser) { Coverage coverage = new Coverage(); - for (File tracefile : lcovTracefiles) { + for (File file : files) { try { - logger.log(Level.SEVERE, "Parsing tracefile " + tracefile.toString()); - List<SourceFileCoverage> sourceFilesCoverage = - LcovParser.parse(new FileInputStream(tracefile)); + logger.log(Level.SEVERE, "Parsing file " + file.toString()); + List<SourceFileCoverage> sourceFilesCoverage = parser.parse(new FileInputStream(file)); for (SourceFileCoverage sourceFileCoverage : sourceFilesCoverage) { coverage.add(sourceFileCoverage); } } catch (IOException e) { - logger.log(Level.SEVERE, "Tracefile " + tracefile.getAbsolutePath() + " was deleted"); + logger.log( + Level.SEVERE, + "File " + file.getAbsolutePath() + " could not be parsed due to: " + e.getMessage()); System.exit(1); } } - int exitStatus = 0; - String outputFile = flags.get("output_file"); - try { - File coverageFile = new File(outputFile); - LcovPrinter.print(new FileOutputStream(coverageFile), coverage); - } catch (IOException e) { - logger.log(Level.SEVERE, - "Could not write to output file " + outputFile + " due to " + e.getMessage()); - exitStatus = 1; - } - System.exit(exitStatus); + return coverage; } /** - * Returns a list of all the files with a “.dat” extension found recursively under the given - * directory. + * Returns a list of all the files with the given extension found recursively under the given dir. */ @VisibleForTesting - static List<File> getLcovTracefilesFromDir(String coverageDir) { - List<File> datFiles = new ArrayList<>(); - try (Stream<Path> stream = Files.walk(Paths.get(coverageDir))) { - datFiles = stream.filter(p -> p.toString().endsWith(TRACEFILE_EXTENSION)) - .map(path -> path.toFile()) - .collect(Collectors.toList()); + static List<File> getCoverageFilesInDir(String dir) { + List<File> files = new ArrayList<>(); + try (Stream<Path> stream = Files.walk(Paths.get(dir))) { + files = + stream + .filter( + p -> + p.toString().endsWith(TRACEFILE_EXTENSION) + || p.toString().endsWith(GCOV_EXTENSION)) + .map(path -> path.toFile()) + .collect(Collectors.toList()); } catch (IOException ex) { - logger.log(Level.SEVERE, "Error reading folder " + coverageDir + ": " + ex.getMessage()); + logger.log(Level.SEVERE, "Error reading folder " + dir + ": " + ex.getMessage()); } + return files; + } - return datFiles; + static List<File> getFilesWithExtension(List<File> files, String extension) { + return files + .stream() + .filter(file -> file.toString().endsWith(extension)) + .collect(Collectors.toList()); } - static List<File> getLcovTracefilesFromFile(String reportsFile) { + static List<File> getTracefilesFromFile(String reportsFile) { List<File> datFiles = new ArrayList<>(); try (FileInputStream inputStream = new FileInputStream(reportsFile)) { InputStreamReader inputStreamReader = new InputStreamReader(inputStream, UTF_8); diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Parser.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Parser.java new file mode 100644 index 0000000000..4cfafdce07 --- /dev/null +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/Parser.java @@ -0,0 +1,23 @@ +// 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 java.io.IOException; +import java.io.InputStream; +import java.util.List; + +interface Parser { + List<SourceFileCoverage> parse(InputStream inputStream) throws IOException; +} diff --git a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/SourceFileCoverage.java b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/SourceFileCoverage.java index 0cdb1a6f85..b98a873a8c 100644 --- a/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/SourceFileCoverage.java +++ b/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/SourceFileCoverage.java @@ -237,6 +237,10 @@ class SourceFileCoverage { } void addBranch(Integer lineNumber, BranchCoverage branch) { + if (this.branches.get(lineNumber) != null) { + this.branches.put(lineNumber, BranchCoverage.merge(this.branches.get(lineNumber), branch)); + return; + } this.branches.put(lineNumber, branch); } @@ -245,6 +249,10 @@ class SourceFileCoverage { } void addLine(Integer lineNumber, LineCoverage line) { + if (this.lines.get(lineNumber) != null) { + this.lines.put(lineNumber, LineCoverage.merge(line, this.lines.get(lineNumber))); + return; + } this.lines.put(lineNumber, line); } |