// 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 java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.VisibleForTesting; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; /** * Prints coverage data stored in a collection of {@link SourceFileCoverage} in a * lcov tracefile format */ class LcovPrinter { private static final Logger logger = Logger.getLogger(LcovPrinter.class.getName()); private final BufferedWriter bufferedWriter; private LcovPrinter(BufferedWriter bufferedWriter) { this.bufferedWriter = bufferedWriter; } static boolean print(OutputStream outputStream, Coverage coverage) { BufferedWriter bufferedWriter; try (Writer fileWriter = new OutputStreamWriter(outputStream, UTF_8)) { bufferedWriter = new BufferedWriter(fileWriter); LcovPrinter lcovPrinter = new LcovPrinter(bufferedWriter); lcovPrinter.print(coverage); bufferedWriter.close(); } catch (IOException exception) { logger.log(Level.SEVERE, "Could not write to output file."); return false; } return true; } private boolean print(Coverage coverage) { try { for (SourceFileCoverage sourceFile : coverage.getAllSourceFiles()) { print(sourceFile); } } catch (IOException exception) { logger.log(Level.SEVERE, "Could not write to output file."); return false; } return true; } /** * Prints the given source data in an lcov tracefile format. * * Assumes the file is opened and closed outside of this method. */ @VisibleForTesting void print(SourceFileCoverage sourceFile) throws IOException { printSFLine(sourceFile); printFNLines(sourceFile); printFNDALines(sourceFile); printFNFLine(sourceFile); printFNHLine(sourceFile); printBRDALines(sourceFile); printBALines(sourceFile); printBRFLine(sourceFile); printBRHLine(sourceFile); printDALines(sourceFile); printLHLine(sourceFile); printLFLine(sourceFile); printEndOfRecordLine(); } // SF: private void printSFLine(SourceFileCoverage sourceFile) throws IOException { bufferedWriter.write(Constants.SF_MARKER); bufferedWriter.write(sourceFile.sourceFileName()); bufferedWriter.newLine(); } // FN:, private void printFNLines(SourceFileCoverage sourceFile) throws IOException { for (Entry entry : sourceFile.getAllLineNumbers()) { bufferedWriter.write(Constants.FN_MARKER); bufferedWriter.write(Integer.toString(entry.getValue())); // line number of function start bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(entry.getKey()); // function name bufferedWriter.newLine(); } } // FNDA:, private void printFNDALines(SourceFileCoverage sourceFile) throws IOException { for (Entry entry : sourceFile.getAllExecutionCount()) { bufferedWriter.write(Constants.FNDA_MARKER); bufferedWriter.write(Integer.toString(entry.getValue())); // execution count bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(entry.getKey()); // function name bufferedWriter.newLine(); } } // FNF: private void printFNFLine(SourceFileCoverage sourceFile) throws IOException { bufferedWriter.write(Constants.FNF_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrFunctionsFound())); bufferedWriter.newLine(); } // FNH: private void printFNHLine(SourceFileCoverage sourceFile) throws IOException { bufferedWriter.write(Constants.FNH_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrFunctionsHit())); bufferedWriter.newLine(); } // BRDA:,,, private void printBRDALines(SourceFileCoverage sourceFile) throws IOException { for (BranchCoverage branch : sourceFile.getAllBranches()) { if (branch.blockNumber().isEmpty() || branch.branchNumber().isEmpty()) { // We skip printing this as a BRDA line and print it later as a BA line. continue; } bufferedWriter.write(Constants.BRDA_MARKER); bufferedWriter.write(Integer.toString(branch.lineNumber())); bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(branch.blockNumber()); bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(branch.branchNumber()); bufferedWriter.write(Constants.DELIMITER); if (branch.wasExecuted()) { bufferedWriter.write(Integer.toString(branch.nrOfExecutions())); } else { bufferedWriter.write(Constants.TAKEN); } bufferedWriter.newLine(); } } // BA:, private void printBALines(SourceFileCoverage sourceFile) throws IOException { for (BranchCoverage branch : sourceFile.getAllBranches()) { if (!branch.blockNumber().isEmpty() && !branch.branchNumber().isEmpty()) { // This branch was already printed with more information as a BRDA line. continue; } bufferedWriter.write(Constants.BA_MARKER); bufferedWriter.write(Integer.toString(branch.lineNumber())); bufferedWriter.write(Constants.DELIMITER); // 0 = branch was not executed // 1 = branch was executed but not taken // 2 = branch was executed and taken bufferedWriter.write(branch.wasExecuted() ? "2" : "0"); bufferedWriter.newLine(); } } // BRF: private void printBRFLine(SourceFileCoverage sourceFile) throws IOException { if (sourceFile.nrBranchesFound() > 0) { bufferedWriter.write(Constants.BRF_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrBranchesFound())); bufferedWriter.newLine(); } } // BRH: private void printBRHLine(SourceFileCoverage sourceFile) throws IOException { // Only print if there were any branches found. if (sourceFile.nrBranchesFound() > 0) { bufferedWriter.write(Constants.BRH_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrBranchesHit())); bufferedWriter.newLine(); } } // DA:,[,] private void printDALines(SourceFileCoverage sourceFile) throws IOException { for (LineCoverage lineExecution : sourceFile.getAllLineExecution()) { bufferedWriter.write(Constants.DA_MARKER); bufferedWriter.write(Integer.toString(lineExecution.lineNumber())); bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(Integer.toString(lineExecution.executionCount())); if (lineExecution.checksum() != null) { bufferedWriter.write(Constants.DELIMITER); bufferedWriter.write(lineExecution.checksum()); } bufferedWriter.newLine(); } } // LH: private void printLHLine(SourceFileCoverage sourceFile) throws IOException { bufferedWriter.write(Constants.LH_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrOfLinesWithNonZeroExecution())); bufferedWriter.newLine(); } // LF: private void printLFLine(SourceFileCoverage sourceFile) throws IOException { bufferedWriter.write(Constants.LF_MARKER); bufferedWriter.write(Integer.toString(sourceFile.nrOfInstrumentedLines())); bufferedWriter.newLine(); } // end_of_record private void printEndOfRecordLine() throws IOException { bufferedWriter.write(Constants.END_OF_RECORD_MARKER); bufferedWriter.newLine(); } }