File size: 6,411 Bytes
6b5095a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""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())