import optparse
import re
import sys
def parse_gcov_report(gcov_input):
per_file_coverage = {}
total_coverage = None
for line in sys.stdin:
line = line.strip()
match_obj = re.match("^File '(.*)'$", line)
if match_obj:
current_file = match_obj.group(1)
continue
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:
total_coverage = (coverage, lines)
continue
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):
max_file_name_length = max(len(fname) for fname in per_file_coverage.keys())
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")
) print(separator)
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))
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(",")}
per_file_coverage, total_coverage = parse_gcov_report(sys.stdin)
if len(interested_files):
per_file_coverage = {
fname: per_file_coverage[fname]
for fname in interested_files
if fname in per_file_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()