From e0503951abc86cd1ffd884658b91199207426c18 Mon Sep 17 00:00:00 2001 From: stepeos Date: Sat, 22 Oct 2022 01:32:29 +0200 Subject: [PATCH 01/17] refactoring normal reporting text output --- coverage/cmdline.py | 7 +++ coverage/config.py | 2 + coverage/control.py | 11 +++- coverage/summary.py | 125 ++++++++++++++++++++++++++++---------------- 4 files changed, 98 insertions(+), 47 deletions(-) diff --git a/coverage/cmdline.py b/coverage/cmdline.py index 65ee73f8a..c3518111c 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -96,6 +96,10 @@ class Opts: '', '--fail-under', action='store', metavar="MIN", type="float", help="Exit with a status of 2 if the total coverage is less than MIN.", ) + format_text = optparse.make_option( + '', '--format-text', action='store', metavar="text,markdown", + help="Deaults to 'text', will print markdown syntax otherwise", + ) help = optparse.make_option( '-h', '--help', action='store_true', help="Get help on this command.", @@ -245,6 +249,7 @@ def __init__(self, *args, **kwargs): debug=None, directory=None, fail_under=None, + format_text=None, help=None, ignore_errors=None, include=None, @@ -482,6 +487,7 @@ def get_prog_name(self): Opts.contexts, Opts.input_datafile, Opts.fail_under, + Opts.format_text, Opts.ignore_errors, Opts.include, Opts.omit, @@ -689,6 +695,7 @@ def command_line(self, argv): skip_covered=options.skip_covered, skip_empty=options.skip_empty, sort=options.sort, + format_text=options.format_text, **report_args ) elif options.action == "annotate": diff --git a/coverage/config.py b/coverage/config.py index 1ad46597c..a5e6c9c0a 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -199,6 +199,7 @@ def __init__(self): # Defaults for [report] self.exclude_list = DEFAULT_EXCLUDE[:] self.fail_under = 0.0 + self.format_text = None self.ignore_errors = False self.report_include = None self.report_omit = None @@ -374,6 +375,7 @@ def copy(self): # [report] ('exclude_list', 'report:exclude_lines', 'regexlist'), ('fail_under', 'report:fail_under', 'float'), + ('format_text', 'report:format_text', 'boolean'), ('ignore_errors', 'report:ignore_errors', 'boolean'), ('partial_always_list', 'report:partial_branches_always', 'regexlist'), ('partial_list', 'report:partial_branches', 'regexlist'), diff --git a/coverage/control.py b/coverage/control.py index 91e604e00..cd1322482 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -908,7 +908,8 @@ def _get_file_reporters(self, morfs=None): def report( self, morfs=None, show_missing=None, ignore_errors=None, file=None, omit=None, include=None, skip_covered=None, - contexts=None, skip_empty=None, precision=None, sort=None + contexts=None, skip_empty=None, precision=None, sort=None, + format_text=None ): """Write a textual summary report to `file`. @@ -922,6 +923,9 @@ def report( `file` is a file-like object, suitable for writing. + `format_text` provides options, to print eitehr as plain text, or as + markdown code + `include` is a list of file name patterns. Files that match will be included in the report. Files matching `omit` will not be included in the report. @@ -953,13 +957,16 @@ def report( .. versionadded:: 5.2 The `precision` parameter. + .. veresionadded:: 6.6 + The `format` parameter. + """ with override_config( self, ignore_errors=ignore_errors, report_omit=omit, report_include=include, show_missing=show_missing, skip_covered=skip_covered, report_contexts=contexts, skip_empty=skip_empty, precision=precision, - sort=sort + sort=sort, format_text=format_text ): reporter = SummaryReporter(self) return reporter.report(morfs, outfile=file) diff --git a/coverage/summary.py b/coverage/summary.py index 861fbc536..62da4f0c8 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -30,6 +30,62 @@ def writeout(self, line): self.outfile.write(line.rstrip()) self.outfile.write("\n") + def report_text(self, header, lines_values, sort_option, reverse, + total_line): + # Prepare the formatting strings, header, and column sorting. + max_name = max([len(fr.relative_filename()) for (fr, analysis) in \ + self.fr_analysis] + [5]) + + header_row_format ="{:{name_len}}" + "{:>7}" * (len(header)- 2) + " " + header_row_format += "{:>9}" if self.config.show_missing else "{:>7}" + header_str = header_row_format.format(*header, name_len=max_name) + rule = "-" * len(header_str) + + # Write the header + self.writeout(header_str) + self.writeout(rule) + + column_order = dict(name=0, stmts=1, miss=2, cover=-1) + if self.branches: + column_order.update(dict(branch=3, brpart=4)) + + # `lines` is a list of pairs, (line text, line values). The line text + # is a string that will be printed, and line values is a tuple of + # sortable values. + line_row_format = "{:{name_len}}" + "{:>7}" * (len(header)- 3) + line_row_format += "{:>6}% " + line_row_format += " {}" if self.config.show_missing else "{:>7}" + lines = [] + + for values in lines_values: + # build string with line values + text = line_row_format.format(*values, name_len=max_name) + lines.append((text, values)) + + # Sort the lines and write them out. + if sort_option == "name": + lines = human_sorted_items(lines, reverse=reverse) + else: + position = column_order.get(sort_option) + if position is None: + raise ConfigError(f"Invalid sorting option: {self.config.sort!r}") + lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse) + + for line in lines: + self.writeout(line[0]) + + # Write a TOTAL line if we had at least one file. + if self.total.n_files > 0: + self.writeout(rule) + self.writeout(line_row_format.format(*total_line, name_len=max_name)) + + return self.total.n_statements and self.total.pc_covered + + def report_markdown(self, header, lines_values, sort_option, reverse, + total_line): + pass + + def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. @@ -44,36 +100,19 @@ def report(self, morfs, outfile=None): self.report_one_file(fr, analysis) # Prepare the formatting strings, header, and column sorting. - max_name = max([len(fr.relative_filename()) for (fr, analysis) in self.fr_analysis] + [5]) - fmt_name = "%%- %ds " % max_name - fmt_skip_covered = "\n%s file%s skipped due to complete coverage." - fmt_skip_empty = "\n%s empty file%s skipped." - - header = (fmt_name % "Name") + " Stmts Miss" - fmt_coverage = fmt_name + "%6d %6d" + header = ("Name", "Stmts", "Miss",) if self.branches: - header += " Branch BrPart" - fmt_coverage += " %6d %6d" - width100 = Numbers(precision=self.config.precision).pc_str_width() - header += "%*s" % (width100+4, "Cover") - fmt_coverage += "%%%ds%%%%" % (width100+3,) + header += ("Branch", "BrPart",) + header += ("Cover",) if self.config.show_missing: - header += " Missing" - fmt_coverage += " %s" - rule = "-" * len(header) + header += ("Missing",) column_order = dict(name=0, stmts=1, miss=2, cover=-1) if self.branches: column_order.update(dict(branch=3, brpart=4)) - # Write the header - self.writeout(header) - self.writeout(rule) - - # `lines` is a list of pairs, (line text, line values). The line text - # is a string that will be printed, and line values is a tuple of - # sortable values. - lines = [] + # `lines_values` is list of tuples of sortable values. + lines_values = [] for (fr, analysis) in self.fr_analysis: nums = analysis.numbers @@ -84,12 +123,10 @@ def report(self, morfs, outfile=None): args += (nums.pc_covered_str,) if self.config.show_missing: args += (analysis.missing_formatted(branches=True),) - text = fmt_coverage % args - # Add numeric percent coverage so that sorting makes sense. args += (nums.pc_covered,) - lines.append((text, args)) + lines_values.append(args) - # Sort the lines and write them out. + # line-sorting. sort_option = (self.config.sort or "name").lower() reverse = False if sort_option[0] == '-': @@ -98,37 +135,35 @@ def report(self, morfs, outfile=None): elif sort_option[0] == '+': sort_option = sort_option[1:] - if sort_option == "name": - lines = human_sorted_items(lines, reverse=reverse) - else: - position = column_order.get(sort_option) - if position is None: - raise ConfigError(f"Invalid sorting option: {self.config.sort!r}") - lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse) - - for line in lines: - self.writeout(line[0]) - - # Write a TOTAL line if we had at least one file. + # calculate total if we had at least one file. + total_line = () if self.total.n_files > 0: - self.writeout(rule) - args = ("TOTAL", self.total.n_statements, self.total.n_missing) + total_line = ("TOTAL", self.total.n_statements, self.total.n_missing) if self.branches: - args += (self.total.n_branches, self.total.n_partial_branches) - args += (self.total.pc_covered_str,) + total_line += (self.total.n_branches, self.total.n_partial_branches) + total_line += (self.total.pc_covered_str,) if self.config.show_missing: - args += ("",) - self.writeout(fmt_coverage % args) + total_line += ("",) + + text_format = self.config.format_text or 'text' + if text_format.lower() == 'markdown': + self.report_markdown(header, lines_values, sort_option, reverse, + total_line) + else: + self.report_text(header, lines_values, sort_option, reverse, + total_line) # Write other final lines. if not self.total.n_files and not self.skipped_count: raise NoDataError("No data to report.") if self.config.skip_covered and self.skipped_count: + fmt_skip_covered = "\n%s file%s skipped due to complete coverage." self.writeout( fmt_skip_covered % (self.skipped_count, 's' if self.skipped_count > 1 else '') ) if self.config.skip_empty and self.empty_count: + fmt_skip_empty = "\n%s empty file%s skipped." self.writeout( fmt_skip_empty % (self.empty_count, 's' if self.empty_count > 1 else '') ) From 3712391c9ddc3f51d4524bb892813fcdc2dd063f Mon Sep 17 00:00:00 2001 From: stepeos Date: Sat, 22 Oct 2022 12:38:10 +0200 Subject: [PATCH 02/17] implemented markdown feature from #1418 --- coverage/summary.py | 93 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/coverage/summary.py b/coverage/summary.py index 62da4f0c8..2718bdff6 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -30,11 +30,12 @@ def writeout(self, line): self.outfile.write(line.rstrip()) self.outfile.write("\n") - def report_text(self, header, lines_values, sort_option, reverse, + def _report_text(self, header, lines_values, sort_option, reverse, total_line): + "internal method to print report data in text format" # Prepare the formatting strings, header, and column sorting. max_name = max([len(fr.relative_filename()) for (fr, analysis) in \ - self.fr_analysis] + [5]) + self.fr_analysis] + [5]) + 2 header_row_format ="{:{name_len}}" + "{:>7}" * (len(header)- 2) + " " header_row_format += "{:>9}" if self.config.show_missing else "{:>7}" @@ -77,13 +78,91 @@ def report_text(self, header, lines_values, sort_option, reverse, # Write a TOTAL line if we had at least one file. if self.total.n_files > 0: self.writeout(rule) - self.writeout(line_row_format.format(*total_line, name_len=max_name)) + self.writeout( + line_row_format.format(*total_line, name_len=max_name)) return self.total.n_statements and self.total.pc_covered - def report_markdown(self, header, lines_values, sort_option, reverse, + def _report_markdown(self, header, lines_values, sort_option, reverse, total_line): - pass + "internal method to print report data in markdown format" + # Prepare the formatting strings, header, and column sorting. + max_name = max([len(fr.relative_filename().replace("_",r"\_")) for (fr, analysis) in \ + self.fr_analysis] + [5]) + 1 + h_form = dict( + Name="| {:{name_len}}|", Stmts="{:>7} |", Miss="{:>7} |", + Branch="{:>7} |", BrPart="{:>7} |", Cover="{:>7} |", + Missing="{:>9} |") + header_items = [ + h_form[item].format(item, name_len=max_name) for item in header] + header_str = "".join(header_items) + rule_str = "|" + " ".join(["- |".rjust(len(header_items[0])-1, '-')] + + ["-: |".rjust(len(item)-1, '-') for item in header_items[1:]]) + + # Write the header + self.writeout(header_str) + self.writeout(rule_str) + + column_order = dict(name=0, stmts=1, miss=2, cover=-1) + if self.branches: + column_order.update(dict(branch=3, brpart=4)) + + # `lines` is a list of pairs, (line text, line values). The line text + # is a string that will be printed, and line values is a tuple of + # sortable values. + + if self.config.show_missing: + line_row_format = "| {:{name_len}}|" + "{:>7} |" * (len(header)-3) + else: + line_row_format = "| {:{name_len}}|" + "{:>7} |" * (len(header)-2) + line_row_format += "{:>7}% |" + line_row_format += " {:<9} |" if self.config.show_missing else "" + lines = [] + + for values in lines_values: + # build string with line values + h_form.update(dict(Cover="{:>6}% |")) + line_items = [ + h_form[item].format(str(value).replace("_", r"\_"), + name_len=max_name) for item, value in zip(header, values)] + text = "".join(line_items) + # text = line_row_format.format(*values, name_len=max_name) + lines.append((text, values)) + + # Sort the lines and write them out. + if sort_option == "name": + lines = human_sorted_items(lines, reverse=reverse) + else: + position = column_order.get(sort_option) + if position is None: + raise ConfigError(f"Invalid sorting option: {self.config.sort!r}") + lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse) + + for line in lines: + self.writeout(line[0]) + + # Write a TOTAL line if we had at least one file. + if self.total.n_files > 0: + total_form = dict( + Name="| {:>{name_len_1}}** |", Stmts="{:>5}** |", Miss="{:>5}** |", + Branch="{:>5}** |", BrPart="{:>5}** |", Cover="{:>4}%** |", + Missing="{:>9} |") + total_line_items = [] + for item, value in zip(header, total_line): + if item == "Missing": + if value == '': + insert = value + else: + insert = "**" + value + "**" + total_line_items += total_form[item].format(\ + insert, name_len_1=max_name-3) + else: + total_line_items += total_form[item].format(\ + "**"+str(value), name_len_1=max_name-3) + total_row_str = "".join(total_line_items) + self.writeout(total_row_str) + + return self.total.n_statements and self.total.pc_covered def report(self, morfs, outfile=None): @@ -147,10 +226,10 @@ def report(self, morfs, outfile=None): text_format = self.config.format_text or 'text' if text_format.lower() == 'markdown': - self.report_markdown(header, lines_values, sort_option, reverse, + self._report_markdown(header, lines_values, sort_option, reverse, total_line) else: - self.report_text(header, lines_values, sort_option, reverse, + self._report_text(header, lines_values, sort_option, reverse, total_line) # Write other final lines. From b2224765db03ceb40435df7585ed33620a18c861 Mon Sep 17 00:00:00 2001 From: stepeos Date: Sat, 22 Oct 2022 12:40:22 +0200 Subject: [PATCH 03/17] minor changes --- coverage/summary.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coverage/summary.py b/coverage/summary.py index 2718bdff6..dab279541 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -87,8 +87,8 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, total_line): "internal method to print report data in markdown format" # Prepare the formatting strings, header, and column sorting. - max_name = max([len(fr.relative_filename().replace("_",r"\_")) for (fr, analysis) in \ - self.fr_analysis] + [5]) + 1 + max_name = max([len(fr.relative_filename().replace("_",r"\_")) for\ + (fr, analysis) in self.fr_analysis] + [5]) + 1 h_form = dict( Name="| {:{name_len}}|", Stmts="{:>7} |", Miss="{:>7} |", Branch="{:>7} |", BrPart="{:>7} |", Cover="{:>7} |", @@ -96,7 +96,7 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, header_items = [ h_form[item].format(item, name_len=max_name) for item in header] header_str = "".join(header_items) - rule_str = "|" + " ".join(["- |".rjust(len(header_items[0])-1, '-')] + + rule_str = "|" + " ".join(["- |".rjust(len(header_items[0])-1, '-')] + ["-: |".rjust(len(item)-1, '-') for item in header_items[1:]]) # Write the header From 286dd66e0de26c3c7d1189983c8a2beb15f77da7 Mon Sep 17 00:00:00 2001 From: stepeos Date: Sat, 22 Oct 2022 14:10:46 +0200 Subject: [PATCH 04/17] fixed text output --- coverage/summary.py | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/coverage/summary.py b/coverage/summary.py index dab279541..7d5411c45 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -36,10 +36,13 @@ def _report_text(self, header, lines_values, sort_option, reverse, # Prepare the formatting strings, header, and column sorting. max_name = max([len(fr.relative_filename()) for (fr, analysis) in \ self.fr_analysis] + [5]) + 2 - - header_row_format ="{:{name_len}}" + "{:>7}" * (len(header)- 2) + " " - header_row_format += "{:>9}" if self.config.show_missing else "{:>7}" - header_str = header_row_format.format(*header, name_len=max_name) + h_form = dict( + Name="{:{name_len}}", Stmts="{:>7}", Miss="{:>7}", + Branch="{:>7}", BrPart="{:>7}", Cover="{:>7}", + Missing="{:>9}") + header_items = [ + h_form[item].format(item, name_len=max_name) for item in header] + header_str = "".join(header_items) rule = "-" * len(header_str) # Write the header @@ -53,14 +56,15 @@ def _report_text(self, header, lines_values, sort_option, reverse, # `lines` is a list of pairs, (line text, line values). The line text # is a string that will be printed, and line values is a tuple of # sortable values. - line_row_format = "{:{name_len}}" + "{:>7}" * (len(header)- 3) - line_row_format += "{:>6}% " - line_row_format += " {}" if self.config.show_missing else "{:>7}" lines = [] for values in lines_values: # build string with line values - text = line_row_format.format(*values, name_len=max_name) + h_form.update(dict(Cover="{:>6}%"), Missing=" {:9}") + line_items = [ + h_form[item].format(str(value), + name_len=max_name) for item, value in zip(header, values)] + text = "".join(line_items) lines.append((text, values)) # Sort the lines and write them out. @@ -78,8 +82,11 @@ def _report_text(self, header, lines_values, sort_option, reverse, # Write a TOTAL line if we had at least one file. if self.total.n_files > 0: self.writeout(rule) - self.writeout( - line_row_format.format(*total_line, name_len=max_name)) + line_items = [ + h_form[item].format(str(value), + name_len=max_name) for item, value in zip(header, total_line)] + text = "".join(line_items) + self.writeout(text) return self.total.n_statements and self.total.pc_covered @@ -110,13 +117,6 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, # `lines` is a list of pairs, (line text, line values). The line text # is a string that will be printed, and line values is a tuple of # sortable values. - - if self.config.show_missing: - line_row_format = "| {:{name_len}}|" + "{:>7} |" * (len(header)-3) - else: - line_row_format = "| {:{name_len}}|" + "{:>7} |" * (len(header)-2) - line_row_format += "{:>7}% |" - line_row_format += " {:<9} |" if self.config.show_missing else "" lines = [] for values in lines_values: @@ -126,7 +126,6 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, h_form[item].format(str(value).replace("_", r"\_"), name_len=max_name) for item, value in zip(header, values)] text = "".join(line_items) - # text = line_row_format.format(*values, name_len=max_name) lines.append((text, values)) # Sort the lines and write them out. From 8746883c984023d437f9bf1ae07d95da298112c6 Mon Sep 17 00:00:00 2001 From: stepeos Date: Sat, 22 Oct 2022 14:47:48 +0200 Subject: [PATCH 05/17] fixed precision for text and markdown report format --- coverage/summary.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/coverage/summary.py b/coverage/summary.py index 7d5411c45..5ead724e6 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -36,12 +36,15 @@ def _report_text(self, header, lines_values, sort_option, reverse, # Prepare the formatting strings, header, and column sorting. max_name = max([len(fr.relative_filename()) for (fr, analysis) in \ self.fr_analysis] + [5]) + 2 + n = self.config.precision + max_n = max(n+6, 7) h_form = dict( Name="{:{name_len}}", Stmts="{:>7}", Miss="{:>7}", - Branch="{:>7}", BrPart="{:>7}", Cover="{:>7}", + Branch="{:>7}", BrPart="{:>7}", Cover="{:>{n}}", Missing="{:>9}") header_items = [ - h_form[item].format(item, name_len=max_name) for item in header] + h_form[item].format(item, name_len=max_name, n=max_n) + for item in header] header_str = "".join(header_items) rule = "-" * len(header_str) @@ -60,10 +63,10 @@ def _report_text(self, header, lines_values, sort_option, reverse, for values in lines_values: # build string with line values - h_form.update(dict(Cover="{:>6}%"), Missing=" {:9}") + h_form.update(dict(Cover="{:>{n}}%"), Missing=" {:9}") line_items = [ h_form[item].format(str(value), - name_len=max_name) for item, value in zip(header, values)] + name_len=max_name, n=max_n-1) for item, value in zip(header, values)] text = "".join(line_items) lines.append((text, values)) @@ -84,7 +87,7 @@ def _report_text(self, header, lines_values, sort_option, reverse, self.writeout(rule) line_items = [ h_form[item].format(str(value), - name_len=max_name) for item, value in zip(header, total_line)] + name_len=max_name, n=max_n) for item, value in zip(header, total_line)] text = "".join(line_items) self.writeout(text) @@ -98,10 +101,12 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, (fr, analysis) in self.fr_analysis] + [5]) + 1 h_form = dict( Name="| {:{name_len}}|", Stmts="{:>7} |", Miss="{:>7} |", - Branch="{:>7} |", BrPart="{:>7} |", Cover="{:>7} |", + Branch="{:>7} |", BrPart="{:>7} |", Cover="{:>{n}} |", Missing="{:>9} |") + n = self.config.precision + max_n = max(n+6, 7) header_items = [ - h_form[item].format(item, name_len=max_name) for item in header] + h_form[item].format(item, name_len=max_name, n=max_n) for item in header] header_str = "".join(header_items) rule_str = "|" + " ".join(["- |".rjust(len(header_items[0])-1, '-')] + ["-: |".rjust(len(item)-1, '-') for item in header_items[1:]]) @@ -121,10 +126,10 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, for values in lines_values: # build string with line values - h_form.update(dict(Cover="{:>6}% |")) + h_form.update(dict(Cover="{:>{n}}% |")) line_items = [ h_form[item].format(str(value).replace("_", r"\_"), - name_len=max_name) for item, value in zip(header, values)] + name_len=max_name, n=max_n-1) for item, value in zip(header, values)] text = "".join(line_items) lines.append((text, values)) @@ -143,8 +148,8 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, # Write a TOTAL line if we had at least one file. if self.total.n_files > 0: total_form = dict( - Name="| {:>{name_len_1}}** |", Stmts="{:>5}** |", Miss="{:>5}** |", - Branch="{:>5}** |", BrPart="{:>5}** |", Cover="{:>4}%** |", + Name="| {:>{name_len}}** |", Stmts="{:>5}** |", Miss="{:>5}** |", + Branch="{:>5}** |", BrPart="{:>5}** |", Cover="{:>{n}}%** |", Missing="{:>9} |") total_line_items = [] for item, value in zip(header, total_line): @@ -154,10 +159,10 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, else: insert = "**" + value + "**" total_line_items += total_form[item].format(\ - insert, name_len_1=max_name-3) + insert, name_len=max_name-3) else: total_line_items += total_form[item].format(\ - "**"+str(value), name_len_1=max_name-3) + "**"+str(value), name_len=max_name-3, n=max_n-4) total_row_str = "".join(total_line_items) self.writeout(total_row_str) From b41de9be3daed20bfbf6c5e6b14145f070ded509 Mon Sep 17 00:00:00 2001 From: stepeos Date: Sat, 22 Oct 2022 14:49:38 +0200 Subject: [PATCH 06/17] minor changes --- coverage/summary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coverage/summary.py b/coverage/summary.py index 5ead724e6..6071002df 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -104,7 +104,7 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, Branch="{:>7} |", BrPart="{:>7} |", Cover="{:>{n}} |", Missing="{:>9} |") n = self.config.precision - max_n = max(n+6, 7) + max_n = max(n+6, 7) + 4 header_items = [ h_form[item].format(item, name_len=max_name, n=max_n) for item in header] header_str = "".join(header_items) @@ -162,7 +162,7 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, insert, name_len=max_name-3) else: total_line_items += total_form[item].format(\ - "**"+str(value), name_len=max_name-3, n=max_n-4) + "**"+str(value), name_len=max_name-3, n=max_n-3) total_row_str = "".join(total_line_items) self.writeout(total_row_str) From b3179db61fcf593fa41ce3f862f0af39d3383f7a Mon Sep 17 00:00:00 2001 From: stepeos Date: Sun, 23 Oct 2022 21:27:03 +0200 Subject: [PATCH 07/17] finished testing for markdown format feature --- coverage/summary.py | 10 +- tests/test_summary.py | 252 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 239 insertions(+), 23 deletions(-) diff --git a/coverage/summary.py b/coverage/summary.py index 6071002df..8d40880a6 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -61,9 +61,9 @@ def _report_text(self, header, lines_values, sort_option, reverse, # sortable values. lines = [] + h_form.update(dict(Cover="{:>{n}}%"), Missing=" {:9}") for values in lines_values: # build string with line values - h_form.update(dict(Cover="{:>{n}}%"), Missing=" {:9}") line_items = [ h_form[item].format(str(value), name_len=max_name, n=max_n-1) for item, value in zip(header, values)] @@ -87,7 +87,7 @@ def _report_text(self, header, lines_values, sort_option, reverse, self.writeout(rule) line_items = [ h_form[item].format(str(value), - name_len=max_name, n=max_n) for item, value in zip(header, total_line)] + name_len=max_name, n=max_n-1) for item, value in zip(header, total_line)] text = "".join(line_items) self.writeout(text) @@ -97,8 +97,8 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, total_line): "internal method to print report data in markdown format" # Prepare the formatting strings, header, and column sorting. - max_name = max([len(fr.relative_filename().replace("_",r"\_")) for\ - (fr, analysis) in self.fr_analysis] + [5]) + 1 + max_name = max([len(fr.relative_filename().replace("_","\\_")) for\ + (fr, analysis) in self.fr_analysis] + [9]) + 1 h_form = dict( Name="| {:{name_len}}|", Stmts="{:>7} |", Miss="{:>7} |", Branch="{:>7} |", BrPart="{:>7} |", Cover="{:>{n}} |", @@ -128,7 +128,7 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, # build string with line values h_form.update(dict(Cover="{:>{n}}% |")) line_items = [ - h_form[item].format(str(value).replace("_", r"\_"), + h_form[item].format(str(value).replace("_", "\\_"), name_len=max_name, n=max_n-1) for item, value in zip(header, values)] text = "".join(line_items) lines.append((text, values)) diff --git a/tests/test_summary.py b/tests/test_summary.py index d603062be..71bae90bd 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -57,6 +57,21 @@ def test_report(self): assert "/tests/zipmods.zip/covmodzip1.py " in report assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 8 0 100%" + report = self.get_report(cov, format_text="markdown", squeeze=False) + print(report) + # | Name | Stmts | Miss | Cover | + # |---------------------------------------- | -----: | -----: | ---------: | + # | foo/bar/tests/modules/covmod1.py | 2 | 0 | 100% | + # | foo/bar/tests/zipmods.zip/covmodzip1.py | 2 | 0 | 100% | + # | mycode.py | 4 | 0 | 100% | + # | **TOTAL** | **8** | **0** | **100%** | + assert "/coverage//_/_init/_/_/" not in report + assert "/tests/modules/covmod1.py " in report + assert "/tests/zipmods.zip/covmodzip1.py " in report + assert "mycode.py " in report + assert report.split("\n")[5] == "| "\ + " **TOTAL** | **8** |"\ + " **0** | **100%** |" def test_report_just_one(self): # Try reporting just one module @@ -70,7 +85,6 @@ def test_report_just_one(self): # mycode.py 4 0 100% # ------------------------------- # TOTAL 4 0 100% - assert self.line_count(report) == 5 assert "/coverage/" not in report assert "/tests/modules/covmod1.py " not in report @@ -78,6 +92,22 @@ def test_report_just_one(self): assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" + report = self.get_report(cov, squeeze=False, format_text="markdown", + morfs=["mycode.py"]) + print(report) + # | Name | Stmts | Miss | Cover | + # |---------- | -----: | -----: | ---------: | + # | mycode.py | 4 | 0 | 100% | + # | **TOTAL** | **4** | **0** | **100%** | + + assert self.line_count(report) == 4 + assert "/coverage/" not in report + assert "/tests/modules/covmod1.py " not in report + assert "/tests/zipmods.zip/covmodzip1.py " not in report + assert "mycode.py " in report + assert report.split("\n")[3] == "| **TOTAL** | **4** | **0** |"\ + " **100%** |" + def test_report_wildcard(self): # Try reporting using wildcards to get the modules. self.make_mycode() @@ -97,6 +127,19 @@ def test_report_wildcard(self): assert "/tests/zipmods.zip/covmodzip1.py " not in report assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" + report = self.report_from_command("coverage report --format=markdown"\ + " my*.py") + # | Name | Stmts | Miss | Cover | + # |---------- | -----: | -----: | ---------: | + # | mycode.py | 4 | 0 | 100% | + # | **TOTAL** | **4** | **0** | **100%** | + assert self.line_count(report) == 4 + assert "/coverage/" not in report + assert "/tests/modules/covmod1.py " not in report + assert "/tests/zipmods.zip/covmodzip1.py " not in report + assert "mycode.py " in report + assert report.split("\n")[3] == "| **TOTAL** | **4** | **0** | "\ + " **100%** |" def test_report_omitting(self): # Try reporting while omitting some modules @@ -118,6 +161,19 @@ def test_report_omitting(self): assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" + report = self.get_report(cov, squeeze=False, format_text="markdown", + omit=[f"{TESTS_DIR}/*", "*/site-packages/*"]) + # | Name | Stmts | Miss | Cover | + # |---------- | -----: | -----: | ---------: | + # | mycode.py | 4 | 0 | 100% | + # | **TOTAL** | **4** | **0** | **100%** | + assert self.line_count(report) == 4 + assert "/coverage/" not in report + assert "/tests/modules/covmod1.py " not in report + assert "/tests/zipmods.zip/covmodzip1.py " not in report + assert "mycode.py " in report + assert report.split("\n")[3] == "| **TOTAL** | **4** | **0** | **100%** |" + def test_report_including(self): # Try reporting while including some modules self.make_mycode() @@ -138,6 +194,17 @@ def test_report_including(self): assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" + report = self.get_report(cov, include=["mycode*"], + format_text="markdown", squeeze=False) + print(report) + assert self.line_count(report) == 4 + assert "/coverage/" not in report + assert "/tests/modules/covmod1.py " not in report + assert "/tests/zipmods.zip/covmodzip1.py " not in report + assert "mycode.py " in report + assert report.split("\n")[3] == "| **TOTAL** | **4** | **0** | "\ + "**100%** |" + def test_run_source_vs_report_include(self): # https://github.com/nedbat/coveragepy/issues/621 self.make_file(".coveragerc", """\ @@ -195,6 +262,16 @@ def branch(x): assert self.line_count(report) == 5 assert "mybranch.py " in report assert self.last_line_squeezed(report) == "TOTAL 5 0 2 1 86%" + report = self.get_report(cov, squeeze=False, format_text="markdown") + # | Name | Stmts | Miss | Branch | BrPart | Cover | + # |------------ | -----: | -----: | -----: | -----: | ---------: | + # | mybranch.py | 5 | 0 | 2 | 1 | 86% | + # | **TOTAL** | **5** | **0** | **2** | **1** | **86%** | + + assert self.line_count(report) == 4 + assert "mybranch.py " in report + assert report.split("\n")[3] == "| **TOTAL** | **5** | **0** |"\ + " **2** | **1** | **86%** |" def test_report_show_missing(self): self.make_file("mymissing.py", """\ @@ -228,6 +305,19 @@ def missing(x, y): squeezed = self.squeezed_lines(report) assert squeezed[2] == "mymissing.py 14 3 79% 3-4, 10" assert squeezed[4] == "TOTAL 14 3 79%" + report = self.get_report(cov, show_missing=True, squeeze=False, + format_text="markdown") + # | Name | Stmts | Miss | Cover | Missing | + # |------------- | -----: | -----: | ---------: | -------: | + # | mymissing.py | 14 | 3 | 79% | 3-4, 10 | + # | **TOTAL** | **14** | **3** | **79%** | | + + + assert self.line_count(report) == 4 + assert report.split("\n")[2] == "| mymissing.py | 14 | 3 | "\ + "79% | 3-4, 10 |" + assert report.split("\n")[3] == "| **TOTAL** | **14** | **3** |"\ + " **79%** | |" def test_report_show_missing_branches(self): self.make_file("mybranch.py", """\ @@ -253,6 +343,13 @@ def branch(x, y): squeezed = self.squeezed_lines(report) assert squeezed[2] == "mybranch.py 6 0 4 2 80% 2->4, 4->exit" assert squeezed[4] == "TOTAL 6 0 4 2 80%" + report = self.get_report(cov, show_missing=True, squeeze=False, + format_text="markdown") + assert self.line_count(report) == 4 + assert report.split("\n")[2] == "| mybranch.py | 6 | 0 |"\ + " 4 | 2 | 80% |2->4, 4->exit |" + assert report.split("\n")[3] == "| **TOTAL** | **6** | **0** |"\ + " **4** | **2** | **80%** | |" def test_report_show_missing_branches_and_lines(self): self.make_file("main.py", """\ @@ -274,17 +371,26 @@ def branch(x, y, z): self.start_import_stop(cov, "main") assert self.stdout() == 'x\ny\n' report_lines = self.get_report(cov, squeeze=False, show_missing=True).splitlines() - expected = [ - 'Name Stmts Miss Branch BrPart Cover Missing', + 'Name Stmts Miss Branch BrPart Cover Missing', '---------------------------------------------------------', - 'main.py 1 0 0 0 100%', - 'mybranch.py 10 2 8 3 61% 2->4, 4->6, 7-8', + 'main.py 1 0 0 0 100%', + 'mybranch.py 10 2 8 3 61% 2->4, 4->6, 7-8', '---------------------------------------------------------', - 'TOTAL 11 2 8 3 63%', + 'TOTAL 11 2 8 3 63%', ] assert expected == report_lines + report = self.get_report(cov, squeeze=False, show_missing=True, + format_text="markdown") + assert report.split("\n")[2] == "| main.py | 1 | 0 |"\ + " 0 | 0 | 100% | |" + assert report.split("\n")[3] == "| mybranch.py | 10 | 2 |"\ + " 8 | 3 | 61% |2->4, 4->6, 7-8 |" + assert report.split("\n")[4] == "| **TOTAL** | **11** | **2** |"\ + " **8** | **3** | **63%** | |" + + def test_report_skip_covered_no_branches(self): self.make_file("main.py", """ import not_covered @@ -317,6 +423,22 @@ def not_covered(): assert squeezed[6] == "1 file skipped due to complete coverage." assert self.last_command_status == 0 + report = self.report_from_command( + "coverage report --skip-covered --fail-under=70 --format=markdown") + # | Name | Stmts | Miss | Cover | + # |---------------- | -----: | -----: | ---------: | + # | not\_covered.py | 2 | 1 | 50% | + # | **TOTAL** | **6** | **1** | **83%** | + + # 1 file skipped due to complete coverage. + assert self.line_count(report) == 6, report + assert report.split("\n")[2] == "| not/_covered.py | 2 | 1"\ + " | 50% |" + assert report.split("\n")[3] == "| **TOTAL** | **6** | **1**"\ + " | **83%** |" + assert report.split("\n")[5] == "1 file skipped due to complete coverage." + assert self.last_command_status == 0 + def test_report_skip_covered_branches(self): self.make_file("main.py", """ import not_covered, covered @@ -356,6 +478,20 @@ def foo(): assert squeezed[2] == "not_covered.py 4 0 2 1 83%" assert squeezed[4] == "TOTAL 13 0 4 1 94%" assert squeezed[6] == "2 files skipped due to complete coverage." + report = self.get_report(cov, skip_covered=True, squeeze=False, + format_text="markdown") + # | Name | Stmts | Miss | Branch | BrPart | Cover | + # |---------------- | -----: | -----: | -----: | -----: | ---------: | + # | not/_covered.py | 4 | 0 | 2 | 1 | 83% | + # | **TOTAL** | **13** | **0** | **4** | **1** | **94%** | + + # 2 files skipped due to complete coverage. + assert self.line_count(report) == 6, report + assert report.split("\n")[2] == "| not/_covered.py | 4 | "\ + "0 | 2 | 1 | 83% |" + assert report.split("\n")[3] == "| **TOTAL** | **13** | **0** "\ + "| **4** | **1** | **94%** |" + assert report.split("\n")[5] == "2 files skipped due to complete coverage." def test_report_skip_covered_branches_with_totals(self): self.make_file("main.py", """ @@ -399,6 +535,24 @@ def does_not_appear_in_this_film(ni): assert squeezed[5] == "TOTAL 13 1 4 1 88%" assert squeezed[7] == "1 file skipped due to complete coverage." + report = self.get_report(cov, skip_covered=True, squeeze=False, + format_text="markdown") + # | Name | Stmts | Miss | Branch | BrPart | Cover | + # |------------------ | -----: | -----: | -----: | -----: | ---------: | + # | also/_not/_run.py | 2 | 1 | 0 | 0 | 50% | + # | not/_covered.py | 4 | 0 | 2 | 1 | 83% | + # | **TOTAL** | **13** | **1** | **4** | **1** | **88%** | + assert self.line_count(report) == 7, report + assert report.split("\n")[2] == "| also/_not/_run.py | 2 | "\ + "1 | 0 | 0 | 50% |" + assert report.split("\n")[3] == "| not/_covered.py | 4 | "\ + "0 | 2 | 1 | 83% |" + assert report.split("\n")[4] == "| **TOTAL** | **13** | "\ + "**1** | **4** | **1** | **88%** |" + assert report.split("\n")[6] == "1 file skipped due to complete coverage." + + + def test_report_skip_covered_all_files_covered(self): self.make_file("main.py", """ def foo(): @@ -420,6 +574,12 @@ def foo(): assert self.line_count(report) == 6, report squeezed = self.squeezed_lines(report) assert squeezed[5] == "1 file skipped due to complete coverage." + report = self.get_report(cov, skip_covered=True, + format_text="markdown") + + assert self.line_count(report) == 5, report + squeezed = self.squeezed_lines(report) + assert squeezed[4] == "1 file skipped due to complete coverage." def test_report_skip_covered_longfilename(self): self.make_file("long_______________filename.py", """ @@ -441,7 +601,7 @@ def foo(): assert self.line_count(report) == 6, report lines = self.report_lines(report) - assert lines[0] == "Name Stmts Miss Branch BrPart Cover" + assert lines[0] == "Name Stmts Miss Branch BrPart Cover" squeezed = self.squeezed_lines(report) assert squeezed[5] == "1 file skipped due to complete coverage." @@ -489,13 +649,30 @@ def test_report_skip_empty_no_data(self): # Name Stmts Miss Cover # ------------------------------------ + # ------------------------------------ + # TOTAL 0 0 100% # # 1 empty file skipped. assert self.line_count(report) == 6, report - squeezed = self.squeezed_lines(report) - assert squeezed[3] == "TOTAL 0 0 100%" - assert squeezed[5] == "1 empty file skipped." + assert report.split("\n")[3] == "TOTAL 0 0 100%" + assert report.split("\n")[5] == "1 empty file skipped." + report = self.get_report(cov, squeeze=False, + skip_empty=True, format_text="markdown") + + # | Name | Stmts | Miss | Cover | + # |---------- | -----: | -----: | ---------: | + # | **TOTAL** | **0** | **0** | **100%** | + # + # 1 empty file skipped. + + assert self.line_count(report) == 5, report + assert report.split("\n")[2] == \ + "| **TOTAL** | **0** | **0** | **100%** |" + assert report.split("\n")[4] == "1 empty file skipped." + + + def test_report_precision(self): self.make_file(".coveragerc", """\ @@ -541,6 +718,20 @@ def foo(): assert squeezed[2] == "covered.py 3 0 0 0 100.000%" assert squeezed[4] == "not_covered.py 4 0 2 1 83.333%" assert squeezed[6] == "TOTAL 13 0 4 1 94.118%" + report = self.get_report(cov, squeeze=False, format_text="markdown") + + #| Name | Stmts | Miss | Branch | BrPart | Cover | + #|---------------- | -----: | -----: | -----: | -----: | -----------: | + #| covered.py | 3 | 0 | 0 | 0 | 100.000% | + #| main.py | 6 | 0 | 2 | 0 | 100.000% | + #| not/_covered.py | 4 | 0 | 2 | 1 | 83.333% | + #| **TOTAL** | **13** | **0** | **4** | **1** | **94.118%** | + + assert self.line_count(report) == 6, report + squeezed = self.squeezed_lines(report) + assert squeezed[2] == "| covered.py | 3 | 0 | 0 | 0 | 100.000% |" + assert squeezed[4] == "| not/_covered.py | 4 | 0 | 2 | 1 | 83.333% |" + assert squeezed[5] == "| **TOTAL** | **13** | **0** | **4** | **1** | **94.118%** |" def test_dotpy_not_python(self): # We run a .py file, and when reporting, we can't parse it as Python. @@ -559,17 +750,20 @@ def test_accented_directory(self): self.make_file("\xe2/accented.py", "print('accented')") self.make_data_file(lines={abs_file("\xe2/accented.py"): [1]}) report_expected = ( - "Name Stmts Miss Cover\n" + - "-----------------------------------\n" + - "\xe2/accented.py 1 0 100%\n" + - "-----------------------------------\n" + - "TOTAL 1 0 100%\n" + "Name Stmts Miss Cover\n" + + "------------------------------------\n" + + "\xe2/accented.py 1 0 100%\n" + + "------------------------------------\n" + + "TOTAL 1 0 100%\n" ) - cov = coverage.Coverage() cov.load() output = self.get_report(cov, squeeze=False) assert output == report_expected + output = self.get_report(cov, squeeze=False, format_text="markdown") + markdown_expected = \ + "| \xe2/accented.py | 1 | 0 | 100% |" + assert output.split("\n")[2] == markdown_expected @pytest.mark.skipif(env.JYTHON, reason="Jython doesn't like accented file names") def test_accenteddotpy_not_python(self): @@ -628,6 +822,9 @@ def test_report_no_extension(self): cov.load() report = self.get_report(cov) assert self.last_line_squeezed(report) == "TOTAL 7 1 86%" + report = self.get_report(cov, squeeze=False, format_text="markdown") + assert report.split("\n")[-2] == \ + "| **TOTAL** | **7** | **1** | **86%** |" def test_report_with_chdir(self): self.make_file("chdir.py", """\ @@ -642,6 +839,9 @@ def test_report_with_chdir(self): assert out == "Line One\nLine Two\nhello\n" report = self.report_from_command("coverage report") assert self.last_line_squeezed(report) == "TOTAL 5 0 100%" + report = self.report_from_command("coverage report --format=markdown") + assert report.split("\n")[-2] == \ + "| **TOTAL** | **5** | **0** | **100%** |" def test_bug_156_file_not_run_should_be_zero(self): # https://github.com/nedbat/coveragepy/issues/156 @@ -659,6 +859,11 @@ def branch(x): self.start_import_stop(cov, "main") report = self.get_report(cov).splitlines() assert "mybranch.py 5 5 2 0 0%" in report + report = self.get_report(cov, squeeze=False, format_text="markdown") + assert \ + "| mybranch.py | 5 | 5 | 2 | 0 | 0% |"\ + in report + def run_TheCode_and_report_it(self): """A helper for the next few tests.""" @@ -671,7 +876,6 @@ def test_bug_203_mixed_case_listed_twice_with_rc(self): self.make_file(".coveragerc", "[run]\nsource = .\n") report = self.run_TheCode_and_report_it() - assert "TheCode" in report assert "thecode" not in report @@ -719,6 +923,10 @@ def test_tracing_pyc_file(self): report = self.get_report(cov).splitlines() assert "mod.py 1 0 100%" in report + report = self.get_report(cov, squeeze=False, format_text="markdown") + print(report) + assert report.split("\n")[4] == \ + "| **TOTAL** | **2** | **0** | **100%** |" def test_missing_py_file_during_run(self): # Create two Python files. @@ -745,6 +953,8 @@ def test_missing_py_file_during_run(self): self.make_file("mod.py", "a = 1\n") report = self.get_report(cov).splitlines() assert "mod.py 1 0 100%" in report + report = self.get_report(cov, squeeze=False, format_text="markdown") + assert "| mod.py | 1 | 0 | 100% |" in report def test_empty_files(self): # Shows that empty files like __init__.py are listed as having zero @@ -756,6 +966,13 @@ def test_empty_files(self): report = self.get_report(cov) assert "tests/modules/pkg1/__init__.py 1 0 0 0 100%" in report assert "tests/modules/pkg2/__init__.py 0 0 0 0 100%" in report + report = self.get_report(cov, squeeze=False, format_text="markdown") + # get_report() escapes backslash so we expect forward slash escaped + # underscore + assert "tests/modules/pkg1//_/_init/_/_.py | 1 | "\ + " 0 | 0 | 0 | 100% |" in report + assert "tests/modules/pkg2//_/_init/_/_.py | 0 | "\ + " 0 | 0 | 0 | 100% |" in report class ReportingReturnValueTest(CoverageTest): @@ -834,7 +1051,6 @@ def test_test_data(self): # about them are still valid. We want the three columns of numbers to # sort in three different orders. report = self.get_summary_text() - print(report) # Name Stmts Miss Cover # ------------------------------ # file1.py 339 155 54% From 3f3c6bc02cdaf5f7402ab274f5546026b47d07e2 Mon Sep 17 00:00:00 2001 From: stepeos Date: Sun, 23 Oct 2022 23:32:24 +0200 Subject: [PATCH 08/17] fixed testing outside test_summary.py --- tests/test_api.py | 54 +++++++++++++++++++++++++------------------ tests/test_cmdline.py | 2 +- tests/test_plugins.py | 53 +++++++++++++++++++++--------------------- tests/test_process.py | 11 +++++---- tests/test_summary.py | 1 - 5 files changed, 65 insertions(+), 56 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 375edcec1..847ead7ee 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -590,15 +590,15 @@ def test_source_and_include_dont_conflict(self): cov.load() # There should be no exception. At one point, report() threw: # CoverageException: --include and --source are mutually exclusive - cov.report() + report = self.get_report(cov, squeeze=False) expected = textwrap.dedent("""\ - Name Stmts Miss Cover - --------------------------- - b.py 1 0 100% - --------------------------- - TOTAL 1 0 100% + Name Stmts Miss Cover + ---------------------------- + b.py 1 0 100% + ---------------------------- + TOTAL 1 0 100% """) - assert expected == self.stdout() + assert expected == report def make_test_files(self): """Create a simple file representing a method with two tests. @@ -1054,13 +1054,17 @@ def pretend_to_be_nose_with_cover(self, erase=False, cd=False): os.chdir("sub") cov.combine() cov.save() - cov.report(["no_biggie.py"], show_missing=True) - assert self.stdout() == textwrap.dedent("""\ - Name Stmts Miss Cover Missing - -------------------------------------------- - no_biggie.py 4 1 75% 4 - -------------------------------------------- - TOTAL 4 1 75% + cov.load() + report = self.get_report(cov, squeeze=False) + + print(report) + # cov.report(["no_biggie.py"], show_missing=True) + assert report == textwrap.dedent("""\ + Name Stmts Miss Cover + ----------------------------------- + no_biggie.py 4 1 75% + ----------------------------------- + TOTAL 4 1 75% """) if cd: os.chdir("..") @@ -1097,15 +1101,19 @@ def pretend_to_be_pytestcov(self, append): self.start_import_stop(cov, "prog") cov.combine() cov.save() - report = io.StringIO() - cov.report(show_missing=None, ignore_errors=True, file=report, skip_covered=None, - skip_empty=None) - assert report.getvalue() == textwrap.dedent("""\ - Name Stmts Miss Cover - ----------------------------- - prog.py 4 1 75% - ----------------------------- - TOTAL 4 1 75% + cov.load() + # report = io.StringIO() + report = self.get_report(cov, show_missing=None, ignore_errors=True, + skip_covered=None, skip_empty=None, squeeze=False) + print(report) + # cov.report(show_missing=None, ignore_errors=True, file=report, skip_covered=None, + # skip_empty=None) + assert report == textwrap.dedent("""\ + Name Stmts Miss Cover + ------------------------------ + prog.py 4 1 75% + ------------------------------ + TOTAL 4 1 75% """) self.assert_file_count(".coverage", 0) self.assert_file_count(".coverage.*", 1) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 305fbdbff..1f7336580 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -44,7 +44,7 @@ class BaseCmdLineTest(CoverageTest): _defaults.Coverage().report( ignore_errors=None, include=None, omit=None, morfs=[], show_missing=None, skip_covered=None, contexts=None, skip_empty=None, precision=None, - sort=None, + sort=None, format_text=None ) _defaults.Coverage().xml_report( ignore_errors=None, include=None, omit=None, morfs=[], outfile=None, diff --git a/tests/test_plugins.py b/tests/test_plugins.py index b4239700d..d7f4a90c6 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -7,6 +7,7 @@ import io import math import os.path +import textwrap from xml.etree import ElementTree import pytest @@ -416,19 +417,20 @@ def test_plugin2_with_text_report(self): self.start_import_stop(cov, "caller") - repout = io.StringIO() - total = cov.report(file=repout, include=["*.html"], omit=["uni*.html"], show_missing=True) - report = repout.getvalue().splitlines() - expected = [ - 'Name Stmts Miss Branch BrPart Cover Missing', - '--------------------------------------------------------', - 'bar_4.html 4 2 0 0 50% 1, 4', - 'foo_7.html 7 5 0 0 29% 1-3, 6-7', - '--------------------------------------------------------', - 'TOTAL 11 7 0 0 36%', - ] - assert expected == report - assert math.isclose(total, 4 / 11 * 100) + cov.save() + cov.load() + report = self.get_report(cov, squeeze=False, + include=["*.html"], omit=["uni*.html"], show_missing=True) + # Name Stmts Miss Branch BrPart Cover Missing + # -------------------------------------------------------- + # bar_4.html 4 2 0 0 50% 1, 4 + # foo_7.html 7 5 0 0 29% 1-3, 6-7 + # -------------------------------------------------------- + # TOTAL 11 7 0 0 36% + assert report.split("\n")[2] == \ + "bar_4.html 4 2 0 0 50% 1, 4" + total = int(report.split("\n")[-2].split(" ")[-1][:-1]) + assert 0 == total - int(4/11*100) def test_plugin2_with_html_report(self): self.make_render_and_caller() @@ -515,19 +517,18 @@ def coverage_init(reg, options): cov = coverage.Coverage(include=["unsuspecting.py"]) cov.set_option("run:plugins", ["fairly_odd_plugin"]) self.start_import_stop(cov, "unsuspecting") - - repout = io.StringIO() - total = cov.report(file=repout, show_missing=True) - report = repout.getvalue().splitlines() - expected = [ - 'Name Stmts Miss Cover Missing', - '-----------------------------------------------', - 'unsuspecting.py 6 3 50% 2, 4, 6', - '-----------------------------------------------', - 'TOTAL 6 3 50%', - ] - assert expected == report - assert total == 50 + cov.save() + cov.load() + report = self.get_report(cov, squeeze=False, show_missing=True) + # Name Stmts Miss Cover Missing + # ----------------------------------------------- + # unsuspecting.py 6 3 50% 2, 4, 6 + # ----------------------------------------------- + # TOTAL 6 3 50% + assert report.split("\n")[2] == \ + "unsuspecting.py 6 3 50% 2, 4, 6" + assert report.split("\n")[4] == \ + "TOTAL 6 3 50%" def test_find_unexecuted(self): self.make_file("unexecuted_plugin.py", """\ diff --git a/tests/test_process.py b/tests/test_process.py index b76846e51..8d1dffaa5 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -174,12 +174,13 @@ def test_combine_with_rc(self): # Reporting should still work even with the .rc file out = self.run_command("coverage report") + print(out) assert out == textwrap.dedent("""\ - Name Stmts Miss Cover - ------------------------------- - b_or_c.py 8 0 100% - ------------------------------- - TOTAL 8 0 100% + Name Stmts Miss Cover + -------------------------------- + b_or_c.py 8 0 100% + -------------------------------- + TOTAL 8 0 100% """) def test_combine_with_aliases(self): diff --git a/tests/test_summary.py b/tests/test_summary.py index 71bae90bd..e13e11290 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -58,7 +58,6 @@ def test_report(self): assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 8 0 100%" report = self.get_report(cov, format_text="markdown", squeeze=False) - print(report) # | Name | Stmts | Miss | Cover | # |---------------------------------------- | -----: | -----: | ---------: | # | foo/bar/tests/modules/covmod1.py | 2 | 0 | 100% | From 488d8863c30d83a5fae670716989da4d7a36d87a Mon Sep 17 00:00:00 2001 From: stepeos Date: Mon, 24 Oct 2022 00:39:38 +0200 Subject: [PATCH 09/17] removed fixed-length widespace padding for tests --- tests/test_summary.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_summary.py b/tests/test_summary.py index e13e11290..cc1b23d2a 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -68,9 +68,10 @@ def test_report(self): assert "/tests/modules/covmod1.py " in report assert "/tests/zipmods.zip/covmodzip1.py " in report assert "mycode.py " in report - assert report.split("\n")[5] == "| "\ - " **TOTAL** | **8** |"\ - " **0** | **100%** |" + assert "**TOTAL**" in report.split("\n")[5] + assert "**8**" in report.split("\n")[5] + assert "**0**" in report.split("\n")[5] + assert "**100%**" in report.split("\n")[5] def test_report_just_one(self): # Try reporting just one module @@ -968,10 +969,11 @@ def test_empty_files(self): report = self.get_report(cov, squeeze=False, format_text="markdown") # get_report() escapes backslash so we expect forward slash escaped # underscore - assert "tests/modules/pkg1//_/_init/_/_.py | 1 | "\ - " 0 | 0 | 0 | 100% |" in report - assert "tests/modules/pkg2//_/_init/_/_.py | 0 | "\ - " 0 | 0 | 0 | 100% |" in report + print(report) + assert "tests/modules/pkg1//_/_init/_/_.py " in report + assert "| 1 | 0 | 0 | 0 | 100% |" in report + assert "tests/modules/pkg2//_/_init/_/_.py " in report + assert "| 0 | 0 | 0 | 0 | 100% |" in report class ReportingReturnValueTest(CoverageTest): From 28e690f05924f2b8ad0377965251062e781a5ec9 Mon Sep 17 00:00:00 2001 From: stepeos Date: Mon, 24 Oct 2022 01:05:05 +0200 Subject: [PATCH 10/17] removed whitespaces --- coverage/config.py | 2 +- coverage/summary.py | 2 +- tests/test_summary.py | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/coverage/config.py b/coverage/config.py index a5e6c9c0a..67dfebb47 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -199,7 +199,7 @@ def __init__(self): # Defaults for [report] self.exclude_list = DEFAULT_EXCLUDE[:] self.fail_under = 0.0 - self.format_text = None + self.format_text = None self.ignore_errors = False self.report_include = None self.report_omit = None diff --git a/coverage/summary.py b/coverage/summary.py index 8d40880a6..166a5c29e 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -43,7 +43,7 @@ def _report_text(self, header, lines_values, sort_option, reverse, Branch="{:>7}", BrPart="{:>7}", Cover="{:>{n}}", Missing="{:>9}") header_items = [ - h_form[item].format(item, name_len=max_name, n=max_n) + h_form[item].format(item, name_len=max_name, n=max_n) for item in header] header_str = "".join(header_items) rule = "-" * len(header_str) diff --git a/tests/test_summary.py b/tests/test_summary.py index cc1b23d2a..f8bcda181 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -671,9 +671,6 @@ def test_report_skip_empty_no_data(self): "| **TOTAL** | **0** | **0** | **100%** |" assert report.split("\n")[4] == "1 empty file skipped." - - - def test_report_precision(self): self.make_file(".coveragerc", """\ [report] @@ -926,7 +923,7 @@ def test_tracing_pyc_file(self): report = self.get_report(cov, squeeze=False, format_text="markdown") print(report) assert report.split("\n")[4] == \ - "| **TOTAL** | **2** | **0** | **100%** |" + "| **TOTAL** | **2** | **0** | **100%** |" def test_missing_py_file_during_run(self): # Create two Python files. From 20edcfada16bd07fcc0359d2fca4801dd2a0d3d6 Mon Sep 17 00:00:00 2001 From: stepeos Date: Tue, 25 Oct 2022 21:25:43 +0200 Subject: [PATCH 11/17] refactoring, fixing docs, rewriting cmd args --- coverage/cmdline.py | 12 ++--- coverage/config.py | 4 +- coverage/control.py | 6 +-- coverage/summary.py | 101 ++++++++++++++++-------------------------- doc/cmd.rst | 3 +- tests/test_cmdline.py | 2 +- tests/test_summary.py | 40 ++++++++--------- 7 files changed, 72 insertions(+), 96 deletions(-) diff --git a/coverage/cmdline.py b/coverage/cmdline.py index c3518111c..89b0807d8 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -96,9 +96,9 @@ class Opts: '', '--fail-under', action='store', metavar="MIN", type="float", help="Exit with a status of 2 if the total coverage is less than MIN.", ) - format_text = optparse.make_option( - '', '--format-text', action='store', metavar="text,markdown", - help="Deaults to 'text', will print markdown syntax otherwise", + output_format = optparse.make_option( + '', '--format', action='store', metavar="FORMAT", dest="output_format", + help="Output format, either text (default) or markdown", ) help = optparse.make_option( '-h', '--help', action='store_true', @@ -249,7 +249,7 @@ def __init__(self, *args, **kwargs): debug=None, directory=None, fail_under=None, - format_text=None, + output_format=None, help=None, ignore_errors=None, include=None, @@ -487,7 +487,7 @@ def get_prog_name(self): Opts.contexts, Opts.input_datafile, Opts.fail_under, - Opts.format_text, + Opts.output_format, Opts.ignore_errors, Opts.include, Opts.omit, @@ -695,7 +695,7 @@ def command_line(self, argv): skip_covered=options.skip_covered, skip_empty=options.skip_empty, sort=options.sort, - format_text=options.format_text, + output_format=options.output_format, **report_args ) elif options.action == "annotate": diff --git a/coverage/config.py b/coverage/config.py index 67dfebb47..ab2581567 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -199,7 +199,7 @@ def __init__(self): # Defaults for [report] self.exclude_list = DEFAULT_EXCLUDE[:] self.fail_under = 0.0 - self.format_text = None + self.output_format = None self.ignore_errors = False self.report_include = None self.report_omit = None @@ -375,7 +375,7 @@ def copy(self): # [report] ('exclude_list', 'report:exclude_lines', 'regexlist'), ('fail_under', 'report:fail_under', 'float'), - ('format_text', 'report:format_text', 'boolean'), + ('output_format', 'report:output_format', 'boolean'), ('ignore_errors', 'report:ignore_errors', 'boolean'), ('partial_always_list', 'report:partial_branches_always', 'regexlist'), ('partial_list', 'report:partial_branches', 'regexlist'), diff --git a/coverage/control.py b/coverage/control.py index cd1322482..c154dfd3b 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -909,7 +909,7 @@ def report( self, morfs=None, show_missing=None, ignore_errors=None, file=None, omit=None, include=None, skip_covered=None, contexts=None, skip_empty=None, precision=None, sort=None, - format_text=None + output_format=None ): """Write a textual summary report to `file`. @@ -923,7 +923,7 @@ def report( `file` is a file-like object, suitable for writing. - `format_text` provides options, to print eitehr as plain text, or as + `output_format` provides options, to print eitehr as plain text, or as markdown code `include` is a list of file name patterns. Files that match will be @@ -966,7 +966,7 @@ def report( ignore_errors=ignore_errors, report_omit=omit, report_include=include, show_missing=show_missing, skip_covered=skip_covered, report_contexts=contexts, skip_empty=skip_empty, precision=precision, - sort=sort, format_text=format_text + sort=sort, output_format=output_format ): reporter = SummaryReporter(self) return reporter.report(morfs, outfile=file) diff --git a/coverage/summary.py b/coverage/summary.py index 166a5c29e..fba4eb972 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -6,7 +6,7 @@ import sys from coverage.exceptions import ConfigError, NoDataError -from coverage.misc import human_sorted_items +from coverage.misc import human_key from coverage.report import get_analysis_to_report from coverage.results import Numbers @@ -30,8 +30,7 @@ def writeout(self, line): self.outfile.write(line.rstrip()) self.outfile.write("\n") - def _report_text(self, header, lines_values, sort_option, reverse, - total_line): + def _report_text(self, header, lines_values, total_line, end_lines): "internal method to print report data in text format" # Prepare the formatting strings, header, and column sorting. max_name = max([len(fr.relative_filename()) for (fr, analysis) in \ @@ -56,11 +55,6 @@ def _report_text(self, header, lines_values, sort_option, reverse, if self.branches: column_order.update(dict(branch=3, brpart=4)) - # `lines` is a list of pairs, (line text, line values). The line text - # is a string that will be printed, and line values is a tuple of - # sortable values. - lines = [] - h_form.update(dict(Cover="{:>{n}}%"), Missing=" {:9}") for values in lines_values: # build string with line values @@ -68,22 +62,10 @@ def _report_text(self, header, lines_values, sort_option, reverse, h_form[item].format(str(value), name_len=max_name, n=max_n-1) for item, value in zip(header, values)] text = "".join(line_items) - lines.append((text, values)) - - # Sort the lines and write them out. - if sort_option == "name": - lines = human_sorted_items(lines, reverse=reverse) - else: - position = column_order.get(sort_option) - if position is None: - raise ConfigError(f"Invalid sorting option: {self.config.sort!r}") - lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse) - - for line in lines: - self.writeout(line[0]) + self.writeout(text) - # Write a TOTAL line if we had at least one file. - if self.total.n_files > 0: + # Write a TOTAL line + if total_line: self.writeout(rule) line_items = [ h_form[item].format(str(value), @@ -91,10 +73,11 @@ def _report_text(self, header, lines_values, sort_option, reverse, text = "".join(line_items) self.writeout(text) + for end_line in end_lines: + self.writeout(end_line) return self.total.n_statements and self.total.pc_covered - def _report_markdown(self, header, lines_values, sort_option, reverse, - total_line): + def _report_markdown(self, header, lines_values, total_line, end_lines): "internal method to print report data in markdown format" # Prepare the formatting strings, header, and column sorting. max_name = max([len(fr.relative_filename().replace("_","\\_")) for\ @@ -119,11 +102,6 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, if self.branches: column_order.update(dict(branch=3, brpart=4)) - # `lines` is a list of pairs, (line text, line values). The line text - # is a string that will be printed, and line values is a tuple of - # sortable values. - lines = [] - for values in lines_values: # build string with line values h_form.update(dict(Cover="{:>{n}}% |")) @@ -131,22 +109,10 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, h_form[item].format(str(value).replace("_", "\\_"), name_len=max_name, n=max_n-1) for item, value in zip(header, values)] text = "".join(line_items) - lines.append((text, values)) - - # Sort the lines and write them out. - if sort_option == "name": - lines = human_sorted_items(lines, reverse=reverse) - else: - position = column_order.get(sort_option) - if position is None: - raise ConfigError(f"Invalid sorting option: {self.config.sort!r}") - lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse) - - for line in lines: - self.writeout(line[0]) + self.writeout(text) - # Write a TOTAL line if we had at least one file. - if self.total.n_files > 0: + # Write the TOTAL line + if total_line: total_form = dict( Name="| {:>{name_len}}** |", Stmts="{:>5}** |", Miss="{:>5}** |", Branch="{:>5}** |", BrPart="{:>5}** |", Cover="{:>{n}}%** |", @@ -165,7 +131,8 @@ def _report_markdown(self, header, lines_values, sort_option, reverse, "**"+str(value), name_len=max_name-3, n=max_n-3) total_row_str = "".join(total_line_items) self.writeout(total_row_str) - + for end_line in end_lines: + self.writeout(end_line) return self.total.n_statements and self.total.pc_covered @@ -217,6 +184,15 @@ def report(self, morfs, outfile=None): sort_option = sort_option[1:] elif sort_option[0] == '+': sort_option = sort_option[1:] + sort_idx = column_order.get(sort_option) + if sort_idx is None: + raise ConfigError(f"Invalid sorting option: {self.config.sort!r}") + if sort_option == "name": + lines_values.sort(key=lambda tup: (human_key(tup[0]), tup[1]), + reverse=reverse) + else: + lines_values.sort(key=lambda tup: (tup[sort_idx], tup[0]), + reverse=reverse) # calculate total if we had at least one file. total_line = () @@ -228,28 +204,27 @@ def report(self, morfs, outfile=None): if self.config.show_missing: total_line += ("",) - text_format = self.config.format_text or 'text' - if text_format.lower() == 'markdown': - self._report_markdown(header, lines_values, sort_option, reverse, - total_line) - else: - self._report_text(header, lines_values, sort_option, reverse, - total_line) - - # Write other final lines. + # create other final lines + end_lines = [] if not self.total.n_files and not self.skipped_count: raise NoDataError("No data to report.") if self.config.skip_covered and self.skipped_count: - fmt_skip_covered = "\n%s file%s skipped due to complete coverage." - self.writeout( - fmt_skip_covered % (self.skipped_count, 's' if self.skipped_count > 1 else '') - ) + file_suffix = 's' if self.skipped_count>1 else '' + fmt_skip_covered = f"\n{self.skipped_count} file{file_suffix} "\ + "skipped due to complete coverage." + end_lines.append(fmt_skip_covered) if self.config.skip_empty and self.empty_count: - fmt_skip_empty = "\n%s empty file%s skipped." - self.writeout( - fmt_skip_empty % (self.empty_count, 's' if self.empty_count > 1 else '') - ) + file_suffix = 's' if self.empty_count>1 else '' + fmt_skip_empty = \ + f"\n{self.empty_count} empty file{file_suffix} skipped." + end_lines.append(fmt_skip_empty) + + text_format = self.config.output_format or 'text' + if text_format.lower() == 'markdown': + self._report_markdown(header, lines_values, total_line, end_lines) + else: + self._report_text(header, lines_values, total_line, end_lines) return self.total.n_statements and self.total.pc_covered diff --git a/doc/cmd.rst b/doc/cmd.rst index cb9a147ee..6f811dca3 100644 --- a/doc/cmd.rst +++ b/doc/cmd.rst @@ -512,6 +512,7 @@ as a percentage. file. Defaults to '.coverage'. [env: COVERAGE_FILE] --fail-under=MIN Exit with a status of 2 if the total coverage is less than MIN. + --format=FORMAT Output format, either text (default) or markdown -i, --ignore-errors Ignore errors while reading source files. --include=PAT1,PAT2,... Include only files whose paths match one of these @@ -534,7 +535,7 @@ as a percentage. --rcfile=RCFILE Specify configuration file. By default '.coveragerc', 'setup.cfg', 'tox.ini', and 'pyproject.toml' are tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (checksum: 2f8dde61bab2f44fbfe837aeae87dfd2) +.. [[[end]]] (checksum: 8c671de502a388159689082d906f786a) The ``-m`` flag also shows the line numbers of missing statements:: diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 1f7336580..d5d63d882 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -44,7 +44,7 @@ class BaseCmdLineTest(CoverageTest): _defaults.Coverage().report( ignore_errors=None, include=None, omit=None, morfs=[], show_missing=None, skip_covered=None, contexts=None, skip_empty=None, precision=None, - sort=None, format_text=None + sort=None, output_format=None ) _defaults.Coverage().xml_report( ignore_errors=None, include=None, omit=None, morfs=[], outfile=None, diff --git a/tests/test_summary.py b/tests/test_summary.py index f8bcda181..3da1009e1 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -57,7 +57,7 @@ def test_report(self): assert "/tests/zipmods.zip/covmodzip1.py " in report assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 8 0 100%" - report = self.get_report(cov, format_text="markdown", squeeze=False) + report = self.get_report(cov, output_format="markdown", squeeze=False) # | Name | Stmts | Miss | Cover | # |---------------------------------------- | -----: | -----: | ---------: | # | foo/bar/tests/modules/covmod1.py | 2 | 0 | 100% | @@ -92,7 +92,7 @@ def test_report_just_one(self): assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" - report = self.get_report(cov, squeeze=False, format_text="markdown", + report = self.get_report(cov, squeeze=False, output_format="markdown", morfs=["mycode.py"]) print(report) # | Name | Stmts | Miss | Cover | @@ -161,7 +161,7 @@ def test_report_omitting(self): assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" - report = self.get_report(cov, squeeze=False, format_text="markdown", + report = self.get_report(cov, squeeze=False, output_format="markdown", omit=[f"{TESTS_DIR}/*", "*/site-packages/*"]) # | Name | Stmts | Miss | Cover | # |---------- | -----: | -----: | ---------: | @@ -195,7 +195,7 @@ def test_report_including(self): assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" report = self.get_report(cov, include=["mycode*"], - format_text="markdown", squeeze=False) + output_format="markdown", squeeze=False) print(report) assert self.line_count(report) == 4 assert "/coverage/" not in report @@ -262,7 +262,7 @@ def branch(x): assert self.line_count(report) == 5 assert "mybranch.py " in report assert self.last_line_squeezed(report) == "TOTAL 5 0 2 1 86%" - report = self.get_report(cov, squeeze=False, format_text="markdown") + report = self.get_report(cov, squeeze=False, output_format="markdown") # | Name | Stmts | Miss | Branch | BrPart | Cover | # |------------ | -----: | -----: | -----: | -----: | ---------: | # | mybranch.py | 5 | 0 | 2 | 1 | 86% | @@ -306,7 +306,7 @@ def missing(x, y): assert squeezed[2] == "mymissing.py 14 3 79% 3-4, 10" assert squeezed[4] == "TOTAL 14 3 79%" report = self.get_report(cov, show_missing=True, squeeze=False, - format_text="markdown") + output_format="markdown") # | Name | Stmts | Miss | Cover | Missing | # |------------- | -----: | -----: | ---------: | -------: | # | mymissing.py | 14 | 3 | 79% | 3-4, 10 | @@ -344,7 +344,7 @@ def branch(x, y): assert squeezed[2] == "mybranch.py 6 0 4 2 80% 2->4, 4->exit" assert squeezed[4] == "TOTAL 6 0 4 2 80%" report = self.get_report(cov, show_missing=True, squeeze=False, - format_text="markdown") + output_format="markdown") assert self.line_count(report) == 4 assert report.split("\n")[2] == "| mybranch.py | 6 | 0 |"\ " 4 | 2 | 80% |2->4, 4->exit |" @@ -382,7 +382,7 @@ def branch(x, y, z): assert expected == report_lines report = self.get_report(cov, squeeze=False, show_missing=True, - format_text="markdown") + output_format="markdown") assert report.split("\n")[2] == "| main.py | 1 | 0 |"\ " 0 | 0 | 100% | |" assert report.split("\n")[3] == "| mybranch.py | 10 | 2 |"\ @@ -479,7 +479,7 @@ def foo(): assert squeezed[4] == "TOTAL 13 0 4 1 94%" assert squeezed[6] == "2 files skipped due to complete coverage." report = self.get_report(cov, skip_covered=True, squeeze=False, - format_text="markdown") + output_format="markdown") # | Name | Stmts | Miss | Branch | BrPart | Cover | # |---------------- | -----: | -----: | -----: | -----: | ---------: | # | not/_covered.py | 4 | 0 | 2 | 1 | 83% | @@ -536,7 +536,7 @@ def does_not_appear_in_this_film(ni): assert squeezed[7] == "1 file skipped due to complete coverage." report = self.get_report(cov, skip_covered=True, squeeze=False, - format_text="markdown") + output_format="markdown") # | Name | Stmts | Miss | Branch | BrPart | Cover | # |------------------ | -----: | -----: | -----: | -----: | ---------: | # | also/_not/_run.py | 2 | 1 | 0 | 0 | 50% | @@ -575,7 +575,7 @@ def foo(): squeezed = self.squeezed_lines(report) assert squeezed[5] == "1 file skipped due to complete coverage." report = self.get_report(cov, skip_covered=True, - format_text="markdown") + output_format="markdown") assert self.line_count(report) == 5, report squeezed = self.squeezed_lines(report) @@ -658,7 +658,7 @@ def test_report_skip_empty_no_data(self): assert report.split("\n")[3] == "TOTAL 0 0 100%" assert report.split("\n")[5] == "1 empty file skipped." report = self.get_report(cov, squeeze=False, - skip_empty=True, format_text="markdown") + skip_empty=True, output_format="markdown") # | Name | Stmts | Miss | Cover | # |---------- | -----: | -----: | ---------: | @@ -715,7 +715,7 @@ def foo(): assert squeezed[2] == "covered.py 3 0 0 0 100.000%" assert squeezed[4] == "not_covered.py 4 0 2 1 83.333%" assert squeezed[6] == "TOTAL 13 0 4 1 94.118%" - report = self.get_report(cov, squeeze=False, format_text="markdown") + report = self.get_report(cov, squeeze=False, output_format="markdown") #| Name | Stmts | Miss | Branch | BrPart | Cover | #|---------------- | -----: | -----: | -----: | -----: | -----------: | @@ -757,7 +757,7 @@ def test_accented_directory(self): cov.load() output = self.get_report(cov, squeeze=False) assert output == report_expected - output = self.get_report(cov, squeeze=False, format_text="markdown") + output = self.get_report(cov, squeeze=False, output_format="markdown") markdown_expected = \ "| \xe2/accented.py | 1 | 0 | 100% |" assert output.split("\n")[2] == markdown_expected @@ -819,7 +819,7 @@ def test_report_no_extension(self): cov.load() report = self.get_report(cov) assert self.last_line_squeezed(report) == "TOTAL 7 1 86%" - report = self.get_report(cov, squeeze=False, format_text="markdown") + report = self.get_report(cov, squeeze=False, output_format="markdown") assert report.split("\n")[-2] == \ "| **TOTAL** | **7** | **1** | **86%** |" @@ -856,7 +856,7 @@ def branch(x): self.start_import_stop(cov, "main") report = self.get_report(cov).splitlines() assert "mybranch.py 5 5 2 0 0%" in report - report = self.get_report(cov, squeeze=False, format_text="markdown") + report = self.get_report(cov, squeeze=False, output_format="markdown") assert \ "| mybranch.py | 5 | 5 | 2 | 0 | 0% |"\ in report @@ -920,7 +920,7 @@ def test_tracing_pyc_file(self): report = self.get_report(cov).splitlines() assert "mod.py 1 0 100%" in report - report = self.get_report(cov, squeeze=False, format_text="markdown") + report = self.get_report(cov, squeeze=False, output_format="markdown") print(report) assert report.split("\n")[4] == \ "| **TOTAL** | **2** | **0** | **100%** |" @@ -950,7 +950,7 @@ def test_missing_py_file_during_run(self): self.make_file("mod.py", "a = 1\n") report = self.get_report(cov).splitlines() assert "mod.py 1 0 100%" in report - report = self.get_report(cov, squeeze=False, format_text="markdown") + report = self.get_report(cov, squeeze=False, output_format="markdown") assert "| mod.py | 1 | 0 | 100% |" in report def test_empty_files(self): @@ -963,7 +963,7 @@ def test_empty_files(self): report = self.get_report(cov) assert "tests/modules/pkg1/__init__.py 1 0 0 0 100%" in report assert "tests/modules/pkg2/__init__.py 0 0 0 0 100%" in report - report = self.get_report(cov, squeeze=False, format_text="markdown") + report = self.get_report(cov, squeeze=False, output_format="markdown") # get_report() escapes backslash so we expect forward slash escaped # underscore print(report) @@ -1056,7 +1056,7 @@ def test_test_data(self): # file10.py 234 228 3% # ------------------------------ # TOTAL 586 386 34% - + print(report) lines = report.splitlines()[2:-2] assert len(lines) == 3 nums = [list(map(int, l.replace('%', '').split()[1:])) for l in lines] From ceef236af9b1c75ca9b838a2a4c4856fac902486 Mon Sep 17 00:00:00 2001 From: stepeos Date: Tue, 25 Oct 2022 21:45:24 +0200 Subject: [PATCH 12/17] fixing code quality --- tests/test_plugins.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_plugins.py b/tests/test_plugins.py index d7f4a90c6..584325c33 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -7,7 +7,6 @@ import io import math import os.path -import textwrap from xml.etree import ElementTree import pytest From 3017cb1a73c68e1c8a90a3d3a19eb99086f2050a Mon Sep 17 00:00:00 2001 From: stepeos Date: Tue, 1 Nov 2022 19:06:56 +0100 Subject: [PATCH 13/17] implementing requested changes --- coverage/summary.py | 129 +++++++++--------- tests/test_api.py | 54 ++++---- tests/test_plugins.py | 52 ++++---- tests/test_process.py | 11 +- tests/test_summary.py | 296 +++++++++--------------------------------- 5 files changed, 175 insertions(+), 367 deletions(-) diff --git a/coverage/summary.py b/coverage/summary.py index fba4eb972..92b1aec65 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -31,16 +31,23 @@ def writeout(self, line): self.outfile.write("\n") def _report_text(self, header, lines_values, total_line, end_lines): - "internal method to print report data in text format" - # Prepare the formatting strings, header, and column sorting. - max_name = max([len(fr.relative_filename()) for (fr, analysis) in \ - self.fr_analysis] + [5]) + 2 - n = self.config.precision - max_n = max(n+6, 7) + """Internal method that prints report data in text format. + `header` is a tuple with captions. + + `lines_values` is list of tuples of sortable values. + `total_line` is a tuple with values of the total line. + `end_lines` is a tuple of ending lines with information about skipped files. + """ # Prepare the formatting strings, header, and column sorting. + max_name = max([len(line[0]) for line in lines_values] + [5] + ) + 1 + max_n = max(len(total_line[header.index("Cover")]) + 2, + len(" Cover") + ) + 1 h_form = dict( Name="{:{name_len}}", Stmts="{:>7}", Miss="{:>7}", Branch="{:>7}", BrPart="{:>7}", Cover="{:>{n}}", - Missing="{:>9}") + Missing="{:>10}" + ) header_items = [ h_form[item].format(item, name_len=max_name, n=max_n) for item in header] @@ -51,11 +58,7 @@ def _report_text(self, header, lines_values, total_line, end_lines): self.writeout(header_str) self.writeout(rule) - column_order = dict(name=0, stmts=1, miss=2, cover=-1) - if self.branches: - column_order.update(dict(branch=3, brpart=4)) - - h_form.update(dict(Cover="{:>{n}}%"), Missing=" {:9}") + h_form.update(dict(Cover="{:>{n}}%"), Missing=" {:9}") for values in lines_values: # build string with line values line_items = [ @@ -65,43 +68,46 @@ def _report_text(self, header, lines_values, total_line, end_lines): self.writeout(text) # Write a TOTAL line - if total_line: - self.writeout(rule) - line_items = [ - h_form[item].format(str(value), - name_len=max_name, n=max_n-1) for item, value in zip(header, total_line)] - text = "".join(line_items) - self.writeout(text) + self.writeout(rule) + line_items = [ + h_form[item].format(str(value), + name_len=max_name, n=max_n-1) for item, value in zip(header, total_line)] + text = "".join(line_items) + self.writeout(text) for end_line in end_lines: self.writeout(end_line) - return self.total.n_statements and self.total.pc_covered def _report_markdown(self, header, lines_values, total_line, end_lines): - "internal method to print report data in markdown format" + """Internal method that prints report data in markdown format. + `header` is a tuple with captions. + + `lines_values` is a sorted list of tuples containing coverage information. + `total_line` is a tuple with values of the total line. + `end_lines` is a tuple of ending lines with information about skipped files. + """ # Prepare the formatting strings, header, and column sorting. - max_name = max([len(fr.relative_filename().replace("_","\\_")) for\ - (fr, analysis) in self.fr_analysis] + [9]) + 1 + max_name = max([len(line[0].replace("_", "\\_")) for line in lines_values] + [9] + ) + max_name += 1 h_form = dict( - Name="| {:{name_len}}|", Stmts="{:>7} |", Miss="{:>7} |", - Branch="{:>7} |", BrPart="{:>7} |", Cover="{:>{n}} |", - Missing="{:>9} |") - n = self.config.precision - max_n = max(n+6, 7) + 4 + Name="| {:{name_len}}|", Stmts="{:>9} |", Miss="{:>9} |", + Branch="{:>9} |", BrPart="{:>9} |", Cover="{:>{n}} |", + Missing="{:>10} |") + max_n = max(len(total_line[header.index("Cover")]) + 6, + len(" Cover ") + ) header_items = [ h_form[item].format(item, name_len=max_name, n=max_n) for item in header] header_str = "".join(header_items) rule_str = "|" + " ".join(["- |".rjust(len(header_items[0])-1, '-')] + - ["-: |".rjust(len(item)-1, '-') for item in header_items[1:]]) + ["-: |".rjust(len(item)-1, '-') for item in header_items[1:]] + ) # Write the header self.writeout(header_str) self.writeout(rule_str) - column_order = dict(name=0, stmts=1, miss=2, cover=-1) - if self.branches: - column_order.update(dict(branch=3, brpart=4)) - for values in lines_values: # build string with line values h_form.update(dict(Cover="{:>{n}}% |")) @@ -112,29 +118,22 @@ def _report_markdown(self, header, lines_values, total_line, end_lines): self.writeout(text) # Write the TOTAL line - if total_line: - total_form = dict( - Name="| {:>{name_len}}** |", Stmts="{:>5}** |", Miss="{:>5}** |", - Branch="{:>5}** |", BrPart="{:>5}** |", Cover="{:>{n}}%** |", - Missing="{:>9} |") - total_line_items = [] - for item, value in zip(header, total_line): - if item == "Missing": - if value == '': - insert = value - else: - insert = "**" + value + "**" - total_line_items += total_form[item].format(\ - insert, name_len=max_name-3) - else: - total_line_items += total_form[item].format(\ - "**"+str(value), name_len=max_name-3, n=max_n-3) - total_row_str = "".join(total_line_items) - self.writeout(total_row_str) + h_form.update(dict(Name="|{:>{name_len}} |", Cover="{:>{n}} |")) + total_line_items = [] + for item, value in zip(header, total_line): + if value == '': + insert = value + elif item == "Cover": + insert = " **"+str(value)+"%**" + else: + insert = " **"+str(value)+"**" + total_line_items += h_form[item].format( + insert, name_len=max_name, n=max_n + ) + total_row_str = "".join(total_line_items) + self.writeout(total_row_str) for end_line in end_lines: self.writeout(end_line) - return self.total.n_statements and self.total.pc_covered - def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. @@ -195,14 +194,12 @@ def report(self, morfs, outfile=None): reverse=reverse) # calculate total if we had at least one file. - total_line = () - if self.total.n_files > 0: - total_line = ("TOTAL", self.total.n_statements, self.total.n_missing) - if self.branches: - total_line += (self.total.n_branches, self.total.n_partial_branches) - total_line += (self.total.pc_covered_str,) - if self.config.show_missing: - total_line += ("",) + total_line = ("TOTAL", self.total.n_statements, self.total.n_missing) + if self.branches: + total_line += (self.total.n_branches, self.total.n_partial_branches) + total_line += (self.total.pc_covered_str,) + if self.config.show_missing: + total_line += ("",) # create other final lines end_lines = [] @@ -211,13 +208,13 @@ def report(self, morfs, outfile=None): if self.config.skip_covered and self.skipped_count: file_suffix = 's' if self.skipped_count>1 else '' - fmt_skip_covered = f"\n{self.skipped_count} file{file_suffix} "\ - "skipped due to complete coverage." + fmt_skip_covered = (f"\n{self.skipped_count} file{file_suffix} skipped due to " + + "complete coverage." + ) end_lines.append(fmt_skip_covered) if self.config.skip_empty and self.empty_count: file_suffix = 's' if self.empty_count>1 else '' - fmt_skip_empty = \ - f"\n{self.empty_count} empty file{file_suffix} skipped." + fmt_skip_empty = f"\n{self.empty_count} empty file{file_suffix} skipped." end_lines.append(fmt_skip_empty) text_format = self.config.output_format or 'text' diff --git a/tests/test_api.py b/tests/test_api.py index 847ead7ee..375edcec1 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -590,15 +590,15 @@ def test_source_and_include_dont_conflict(self): cov.load() # There should be no exception. At one point, report() threw: # CoverageException: --include and --source are mutually exclusive - report = self.get_report(cov, squeeze=False) + cov.report() expected = textwrap.dedent("""\ - Name Stmts Miss Cover - ---------------------------- - b.py 1 0 100% - ---------------------------- - TOTAL 1 0 100% + Name Stmts Miss Cover + --------------------------- + b.py 1 0 100% + --------------------------- + TOTAL 1 0 100% """) - assert expected == report + assert expected == self.stdout() def make_test_files(self): """Create a simple file representing a method with two tests. @@ -1054,17 +1054,13 @@ def pretend_to_be_nose_with_cover(self, erase=False, cd=False): os.chdir("sub") cov.combine() cov.save() - cov.load() - report = self.get_report(cov, squeeze=False) - - print(report) - # cov.report(["no_biggie.py"], show_missing=True) - assert report == textwrap.dedent("""\ - Name Stmts Miss Cover - ----------------------------------- - no_biggie.py 4 1 75% - ----------------------------------- - TOTAL 4 1 75% + cov.report(["no_biggie.py"], show_missing=True) + assert self.stdout() == textwrap.dedent("""\ + Name Stmts Miss Cover Missing + -------------------------------------------- + no_biggie.py 4 1 75% 4 + -------------------------------------------- + TOTAL 4 1 75% """) if cd: os.chdir("..") @@ -1101,19 +1097,15 @@ def pretend_to_be_pytestcov(self, append): self.start_import_stop(cov, "prog") cov.combine() cov.save() - cov.load() - # report = io.StringIO() - report = self.get_report(cov, show_missing=None, ignore_errors=True, - skip_covered=None, skip_empty=None, squeeze=False) - print(report) - # cov.report(show_missing=None, ignore_errors=True, file=report, skip_covered=None, - # skip_empty=None) - assert report == textwrap.dedent("""\ - Name Stmts Miss Cover - ------------------------------ - prog.py 4 1 75% - ------------------------------ - TOTAL 4 1 75% + report = io.StringIO() + cov.report(show_missing=None, ignore_errors=True, file=report, skip_covered=None, + skip_empty=None) + assert report.getvalue() == textwrap.dedent("""\ + Name Stmts Miss Cover + ----------------------------- + prog.py 4 1 75% + ----------------------------- + TOTAL 4 1 75% """) self.assert_file_count(".coverage", 0) self.assert_file_count(".coverage.*", 1) diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 584325c33..b4239700d 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -416,20 +416,19 @@ def test_plugin2_with_text_report(self): self.start_import_stop(cov, "caller") - cov.save() - cov.load() - report = self.get_report(cov, squeeze=False, - include=["*.html"], omit=["uni*.html"], show_missing=True) - # Name Stmts Miss Branch BrPart Cover Missing - # -------------------------------------------------------- - # bar_4.html 4 2 0 0 50% 1, 4 - # foo_7.html 7 5 0 0 29% 1-3, 6-7 - # -------------------------------------------------------- - # TOTAL 11 7 0 0 36% - assert report.split("\n")[2] == \ - "bar_4.html 4 2 0 0 50% 1, 4" - total = int(report.split("\n")[-2].split(" ")[-1][:-1]) - assert 0 == total - int(4/11*100) + repout = io.StringIO() + total = cov.report(file=repout, include=["*.html"], omit=["uni*.html"], show_missing=True) + report = repout.getvalue().splitlines() + expected = [ + 'Name Stmts Miss Branch BrPart Cover Missing', + '--------------------------------------------------------', + 'bar_4.html 4 2 0 0 50% 1, 4', + 'foo_7.html 7 5 0 0 29% 1-3, 6-7', + '--------------------------------------------------------', + 'TOTAL 11 7 0 0 36%', + ] + assert expected == report + assert math.isclose(total, 4 / 11 * 100) def test_plugin2_with_html_report(self): self.make_render_and_caller() @@ -516,18 +515,19 @@ def coverage_init(reg, options): cov = coverage.Coverage(include=["unsuspecting.py"]) cov.set_option("run:plugins", ["fairly_odd_plugin"]) self.start_import_stop(cov, "unsuspecting") - cov.save() - cov.load() - report = self.get_report(cov, squeeze=False, show_missing=True) - # Name Stmts Miss Cover Missing - # ----------------------------------------------- - # unsuspecting.py 6 3 50% 2, 4, 6 - # ----------------------------------------------- - # TOTAL 6 3 50% - assert report.split("\n")[2] == \ - "unsuspecting.py 6 3 50% 2, 4, 6" - assert report.split("\n")[4] == \ - "TOTAL 6 3 50%" + + repout = io.StringIO() + total = cov.report(file=repout, show_missing=True) + report = repout.getvalue().splitlines() + expected = [ + 'Name Stmts Miss Cover Missing', + '-----------------------------------------------', + 'unsuspecting.py 6 3 50% 2, 4, 6', + '-----------------------------------------------', + 'TOTAL 6 3 50%', + ] + assert expected == report + assert total == 50 def test_find_unexecuted(self): self.make_file("unexecuted_plugin.py", """\ diff --git a/tests/test_process.py b/tests/test_process.py index 8d1dffaa5..b76846e51 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -174,13 +174,12 @@ def test_combine_with_rc(self): # Reporting should still work even with the .rc file out = self.run_command("coverage report") - print(out) assert out == textwrap.dedent("""\ - Name Stmts Miss Cover - -------------------------------- - b_or_c.py 8 0 100% - -------------------------------- - TOTAL 8 0 100% + Name Stmts Miss Cover + ------------------------------- + b_or_c.py 8 0 100% + ------------------------------- + TOTAL 8 0 100% """) def test_combine_with_aliases(self): diff --git a/tests/test_summary.py b/tests/test_summary.py index 3da1009e1..58a33745a 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -57,21 +57,6 @@ def test_report(self): assert "/tests/zipmods.zip/covmodzip1.py " in report assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 8 0 100%" - report = self.get_report(cov, output_format="markdown", squeeze=False) - # | Name | Stmts | Miss | Cover | - # |---------------------------------------- | -----: | -----: | ---------: | - # | foo/bar/tests/modules/covmod1.py | 2 | 0 | 100% | - # | foo/bar/tests/zipmods.zip/covmodzip1.py | 2 | 0 | 100% | - # | mycode.py | 4 | 0 | 100% | - # | **TOTAL** | **8** | **0** | **100%** | - assert "/coverage//_/_init/_/_/" not in report - assert "/tests/modules/covmod1.py " in report - assert "/tests/zipmods.zip/covmodzip1.py " in report - assert "mycode.py " in report - assert "**TOTAL**" in report.split("\n")[5] - assert "**8**" in report.split("\n")[5] - assert "**0**" in report.split("\n")[5] - assert "**100%**" in report.split("\n")[5] def test_report_just_one(self): # Try reporting just one module @@ -92,22 +77,6 @@ def test_report_just_one(self): assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" - report = self.get_report(cov, squeeze=False, output_format="markdown", - morfs=["mycode.py"]) - print(report) - # | Name | Stmts | Miss | Cover | - # |---------- | -----: | -----: | ---------: | - # | mycode.py | 4 | 0 | 100% | - # | **TOTAL** | **4** | **0** | **100%** | - - assert self.line_count(report) == 4 - assert "/coverage/" not in report - assert "/tests/modules/covmod1.py " not in report - assert "/tests/zipmods.zip/covmodzip1.py " not in report - assert "mycode.py " in report - assert report.split("\n")[3] == "| **TOTAL** | **4** | **0** |"\ - " **100%** |" - def test_report_wildcard(self): # Try reporting using wildcards to get the modules. self.make_mycode() @@ -127,19 +96,6 @@ def test_report_wildcard(self): assert "/tests/zipmods.zip/covmodzip1.py " not in report assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" - report = self.report_from_command("coverage report --format=markdown"\ - " my*.py") - # | Name | Stmts | Miss | Cover | - # |---------- | -----: | -----: | ---------: | - # | mycode.py | 4 | 0 | 100% | - # | **TOTAL** | **4** | **0** | **100%** | - assert self.line_count(report) == 4 - assert "/coverage/" not in report - assert "/tests/modules/covmod1.py " not in report - assert "/tests/zipmods.zip/covmodzip1.py " not in report - assert "mycode.py " in report - assert report.split("\n")[3] == "| **TOTAL** | **4** | **0** | "\ - " **100%** |" def test_report_omitting(self): # Try reporting while omitting some modules @@ -161,19 +117,6 @@ def test_report_omitting(self): assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" - report = self.get_report(cov, squeeze=False, output_format="markdown", - omit=[f"{TESTS_DIR}/*", "*/site-packages/*"]) - # | Name | Stmts | Miss | Cover | - # |---------- | -----: | -----: | ---------: | - # | mycode.py | 4 | 0 | 100% | - # | **TOTAL** | **4** | **0** | **100%** | - assert self.line_count(report) == 4 - assert "/coverage/" not in report - assert "/tests/modules/covmod1.py " not in report - assert "/tests/zipmods.zip/covmodzip1.py " not in report - assert "mycode.py " in report - assert report.split("\n")[3] == "| **TOTAL** | **4** | **0** | **100%** |" - def test_report_including(self): # Try reporting while including some modules self.make_mycode() @@ -194,17 +137,6 @@ def test_report_including(self): assert "mycode.py " in report assert self.last_line_squeezed(report) == "TOTAL 4 0 100%" - report = self.get_report(cov, include=["mycode*"], - output_format="markdown", squeeze=False) - print(report) - assert self.line_count(report) == 4 - assert "/coverage/" not in report - assert "/tests/modules/covmod1.py " not in report - assert "/tests/zipmods.zip/covmodzip1.py " not in report - assert "mycode.py " in report - assert report.split("\n")[3] == "| **TOTAL** | **4** | **0** | "\ - "**100%** |" - def test_run_source_vs_report_include(self): # https://github.com/nedbat/coveragepy/issues/621 self.make_file(".coveragerc", """\ @@ -258,20 +190,9 @@ def branch(x): # mybranch.py 5 0 2 1 86% # ----------------------------------------------- # TOTAL 5 0 2 1 86% - assert self.line_count(report) == 5 assert "mybranch.py " in report assert self.last_line_squeezed(report) == "TOTAL 5 0 2 1 86%" - report = self.get_report(cov, squeeze=False, output_format="markdown") - # | Name | Stmts | Miss | Branch | BrPart | Cover | - # |------------ | -----: | -----: | -----: | -----: | ---------: | - # | mybranch.py | 5 | 0 | 2 | 1 | 86% | - # | **TOTAL** | **5** | **0** | **2** | **1** | **86%** | - - assert self.line_count(report) == 4 - assert "mybranch.py " in report - assert report.split("\n")[3] == "| **TOTAL** | **5** | **0** |"\ - " **2** | **1** | **86%** |" def test_report_show_missing(self): self.make_file("mymissing.py", """\ @@ -299,25 +220,12 @@ def missing(x, y): # -------------------------------------------- # mymissing.py 14 3 79% 3-4, 10 # -------------------------------------------- - # TOTAL 14 3 79% 3-4, 10 + # TOTAL 14 3 79% assert self.line_count(report) == 5 squeezed = self.squeezed_lines(report) assert squeezed[2] == "mymissing.py 14 3 79% 3-4, 10" assert squeezed[4] == "TOTAL 14 3 79%" - report = self.get_report(cov, show_missing=True, squeeze=False, - output_format="markdown") - # | Name | Stmts | Miss | Cover | Missing | - # |------------- | -----: | -----: | ---------: | -------: | - # | mymissing.py | 14 | 3 | 79% | 3-4, 10 | - # | **TOTAL** | **14** | **3** | **79%** | | - - - assert self.line_count(report) == 4 - assert report.split("\n")[2] == "| mymissing.py | 14 | 3 | "\ - "79% | 3-4, 10 |" - assert report.split("\n")[3] == "| **TOTAL** | **14** | **3** |"\ - " **79%** | |" def test_report_show_missing_branches(self): self.make_file("mybranch.py", """\ @@ -331,25 +239,6 @@ def branch(x, y): cov = coverage.Coverage(branch=True) self.start_import_stop(cov, "mybranch") assert self.stdout() == 'x\ny\n' - report = self.get_report(cov, show_missing=True) - - # Name Stmts Miss Branch BrPart Cover Missing - # ---------------------------------------------------------- - # mybranch.py 6 0 4 2 80% 2->4, 4->exit - # ---------------------------------------------------------- - # TOTAL 6 0 4 2 80% - - assert self.line_count(report) == 5 - squeezed = self.squeezed_lines(report) - assert squeezed[2] == "mybranch.py 6 0 4 2 80% 2->4, 4->exit" - assert squeezed[4] == "TOTAL 6 0 4 2 80%" - report = self.get_report(cov, show_missing=True, squeeze=False, - output_format="markdown") - assert self.line_count(report) == 4 - assert report.split("\n")[2] == "| mybranch.py | 6 | 0 |"\ - " 4 | 2 | 80% |2->4, 4->exit |" - assert report.split("\n")[3] == "| **TOTAL** | **6** | **0** |"\ - " **4** | **2** | **80%** | |" def test_report_show_missing_branches_and_lines(self): self.make_file("main.py", """\ @@ -370,26 +259,6 @@ def branch(x, y, z): cov = coverage.Coverage(branch=True) self.start_import_stop(cov, "main") assert self.stdout() == 'x\ny\n' - report_lines = self.get_report(cov, squeeze=False, show_missing=True).splitlines() - expected = [ - 'Name Stmts Miss Branch BrPart Cover Missing', - '---------------------------------------------------------', - 'main.py 1 0 0 0 100%', - 'mybranch.py 10 2 8 3 61% 2->4, 4->6, 7-8', - '---------------------------------------------------------', - 'TOTAL 11 2 8 3 63%', - ] - assert expected == report_lines - - report = self.get_report(cov, squeeze=False, show_missing=True, - output_format="markdown") - assert report.split("\n")[2] == "| main.py | 1 | 0 |"\ - " 0 | 0 | 100% | |" - assert report.split("\n")[3] == "| mybranch.py | 10 | 2 |"\ - " 8 | 3 | 61% |2->4, 4->6, 7-8 |" - assert report.split("\n")[4] == "| **TOTAL** | **11** | **2** |"\ - " **8** | **3** | **63%** | |" - def test_report_skip_covered_no_branches(self): self.make_file("main.py", """ @@ -423,22 +292,6 @@ def not_covered(): assert squeezed[6] == "1 file skipped due to complete coverage." assert self.last_command_status == 0 - report = self.report_from_command( - "coverage report --skip-covered --fail-under=70 --format=markdown") - # | Name | Stmts | Miss | Cover | - # |---------------- | -----: | -----: | ---------: | - # | not\_covered.py | 2 | 1 | 50% | - # | **TOTAL** | **6** | **1** | **83%** | - - # 1 file skipped due to complete coverage. - assert self.line_count(report) == 6, report - assert report.split("\n")[2] == "| not/_covered.py | 2 | 1"\ - " | 50% |" - assert report.split("\n")[3] == "| **TOTAL** | **6** | **1**"\ - " | **83%** |" - assert report.split("\n")[5] == "1 file skipped due to complete coverage." - assert self.last_command_status == 0 - def test_report_skip_covered_branches(self): self.make_file("main.py", """ import not_covered, covered @@ -478,20 +331,6 @@ def foo(): assert squeezed[2] == "not_covered.py 4 0 2 1 83%" assert squeezed[4] == "TOTAL 13 0 4 1 94%" assert squeezed[6] == "2 files skipped due to complete coverage." - report = self.get_report(cov, skip_covered=True, squeeze=False, - output_format="markdown") - # | Name | Stmts | Miss | Branch | BrPart | Cover | - # |---------------- | -----: | -----: | -----: | -----: | ---------: | - # | not/_covered.py | 4 | 0 | 2 | 1 | 83% | - # | **TOTAL** | **13** | **0** | **4** | **1** | **94%** | - - # 2 files skipped due to complete coverage. - assert self.line_count(report) == 6, report - assert report.split("\n")[2] == "| not/_covered.py | 4 | "\ - "0 | 2 | 1 | 83% |" - assert report.split("\n")[3] == "| **TOTAL** | **13** | **0** "\ - "| **4** | **1** | **94%** |" - assert report.split("\n")[5] == "2 files skipped due to complete coverage." def test_report_skip_covered_branches_with_totals(self): self.make_file("main.py", """ @@ -535,24 +374,6 @@ def does_not_appear_in_this_film(ni): assert squeezed[5] == "TOTAL 13 1 4 1 88%" assert squeezed[7] == "1 file skipped due to complete coverage." - report = self.get_report(cov, skip_covered=True, squeeze=False, - output_format="markdown") - # | Name | Stmts | Miss | Branch | BrPart | Cover | - # |------------------ | -----: | -----: | -----: | -----: | ---------: | - # | also/_not/_run.py | 2 | 1 | 0 | 0 | 50% | - # | not/_covered.py | 4 | 0 | 2 | 1 | 83% | - # | **TOTAL** | **13** | **1** | **4** | **1** | **88%** | - assert self.line_count(report) == 7, report - assert report.split("\n")[2] == "| also/_not/_run.py | 2 | "\ - "1 | 0 | 0 | 50% |" - assert report.split("\n")[3] == "| not/_covered.py | 4 | "\ - "0 | 2 | 1 | 83% |" - assert report.split("\n")[4] == "| **TOTAL** | **13** | "\ - "**1** | **4** | **1** | **88%** |" - assert report.split("\n")[6] == "1 file skipped due to complete coverage." - - - def test_report_skip_covered_all_files_covered(self): self.make_file("main.py", """ def foo(): @@ -574,10 +395,24 @@ def foo(): assert self.line_count(report) == 6, report squeezed = self.squeezed_lines(report) assert squeezed[5] == "1 file skipped due to complete coverage." - report = self.get_report(cov, skip_covered=True, + report = self.get_report(cov, squeeze=False, skip_covered=True, output_format="markdown") + # | Name | Stmts | Miss | Branch | BrPart | Cover | + # |---------- | -------: | -------: | -------: | -------: | -------: | + # | **TOTAL** | **3** | **0** | **0** | **0** | **100%** | + # + # 1 file skipped due to complete coverage. assert self.line_count(report) == 5, report + assert report.split("\n")[0] == ( + '| Name | Stmts | Miss | Branch | BrPart | Cover |' + ) + assert report.split("\n")[1] == ( + '|---------- | -------: | -------: | -------: | -------: | -------: |' + ) + assert report.split("\n")[2] == ( + '| **TOTAL** | **3** | **0** | **0** | **0** | **100%** |' + ) squeezed = self.squeezed_lines(report) assert squeezed[4] == "1 file skipped due to complete coverage." @@ -601,7 +436,7 @@ def foo(): assert self.line_count(report) == 6, report lines = self.report_lines(report) - assert lines[0] == "Name Stmts Miss Branch BrPart Cover" + assert lines[0] == "Name Stmts Miss Branch BrPart Cover" squeezed = self.squeezed_lines(report) assert squeezed[5] == "1 file skipped due to complete coverage." @@ -657,19 +492,6 @@ def test_report_skip_empty_no_data(self): assert self.line_count(report) == 6, report assert report.split("\n")[3] == "TOTAL 0 0 100%" assert report.split("\n")[5] == "1 empty file skipped." - report = self.get_report(cov, squeeze=False, - skip_empty=True, output_format="markdown") - - # | Name | Stmts | Miss | Cover | - # |---------- | -----: | -----: | ---------: | - # | **TOTAL** | **0** | **0** | **100%** | - # - # 1 empty file skipped. - - assert self.line_count(report) == 5, report - assert report.split("\n")[2] == \ - "| **TOTAL** | **0** | **0** | **100%** |" - assert report.split("\n")[4] == "1 empty file skipped." def test_report_precision(self): self.make_file(".coveragerc", """\ @@ -700,7 +522,7 @@ def foo(): cov = coverage.Coverage(branch=True) self.start_import_stop(cov, "main") assert self.stdout() == "n\nz\n" - report = self.get_report(cov) + report = self.get_report(cov, squeeze=False) # Name Stmts Miss Branch BrPart Cover # ------------------------------------------------------ @@ -715,20 +537,6 @@ def foo(): assert squeezed[2] == "covered.py 3 0 0 0 100.000%" assert squeezed[4] == "not_covered.py 4 0 2 1 83.333%" assert squeezed[6] == "TOTAL 13 0 4 1 94.118%" - report = self.get_report(cov, squeeze=False, output_format="markdown") - - #| Name | Stmts | Miss | Branch | BrPart | Cover | - #|---------------- | -----: | -----: | -----: | -----: | -----------: | - #| covered.py | 3 | 0 | 0 | 0 | 100.000% | - #| main.py | 6 | 0 | 2 | 0 | 100.000% | - #| not/_covered.py | 4 | 0 | 2 | 1 | 83.333% | - #| **TOTAL** | **13** | **0** | **4** | **1** | **94.118%** | - - assert self.line_count(report) == 6, report - squeezed = self.squeezed_lines(report) - assert squeezed[2] == "| covered.py | 3 | 0 | 0 | 0 | 100.000% |" - assert squeezed[4] == "| not/_covered.py | 4 | 0 | 2 | 1 | 83.333% |" - assert squeezed[5] == "| **TOTAL** | **13** | **0** | **4** | **1** | **94.118%** |" def test_dotpy_not_python(self): # We run a .py file, and when reporting, we can't parse it as Python. @@ -747,20 +555,16 @@ def test_accented_directory(self): self.make_file("\xe2/accented.py", "print('accented')") self.make_data_file(lines={abs_file("\xe2/accented.py"): [1]}) report_expected = ( - "Name Stmts Miss Cover\n" + - "------------------------------------\n" + - "\xe2/accented.py 1 0 100%\n" + - "------------------------------------\n" + - "TOTAL 1 0 100%\n" + "Name Stmts Miss Cover\n" + + "-----------------------------------\n" + + "\xe2/accented.py 1 0 100%\n" + + "-----------------------------------\n" + + "TOTAL 1 0 100%\n" ) cov = coverage.Coverage() cov.load() output = self.get_report(cov, squeeze=False) assert output == report_expected - output = self.get_report(cov, squeeze=False, output_format="markdown") - markdown_expected = \ - "| \xe2/accented.py | 1 | 0 | 100% |" - assert output.split("\n")[2] == markdown_expected @pytest.mark.skipif(env.JYTHON, reason="Jython doesn't like accented file names") def test_accenteddotpy_not_python(self): @@ -819,9 +623,6 @@ def test_report_no_extension(self): cov.load() report = self.get_report(cov) assert self.last_line_squeezed(report) == "TOTAL 7 1 86%" - report = self.get_report(cov, squeeze=False, output_format="markdown") - assert report.split("\n")[-2] == \ - "| **TOTAL** | **7** | **1** | **86%** |" def test_report_with_chdir(self): self.make_file("chdir.py", """\ @@ -837,8 +638,7 @@ def test_report_with_chdir(self): report = self.report_from_command("coverage report") assert self.last_line_squeezed(report) == "TOTAL 5 0 100%" report = self.report_from_command("coverage report --format=markdown") - assert report.split("\n")[-2] == \ - "| **TOTAL** | **5** | **0** | **100%** |" + assert self.last_line_squeezed(report) == "| **TOTAL** | **5** | **0** | **100%** |" def test_bug_156_file_not_run_should_be_zero(self): # https://github.com/nedbat/coveragepy/issues/156 @@ -856,11 +656,6 @@ def branch(x): self.start_import_stop(cov, "main") report = self.get_report(cov).splitlines() assert "mybranch.py 5 5 2 0 0%" in report - report = self.get_report(cov, squeeze=False, output_format="markdown") - assert \ - "| mybranch.py | 5 | 5 | 2 | 0 | 0% |"\ - in report - def run_TheCode_and_report_it(self): """A helper for the next few tests.""" @@ -921,9 +716,8 @@ def test_tracing_pyc_file(self): report = self.get_report(cov).splitlines() assert "mod.py 1 0 100%" in report report = self.get_report(cov, squeeze=False, output_format="markdown") - print(report) - assert report.split("\n")[4] == \ - "| **TOTAL** | **2** | **0** | **100%** |" + assert report.split("\n")[3] == "| mod.py | 1 | 0 | 100% |" + assert report.split("\n")[4] == "| **TOTAL** | **2** | **0** | **100%** |" def test_missing_py_file_during_run(self): # Create two Python files. @@ -950,8 +744,6 @@ def test_missing_py_file_during_run(self): self.make_file("mod.py", "a = 1\n") report = self.get_report(cov).splitlines() assert "mod.py 1 0 100%" in report - report = self.get_report(cov, squeeze=False, output_format="markdown") - assert "| mod.py | 1 | 0 | 100% |" in report def test_empty_files(self): # Shows that empty files like __init__.py are listed as having zero @@ -966,12 +758,41 @@ def test_empty_files(self): report = self.get_report(cov, squeeze=False, output_format="markdown") # get_report() escapes backslash so we expect forward slash escaped # underscore - print(report) assert "tests/modules/pkg1//_/_init/_/_.py " in report - assert "| 1 | 0 | 0 | 0 | 100% |" in report + assert "| 1 | 0 | 0 | 0 | 100% |" in report assert "tests/modules/pkg2//_/_init/_/_.py " in report - assert "| 0 | 0 | 0 | 0 | 100% |" in report + assert "| 0 | 0 | 0 | 0 | 100% |" in report + def test_markdown_with_missing(self): + self.make_file("mymissing.py", """\ + def missing(x, y): + if x: + print("x") + return x + if y: + print("y") + try: + print("z") + 1/0 + print("Never!") + except ZeroDivisionError: + pass + return x + missing(0, 1) + """) + cov = coverage.Coverage(source=["."]) + self.start_import_stop(cov, "mymissing") + assert self.stdout() == 'y\nz\n' + report = self.get_report(cov,squeeze=False, output_format="markdown", show_missing=True) + + # | Name | Stmts | Miss | Cover | Missing | + # |------------- | -------: | -------: | ------: | --------: | + # | mymissing.py | 14 | 3 | 79% | 3-4, 10 | + # | **TOTAL** | **14** | **3** | **79%** | | + assert self.line_count(report) == 4 + report_lines = report.split("\n") + assert report_lines[2] == "| mymissing.py | 14 | 3 | 79% | 3-4, 10 |" + assert report_lines[3] == "| **TOTAL** | **14** | **3** | **79%** | |" class ReportingReturnValueTest(CoverageTest): """Tests of reporting functions returning values.""" @@ -1056,7 +877,6 @@ def test_test_data(self): # file10.py 234 228 3% # ------------------------------ # TOTAL 586 386 34% - print(report) lines = report.splitlines()[2:-2] assert len(lines) == 3 nums = [list(map(int, l.replace('%', '').split()[1:])) for l in lines] From 52c8ae8bcd645a4b84f30665bece6f4d8f2b4e15 Mon Sep 17 00:00:00 2001 From: stepeos Date: Tue, 1 Nov 2022 19:07:52 +0100 Subject: [PATCH 14/17] doc fix --- coverage/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage/control.py b/coverage/control.py index c154dfd3b..04f4c2e2d 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -957,7 +957,7 @@ def report( .. versionadded:: 5.2 The `precision` parameter. - .. veresionadded:: 6.6 + .. versionadded:: 6.6 The `format` parameter. """ From 9f2c978a48cc66cfa18e19812091a48cbf88f411 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 2 Nov 2022 06:59:57 -0400 Subject: [PATCH 15/17] test: add another test of correct report formatting --- tests/test_summary.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_summary.py b/tests/test_summary.py index 58a33745a..f3d7cce7a 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -538,6 +538,29 @@ def foo(): assert squeezed[4] == "not_covered.py 4 0 2 1 83.333%" assert squeezed[6] == "TOTAL 13 0 4 1 94.118%" + def test_report_precision_all_zero(self): + self.make_file("not_covered.py", """ + def not_covered(n): + if n: + print("n") + """) + self.make_file("empty.py", "") + cov = coverage.Coverage(source=["."]) + self.start_import_stop(cov, "empty") + report = self.get_report(cov, precision=6, squeeze=False) + + # Name Stmts Miss Cover + # ----------------------------------------- + # empty.py 0 0 100.000000% + # not_covered.py 3 3 0.000000% + # ----------------------------------------- + # TOTAL 3 3 0.000000% + + assert self.line_count(report) == 6, report + assert "empty.py 0 0 100.000000%" in report + assert "not_covered.py 3 3 0.000000%" in report + assert "TOTAL 3 3 0.000000%" in report + def test_dotpy_not_python(self): # We run a .py file, and when reporting, we can't parse it as Python. # We should get an error message in the report. From 1d43a8336fe216d1cca14129de8ea67c0a5d1a43 Mon Sep 17 00:00:00 2001 From: stepeos Date: Wed, 2 Nov 2022 15:37:47 +0100 Subject: [PATCH 16/17] fixed precision printing test --- coverage/summary.py | 1 + 1 file changed, 1 insertion(+) diff --git a/coverage/summary.py b/coverage/summary.py index 92b1aec65..3a931066a 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -43,6 +43,7 @@ def _report_text(self, header, lines_values, total_line, end_lines): max_n = max(len(total_line[header.index("Cover")]) + 2, len(" Cover") ) + 1 + max_n = max([max_n] + [len(line[header.index("Cover")]) + 2 for line in lines_values]) h_form = dict( Name="{:{name_len}}", Stmts="{:>7}", Miss="{:>7}", Branch="{:>7}", BrPart="{:>7}", Cover="{:>{n}}", From 87113b403193b67498e1c79e31ecbba301551e49 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 2 Nov 2022 07:37:00 -0400 Subject: [PATCH 17/17] style: adjust the formatting --- coverage/control.py | 4 +- coverage/summary.py | 86 +++++++++++++++++++++++-------------------- tests/test_cmdline.py | 2 +- 3 files changed, 49 insertions(+), 43 deletions(-) diff --git a/coverage/control.py b/coverage/control.py index 04f4c2e2d..a8cf1649c 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -909,7 +909,7 @@ def report( self, morfs=None, show_missing=None, ignore_errors=None, file=None, omit=None, include=None, skip_covered=None, contexts=None, skip_empty=None, precision=None, sort=None, - output_format=None + output_format=None, ): """Write a textual summary report to `file`. @@ -966,7 +966,7 @@ def report( ignore_errors=ignore_errors, report_omit=omit, report_include=include, show_missing=show_missing, skip_covered=skip_covered, report_contexts=contexts, skip_empty=skip_empty, precision=precision, - sort=sort, output_format=output_format + sort=sort, output_format=output_format, ): reporter = SummaryReporter(self) return reporter.report(morfs, outfile=file) diff --git a/coverage/summary.py b/coverage/summary.py index 3a931066a..94be1a087 100644 --- a/coverage/summary.py +++ b/coverage/summary.py @@ -32,26 +32,30 @@ def writeout(self, line): def _report_text(self, header, lines_values, total_line, end_lines): """Internal method that prints report data in text format. - `header` is a tuple with captions. + `header` is a tuple with captions. `lines_values` is list of tuples of sortable values. `total_line` is a tuple with values of the total line. `end_lines` is a tuple of ending lines with information about skipped files. - """ # Prepare the formatting strings, header, and column sorting. - max_name = max([len(line[0]) for line in lines_values] + [5] - ) + 1 - max_n = max(len(total_line[header.index("Cover")]) + 2, - len(" Cover") - ) + 1 + + """ + # Prepare the formatting strings, header, and column sorting. + max_name = max([len(line[0]) for line in lines_values] + [5]) + 1 + max_n = max(len(total_line[header.index("Cover")]) + 2, len(" Cover")) + 1 max_n = max([max_n] + [len(line[header.index("Cover")]) + 2 for line in lines_values]) h_form = dict( - Name="{:{name_len}}", Stmts="{:>7}", Miss="{:>7}", - Branch="{:>7}", BrPart="{:>7}", Cover="{:>{n}}", - Missing="{:>10}" - ) + Name="{:{name_len}}", + Stmts="{:>7}", + Miss="{:>7}", + Branch="{:>7}", + BrPart="{:>7}", + Cover="{:>{n}}", + Missing="{:>10}", + ) header_items = [ h_form[item].format(item, name_len=max_name, n=max_n) - for item in header] + for item in header + ] header_str = "".join(header_items) rule = "-" * len(header_str) @@ -64,7 +68,8 @@ def _report_text(self, header, lines_values, total_line, end_lines): # build string with line values line_items = [ h_form[item].format(str(value), - name_len=max_name, n=max_n-1) for item, value in zip(header, values)] + name_len=max_name, n=max_n-1) for item, value in zip(header, values) + ] text = "".join(line_items) self.writeout(text) @@ -72,7 +77,8 @@ def _report_text(self, header, lines_values, total_line, end_lines): self.writeout(rule) line_items = [ h_form[item].format(str(value), - name_len=max_name, n=max_n-1) for item, value in zip(header, total_line)] + name_len=max_name, n=max_n-1) for item, value in zip(header, total_line) + ] text = "".join(line_items) self.writeout(text) @@ -81,25 +87,27 @@ def _report_text(self, header, lines_values, total_line, end_lines): def _report_markdown(self, header, lines_values, total_line, end_lines): """Internal method that prints report data in markdown format. - `header` is a tuple with captions. + `header` is a tuple with captions. `lines_values` is a sorted list of tuples containing coverage information. `total_line` is a tuple with values of the total line. `end_lines` is a tuple of ending lines with information about skipped files. + """ # Prepare the formatting strings, header, and column sorting. - max_name = max([len(line[0].replace("_", "\\_")) for line in lines_values] + [9] - ) + max_name = max([len(line[0].replace("_", "\\_")) for line in lines_values] + [9]) max_name += 1 h_form = dict( - Name="| {:{name_len}}|", Stmts="{:>9} |", Miss="{:>9} |", - Branch="{:>9} |", BrPart="{:>9} |", Cover="{:>{n}} |", - Missing="{:>10} |") - max_n = max(len(total_line[header.index("Cover")]) + 6, - len(" Cover ") + Name="| {:{name_len}}|", + Stmts="{:>9} |", + Miss="{:>9} |", + Branch="{:>9} |", + BrPart="{:>9} |", + Cover="{:>{n}} |", + Missing="{:>10} |", ) - header_items = [ - h_form[item].format(item, name_len=max_name, n=max_n) for item in header] + max_n = max(len(total_line[header.index("Cover")]) + 6, len(" Cover ")) + header_items = [h_form[item].format(item, name_len=max_name, n=max_n) for item in header] header_str = "".join(header_items) rule_str = "|" + " ".join(["- |".rjust(len(header_items[0])-1, '-')] + ["-: |".rjust(len(item)-1, '-') for item in header_items[1:]] @@ -114,7 +122,8 @@ def _report_markdown(self, header, lines_values, total_line, end_lines): h_form.update(dict(Cover="{:>{n}}% |")) line_items = [ h_form[item].format(str(value).replace("_", "\\_"), - name_len=max_name, n=max_n-1) for item, value in zip(header, values)] + name_len=max_name, n=max_n-1) for item, value in zip(header, values) + ] text = "".join(line_items) self.writeout(text) @@ -125,12 +134,10 @@ def _report_markdown(self, header, lines_values, total_line, end_lines): if value == '': insert = value elif item == "Cover": - insert = " **"+str(value)+"%**" + insert = f" **{value}%**" else: - insert = " **"+str(value)+"**" - total_line_items += h_form[item].format( - insert, name_len=max_name, n=max_n - ) + insert = f" **{value}**" + total_line_items += h_form[item].format(insert, name_len=max_name, n=max_n) total_row_str = "".join(total_line_items) self.writeout(total_row_str) for end_line in end_lines: @@ -188,11 +195,9 @@ def report(self, morfs, outfile=None): if sort_idx is None: raise ConfigError(f"Invalid sorting option: {self.config.sort!r}") if sort_option == "name": - lines_values.sort(key=lambda tup: (human_key(tup[0]), tup[1]), - reverse=reverse) + lines_values.sort(key=lambda tup: (human_key(tup[0]), tup[1]), reverse=reverse) else: - lines_values.sort(key=lambda tup: (tup[sort_idx], tup[0]), - reverse=reverse) + lines_values.sort(key=lambda tup: (tup[sort_idx], tup[0]), reverse=reverse) # calculate total if we had at least one file. total_line = ("TOTAL", self.total.n_statements, self.total.n_missing) @@ -209,8 +214,8 @@ def report(self, morfs, outfile=None): if self.config.skip_covered and self.skipped_count: file_suffix = 's' if self.skipped_count>1 else '' - fmt_skip_covered = (f"\n{self.skipped_count} file{file_suffix} skipped due to " - + "complete coverage." + fmt_skip_covered = ( + f"\n{self.skipped_count} file{file_suffix} skipped due to complete coverage." ) end_lines.append(fmt_skip_covered) if self.config.skip_empty and self.empty_count: @@ -218,11 +223,12 @@ def report(self, morfs, outfile=None): fmt_skip_empty = f"\n{self.empty_count} empty file{file_suffix} skipped." end_lines.append(fmt_skip_empty) - text_format = self.config.output_format or 'text' - if text_format.lower() == 'markdown': - self._report_markdown(header, lines_values, total_line, end_lines) + text_format = self.config.output_format or "text" + if text_format == "markdown": + formatter = self._report_markdown else: - self._report_text(header, lines_values, total_line, end_lines) + formatter = self._report_text + formatter(header, lines_values, total_line, end_lines) return self.total.n_statements and self.total.pc_covered diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index d5d63d882..1b9a1ef09 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -44,7 +44,7 @@ class BaseCmdLineTest(CoverageTest): _defaults.Coverage().report( ignore_errors=None, include=None, omit=None, morfs=[], show_missing=None, skip_covered=None, contexts=None, skip_empty=None, precision=None, - sort=None, output_format=None + sort=None, output_format=None, ) _defaults.Coverage().xml_report( ignore_errors=None, include=None, omit=None, morfs=[], outfile=None,