"""Self-test suite for the unity_agent package. Run with:: python -m unity_agent.tests The tests do not require Unity to be installed; they only verify that the Python layer produces files with the expected names and that the generated C# code passes the brace-balance and namespace checks. """ from __future__ import annotations import shutil import sys import tempfile from pathlib import Path # Make the package importable when run as a script. ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT.parent)) from unity_agent.config import Settings from unity_agent.transport.unity_transport import UnityTransport from unity_agent.tools.base import get_registry import unity_agent.tools.unity_tools # noqa: F401 - registers tools from unity_agent.tools.unity_tools import ( GenerateCompleteGameTool, SetupUnityProjectTool, WriteCSharpScriptTool, WriteSceneFileTool, ) from unity_agent.qa import ProjectQA def _count_braces(src: str) -> bool: return src.count("{") == src.count("}") def test_tool_registry() -> None: names = get_registry().all_names() assert "generate_complete_game" in names assert "setup_unity_project" in names assert "write_csharp_script" in names # The user spec asked for 20+ tools. assert len(names) >= 20, f"Expected >= 20 tools, got {len(names)}" print(f" [ok] tool registry has {len(names)} tools") def test_setup_project(tmp: Path) -> None: settings = Settings(output_dir=str(tmp), product_name="TestProj") transport = UnityTransport(settings) r = SetupUnityProjectTool(settings, transport).run(project_name="TestProj") assert r.ok, r.error for f in ("Assets", "Packages/manifest.json", "ProjectSettings/ProjectSettings.asset"): assert (tmp / "TestProj" / f).exists(), f"missing {f}" print(" [ok] setup_unity_project writes the full scaffold") def test_csharp_braces(tmp: Path) -> None: settings = Settings(output_dir=str(tmp), product_name="BracesProj") transport = UnityTransport(settings) transport.open_project("BracesProj") code = "namespace X { public class Y { void Z() {} } }" r = WriteCSharpScriptTool(settings, transport).run( filename="Foo.cs", code=code, project_name="BracesProj") assert r.ok src = (tmp / "BracesProj/Assets/Scripts/Foo.cs").read_text() assert _count_braces(src) print(" [ok] write_csharp_script produces balanced C#") def test_complete_game(tmp: Path) -> None: settings = Settings(output_dir=str(tmp), product_name="GenGame") transport = UnityTransport(settings) r = GenerateCompleteGameTool(settings, transport).run( game_name="GenGame", preset="open_world_city") assert r.ok, r.error # Expect a C# file per major system. scripts = list((tmp / "GenGame/Assets/Scripts").glob("*.cs")) assert len(scripts) >= 12, f"only {len(scripts)} scripts generated" # Every C# file must have balanced braces. for s in scripts: assert _count_braces(s.read_text(encoding="utf-8")), f"unbalanced braces in {s.name}" # Scene file present. assert (tmp / "GenGame/Assets/Scenes/MainScene.unity").exists() print(f" [ok] generate_complete_game produced {len(scripts)} scripts + scene") def test_qa(tmp: Path) -> None: settings = Settings(output_dir=str(tmp), product_name="QAProj") transport = UnityTransport(settings) GenerateCompleteGameTool(settings, transport).run( game_name="QAProj", preset="open_world_city") report = ProjectQA(tmp / "QAProj").run() assert report.passed, "QA must pass on a freshly generated project" print(f" [ok] QA passes on generated project ({len(report.issues)} issues)") def main() -> int: tmp = Path(tempfile.mkdtemp(prefix="unity_agent_test_")) try: test_tool_registry() test_setup_project(tmp) test_csharp_braces(tmp) test_complete_game(tmp) test_qa(tmp) except AssertionError as e: print(f"\nFAIL: {e}") return 1 finally: shutil.rmtree(tmp, ignore_errors=True) print("\nAll tests passed.") return 0 if __name__ == "__main__": sys.exit(main())