summaryrefslogtreecommitdiffstats
path: root/src/rocksdb/coverage/parse_gcov_output.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/rocksdb/coverage/parse_gcov_output.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/src/rocksdb/coverage/parse_gcov_output.py b/src/rocksdb/coverage/parse_gcov_output.py
new file mode 100644
index 000000000..b9788ec81
--- /dev/null
+++ b/src/rocksdb/coverage/parse_gcov_output.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
+
+from __future__ import print_function
+
+import optparse
+import re
+import sys
+
+# the gcov report follows certain pattern. Each file will have two lines
+# of report, from which we can extract the file name, total lines and coverage
+# percentage.
+def parse_gcov_report(gcov_input):
+ per_file_coverage = {}
+ total_coverage = None
+
+ for line in sys.stdin:
+ line = line.strip()
+
+ # --First line of the coverage report (with file name in it)?
+ match_obj = re.match("^File '(.*)'$", line)
+ if match_obj:
+ # fetch the file name from the first line of the report.
+ current_file = match_obj.group(1)
+ continue
+
+ # -- Second line of the file report (with coverage percentage)
+ match_obj = re.match("^Lines executed:(.*)% of (.*)", line)
+
+ if match_obj:
+ coverage = float(match_obj.group(1))
+ lines = int(match_obj.group(2))
+
+ if current_file is not None:
+ per_file_coverage[current_file] = (coverage, lines)
+ current_file = None
+ else:
+ # If current_file is not set, we reach the last line of report,
+ # which contains the summarized coverage percentage.
+ total_coverage = (coverage, lines)
+ continue
+
+ # If the line's pattern doesn't fall into the above categories. We
+ # can simply ignore them since they're either empty line or doesn't
+ # find executable lines of the given file.
+ current_file = None
+
+ return per_file_coverage, total_coverage
+
+
+def get_option_parser():
+ usage = (
+ "Parse the gcov output and generate more human-readable code "
+ + "coverage report."
+ )
+ parser = optparse.OptionParser(usage)
+
+ parser.add_option(
+ "--interested-files",
+ "-i",
+ dest="filenames",
+ help="Comma separated files names. if specified, we will display "
+ + "the coverage report only for interested source files. "
+ + "Otherwise we will display the coverage report for all "
+ + "source files.",
+ )
+ return parser
+
+
+def display_file_coverage(per_file_coverage, total_coverage):
+ # To print out auto-adjustable column, we need to know the longest
+ # length of file names.
+ max_file_name_length = max(len(fname) for fname in per_file_coverage.keys())
+
+ # -- Print header
+ # size of separator is determined by 3 column sizes:
+ # file name, coverage percentage and lines.
+ header_template = "%" + str(max_file_name_length) + "s\t%s\t%s"
+ separator = "-" * (max_file_name_length + 10 + 20)
+ print(
+ header_template % ("Filename", "Coverage", "Lines")
+ ) # noqa: E999 T25377293 Grandfathered in
+ print(separator)
+
+ # -- Print body
+ # template for printing coverage report for each file.
+ record_template = "%" + str(max_file_name_length) + "s\t%5.2f%%\t%10d"
+
+ for fname, coverage_info in per_file_coverage.items():
+ coverage, lines = coverage_info
+ print(record_template % (fname, coverage, lines))
+
+ # -- Print footer
+ if total_coverage:
+ print(separator)
+ print(record_template % ("Total", total_coverage[0], total_coverage[1]))
+
+
+def report_coverage():
+ parser = get_option_parser()
+ (options, args) = parser.parse_args()
+
+ interested_files = set()
+ if options.filenames is not None:
+ interested_files = {f.strip() for f in options.filenames.split(",")}
+
+ # To make things simple, right now we only read gcov report from the input
+ per_file_coverage, total_coverage = parse_gcov_report(sys.stdin)
+
+ # Check if we need to display coverage info for interested files.
+ if len(interested_files):
+ per_file_coverage = dict(
+ (fname, per_file_coverage[fname])
+ for fname in interested_files
+ if fname in per_file_coverage
+ )
+ # If we only interested in several files, it makes no sense to report
+ # the total_coverage
+ total_coverage = None
+
+ if not len(per_file_coverage):
+ print("Cannot find coverage info for the given files.", file=sys.stderr)
+ return
+ display_file_coverage(per_file_coverage, total_coverage)
+
+
+if __name__ == "__main__":
+ report_coverage()