Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods to cobertura report #655

Closed
wants to merge 4 commits into from
Closed
Changes from 1 commit
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
63 changes: 57 additions & 6 deletions gcovr/writer/cobertura.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

from ..version import __version__
from ..utils import open_binary_for_writing, presentable_filename
from ..coverage import CovData, CoverageStat, LineCoverage, SummarizedStats
from ..coverage import CovData, CoverageStat, FileCoverage, FunctionCoverage, LineCoverage, SummarizedStats


def print_cobertura_report(covdata: CovData, output_file, options):
Expand Down Expand Up @@ -74,14 +74,52 @@ def print_cobertura_report(covdata: CovData, output_file, options):
),
)
c = etree.Element("class")
# The Cobertura DTD requires a methods section, which isn't
# trivial to get from gcov (so we will leave it blank)
etree.SubElement(c, "methods")
lines = etree.SubElement(c, "lines")

sorted_lines = sorted(data.lines)
iter_reversed_sorted_lines = iter(reversed(sorted_lines))
methods = []
for function in sorted(data.functions, key=lambda f: data.functions[f].lineno, reverse=True):
function_cov = data.functions[function]
elem = _method_element(function_cov)
methods.append(elem)

lines = etree.Element("lines")
elem.append(lines)
function_branch = CoverageStat(0, 0)
function_linenos = []
for lineno in iter_reversed_sorted_lines:
if lineno >= function_cov.lineno:
function_linenos.insert(0, lineno)
else:
break
Comment on lines +98 to +103
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to assume that every line belongs to a function, and that there's no code between functions. Perhaps it would be better to avoid making such guesses, and just generate an empty <lines/> element in each method section.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then the method element has no information about coverage.

# Dummy file to get line coverage for function
dummy_file_coverage = FileCoverage("dummy")
for lineno in function_linenos:
line_cov = data.lines[lineno]
dummy_file_coverage.lines[lineno] = line_cov
if not (line_cov.is_covered or line_cov.is_uncovered):
continue

b = line_cov.branch_coverage()
if b.total:
function_branch += b
lines.append(_line_element(line_cov))
Comment on lines +106 to +115
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like duplicate code to the line element generation below – could probably extracted into a function.


stats = SummarizedStats.from_file(dummy_file_coverage)

elem.set("line-rate", _rate(stats.line))
elem.set("branch-rate", _rate(function_branch))
elem.set("complexity", "0.0")


elem = etree.SubElement(c, "methods")
for method in reversed(methods):
elem.append(method)

lines = etree.SubElement(c, "lines")
# TODO should use FileCoverage.branch_coverage() calculation
class_branch = CoverageStat(0, 0)
for lineno in sorted(data.lines):
for lineno in sorted_lines:
line_cov = data.lines[lineno]
if not (line_cov.is_covered or line_cov.is_uncovered):
continue
Expand Down Expand Up @@ -151,6 +189,19 @@ def _rate(stat: CoverageStat) -> str:
return str(covered / total)


def _method_element(function: FunctionCoverage) -> etree.Element:
elem = etree.Element("method")
function_name = function.name
function_signature = "-"
if "(" in function_name:
function_name, function_signature = function_name.split("(", maxsplit=1)
function_signature = f"({function_signature}"
elem.set("name", function_name)
elem.set("signature", function_signature)

return elem


def _line_element(line: LineCoverage) -> etree.Element:
branch = line.branch_coverage()

Expand Down