3"""Deterministic fixtures for the MISRA deviation-register checker self-test."""
5from __future__
import annotations
7Case = tuple[str, str, str, str, int]
10def _fixture_baseline(rules: list[str]) -> tuple[str, list[tuple[str, str, int]]]:
11 """Build the synthetic baseline and return its parsed source rows."""
12 base_rows = [(
"a.c", rules[0], 3), (
"b.c", rules[0], 1)]
13 base_rows += [(f
"f{i}.c", rules[i - 1], i)
for i
in range(2, len(rules) + 1)]
14 total = sum(count
for _file, _rule, count
in base_rows)
18 "# cppcheck: Cppcheck 9.9.9",
19 f
"# total findings: {total}",
20 "# columns: file<TAB>rule<TAB>count",
21 *[f
"{file}\t{rule}\t{count}" for file, rule, count
in sorted(base_rows)],
25 return baseline, base_rows
28def _fixture_suppressions(rules: list[str]) -> str:
29 """Build the synthetic cppcheck suppression list."""
32 "# fixture suppressions",
33 "unusedStructMember:inc/*.h",
37 *[f
"{rule}:filler{i}.c" for i, rule
in enumerate(rules[2:12])],
43def _fixture_index_lines(
44 rules: list[str], base_rows: list[tuple[str, str, int]]
45) -> tuple[list[str], list[tuple[str, str]]]:
46 """Build the register index and machine-derived population lines."""
47 deviations = [(f
"D-{i:03d}", rules[i - 1])
for i
in range(1, 7)]
49 f
"| {deviation} | {rule} | Advisory | Fixture | Active | 2027-01-01 "
50 f
"| {4 if rule == rules[0] else int(rule.split('-')[2].split('.')[0])} "
51 f
"| {2 if rule == rules[0] else 1} |"
52 for deviation, rule
in deviations
54 residual_rules = rules[6:]
55 residual_findings = sum(int(rule.split(
"-")[2].split(
".")[0])
for rule
in residual_rules)
56 total = sum(count
for _file, _rule, count
in base_rows)
60 "Fixture header (D-001..D-006 active).",
64 "| ID | Rule | Category | Class | Status | MAR | Findings | Files |",
65 "|-------|------|----------|-------|--------|-----|---------:|------:|",
68 "## Derived population",
70 f
"Baseline: {total} findings across {len(base_rows)} file/rule rows (Cppcheck 9.9.9).",
72 f
"Residual (no deviation record): {len(residual_rules)} rules, "
73 f
"{residual_findings} findings, {len(residual_rules)} rows.",
76 return lines, deviations
79def _fixture_detail_lines(rules: list[str], deviations: list[tuple[str, str]]) -> list[str]:
80 """Build ownership, excerpt, and deviation-section fixture lines."""
82 f
"- `{rules[0]}` (2 rows, 1 path): fixture family one.",
83 f
"- `{rules[1]}` (1 row, 1 path): fixture family two.",
84 *[f
"- `{rule}` (1 row, 1 path): fixture filler family." for rule
in rules[2:12]],
86 f
"Highest-count files for {rules[0]} (top 2, derived):",
88 "| File | Findings |",
89 "|------|---------:|",
94 f
"## {deviation}: Rule {rule.removeprefix('misra-c2012-')} -- fixture\n"
95 for deviation, rule
in deviations
97 "Population note under the last section: 6 findings across 1 files.",
102def fixture_files() -> dict[str, str]:
103 """Build a mutually consistent register, baseline, and suppression fixture."""
104 rules = [f
"misra-c2012-{i}.1" for i
in range(1, 33)]
105 baseline, base_rows = _fixture_baseline(rules)
106 index_lines, deviations = _fixture_index_lines(rules, base_rows)
107 document =
"\n".join(index_lines + _fixture_detail_lines(rules, deviations))
110 "baseline": baseline,
111 "suppressions": _fixture_suppressions(rules),
115def _register_cases(exit_drift: int, exit_malformed: int) -> list[Case]:
116 """Return index-row and heading mutation cases."""
119 "stale register count fires",
121 "2027-01-01 | 4 | 2 |",
122 "2027-01-01 | 5 | 2 |",
126 "missing register row fires",
128 "| D-003 | misra-c2012-3.1 | Advisory | Fixture | Active | 2027-01-01 | 3 | 1 |\n",
133 "extra register row fires",
135 "## Derived population",
136 "| D-099 | misra-c2012-31.1 | Advisory | Fixture | Active | 2027-01-01 | 31 | 1 |\n"
137 "\n## Derived population",
141 "misshapen register row is malformed",
143 "| D-002 | misra-c2012-2.1 | Advisory | Fixture | Active | 2027-01-01 | 2 | 1 |",
144 "| D-002 | misra-c2012-2.1 | 2 | 1 |",
148 "heading rule mismatch fires",
150 "## D-002: Rule 2.1 -- fixture",
151 "## D-002: Rule 2.2 -- fixture",
157def _claim_cases(exit_drift: int, exit_malformed: int) -> list[Case]:
158 """Return derived-claim and suppression-ownership mutation cases."""
160 (
"missing provenance line is malformed",
"doc",
"Baseline: ",
"Basel1ne: ", exit_malformed),
162 "stale residual fires",
164 "Residual (no deviation record): 26 rules,",
165 "Residual (no deviation record): 27 rules,",
169 "suppression row drift fires",
171 "misra-c2012-1.1:a.c:20",
172 "misra-c2012-1.1:a.c:20\nmisra-c2012-1.1:c.c:9",
176 "unowned suppression family fires",
178 "misra-c2012-2.1:glob/*",
179 "misra-c2012-2.1:glob/*\nmisra-c2012-3.1:zzz.c",
183 "ghost ownership bullet fires",
185 "misra-c2012-2.1:glob/*\n",
192def _evidence_cases(exit_drift: int, exit_malformed: int) -> list[Case]:
193 """Return baseline, excerpt, and population mutation cases."""
196 "misordered excerpt fires",
198 "| `a.c` | 3 |\n| `b.c` | 1 |",
199 "| `b.c` | 1 |\n| `a.c` | 3 |",
203 "tampered baseline header is malformed",
205 "# total findings: ",
206 "# total findings: 1",
210 "vacuous baseline is malformed",
212 "a.c\tmisra-c2012-1.1\t3",
213 "a.c\tmisra-c2012-1.1\tX",
217 "two-column baseline row is malformed",
219 "b.c\tmisra-c2012-1.1\t1",
220 "b.c\tmisra-c2012-1.1",
224 "stale in-section population fires",
226 "6 findings across 1 files.",
227 "7 findings across 1 files.",
233def _shape_cases(exit_drift: int, exit_malformed: int) -> list[Case]:
234 """Return malformed-shape and deleted-evidence mutation cases."""
237 "wrapped excerpt intro is malformed",
239 "Highest-count files for misra-c2012-1.1 (top 2, derived):",
240 "Highest-count files for misra-c2012-1.1\n(top 2, derived):",
244 "short deviation-id register row is malformed",
246 "## Derived population",
247 "| D-11 | misra-c2012-31.1 | Advisory | Fixture | Active | 2027-01-01 | 31 | 1 |\n"
248 "\n## Derived population",
252 "short deviation-id heading is malformed",
254 "## D-002: Rule 2.1 -- fixture",
255 "## D-11: Rule 2.1 -- fixture",
259 "deleting the whole excerpt block is malformed",
261 "Highest-count files for misra-c2012-1.1 (top 2, derived):\n\n"
262 "| File | Findings |\n|------|---------:|\n| `a.c` | 3 |\n| `b.c` | 1 |\n",
267 "deleting one ownership bullet fires as an unowned waiver",
269 "- `misra-c2012-1.1` (2 rows, 1 path): fixture family one.",
276def _retirement_cases(exit_drift: int, exit_malformed: int) -> list[Case]:
277 """Return population-placement and deviation-retirement mutation cases."""
280 "population claim outside a deviation section is malformed",
282 "## Derived population",
283 "Rule 1.1 alone accounts for 4 findings across 2 files.\n\n## Derived population",
287 "population claim reworded with 'in' still fires",
289 "6 findings across 1 files.",
290 "7 findings in 1 file.",
294 "retiring a deviation without amending the header range fires",
296 "| D-006 | misra-c2012-6.1 | Advisory | Fixture | Active | 2027-01-01 | 6 | 1 |\n",
301 "a register row naming a rule absent from the baseline fires",
303 "| D-006 | misra-c2012-6.1 | Advisory | Fixture | Active | 2027-01-01 | 6 | 1 |",
304 "| D-006 | misra-c2012-99.9 | Advisory | Fixture | Active | 2027-01-01 | 0 | 0 |",
308 "a missing header range is malformed",
310 "Fixture header (D-001..D-006 active).",
317def selftest_cases(exit_ok: int, exit_drift: int, exit_malformed: int) -> list[Case]:
318 """Enumerate all mutation cases and the quiet control."""
320 (
"clean fixture passes",
"doc",
"",
"", exit_ok),
321 *_register_cases(exit_drift, exit_malformed),
322 *_claim_cases(exit_drift, exit_malformed),
323 *_evidence_cases(exit_drift, exit_malformed),
324 *_shape_cases(exit_drift, exit_malformed),
325 *_retirement_cases(exit_drift, exit_malformed),