simready-validator / test_discover_dgxc.py
loginowskid's picture
Update runner: preliminary scan mode (zip-allow + first-asset slice)
6b5095a verified
"""DGXC discovery verifier — exercise the new recursive discover_assets
against four synthetic layouts. Self-contained: imports validate.py with
SIMREADY_INSIDE_KIT + SIMREADY_FOUNDATIONS_PATH already set so the bootstrap
gate clears.
Usage on the pod:
export SIMREADY_FOUNDATIONS_PATH=/home/horde/.simready/simready_foundations
/home/horde/.simready/venv/bin/python3 test_discover_dgxc.py
"""
from __future__ import annotations
import os
import sys
import tempfile
from pathlib import Path
# Skip the Kit re-exec gate (we're testing pure-Python discovery, no Kit).
os.environ.setdefault("SIMREADY_INSIDE_KIT", "1")
# Resolve the repo: env var first (DGXC ad-hoc run), then assume the
# script lives inside a checkout (workflow / local).
_env_repo = os.environ.get("SIMREADY_REPO")
if _env_repo:
REPO = Path(_env_repo)
else:
REPO = Path(__file__).resolve().parents[2]
SKILL = REPO / "tools" / "validation" / "plugins" / "simready-report" / "skills" / "simready-report"
if not SKILL.is_dir():
raise SystemExit(
f"Validator skill dir not found at {SKILL}. "
f"Set SIMREADY_REPO to the simready-oem-library-pm checkout root."
)
sys.path.insert(0, str(SKILL))
import validate as v # noqa: E402
CASES = [
# (label, build_tree, expected_assets, expected_finding_codes)
("well_shaped",
lambda root: [
(root / "well_shaped" / "well_shaped.usda", "interface"),
(root / "well_shaped" / "Geometry" / "mesh.usda", "sublayer (Geometry)"),
(root / "well_shaped" / "Materials" / "mat.usda", "sublayer (Materials)"),
],
# Old behavior preserved: only the bundle interface is returned.
["well_shaped/well_shaped.usda"],
[]),
("flat",
lambda root: [
(root / "flat" / "scene_a.usda", "root-level USD, no bundle"),
(root / "flat" / "scene_b.usda", "root-level USD, no bundle"),
],
# New behavior: both found, both tagged as non-standard.
["flat/scene_a.usda", "flat/scene_b.usda"],
["LAYOUT.NON_STANDARD_BUNDLE", "LAYOUT.NON_STANDARD_BUNDLE"]),
("deep",
lambda root: [
(root / "deep" / "team1" / "v1" / "scenes" / "render_target.usda",
"deeply-nested USD, no bundle wrapper"),
],
# New behavior: found at depth, flagged.
["deep/team1/v1/scenes/render_target.usda"],
["LAYOUT.NON_STANDARD_BUNDLE"]),
("mixed",
lambda root: [
(root / "mixed" / "proper_bundle" / "proper_bundle.usda", "interface"),
(root / "mixed" / "proper_bundle" / "Geometry" / "g.usda", "sublayer"),
(root / "mixed" / "orphan.usda", "standalone — not a bundle"),
],
# Bundle interface and the orphan validate; sublayer excluded; orphan flagged.
["mixed/orphan.usda", "mixed/proper_bundle/proper_bundle.usda"],
["LAYOUT.NON_STANDARD_BUNDLE"]),
# Real-world layout from Aperdata's Kitchen-01 dataset:
# `<top_dir>/<asset>.usd` for the top-level interface plus a
# `<top_dir>/SubUSDs/<many>.usd` sublayer tree. The interface
# stem (`Indoor`) doesn't match the dir name (`0_Kitchen_Indoor`),
# so older logic missed the SubUSDs filter and counted them as
# standalone assets — Aperdata went from 23 expected to 933
# validated. New logic excludes anything under a SUBLAYER_DIR
# ancestor structurally, regardless of bundle naming.
("aperdata_kitchen",
lambda root: [
(root / "aperdata_kitchen" / "0_Kitchen_Indoor" / "Indoor.usda", "interface (stem != parent dir)"),
(root / "aperdata_kitchen" / "0_Kitchen_Indoor" / "SubUSDs" / "CL06.usda", "sublayer (under SubUSDs)"),
(root / "aperdata_kitchen" / "0_Kitchen_Indoor" / "SubUSDs" / "CL06_1.usda", "sublayer (under SubUSDs)"),
(root / "aperdata_kitchen" / "0_Kitchen_Indoor" / "SubUSDs" / "DEC_large_leaf.usda", "sublayer (under SubUSDs)"),
(root / "aperdata_kitchen" / "mug_handheld" / "Collected_dining_mug_handheld" / "dining_mug_handheld.usda",
"interface in nested Collected_* dir"),
],
# Only the two interface USDs validate. SubUSDs/* are excluded
# structurally. The deep mug_handheld interface IS the only
# candidate in its top-level dir, so it counts as a bundle
# interface (no layout warning). Indoor.usd is also the only
# candidate under 0_Kitchen_Indoor → no warning.
[
"aperdata_kitchen/0_Kitchen_Indoor/Indoor.usda",
"aperdata_kitchen/mug_handheld/Collected_dining_mug_handheld/dining_mug_handheld.usda",
],
[]),
]
def _normalize(paths: list[Path], root: Path) -> list[str]:
return sorted(str(p.relative_to(root)).replace("\\", "/") for p in paths)
def main() -> int:
failures = 0
with tempfile.TemporaryDirectory(prefix="sr-discover-test-") as td:
root = Path(td)
for label, build, expected_assets, expected_codes in CASES:
case_dir = root / label
case_dir.mkdir(parents=True, exist_ok=True)
for usd_path, _why in build(root):
usd_path.parent.mkdir(parents=True, exist_ok=True)
usd_path.write_text("()") # empty stage — discover_assets is pure FS walk
assets, findings = v.discover_assets(case_dir)
got_assets = _normalize(assets, root)
got_codes = sorted([f["code"] for f in findings])
exp_assets = sorted(expected_assets)
exp_codes = sorted(expected_codes)
ok = (got_assets == exp_assets and got_codes == exp_codes)
mark = "PASS" if ok else "FAIL"
print(f" [{mark}] {label}")
print(f" expected assets: {exp_assets}")
print(f" got assets: {got_assets}")
print(f" expected findings: {exp_codes}")
print(f" got findings: {got_codes}")
if not ok:
failures += 1
for f in findings:
print(f" finding detail: {f}")
print()
if failures:
print(f"{failures} case(s) FAILED")
return 1
print("all cases passed — recursive discovery + layout findings working end-to-end on DGXC")
return 0
if __name__ == "__main__":
raise SystemExit(main())