Skip to content

Commit

Permalink
Implement function coverage reporting in lcov reports.
Browse files Browse the repository at this point in the history
Quite straightforward: a function has been executed if any of its region’s
lines have been executed.
  • Loading branch information
zackw committed Sep 11, 2024
1 parent 583961e commit bfc3e7c
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
39 changes: 38 additions & 1 deletion coverage/lcovreport.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,43 @@ def lcov_lines(
outfile.write(f"LH:{analysis.numbers.n_executed}\n")


def lcov_functions(
fr: FileReporter,
file_analysis: Analysis,
outfile: IO[str],
) -> None:
"""Emit function coverage records for an analyzed file."""
# lcov 2.2 introduces a new format for function coverage records.
# We continue to generate the old format because we don't know what
# version of the lcov tools will be used to read this report.

# suppressions because of https://github.com/pylint-dev/pylint/issues/9923
functions = [
(min(region.start, min(region.lines)), #pylint: disable=nested-min-max
max(region.start, max(region.lines)), #pylint: disable=nested-min-max
region)
for region in fr.code_regions()
if region.kind == "function"
]
if not functions:
return

functions.sort()
functions_hit = 0
for first_line, last_line, region in functions:
# A function counts as having been executed if any of it has been
# executed.
analysis = file_analysis.narrow(region.lines)
hit = int(analysis.numbers.n_executed > 0)
functions_hit += hit

outfile.write(f"FN:{first_line},{last_line},{region.name}\n")
outfile.write(f"FNDA:{hit},{region.name}\n")

outfile.write(f"FNF:{len(functions)}\n")
outfile.write(f"FNH:{functions_hit}\n")


def lcov_arcs(
analysis: Analysis,
lines: list[int],
Expand Down Expand Up @@ -177,7 +214,7 @@ def lcov_file(
source_lines = []

lcov_lines(analysis, lines, source_lines, outfile)

lcov_functions(fr, analysis, outfile)
if analysis.has_arcs:
lcov_arcs(analysis, lines, outfile)

Expand Down
42 changes: 42 additions & 0 deletions tests/test_lcov.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ def IsItTrue():
DA:5,0
LF:4
LH:2
FN:1,2,cuboid_volume
FNDA:0,cuboid_volume
FN:4,5,IsItTrue
FNDA:0,IsItTrue
FNF:2
FNH:0
end_of_record
""")
self.assert_doesnt_exist(".coverage")
Expand Down Expand Up @@ -96,6 +102,12 @@ def IsItTrue():
DA:5,0,LWILTcvARcydjFFyo9qM0A
LF:4
LH:2
FN:1,2,cuboid_volume
FNDA:0,cuboid_volume
FN:4,5,IsItTrue
FNDA:0,IsItTrue
FNF:2
FNH:0
end_of_record
""")
actual_result = self.get_lcov_report_content()
Expand All @@ -120,6 +132,12 @@ def test_simple_line_coverage_two_files(self) -> None:
DA:5,0
LF:4
LH:2
FN:1,2,cuboid_volume
FNDA:0,cuboid_volume
FN:4,5,IsItTrue
FNDA:0,IsItTrue
FNF:2
FNH:0
end_of_record
SF:test_file.py
DA:1,1
Expand All @@ -132,6 +150,10 @@ def test_simple_line_coverage_two_files(self) -> None:
DA:9,0
LF:8
LH:4
FN:5,9,TestCuboid.test_volume
FNDA:0,TestCuboid.test_volume
FNF:1
FNH:0
end_of_record
""")
actual_result = self.get_lcov_report_content(filename="data.lcov")
Expand Down Expand Up @@ -160,6 +182,10 @@ def is_it_x(x):
DA:5,0
LF:4
LH:1
FN:1,5,is_it_x
FNDA:0,is_it_x
FNF:1
FNH:0
BRDA:2,0,to line 3,-
BRDA:2,0,to line 5,-
BRF:2
Expand Down Expand Up @@ -203,6 +229,10 @@ def test_is_it_x(self):
DA:5,0
LF:4
LH:1
FN:1,5,is_it_x
FNDA:0,is_it_x
FNF:1
FNH:0
BRDA:2,0,to line 3,-
BRDA:2,0,to line 5,-
BRF:2
Expand All @@ -217,6 +247,10 @@ def test_is_it_x(self):
DA:7,0
LF:6
LH:4
FN:5,7,TestIsItX.test_is_it_x
FNDA:0,TestIsItX.test_is_it_x
FNF:1
FNH:0
end_of_record
""")
actual_result = self.get_lcov_report_content()
Expand Down Expand Up @@ -348,6 +382,10 @@ def foo(a):
DA:7,1
LF:7
LH:7
FN:1,3,foo
FNDA:1,foo
FNF:1
FNH:1
BRDA:2,0,to line 3,1
BRDA:2,0,to exit,1
BRF:2
Expand Down Expand Up @@ -381,6 +419,10 @@ def foo(a):
DA:7,1
LF:7
LH:7
FN:1,3,foo
FNDA:1,foo
FNF:1
FNH:1
BRDA:2,0,to line 3,1
BRDA:2,0,to exit 1,1
BRDA:2,0,to exit 2,1
Expand Down

0 comments on commit bfc3e7c

Please sign in to comment.