from __future__ import annotations import argparse import os import subprocess import sys from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parent.parent SCRIPT_DIR = PROJECT_ROOT / "scripts" STAGES: tuple[tuple[str, str, tuple[tuple[str, tuple[str, ...]], ...]], ...] = ( ( "face", "Face first-level edit baseline", ( ("Face edit suite", ("verify_face_edit_suite.py",)), ("Face isolated worker edits", ("verify_isolated_face_edit.py",)), ), ), ( "hole-slot", "Hole and slot first-level edit baseline", ( ("Hole/slot edit suite", ("verify_hole_slot_edit_suite.py",)), ), ), ( "edge", "Edge first-level edit baseline", ( ("Edge edit suite", ("verify_edge_edit_suite.py",)), ("Edge isolated worker edits", ("verify_edge_isolated_edit.py",)), ), ), ( "boss", "Boss first-level edit baseline", ( ("Boss edit suite", ("verify_boss_edit_suite.py",)), ), ), ( "round-chamfer", "Round and chamfer first-level edit baseline", ( ("Round/chamfer edit suite", ("verify_round_chamfer_edit_suite.py",)), ), ), ( "shell", "Shell thickness first-level edit baseline", ( ("Shell edit suite", ("verify_shell_edit_suite.py",)), ), ), ( "analytic", "Analytic surface edit baseline", ( ("Analytic surface edits", ("verify_analytic_surface_resize.py",)), ("Cone semi-angle isolated edits", ("verify_cone_semi_angle_isolation.py",)), ), ), ) QUICK_COMMANDS: tuple[tuple[str, tuple[str, ...]], ...] = ( ("Smoke test", ("main.py", "--smoke-test")), ("Property editor specs", ("verify_property_editor_specs.py",)), ("Property table editor UI", ("verify_property_card_editor_ui.py",)), ("Relation formula rules", ("verify_relation_formula_rules.py",)), ("Parametric component export", ("verify_parametric_component_export.py",)), ("SCDM backend discovery", ("verify_scdm_backend.py",)), ("SCDM runtime status", ("verify_scdm_status.py",)), ("SCDM probe pipeline", ("verify_scdm_probe_pipeline.py",)), ("SCDM edit runner", ("verify_scdm_edit_runner.py",)), ("SCDM result validator", ("verify_scdm_result_validator.py",)), ("Analysis Situs hole bridge", ("verify_asitus_hole_bridge.py",)), ("ICEPAK cylindrical same-domain hole", ("verify_icepak_cylindrical_region_selection.py",)), ("Feature recognition priority", ("verify_feature_recognition_summary.py",)), ("First-level fact graph", ("verify_first_level_fact_graph.py",)), ("Associated feature probe and display budget", ("verify_associated_features.py",)), ("First-level acceptance docs", ("verify_first_level_acceptance_docs.py",)), ) def _stage_names() -> tuple[str, ...]: return tuple(stage[0] for stage in STAGES) def _selected_stages(names: tuple[str, ...]) -> tuple[tuple[str, str, tuple[tuple[str, tuple[str, ...]], ...]], ...]: if not names: return STAGES selected = {name.strip() for name in names if name.strip()} return tuple(stage for stage in STAGES if stage[0] in selected) def _run(label: str, command: tuple[str, ...], *, index: int, total: int) -> None: print(f"\n[{index}/{total}] {label}", flush=True) target = SCRIPT_DIR / command[0] if not target.exists(): target = PROJECT_ROOT / command[0] env = os.environ.copy() env.setdefault("PYTHONIOENCODING", "utf-8") env.setdefault("PYTHONUTF8", "1") subprocess.run( (sys.executable, str(target), *command[1:]), cwd=PROJECT_ROOT, env=env, check=True, ) def _display_command(command: tuple[str, ...]) -> str: target = SCRIPT_DIR / command[0] prefix = "scripts\\" if not target.exists(): prefix = "" args = " ".join(command[1:]) return f"python {prefix}{command[0]} {args}".rstrip() def _commands_for(stages: tuple[tuple[str, str, tuple[tuple[str, tuple[str, ...]], ...]], ...]) -> list[tuple[str, tuple[str, ...]]]: commands: list[tuple[str, tuple[str, ...]]] = [] for _stage_name, stage_label, cases in stages: for label, command in cases: commands.append((f"{stage_label}: {label}", command)) return commands def main(argv: tuple[str, ...] | None = None) -> int: parser = argparse.ArgumentParser( description="Run staged first-level geometry edit verification suites.", ) parser.add_argument( "--stage", action="append", choices=_stage_names(), help="Only run one stage. Repeat this option to run multiple stages.", ) parser.add_argument( "--quick", action="store_true", help="Run lightweight first-level regression checks instead of the full geometry suites.", ) parser.add_argument( "--list", action="store_true", help="Print the staged verification plan without running it.", ) args = parser.parse_args(argv) if args.quick: commands = list(QUICK_COMMANDS) heading = "quick first-level regression checks" else: stages = _selected_stages(tuple(args.stage or ())) commands = _commands_for(stages) heading = "first-level edit suites" if args.list: print(f"{heading}:") for index, (label, command) in enumerate(commands, start=1): print(f"{index}. {label}: {_display_command(command)}") return 0 total = len(commands) for index, (label, command) in enumerate(commands, start=1): _run(label, command, index=index, total=total) print(f"\n{heading} passed.", flush=True) return 0 if __name__ == "__main__": raise SystemExit(main())