3 |
3 |
4 """Summary reporting""" |
4 """Summary reporting""" |
5 |
5 |
6 import sys |
6 import sys |
7 |
7 |
8 from coverage import env |
8 from coverage.exceptions import CoverageException |
|
9 from coverage.misc import human_sorted_items |
9 from coverage.report import get_analysis_to_report |
10 from coverage.report import get_analysis_to_report |
10 from coverage.results import Numbers |
11 from coverage.results import Numbers |
11 from coverage.misc import CoverageException, output_encoding |
|
12 |
12 |
13 |
13 |
14 class SummaryReporter(object): |
14 class SummaryReporter: |
15 """A reporter for writing the summary report.""" |
15 """A reporter for writing the summary report.""" |
16 |
16 |
17 def __init__(self, coverage): |
17 def __init__(self, coverage): |
18 self.coverage = coverage |
18 self.coverage = coverage |
19 self.config = self.coverage.config |
19 self.config = self.coverage.config |
20 self.branches = coverage.get_data().has_arcs() |
20 self.branches = coverage.get_data().has_arcs() |
21 self.outfile = None |
21 self.outfile = None |
22 self.fr_analysis = [] |
22 self.fr_analysis = [] |
23 self.skipped_count = 0 |
23 self.skipped_count = 0 |
24 self.empty_count = 0 |
24 self.empty_count = 0 |
25 self.total = Numbers() |
25 self.total = Numbers(precision=self.config.precision) |
26 self.fmt_err = u"%s %s: %s" |
26 self.fmt_err = "%s %s: %s" |
27 |
27 |
28 def writeout(self, line): |
28 def writeout(self, line): |
29 """Write a line to the output, adding a newline.""" |
29 """Write a line to the output, adding a newline.""" |
30 if env.PY2: |
|
31 line = line.encode(output_encoding()) |
|
32 self.outfile.write(line.rstrip()) |
30 self.outfile.write(line.rstrip()) |
33 self.outfile.write("\n") |
31 self.outfile.write("\n") |
34 |
32 |
35 def report(self, morfs, outfile=None): |
33 def report(self, morfs, outfile=None): |
36 """Writes a report summarizing coverage statistics per module. |
34 """Writes a report summarizing coverage statistics per module. |
45 for fr, analysis in get_analysis_to_report(self.coverage, morfs): |
43 for fr, analysis in get_analysis_to_report(self.coverage, morfs): |
46 self.report_one_file(fr, analysis) |
44 self.report_one_file(fr, analysis) |
47 |
45 |
48 # Prepare the formatting strings, header, and column sorting. |
46 # Prepare the formatting strings, header, and column sorting. |
49 max_name = max([len(fr.relative_filename()) for (fr, analysis) in self.fr_analysis] + [5]) |
47 max_name = max([len(fr.relative_filename()) for (fr, analysis) in self.fr_analysis] + [5]) |
50 fmt_name = u"%%- %ds " % max_name |
48 fmt_name = "%%- %ds " % max_name |
51 fmt_skip_covered = u"\n%s file%s skipped due to complete coverage." |
49 fmt_skip_covered = "\n%s file%s skipped due to complete coverage." |
52 fmt_skip_empty = u"\n%s empty file%s skipped." |
50 fmt_skip_empty = "\n%s empty file%s skipped." |
53 |
51 |
54 header = (fmt_name % "Name") + u" Stmts Miss" |
52 header = (fmt_name % "Name") + " Stmts Miss" |
55 fmt_coverage = fmt_name + u"%6d %6d" |
53 fmt_coverage = fmt_name + "%6d %6d" |
56 if self.branches: |
54 if self.branches: |
57 header += u" Branch BrPart" |
55 header += " Branch BrPart" |
58 fmt_coverage += u" %6d %6d" |
56 fmt_coverage += " %6d %6d" |
59 width100 = Numbers.pc_str_width() |
57 width100 = Numbers(precision=self.config.precision).pc_str_width() |
60 header += u"%*s" % (width100+4, "Cover") |
58 header += "%*s" % (width100+4, "Cover") |
61 fmt_coverage += u"%%%ds%%%%" % (width100+3,) |
59 fmt_coverage += "%%%ds%%%%" % (width100+3,) |
62 if self.config.show_missing: |
60 if self.config.show_missing: |
63 header += u" Missing" |
61 header += " Missing" |
64 fmt_coverage += u" %s" |
62 fmt_coverage += " %s" |
65 rule = u"-" * len(header) |
63 rule = "-" * len(header) |
66 |
64 |
67 column_order = dict(name=0, stmts=1, miss=2, cover=-1) |
65 column_order = dict(name=0, stmts=1, miss=2, cover=-1) |
68 if self.branches: |
66 if self.branches: |
69 column_order.update(dict(branch=3, brpart=4)) |
67 column_order.update(dict(branch=3, brpart=4)) |
70 |
68 |
90 # Add numeric percent coverage so that sorting makes sense. |
88 # Add numeric percent coverage so that sorting makes sense. |
91 args += (nums.pc_covered,) |
89 args += (nums.pc_covered,) |
92 lines.append((text, args)) |
90 lines.append((text, args)) |
93 |
91 |
94 # Sort the lines and write them out. |
92 # Sort the lines and write them out. |
95 if getattr(self.config, 'sort', None): |
93 sort_option = (self.config.sort or "name").lower() |
96 sort_option = self.config.sort.lower() |
94 reverse = False |
97 reverse = False |
95 if sort_option[0] == '-': |
98 if sort_option[0] == '-': |
96 reverse = True |
99 reverse = True |
97 sort_option = sort_option[1:] |
100 sort_option = sort_option[1:] |
98 elif sort_option[0] == '+': |
101 elif sort_option[0] == '+': |
99 sort_option = sort_option[1:] |
102 sort_option = sort_option[1:] |
|
103 |
100 |
|
101 if sort_option == "name": |
|
102 lines = human_sorted_items(lines, reverse=reverse) |
|
103 else: |
104 position = column_order.get(sort_option) |
104 position = column_order.get(sort_option) |
105 if position is None: |
105 if position is None: |
106 raise CoverageException("Invalid sorting option: {!r}".format(self.config.sort)) |
106 raise CoverageException(f"Invalid sorting option: {self.config.sort!r}") |
107 lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse) |
107 lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse) |
108 |
108 |
109 for line in lines: |
109 for line in lines: |
110 self.writeout(line[0]) |
110 self.writeout(line[0]) |
111 |
111 |