diff --git a/parser/object_model.py b/parser/object_model.py index fbe7611..59efd88 100644 --- a/parser/object_model.py +++ b/parser/object_model.py @@ -270,7 +270,7 @@ def _scan_errors(src_root: Path, public: set) -> dict: ensure_codes = {f: v["direct"] for f, v in raw.items() if f.startswith("ensure_")} result = {} - for fn in public: + for fn in sorted(public): rec = raw.get(fn) if not rec: continue diff --git a/tests/test_object_model.py b/tests/test_object_model.py index 60dd7aa..fbdb335 100644 --- a/tests/test_object_model.py +++ b/tests/test_object_model.py @@ -11,13 +11,15 @@ import json import re import sys +import tempfile import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) -from parser.object_model import attach_object_model, find_mobilitydb_src +from parser.object_model import ( + _scan_errors, attach_object_model, find_mobilitydb_src) MODEL = ROOT / "meta" / "object-model.json" _INTERNAL = {"T_TDOUBLE2", "T_TDOUBLE3", "T_TDOUBLE4"} # not public classes @@ -198,6 +200,24 @@ def test_errors_source_unavailable_is_honest(self): self.assertEqual(om["errors"]["raises"], {}) # not fabricated self.assertEqual(len(om["errors"]["codes"]), 21) + def test_scanned_errors_are_sorted_for_reproducibility(self): + # The raises map is keyed by the public function set; iterating a set is + # hash-seed dependent, so the keys are sorted to keep the emitted catalog + # byte-identical across runs. + src = ( + 'Datum zzz_fn(int x) {\n' + ' meos_error(ERROR, MEOS_ERR_INVALID_ARG_VALUE, "bad");\n}\n' + 'Datum aaa_fn(int y) {\n' + ' meos_error(ERROR, MEOS_ERR_INVALID_ARG_TYPE, "bad");\n}\n' + 'Datum mmm_fn(int z) {\n' + ' meos_error(ERROR, MEOS_ERR_INVALID_ARG_VALUE, "bad");\n}\n' + ) + with tempfile.TemporaryDirectory() as d: + (Path(d) / "x.c").write_text(src) + result = _scan_errors(Path(d), {"zzz_fn", "aaa_fn", "mmm_fn"}) + self.assertEqual(list(result), ["aaa_fn", "mmm_fn", "zzz_fn"]) + self.assertEqual(list(result), sorted(result)) + # --------------------------------------------------------------------------- # Drift gate: the curated lattice must equal what MEOS actually defines.