diff --git a/scripts/core_probe.py b/scripts/core_probe.py index e2b303899..d16082927 100644 --- a/scripts/core_probe.py +++ b/scripts/core_probe.py @@ -110,6 +110,18 @@ def content_identity(path: Path, label: str | None) -> dict: } +def describe_env_value(value: str) -> str: + """An environment value as a record keeps it: a file by name and sha256. + + The path is a detail of the host that ran the probe, the bytes are what + the run depended on. + """ + path = Path(value) + if path.is_absolute() and path.is_file(): + return f"{path.name} sha256:{file_digest(path, 'sha256')}" + return value + + def parse_assignment(text: str, flag: str) -> tuple[str, str]: key, sep, value = text.partition("=") if not sep or not key: @@ -878,7 +890,7 @@ def probe(args: argparse.Namespace) -> int: "inputs": { "content": content_identity(Path(args.content), args.content_label) if args.content else None, "options": dict(args.option), - "env": dict(args.env), + "env": {key: describe_env_value(value) for key, value in args.env}, "frames": args.frames, "warmup": args.warmup, "repetitions": repetitions, diff --git a/tests/test_core_probe.py b/tests/test_core_probe.py index 21cde243f..d66dc2770 100644 --- a/tests/test_core_probe.py +++ b/tests/test_core_probe.py @@ -147,6 +147,12 @@ class ParsingTests(unittest.TestCase): handle.flush() self.assertEqual(core_probe.elf_comment(Path(handle.name)), []) + def test_env_file_recorded_by_name_and_hash(self) -> None: + library = Path(sys.executable).resolve() + described = core_probe.describe_env_value(str(library)) + self.assertEqual(described, f"{library.name} sha256:{core_probe.file_digest(library, 'sha256')}") + self.assertEqual(core_probe.describe_env_value("2000-01-01 00:00:00"), "2000-01-01 00:00:00") + def test_cue_tracks(self) -> None: with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp" / "tests") as tmp: cue = Path(tmp, "d.cue") diff --git a/wiki/tools.md b/wiki/tools.md index ad6c98dc2..fdedf65d1 100644 --- a/wiki/tools.md +++ b/wiki/tools.md @@ -738,7 +738,7 @@ strace the record says that tracing was unavailable. | `--frames`, `--warmup` | frames timed, and frames run before timing starts | | `--press FRAME:BUTTON[:LEN]` | hold a libretro joypad button on port 0 (`a`, `b`, `start`, `up`...). A menu may need 15 frames or more to see it | | `--joypad-ports N` | ports given a joypad after loading, as RetroArch does (default 2) | -| `--env KEY=VALUE` | environment of the core process. A core that reads the host clock repeats only with the clock fixed, for example `LD_PRELOAD` of libfaketime with `FAKETIME` | +| `--env KEY=VALUE` | environment of the core process. A core that reads the host clock repeats only with the clock fixed, for example `LD_PRELOAD` of libfaketime with `FAKETIME`. A value naming a file is recorded by file name and sha256 | | `--frame-hash-every K`, `--png-dir` | sha1 of every K-th frame, and those frames as PNG | | `--state-var SECTION.VAR` | a variable of a Mednafen save state, as an observable | | `--state-dir` | keep the save state taken at the end of the first repetition |