Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var/
.installed.cfg
*.egg
.eggs
.venv

# Installer logs
pip-log.txt
Expand All @@ -32,6 +33,7 @@ htmlcov/
.tox/
.coverage
.cache
.history
nosetests.xml
coverage.xml
junit.xml
Expand Down
18 changes: 16 additions & 2 deletions pycobertura/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ def get_exit_code(differ: CoberturaDiff, source):
"the --source is a zip archive and the files were zipped under "
"a directory prefix that is not part of the source.",
)
@click.option(
'-cols',
'--show-columns',
help='String in format item1,item2,item3',
multiple=True
)
def show(
cobertura_file,
ignore_regex,
Expand All @@ -127,6 +133,7 @@ def show(
annotation_level,
annotation_title,
annotation_message,
show_columns,
):
"""show coverage summary of a Cobertura report"""

Expand All @@ -138,7 +145,7 @@ def show(
filesystem=filesystem_factory(source, source_prefix=source_prefix),
)
Reporter = reporters[format]
reporter = Reporter(cobertura, ignore_regex)
reporter = Reporter(cobertura, ignore_regex, show_columns)

if format == "csv":
report = reporter.generate(delimiter)
Expand Down Expand Up @@ -271,6 +278,12 @@ def show(
type=str,
help="annotation message for github annotation format",
)
@click.option(
'-cols',
'--show-columns',
help='String in format item1,item2,item3',
multiple=True
)
def diff(
cobertura_file1,
cobertura_file2,
Expand All @@ -287,6 +300,7 @@ def diff(
annotation_level,
annotation_title,
annotation_message,
show_columns
):
"""compare coverage of two Cobertura reports"""
# Assume that the source is located in the same directory as the provided
Expand All @@ -304,7 +318,7 @@ def diff(
cobertura2 = Cobertura(cobertura_file2, filesystem=filesystem2)

Reporter = delta_reporters[format]
reporter_args = [cobertura1, cobertura2, ignore_regex]
reporter_args = [cobertura1, cobertura2, ignore_regex, show_columns]
reporter_kwargs = {"show_source": source}

isatty = True if output is None else output.isatty()
Expand Down
87 changes: 45 additions & 42 deletions pycobertura/reporters.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,22 @@
env.filters["is_not_equal_to_dash"] = filters.is_not_equal_to_dash
env.filters["misses_color"] = filters.misses_color

headers_with_missing = ["Filename", "Stmts", "Miss", "Cover", "Missing"]
headers_without_missing = ["Filename", "Stmts", "Miss", "Cover"]


class Reporter:
def __init__(self, cobertura, ignore_regex=None):
def __init__(self, cobertura, ignore_regex=None, show_columns=None):
self.cobertura: Cobertura = cobertura
self.ignore_regex = ignore_regex
self.show_columns = show_columns

@staticmethod
def format_line_rates(summary_lines):
for i, line_rate in enumerate(summary_lines["Cover"]):
summary_lines["Cover"][i] = f"{line_rate:.2%}"
def format_line_rates(self, summary_lines):
if (not self.show_columns or "Cover" in self.show_columns):
for i, line_rate in enumerate(summary_lines["Cover"]):
summary_lines["Cover"][i] = f"{line_rate:.2%}"

@staticmethod
def format_missing_lines(summary_lines):
for i, missing_lines in enumerate(summary_lines["Missing"]):
summary_lines["Missing"][i] = stringify(missing_lines)
def format_missing_lines(self, summary_lines):
if (not self.show_columns or "Missing" in self.show_columns):
for i, missing_lines in enumerate(summary_lines["Missing"]):
summary_lines["Missing"][i] = stringify(missing_lines)

def get_summary_lines(self):
filenames = self.cobertura.files(ignore_regex=self.ignore_regex)
Expand Down Expand Up @@ -73,6 +71,9 @@ def get_summary_lines(self):
summary_lines["Cover"].append(total_rate)
summary_lines["Missing"].append([])

if self.show_columns:
summary_lines = {k:v for k, v in summary_lines.items() if k in self.show_columns}

return summary_lines

def per_file_stats(self, summary_lines):
Expand Down Expand Up @@ -120,33 +121,36 @@ def generate(self):
summary_lines = self.get_summary_lines()
self.format_line_rates(summary_lines)
self.format_missing_lines(summary_lines)
return tabulate(summary_lines, headers=headers_with_missing)
headers = list(summary_lines.keys())
return tabulate(summary_lines, headers=headers)



class CsvReporter(Reporter):
def generate(self, delimiter):
summary_lines = self.get_summary_lines()
headers = list(summary_lines.keys())
values = list(summary_lines.values())

self.format_line_rates(summary_lines)
self.format_missing_lines(summary_lines)

list_of_lines = [headers_with_missing]
list_of_lines.extend(
[[f"{item}" for item in row] for row in zip(*summary_lines.values())]
)
list_of_lines = [headers]
# Transpose the columns into rows
rows = list(zip(*values))
list_of_lines.extend(rows)

# Explanation here:
# https://stackoverflow.com/a/55889036/9698518
delimiter = delimiter.encode().decode("unicode_escape")

return "\n".join([delimiter.join(line) for line in list_of_lines])
return "\n".join([delimiter.join(map(str, line)) for line in list_of_lines])


class MarkdownReporter(Reporter):
def generate(self):
summary_lines = self.get_summary_lines()
self.format_line_rates(summary_lines)
self.format_missing_lines(summary_lines)
return tabulate(summary_lines, headers=headers_with_missing, tablefmt="github")
return tabulate(summary_lines, headers=list(summary_lines.keys()), tablefmt="github")


class JsonReporter(Reporter):
Expand Down Expand Up @@ -210,6 +214,7 @@ def __init__(
cobertura1,
cobertura2,
ignore_regex=None,
show_columns = None,
show_source=True,
*args,
**kwargs,
Expand All @@ -218,6 +223,7 @@ def __init__(
self.show_source = show_source
self.color = kwargs.pop("color", False)
self.ignore_regex = ignore_regex
self.show_columns = show_columns

def format_line_rate(self, line_rate):
return f"{line_rate:+.2%}" if line_rate else "+100.00%"
Expand Down Expand Up @@ -319,6 +325,10 @@ def get_summary_lines(self):
diff_total_missing[i] for i in indexes_of_files_with_changes
]
summary_lines["Missing"].append("") # for total line


if self.show_columns:
summary_lines = {k:v for k, v in summary_lines.items() if k in self.show_columns}

return summary_lines

Expand All @@ -342,37 +352,31 @@ def per_file_stats(self, summary_lines):
class TextReporterDelta(DeltaReporter):
def generate(self):
summary_lines = self.get_summary_lines()
headers = headers_without_missing
headers = list(summary_lines.keys())

if self.show_source:
if self.show_source and "Missing" in headers:
missed_lines_colored = [
self.color_number([str(m[0]) for m in missing])
for missing in summary_lines["Missing"]
]

summary_lines["Missing"] = missed_lines_colored
headers = headers_with_missing
return tabulate(summary_lines, headers=headers)


class CsvReporterDelta(DeltaReporter):
def generate(self, delimiter):
summary_lines = self.get_summary_lines()

# lines_values: List of lines dictionary values arranged in
# tuples of Table row values
lines_values = list(zip(*summary_lines.values()))
headers = list(summary_lines.keys())
values = list(summary_lines.values())

# Stringify every item in Table row values without using the Missing column
# and store in the list list_of_lines
list_of_lines = [headers_without_missing]
list_of_lines.extend([[f"{item}" for item in row[:-1]] for row in lines_values])
list_of_lines = [headers]
rows = list(zip(*values))
list_of_lines.extend(rows)

if self.show_source:
# Add the Missing header to list_of_lines first inner list
# This is a direct assignment to avoid appending an additional "Missing"
# header in every iteration of the tests which would fail them
list_of_lines[0] = headers_with_missing
if self.show_source and "Missing" in list_of_lines:
# Add to every list inside the list_of_lines the Missing column value
for line_index, missing_line in enumerate(summary_lines["Missing"]):
# for colors, explanation see here:
Expand All @@ -389,29 +393,28 @@ def generate(self, delimiter):
# https://stackoverflow.com/a/55889036/9698518
delimiter = delimiter.encode().decode("unicode_escape")

return "\n".join([delimiter.join(line) for line in list_of_lines])
return "\n".join([delimiter.join(map(str, line)) for line in list_of_lines])


class MarkdownReporterDelta(DeltaReporter):
def generate(self):
summary_lines = self.get_summary_lines()
headers = headers_without_missing
headers = list(summary_lines.keys())

if self.show_source:
if self.show_source and "Missing" in headers:
missed_lines_colored = [
self.color_number([str(m[0]) for m in missing])
for missing in summary_lines["Missing"]
]
summary_lines["Missing"] = missed_lines_colored
headers = headers_with_missing
return tabulate(summary_lines, headers=headers, tablefmt="github")


class JsonReporterDelta(DeltaReporter):
def generate(self):
summary_lines = self.get_summary_lines()

if self.show_source:
if self.show_source and "Missing" in summary_lines.keys():
missed_lines_colored = [
self.color_number([str(m[0]) for m in missing])
for missing in summary_lines["Missing"]
Expand All @@ -433,7 +436,7 @@ class YamlReporterDelta(DeltaReporter):
def generate(self):
summary_lines = self.get_summary_lines()

if self.show_source:
if self.show_source and "Missing" in summary_lines.keys():
missed_lines_colored = [
self.color_number([str(m[0]) for m in missing])
for missing in summary_lines["Missing"]
Expand Down Expand Up @@ -554,4 +557,4 @@ def generate(
)
)
result = "\n".join(result_strs)
return result
return result
Loading
Loading