]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/coverage/parse_gcov_output.py
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / coverage / parse_gcov_output.py
CommitLineData
20effc67 1#!/usr/bin/env python
f67539c2 2# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
20effc67
TL
3
4from __future__ import print_function
5
6import optparse
7c673cae
FG
7import re
8import sys
9
7c673cae
FG
10# the gcov report follows certain pattern. Each file will have two lines
11# of report, from which we can extract the file name, total lines and coverage
12# percentage.
13def parse_gcov_report(gcov_input):
14 per_file_coverage = {}
15 total_coverage = None
16
17 for line in sys.stdin:
18 line = line.strip()
19
20 # --First line of the coverage report (with file name in it)?
21 match_obj = re.match("^File '(.*)'$", line)
22 if match_obj:
23 # fetch the file name from the first line of the report.
24 current_file = match_obj.group(1)
25 continue
26
27 # -- Second line of the file report (with coverage percentage)
28 match_obj = re.match("^Lines executed:(.*)% of (.*)", line)
29
30 if match_obj:
31 coverage = float(match_obj.group(1))
32 lines = int(match_obj.group(2))
33
34 if current_file is not None:
35 per_file_coverage[current_file] = (coverage, lines)
36 current_file = None
37 else:
38 # If current_file is not set, we reach the last line of report,
39 # which contains the summarized coverage percentage.
40 total_coverage = (coverage, lines)
41 continue
42
43 # If the line's pattern doesn't fall into the above categories. We
44 # can simply ignore them since they're either empty line or doesn't
45 # find executable lines of the given file.
46 current_file = None
47
48 return per_file_coverage, total_coverage
49
50def get_option_parser():
51 usage = "Parse the gcov output and generate more human-readable code " +\
52 "coverage report."
20effc67 53 parser = optparse.OptionParser(usage)
7c673cae
FG
54
55 parser.add_option(
56 "--interested-files", "-i",
57 dest="filenames",
58 help="Comma separated files names. if specified, we will display " +
59 "the coverage report only for interested source files. " +
60 "Otherwise we will display the coverage report for all " +
61 "source files."
62 )
63 return parser
64
65def display_file_coverage(per_file_coverage, total_coverage):
66 # To print out auto-adjustable column, we need to know the longest
67 # length of file names.
68 max_file_name_length = max(
69 len(fname) for fname in per_file_coverage.keys()
70 )
71
72 # -- Print header
73 # size of separator is determined by 3 column sizes:
74 # file name, coverage percentage and lines.
75 header_template = \
76 "%" + str(max_file_name_length) + "s\t%s\t%s"
77 separator = "-" * (max_file_name_length + 10 + 20)
20effc67
TL
78 print(header_template % ("Filename", "Coverage", "Lines")) # noqa: E999 T25377293 Grandfathered in
79 print(separator)
7c673cae
FG
80
81 # -- Print body
82 # template for printing coverage report for each file.
83 record_template = "%" + str(max_file_name_length) + "s\t%5.2f%%\t%10d"
84
85 for fname, coverage_info in per_file_coverage.items():
86 coverage, lines = coverage_info
20effc67 87 print(record_template % (fname, coverage, lines))
7c673cae
FG
88
89 # -- Print footer
90 if total_coverage:
20effc67
TL
91 print(separator)
92 print(record_template % ("Total", total_coverage[0], total_coverage[1]))
7c673cae
FG
93
94def report_coverage():
95 parser = get_option_parser()
96 (options, args) = parser.parse_args()
97
98 interested_files = set()
99 if options.filenames is not None:
100 interested_files = set(f.strip() for f in options.filenames.split(','))
101
102 # To make things simple, right now we only read gcov report from the input
103 per_file_coverage, total_coverage = parse_gcov_report(sys.stdin)
104
105 # Check if we need to display coverage info for interested files.
106 if len(interested_files):
107 per_file_coverage = dict(
108 (fname, per_file_coverage[fname]) for fname in interested_files
109 if fname in per_file_coverage
110 )
111 # If we only interested in several files, it makes no sense to report
112 # the total_coverage
113 total_coverage = None
114
115 if not len(per_file_coverage):
20effc67 116 print("Cannot find coverage info for the given files.", file=sys.stderr)
7c673cae
FG
117 return
118 display_file_coverage(per_file_coverage, total_coverage)
119
120if __name__ == "__main__":
121 report_coverage()