ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
suppression_identity_selftest.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Both-direction fixtures for the durable site/binding identity."""
4
5from __future__ import annotations
6
7from dataclasses import replace
8from pathlib import Path
9
10from selftest_assert import expect
11from suppression_identity import assign_identities
12from suppression_model import Inventory
13from suppression_scan import scan_paths
14
15
16def _rows(root: Path, files: dict[str, str]) -> dict[tuple[str, int], object]:
17 """Scan an explicit fixture tree and index rows by path and line."""
18 for rel, text in files.items():
19 target = root / rel
20 target.parent.mkdir(parents=True, exist_ok=True)
21 target.write_text(text, encoding="ascii")
22 inventory = scan_paths(root, sorted(files))
23 return {(item.path, item.line): item for item in inventory.suppressions}
24
25
26def _fixture(marker_a: str, marker_b: str, prefix: str = "", indent: str = "") -> str:
27 """Compose one two-marker shell fixture with optional prefix lines."""
28 return (
29 f"{prefix}#!/bin/sh\n"
30 f"{indent}probe || true # {marker_a}\n"
31 "echo steady\n"
32 f"other || true # {marker_b}\n"
33 )
34
35
36def assert_identity_semantics(base: Path, failures: list[str]) -> None:
37 """Assert site identity survives movement and binding tracks content."""
38 left = base / "identity-a"
39 right = base / "identity-b"
40 files = {"tool.sh": _fixture("cleanup best effort", "second reason")}
41 moved = {"tool.sh": _fixture("cleanup best effort", "second reason", prefix="# banner\n")}
42 rows_a = _rows(left, files)
43 rows_b = _rows(right, moved)
44 first_a = rows_a[("tool.sh", 2)]
45 first_b = rows_b[("tool.sh", 3)]
46 expect(
47 bool(first_a.site_id) and first_a.site_id == first_b.site_id,
48 "quiet: a line inserted above a site preserves its site_id",
49 failures,
50 )
51 expect(
52 first_a.binding_sha256 == first_b.binding_sha256,
53 "quiet: a line inserted above a site preserves its binding",
54 failures,
55 )
56 indented = _rows(
57 base / "identity-c",
58 {"tool.sh": _fixture("cleanup best effort", "second reason", indent=" ")},
59 )
60 expect(
61 indented[("tool.sh", 2)].site_id == first_a.site_id,
62 "quiet: indentation-only movement preserves site identity",
63 failures,
64 )
65 _assert_identity_content(base, first_a, failures)
66
67
68def _assert_identity_content(base: Path, first_a: object, failures: list[str]) -> None:
69 """Assert reason edits preserve sites while construct edits rename them."""
70 reworded = _rows(
71 base / "identity-d", {"tool.sh": _fixture("a different rationale", "second reason")}
72 )
73 changed = reworded[("tool.sh", 2)]
74 expect(
75 changed.site_id == first_a.site_id and changed.binding_sha256 != first_a.binding_sha256,
76 "quiet: reason-only changes preserve the site and invalidate the binding",
77 failures,
78 )
79 reflowed = _rows(
80 base / "identity-reflow",
81 {"tool.sh": "#!/bin/sh\n probe || true # reformatted rationale\n"},
82 )[("tool.sh", 2)]
83 expect(
84 reflowed.site_id == first_a.site_id,
85 "quiet: reason and whitespace reflow preserve site identity",
86 failures,
87 )
88 rewritten = _rows(
89 base / "identity-e",
90 {"tool.sh": "#!/bin/sh\nrewritten_probe || true # cleanup best effort\n"},
91 )
92 expect(
93 rewritten[("tool.sh", 2)].site_id != first_a.site_id,
94 "must fire: changing the suppressed construct renames the site",
95 failures,
96 )
97 _assert_scope_and_directive_rekey(base / "identity-a", first_a, failures)
98 _assert_repeated_identity(base, failures)
99
100
101def _assert_scope_and_directive_rekey(root: Path, original: object, failures: list[str]) -> None:
102 """Assert approved identity dimensions other than reason still re-key."""
103 variants = [
104 replace(original, scope="changed-scope", site_id="", binding_sha256="", anchor=""),
105 replace(original, directive="changed-directive", site_id="", binding_sha256="", anchor=""),
106 ]
107 inventory = Inventory(suppressions=variants)
108 assign_identities(inventory, root)
109 expect(
110 all(item.site_id != original.site_id for item in inventory.suppressions),
111 "must fire: scope and directive changes rename the site",
112 failures,
113 )
114
115
116def _assert_repeated_identity(base: Path, failures: list[str]) -> None:
117 """Assert ordinals separate repeats without making order semantic."""
118 repeated = _rows(
119 base / "identity-f",
120 {"tool.sh": "#!/bin/sh\nprobe || true # same\nprobe || true # same\n"},
121 )
122 ids = {item.site_id for item in repeated.values()}
123 expect(
124 len(repeated) > 1 and len(ids) == len(repeated),
125 "must fire: repeated identical directives stay individually represented",
126 failures,
127 )
128 swapped_one = _rows(
129 base / "identity-g",
130 {"tool.sh": "#!/bin/sh\nalpha || true # one\nbeta || true # two\n"},
131 )
132 swapped_two = _rows(
133 base / "identity-h",
134 {"tool.sh": "#!/bin/sh\nbeta || true # two\nalpha || true # one\n"},
135 )
136 expect(
137 {item.site_id for item in swapped_one.values()}
138 == {item.site_id for item in swapped_two.values()},
139 "quiet: reordering distinct sites preserves the identity set",
140 failures,
141 )