#!/usr/bin/env python3 """The `reach blender` decisions, exercised without launching Blender (T-1273). Same principle as test_environment.py, and for a stronger reason: the thing being launched is a 200 MB GUI application that opens files and writes GLBs, so "run it and see" is not a test. Every decision the retired bash wrapper made — which install to use, which script a name refers to, which arguments need absolutising — is a pure function here, and this is what pins them. The absolutise rule is the one worth having a test for. Flatpak resolves relative paths against a different root, so a relative path silently becomes a file-not-found raised from inside Blender, a long way from its cause. Run: python3 tooling/test_blender.py """ from __future__ import annotations import sys import tempfile from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(REPO_ROOT)) from tooling.core.errors import ReachError # noqa: E402 from tooling.domains.blender import service # noqa: E402 def test_payloads_are_discoverable(failures: list[str]) -> None: """The carve-out is the only place these names are written down.""" names = service.payloads() if len(names) < 30: failures.append(f"expected the 35-file carve-out, found {len(names)} payloads") if any(n.endswith(".py") for n in names): failures.append("payload names should be stems, not filenames") def test_registered_name_resolves(failures: list[str]) -> None: """Both the stem and the filename form reach the same file.""" first = service.payloads()[0] by_stem = service.resolve_payload(first) by_filename = service.resolve_payload(f"{first}.py") if by_stem != by_filename: failures.append(f"'{first}' and '{first}.py' resolved differently") if by_stem.parent != service.payload_dir(): failures.append(f"a registered name resolved outside the payload dir: {by_stem}") def test_arbitrary_path_resolves(failures: list[str]) -> None: """The wrapper ran one-off scripts too — the spikes and glb-gen still do.""" with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as handle: handle.write(b"# a one-off script\n") loose = Path(handle.name) try: resolved = service.resolve_payload(str(loose)) if resolved != loose.resolve(): failures.append(f"a script path resolved to {resolved}, expected {loose}") if not resolved.is_absolute(): failures.append("a script path resolved to a relative path") finally: loose.unlink() def test_unknown_name_enumerates(failures: list[str]) -> None: """A payload name is not guessable, so a miss must list the options.""" try: service.resolve_payload("definitely_not_a_payload") failures.append("an unknown payload resolved instead of raising") except ReachError as exc: if not exc.fix or "blender_" not in exc.fix: failures.append("the unknown-payload error does not enumerate the payloads") if exc.exit_code != 2: failures.append( f"an unknown payload exited {exc.exit_code}, expected 2 (usage error)" ) def test_absolutise_only_touches_real_paths(failures: list[str]) -> None: """Flags and values must survive; existing paths must be made absolute.""" with tempfile.TemporaryDirectory() as tmp: real = Path(tmp) / "input.glb" real.write_text("") args = ["--mode", "fit", str(real), "not/a/real/path.glb", "12"] out = service.absolutise(args) if out[0] != "--mode" or out[1] != "fit" or out[4] != "12": failures.append(f"absolutise mangled non-path arguments: {out}") if out[2] != str(real.resolve()): failures.append(f"an existing path was not absolutised: {out[2]}") if out[3] != "not/a/real/path.glb": failures.append(f"a non-existent path was rewritten: {out[3]}") def test_invocation_reports_its_sandbox(failures: list[str]) -> None: """`sandboxed` is what decides whether absolutising is load-bearing.""" native = service.BlenderInvocation(["blender"], "native") flatpak = service.BlenderInvocation(["flatpak", "run", service.FLATPAK_APP], "flatpak") if native.sandboxed: failures.append("a native install was reported as sandboxed") if not flatpak.sandboxed: failures.append("a flatpak install was not reported as sandboxed") def main() -> int: failures: list[str] = [] test_payloads_are_discoverable(failures) test_registered_name_resolves(failures) test_arbitrary_path_resolves(failures) test_unknown_name_enumerates(failures) test_absolutise_only_touches_real_paths(failures) test_invocation_reports_its_sandbox(failures) if failures: print("test_blender: FAIL", file=sys.stderr) for failure in failures: print(f" - {failure}", file=sys.stderr) return 1 print( "test_blender: OK — payload discovery, both resolve forms, enumeration " "on a miss and the flatpak path rule, without launching Blender" ) return 0 if __name__ == "__main__": sys.exit(main())