3"""Both-direction fixtures for the stranded coverage-marker detector.
5A `GCOVR_EXCL_BR_LINE` excludes only the branch on its own physical line, and
6gcov attributes a decision to the line where the controlling expression starts.
7So a marker pushed onto a continuation line by clang-format still reads as
8correct in review while excluding nothing. These fixtures are what stop that
9from recurring silently, which makes their own both-direction coverage
10load-bearing rather than decorative.
12Split out of `suppression_selftest.py` when that module reached the repository
16from __future__
import annotations
18from selftest_assert
import expect
19from suppression_inline_scan
import stranded_branch_findings
22def assert_stranded_branch_markers(failures: list[str]) ->
None:
23 """Assert a wrapped branch marker fires and an attached one stays quiet.
25 Both directions plus the block-comment case: the backward walk must not
26 mistake the interior of a multi-line ``/* ... */`` for an unfinished
27 statement, which is how a correct marker would be reported as stranded.
32 " ra8_err_t e = g();\n"
33 " RA8_RETURN_ON_ERROR(e,\n"
35 ' "msg"); /* GCOVR_EXCL_BR_LINE -- hardware only */\n'
41 " ra8_err_t e = g();\n"
42 ' RA8_RETURN_ON_ERROR(e, tag, "msg"); /* GCOVR_EXCL_BR_LINE -- hardware only */\n'
48 " /* a note that runs\n"
49 " * across several lines */\n"
50 " if (x) { /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
56 len(stranded_branch_findings(
"sample.c", wrapped, frozenset())) == 1,
57 "must fire: a branch marker stranded on a wrapped call excludes nothing",
61 not stranded_branch_findings(
"sample.c", attached, frozenset()),
62 "must stay quiet: a branch marker on its own statement line",
66 not stranded_branch_findings(
"sample.c", after_comment, frozenset({3, 4})),
67 "must stay quiet: a marker following a multi-line block comment",
70 assert_stranded_wrapping_shapes(failures)
78_WRAPPING_FIXTURES: tuple[tuple[str, bool, str], ...] = (
83 " for (uint32_t i = 0U; i < limit;\n"
84 " i++) { /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
88 "attached-for-header",
91 " for (uint32_t i = 0U; i < limit; i++) { /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
95 "wrapped-if-condition",
99 " (b != 0)) { /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
103 "attached-if-condition",
106 " if ((a != 0) && (b != 0)) { /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
113 " const uint32_t d = (ms > cap)\n"
115 " : (uint32_t)ms; /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
122 " const uint32_t d = (ms > cap) ? cap : ms; /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
126 "split-initializer-expression",
128 "static const cfg_t c = {\n"
131 " : 0, /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
135 "attached-initializer-expression",
137 "static const cfg_t c = {\n"
138 " .b = (x != 0) ? 1 : 0, /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
142 "wrapped-macro-invocation",
145 " RA8_RETURN_ON_ERROR(e,\n"
147 ' "msg"); /* GCOVR_EXCL_BR_LINE -- hardware only */\n'
151 "attached-macro-invocation",
154 ' RA8_RETURN_ON_ERROR(e, tag, "msg"); /* GCOVR_EXCL_BR_LINE -- hardware only */\n'
158 "after-preprocessor-directive",
162 " if (x) { /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
166 "paren-in-string-literal",
168 'void f(void)\n{\n log(tag, "a) b"); /* GCOVR_EXCL_BR_LINE -- hardware only */\n}\n',
173def assert_stranded_wrapping_shapes(failures: list[str]) ->
None:
174 """Drive one fixture per wrapping shape through the production scan.
177 failures: Accumulator every ``expect`` appends its message to.
179 for name, must_fire, text
in _WRAPPING_FIXTURES:
180 found = stranded_branch_findings(
"sample.c", text, frozenset())
184 f
"must fire: {name} strands its branch marker",
190 f
"must stay quiet: {name} keeps its branch marker attached",
199 " /* a note that runs\n"
200 " * across several lines */\n"
201 " if (x) { /* GCOVR_EXCL_BR_LINE -- hardware only */\n"
207 not stranded_branch_findings(
"sample.c", across_comment, frozenset({4, 5})),
208 "must stay quiet: a marker separated from its statement by block-comment prose",
213def assert_stranded_line_markers(failures: list[str]) ->
None:
214 """Assert a detached line marker fires and an attached one stays quiet.
216 ``GCOVR_EXCL_LINE`` excludes only the physical line it sits on, so a
217 marker pushed onto a comment-only line excludes a line gcov never
218 counted, while the statement it was written for stays in the measured
219 debt. That failure is invisible in review: the comment still sits
220 beside the code it describes.
223 failures: Accumulator every ``expect`` appends its message to.
229 " /* explanation */ /* GCOVR_EXCL_LINE -- host cannot reach this */\n"
233 "void f(void)\n{\n return; /* GCOVR_EXCL_LINE -- host cannot reach this */\n}\n"
236 len(stranded_branch_findings(
"sample.c", orphaned_line, frozenset())) == 1,
237 "must fire: a line marker on a comment-only line excludes no counted line",
241 not stranded_branch_findings(
"sample.c", attached_line, frozenset()),
242 "must stay quiet: a line marker on the statement it excludes",