Skip to content

Commit 464de8d

Browse files
authored
Merge pull request #384 from PhysShell/fix/repro-regeneration-moved-input
fix(tests): a repro artifact can be regenerated after its input bytes move
2 parents 5323dd4 + d2afc9f commit 464de8d

1 file changed

Lines changed: 78 additions & 3 deletions

File tree

‎tests/test_repro_fixtures.py‎

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,13 @@
5252

5353
from __future__ import annotations
5454

55+
import contextlib
5556
import copy
57+
import io
5658
import json
5759
import os
5860
import sys
61+
import tempfile
5962
from typing import Any
6063

6164
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
@@ -231,12 +234,24 @@ def _plan() -> tuple[dict[str, tuple[str, str]], dict[str, dict[str, Any]],
231234
return plan, refusal_entries, sorted(artifact_names), problems
232235

233236

234-
def _foreign_engines(golden_path: str) -> list[dict[str, Any]]:
237+
def _foreign_engines(golden_path: str,
238+
rewriting: bytes | None = None) -> list[dict[str, Any]]:
235239
"""The engine captures already committed in an artifact that this side did
236240
NOT author. `--write` reads them back and carries them through, because an
237241
engine writes only its own entry: an artifact where one implementation
238242
authored another's capture would be a comparison of one thing against
239-
itself."""
243+
itself.
244+
245+
`rewriting` is the input the artifact is being REWRITTEN for (`--write`
246+
only). A committed foreign capture attests the bytes that engine read; when
247+
the input has moved since, that capture is of a different document, and
248+
carrying it through yields an artifact that cannot verify — so the writer
249+
used to refuse, the port's writer refuses until the reference has written
250+
(it demands the artifact's bytes equal the file's), and the documented
251+
two-pass order could not start. A capture of other bytes is not carried:
252+
it is dropped with its reason, exactly like a pre-v3 one, and the port
253+
re-runs and authors its own. Verify mode passes nothing and drops nothing,
254+
so a stale artifact still reads as stale."""
240255
if not os.path.exists(golden_path):
241256
return []
242257
try:
@@ -245,6 +260,16 @@ def _foreign_engines(golden_path: str) -> list[dict[str, Any]]:
245260
except (OSError, json.JSONDecodeError):
246261
return []
247262
carried, dropped = carry_foreign(committed.get("engines"))
263+
if rewriting is not None:
264+
identity = hash_bytes(rewriting)
265+
fresh = [e for e in carried if e.get("consumed") == identity]
266+
dropped += [
267+
f"the {e.get('id')!r} entry attests other bytes "
268+
f"({str(e['consumed'].get('digest'))[:12]}… ≠ {identity['digest'][:12]}…): "
269+
f"the input moved since that engine ran, so its capture is of a "
270+
f"different document"
271+
for e in carried if e.get("consumed") != identity]
272+
carried = fresh
248273
for reason in dropped:
249274
print(f"NOTE: {os.path.basename(golden_path)}: {reason}. Regenerate it "
250275
f"with OWN_SHADOW_WRITE=1 cargo test -p own-shadow --test engine.")
@@ -1081,6 +1106,55 @@ def _carry_controls() -> list[tuple[str, str]]:
10811106
"this engine's OWN entry was carried forward instead of "
10821107
"re-authored — an engine writes only its own entry, and "
10831108
"carrying its own would replay a stale capture"))
1109+
fails += _moved_input_controls()
1110+
return fails
1111+
1112+
1113+
def _moved_input_controls() -> list[tuple[str, str]]:
1114+
"""The writer meets a committed artifact whose INPUT has moved.
1115+
1116+
No committed fixture can reach this: an artifact and its input are always
1117+
regenerated together, so the state only exists between the two halves of a
1118+
regeneration — which is exactly when it deadlocked. Driven on a real
1119+
artifact projected here, with a foreign entry that attests the old bytes."""
1120+
fails: list[tuple[str, str]] = []
1121+
before = b'{"module": "Moved", "components": []}'
1122+
after = b'{"module": "Moved", "components": []}\n'
1123+
foreign = {"id": "rust-own-bridge", "consumed": hash_bytes(before),
1124+
"derived": {"sarif": {}}, "layers": []}
1125+
directory = tempfile.mkdtemp()
1126+
path = os.path.join(directory, "moved.repro.json")
1127+
try:
1128+
with open(path, "w", encoding="utf-8") as f:
1129+
json.dump({"engines": [{"id": ENGINE_PYTHON, "layers": []}, foreign]}, f)
1130+
with contextlib.redirect_stdout(io.StringIO()) as noted:
1131+
unchanged = _foreign_engines(path, rewriting=before)
1132+
moved = _foreign_engines(path, rewriting=after)
1133+
verifying = _foreign_engines(path)
1134+
finally:
1135+
os.unlink(path)
1136+
os.rmdir(directory)
1137+
if unchanged != [foreign]:
1138+
fails.append(("carry-moved-input",
1139+
"a foreign capture of the SAME bytes was not carried through "
1140+
"a rewrite — an unchanged input must keep the port's entry"))
1141+
if moved:
1142+
fails.append(("carry-moved-input",
1143+
"a foreign capture of OTHER bytes was carried into a rewritten "
1144+
"artifact — it cannot verify there, so the writer refuses and "
1145+
"the port cannot write until the reference has: a regeneration "
1146+
"that cannot start"))
1147+
if "attests other bytes" not in noted.getvalue():
1148+
fails.append(("carry-moved-input",
1149+
"a foreign capture was dropped without saying why"))
1150+
if verifying != [foreign]:
1151+
fails.append(("carry-moved-input",
1152+
"VERIFY dropped a foreign capture — only a rewrite may; in "
1153+
"verify a moved input must still read as a stale artifact"))
1154+
remaining = verify_repro(project_repro(after, moved))
1155+
if remaining:
1156+
fails.append(("carry-moved-input",
1157+
f"the rewritten artifact does not verify: {remaining}"))
10841158
return fails
10851159

10861160

@@ -1532,7 +1606,8 @@ def write() -> int:
15321606
print(f"wrote {DIGESTS} ({len(plan)} documents)")
15331607
for case in artifact_names:
15341608
out = os.path.join(FIXDIR, f"{case}.repro.json")
1535-
artifact = project_repro(_read(plan[case][1]), _foreign_engines(out))
1609+
raw = _read(plan[case][1])
1610+
artifact = project_repro(raw, _foreign_engines(out, rewriting=raw))
15361611
remaining = verify_repro(artifact)
15371612
if remaining:
15381613
print(f"ERROR: {case}: refusing to write an artifact that does not "

0 commit comments

Comments
 (0)