"""Per-version coverage on the Piklib evolution 6.1 -> 7.1 (-> 8.x). Documents what each early engine actually exposes, and guards the tagged-factory type extraction (6.1).""" from __future__ import annotations import json from pathlib import Path import pytest from ams.diff import compute_diff from ams.snapshot import Snapshot SNAP_DIR = Path(__file__).resolve().parents[1] / "snapshots" P61 = SNAP_DIR / "PIKLib61.dll.snapshot.json" P71 = SNAP_DIR / "PIKLIB71.dll.snapshot.json" pytestmark = pytest.mark.skipif(not P61.exists(), reason="6.1/7.1 snapshots not present") def _load(p: Path) -> Snapshot: with open(p, encoding="utf-8") as fh: return Snapshot(json.load(fh)) def test_piklib61_early_engine_partial_surface(): s = _load(P61) assert s.binary["engine"] == "Piklib" # types come from the tag-based CMC_Scene::resolve ladder (names recovered, ctor/size not) names = {t["script_name"] for t in s.types} assert len(names) >= 20 and {"ANIMO", "ARRAY", "BUTTON"} <= names assert all(t.get("cpp_class") is None or t["cpp_class"].startswith("CMC_") for t in s.types) assert s.events and s.fields # 6.1 predates prepareMthHashSet: methods are dispatched by NAME in run(), so they're # recovered without numeric ids, and the id-switch dispatch axis stays empty. assert s.methods and all(m["id"] is None for m in s.methods) assert s.method_dispatch == [] animo = {m["name"] for m in s.methods if m["owner"] == "CMC_Animo"} assert {"show", "hide", "play"} <= animo # 6.1 uses lower/camelCase names def test_piklib71_full_surface(): s = _load(P71) assert s.binary["engine"] == "Piklib" assert s.types and s.methods and s.events and s.fields and s.method_dispatch # 7.1 uses the inline-ctor factory, so most types resolve their C++ class assert sum(1 for t in s.types if t.get("cpp_class")) > len(s.types) // 2 def test_61_to_71_method_id_evolution(): s61, s71 = _load(P61), _load(P71) shared = {t["script_name"] for t in s61.types} & {t["script_name"] for t in s71.types} assert {"ANIMO", "ARRAY", "BUTTON"} <= shared # stable type core across the two early versions # both expose a rich method surface, but only 7.1 carries numeric ids (prepareMthHashSet); # 6.1's are name-only. (Names also differ in case - 6.1 camelCase vs 7.1 UPPERCASE - which is # why a raw method diff is noisy without case folding.) assert len(s61.methods) > 100 and all(m["id"] is None for m in s61.methods) assert len(s71.methods) > 100 and all(m["id"] is not None for m in s71.methods)