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

When invoked with --calls, show call coverage #666

Merged
merged 26 commits into from Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a3dd58f
When invoked with --calls, show call coverage
pablmart Aug 31, 2022
6686c6c
Fix CallCoverage class documentation
pablmart Aug 31, 2022
fae82e7
Extra colgroups for decisions and calls if enabled.
pablmart Aug 31, 2022
4b1a45c
Fix {%if indentations
pablmart Aug 31, 2022
7d45c6f
Use separate styles and variables for call coverage
pablmart Aug 31, 2022
3640cd5
Added a test for call coverage
pablmart Aug 31, 2022
b5155a2
State that calls coverage only is for HTML (not json yet)
pablmart Aug 31, 2022
c805992
Fix PEP-8 formatting errors
pablmart Sep 1, 2022
b5ea922
Update CHANGELOG and AUTHORS
pablmart Sep 1, 2022
a3c57b6
Fix misplaced item in ChangeLog
pablmart Sep 1, 2022
f5d08f6
Fix more formatting errors
pablmart Sep 1, 2022
f4d8a7f
Remove white line that made many expected html output to fail
pablmart Sep 1, 2022
fa7490c
Updated example_html.details.css
pablmart Sep 1, 2022
717c054
Updated test references retrieved from Ubuntu 18.04 vm
pablmart Sep 7, 2022
c611c87
Extend test with logical expression.
Spacetown Sep 7, 2022
e04dbb1
Generate reference data.
Spacetown Sep 7, 2022
d3ee569
Fix test failure.
Spacetown Sep 7, 2022
f49826d
Insert call coverage (optionally) in json output
pablmart Sep 14, 2022
37881e3
Merge branch 'master' of github.com:odkq/gcovr
pablmart Sep 14, 2022
61aa9b1
Support for merging call coverage from json reports
pablmart Sep 16, 2022
e6194ec
Preserve call number when writing the json file
pablmart Sep 20, 2022
1b0f9e7
assert that callno's are the same on call cov merging
pablmart Sep 22, 2022
2d76487
Fix PEP-8 issues
pablmart Sep 27, 2022
a733f02
Fix PEP-8 issues
Spacetown Sep 27, 2022
6ab7cd4
Fix test and update reference.
Spacetown Sep 27, 2022
c316796
Fix some version identifiers in JSON test data.
Spacetown Sep 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS.txt
Expand Up @@ -56,6 +56,7 @@ The following developers contributed to gcovr (ordered alphabetically):
Mikk Leini,
Nikolaj Schumacher,
Oleksiy Pikalo,
Pablo Martín,
Phil Clapham,
Piotr Dziwinski,
Reto Schneider,
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.rst
Expand Up @@ -13,6 +13,7 @@ Breaking changes:
New features and notable changes:
- Accept `NAN %` which is used in GCOV 7.5.0 instead of an invalid value. (:issue:`651`)
- New :option:`--json-base` to define a base bath used in JSON reports. (:issue:`656`)
- New :option:`--calls` to report call coverage: function calls invoked/total (:issue:`666`)

Bug fixes and small improvements:
- Fix :option:`--html-tab-size` feature. (:issue:`650`)
Expand Down
21 changes: 17 additions & 4 deletions doc/examples/example_html.details.css
Expand Up @@ -17,6 +17,8 @@
--takenDecision_color: green;
--uncheckedDecision_color: darkorange;
--notTakenDecision_color: red;
--invokedCall_color: green;
--notInvokedCall_color: red;
}

.theme-blue {
Expand Down Expand Up @@ -335,23 +337,24 @@ span.excludedLine
background-color: var(--excluded_color) !important;
}

.linebranch, .linedecision, .linecount
.linebranch, .linedecision, .linecall, .linecount
{
font-family: monospace;
border-right: 1px gray solid;
background-color: lightgray;
text-align: right;
}

.linebranchDetails, .linedecisionDetails

.linebranchDetails, .linedecisionDetails, .linecallDetails
{
position: relative;
}
.linebranchSummary, .linedecisionSummary
.linebranchSummary, .linedecisionSummary, .linecallSummary
{
cursor: help;
}
.linebranchContents, .linedecisionContents
.linebranchContents, .linedecisionContents, .linecallContents
{
font-family: sans-serif;
font-size: small;
Expand Down Expand Up @@ -390,6 +393,16 @@ span.excludedLine
color: var(--uncheckedDecision_color) !important;
}

.invokedCall
{
color: var(--invokedCall_color) !important;
}

.notInvokedCall
{
color: var(--notInvokedCall_color) !important;
}

.src
{
padding-left: 12px;
Expand Down
21 changes: 17 additions & 4 deletions doc/examples/example_html.html
Expand Up @@ -25,6 +25,8 @@
--takenDecision_color: green;
--uncheckedDecision_color: darkorange;
--notTakenDecision_color: red;
--invokedCall_color: green;
--notInvokedCall_color: red;
}

.theme-blue {
Expand Down Expand Up @@ -343,23 +345,24 @@
background-color: var(--excluded_color) !important;
}

.linebranch, .linedecision, .linecount
.linebranch, .linedecision, .linecall, .linecount
{
font-family: monospace;
border-right: 1px gray solid;
background-color: lightgray;
text-align: right;
}

.linebranchDetails, .linedecisionDetails

.linebranchDetails, .linedecisionDetails, .linecallDetails
{
position: relative;
}
.linebranchSummary, .linedecisionSummary
.linebranchSummary, .linedecisionSummary, .linecallSummary
{
cursor: help;
}
.linebranchContents, .linedecisionContents
.linebranchContents, .linedecisionContents, .linecallContents
{
font-family: sans-serif;
font-size: small;
Expand Down Expand Up @@ -398,6 +401,16 @@
color: var(--uncheckedDecision_color) !important;
}

.invokedCall
{
color: var(--invokedCall_color) !important;
}

.notInvokedCall
{
color: var(--notInvokedCall_color) !important;
}

.src
{
padding-left: 12px;
Expand Down
7 changes: 7 additions & 0 deletions gcovr/configuration.py
Expand Up @@ -763,6 +763,13 @@ def merge_options_and_set_defaults(
help="Report the decision coverage. For HTML and JSON report.",
action="store_true",
),
GcovrConfigOption(
"show_calls",
["--calls"],
group="output_options",
help="Report the calls coverage. For HTML report.",
action="store_true",
),
Spacetown marked this conversation as resolved.
Show resolved Hide resolved
GcovrConfigOption(
"sort_uncovered",
["-u", "--sort-uncovered"],
Expand Down
40 changes: 40 additions & 0 deletions gcovr/coverage.py
Expand Up @@ -72,6 +72,27 @@ def is_covered(self) -> bool:
return self.count > 0


class CallCoverage:
r"""Represent coverage information about a call.

Args:
odkq marked this conversation as resolved.
Show resolved Hide resolved
covered (bool):
Whether the call was performed.
"""

__slots__ = "covered"

def __init__(
self,
covered: bool,
) -> None:
self.covered = covered

@property
def is_covered(self) -> bool:
return self.covered


class DecisionCoverageUncheckable:
r"""Represent coverage information about a decision."""

Expand Down Expand Up @@ -156,6 +177,7 @@ class LineCoverage:
"branches",
"decision",
"functions",
"calls",
)

def __init__(
Expand All @@ -170,6 +192,7 @@ def __init__(
self.excluded: bool = excluded
self.branches: Dict[int, BranchCoverage] = {}
self.decision: Optional[DecisionCoverage] = None
self.calls: Dict[int, CallCoverage] = {}

@property
def is_excluded(self) -> bool:
Expand Down Expand Up @@ -272,6 +295,19 @@ def decision_coverage(self) -> DecisionCoverageStat:

return stat

def call_coverage(self) -> CoverageStat:
covered = 0
total = 0

for line in self.lines.values():
if len(line.calls) > 0:
for call in line.calls.values():
total += 1
if call.is_covered:
covered += 1

return CoverageStat(covered, total)


CovData = Dict[str, FileCoverage]

Expand All @@ -282,6 +318,7 @@ class SummarizedStats:
branch: CoverageStat
function: CoverageStat
decision: DecisionCoverageStat
call: CoverageStat

@staticmethod
def new_empty() -> SummarizedStats:
Expand All @@ -290,6 +327,7 @@ def new_empty() -> SummarizedStats:
branch=CoverageStat.new_empty(),
function=CoverageStat.new_empty(),
decision=DecisionCoverageStat.new_empty(),
call=CoverageStat.new_empty(),
)

@staticmethod
Expand All @@ -306,13 +344,15 @@ def from_file(filecov: FileCoverage) -> SummarizedStats:
branch=filecov.branch_coverage(),
function=filecov.function_coverage(),
decision=filecov.decision_coverage(),
call=filecov.call_coverage(),
)

def __iadd__(self, other: SummarizedStats) -> SummarizedStats:
self.line += other.line
self.branch += other.branch
self.function += other.function
self.decision += other.decision
self.call += other.call
return self


Expand Down
2 changes: 2 additions & 0 deletions gcovr/gcov.py
Expand Up @@ -153,6 +153,8 @@ def process_gcov_data(
parser_flags |= ParserFlags.RESPECT_EXCLUSION_MARKERS
if options.show_decision:
parser_flags |= ParserFlags.PARSE_DECISIONS
if options.show_calls:
parser_flags |= ParserFlags.PARSE_CALLS

coverage = parse_coverage(
lines,
Expand Down
24 changes: 22 additions & 2 deletions gcovr/gcov_parser.py
Expand Up @@ -52,13 +52,14 @@
NoReturn,
)

from .coverage import BranchCoverage, FileCoverage, FunctionCoverage
from .coverage import BranchCoverage, FileCoverage, FunctionCoverage, CallCoverage
from .decision_analysis import DecisionParser
from .merging import (
MergeOptions,
get_or_create_line_coverage,
insert_branch_coverage,
insert_function_coverage,
insert_call_coverage,
)


Expand Down Expand Up @@ -295,6 +296,9 @@ class ParserFlags(enum.Flag):
PARSE_DECISIONS = enum.auto()
"""Whether decision coverage shall be generated."""

PARSE_CALLS = enum.auto()
"""Whether call coverage shall be generated."""


_LineWithError = Tuple[str, Exception]

Expand Down Expand Up @@ -504,7 +508,23 @@ def _gather_coverage_from_line(
return state

# ignore unused line types, such as specialization sections
elif isinstance(line, (_CallLine, _UnconditionalLine, _BlockLine)):
elif isinstance(line, _CallLine):
callno, returned = line
line_cov = coverage.lines[state.lineno] # must already exist


if context.flags & ParserFlags.PARSE_CALLS:
insert_call_coverage(
line_cov,
callno,
CallCoverage(
covered=(returned > 0),
),
)

return state

elif isinstance(line, (_UnconditionalLine, _BlockLine)):
return state

else:
Expand Down
26 changes: 26 additions & 0 deletions gcovr/merging.py
Expand Up @@ -60,6 +60,7 @@
FileCoverage,
FunctionCoverage,
LineCoverage,
CallCoverage,
)


Expand Down Expand Up @@ -233,6 +234,7 @@ def merge_line(
left.excluded |= right.excluded
left.branches = _merge_dict(left.branches, right.branches, merge_branch, options)
left.decision = merge_decision(left.decision, right.decision, options)
left.calls = _merge_dict(left.calls, right.calls, merge_call, options)

return left

Expand Down Expand Up @@ -305,6 +307,30 @@ def merge_branch(
return left


def insert_call_coverage(
target: LineCoverage,
call_id: int,
call: CallCoverage,
options: MergeOptions = DEFAULT_MERGE_OPTIONS,
) -> CallCoverage:
"""Insert BranchCoverage into LineCoverage."""
return _insert_coverage_item(target.calls, call_id, call, merge_call, options)


def merge_call(
left: CallCoverage,
right: CallCoverage,
options: MergeOptions = DEFAULT_MERGE_OPTIONS,
) -> BranchCoverage:
"""
Merge CallCoverage information.

Do not use 'left' or 'right' objects afterwards!
"""
left.covered |= right.covered
return left


def insert_decision_coverage(
target: LineCoverage,
decision: Optional[DecisionCoverage],
Expand Down
23 changes: 22 additions & 1 deletion gcovr/templates/root_page.html
Expand Up @@ -94,7 +94,15 @@
<td>{{info.decisions.total}}</td>
<td class="{{info.decisions.class}}">{{info.decisions.coverage}}%</td>
</tr>
{% endif %}
{% endif %}
{% if SHOW_CALL %}
odkq marked this conversation as resolved.
Show resolved Hide resolved
<tr>
<th scope="row">Calls:</th>
<td>{{info.calls.exec}}</td>
<td>{{info.calls.total}}</td>
<td class="{{info.calls.class}}">{{info.calls.coverage}}%</td>
</tr>
{% endif %}
</table>
</div>
{% endblock %}
Expand All @@ -111,6 +119,12 @@
<colgroup span="3"/>
<colgroup span="2"/>
<colgroup span="2"/>
{% if SHOW_DECISION %}
<colgroup span="2"/>
{% endif %}
{% if SHOW_CALL %}
<colgroup span="2"/>
{% endif %}

<tr>
<th scope="col">File</th>
Expand All @@ -120,6 +134,9 @@
{% if SHOW_DECISION %}
<th scope="colgroup" colspan=2>Decisions</th>
{% endif %}
{% if SHOW_CALL %}
odkq marked this conversation as resolved.
Show resolved Hide resolved
<th scope="colgroup" colspan=2>Calls</th>
{% endif %}
</tr>

{% for row in info.files %}
Expand All @@ -145,6 +162,10 @@
<td class="CoverValue decision-coverage {{row.decisions.class}}">{{row.decisions.coverage}}%</td>
<td class="CoverValue decision-coverage {{row.decisions.class}}">{{row.decisions.exec}} / {{row.decisions.total}}</td>
{% endif %}
{% if SHOW_CALL %}
<td class="CoverValue branch-coverage {{row.branches.class}}">{{row.calls.coverage}}%</td>
<td class="CoverValue branch-coverage {{row.branches.class}}">{{row.calls.exec}} / {{row.calls.total}}</td>
{% endif %}
</tr>

{% endfor %}
Expand Down