{"id":47732180,"url":"https://github.com/kubonsang/testplay-runner","last_synced_at":"2026-04-24T04:03:05.224Z","repository":{"id":346818659,"uuid":"1191254595","full_name":"Kubonsang/testplay-runner","owner":"Kubonsang","description":"Go CLI that makes unity play mode tests reliable for AI agents","archived":false,"fork":false,"pushed_at":"2026-04-23T08:20:04.000Z","size":567,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-23T10:26:23.387Z","etag":null,"topics":["ai-tool","ci-cd","cli","testing-tools","unity","vibe-coding"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kubonsang.png","metadata":{"files":{"readme":"README.ko.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-25T04:08:39.000Z","updated_at":"2026-04-23T08:20:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Kubonsang/testplay-runner","commit_stats":null,"previous_names":["kubonsang/testplay-runner"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/Kubonsang/testplay-runner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kubonsang%2Ftestplay-runner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kubonsang%2Ftestplay-runner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kubonsang%2Ftestplay-runner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kubonsang%2Ftestplay-runner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kubonsang","download_url":"https://codeload.github.com/Kubonsang/testplay-runner/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kubonsang%2Ftestplay-runner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32208480,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T03:15:14.334Z","status":"ssl_error","status_checked_at":"2026-04-24T03:15:11.608Z","response_time":64,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-tool","ci-cd","cli","testing-tools","unity","vibe-coding"],"created_at":"2026-04-02T21:47:46.391Z","updated_at":"2026-04-24T04:03:05.217Z","avatar_url":"https://github.com/Kubonsang.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# testplay-runner\n\n**Unity의 망가진 테스트 러너를 AI 에이전트용 안정적인 계약 레이어로 감싸는 Go CLI — 명확한 exit code, JSON 출력, silent failure 없음.**\n\n한국어 | [English](README.md)\n\n---\n\nUnity의 원시 CLI는 자동화에 적합하지 않습니다. 컴파일 실패에도 종료코드 0을 반환하고, 결과는 XML로만 출력되며, 진행 상황을 알 수 없고, 오류 유형이 모호합니다. `testplay`는 AI 에이전트와 CI 파이프라인을 위해 설계된 6개의 명령으로 이 모든 문제를 해결합니다.\n\n## testplay는 누구를 위한 도구인가\n\ntestplay는 **계약 레이어(contract layer)이지, 속도 레이어가 아닙니다.** 두 종류의 사용자:\n\n- **AI 에이전트와 CI 파이프라인** — 명확한 exit code, 구조화된 JSON, 폴링 가능한 진행 파일이 필요한 자동화 호출자. testplay는 이들을 위해 설계됐습니다.\n- **인간 개발자의 일상 TDD** — Unity의 Test Runner 창을 그대로 쓰세요. testplay는 ms 단위 반복과 경쟁하지 않습니다. *자동화된* 경로를 신뢰 가능하게 만드는 게 역할입니다.\n\nAI 에이전트가 Unity 테스트를 반복 실행한다면, testplay의 모든 일은 매 반복의 결과를 명확하게(legible) 만드는 것입니다. 개별 테스트 실행 속도는 testplay의 최적화 대상이 아닙니다 — 에이전트 루프의 병목은 모델 추론 시간이지, Unity 시작 시간이 아닙니다.\n\n## 해결하는 문제\n\n| 문제 | 해결책 |\n|---|---|\n| 컴파일 실패에도 종료코드 0 반환 | 컴파일 오류는 exit 2, 테스트 실패는 exit 3으로 명확히 구분 |\n| XML 전용 출력 | 모든 stdout을 `schema_version` 포함 JSON으로 출력 |\n| 실행 전 검증 없음 | `testplay check`로 Unity 실행 전 환경 사전 검증 |\n| 진행 상황 불투명 | 실행 중 `testplay-status.json`을 원자적으로 업데이트 |\n| 타임아웃 유형 모호 | JSON에 `timeout_type: compile / test / total` 명시; `compile_ms` + `test_ms` 설정 시 two-phase 실행으로 컴파일/테스트 타임아웃 분리 |\n| 회귀 추적 불가 | `--compare-run`으로 `new_failures` 비교 |\n| 플랫폼별 경로 차이 | 모든 응답에 절대경로 + 상대경로 동시 제공 |\n| 실행 없이 테스트 탐색 불가 | `testplay list`로 알려진 어트리뷰트 정적 스캔 — 커스텀 어트리뷰트 누락 (Known Limitations 참조) |\n| Unity 에디터가 프로젝트 잠금 보유 | 섀도우 워크스페이스가 `.testplay-shadow/`에서 테스트를 실행하여 에디터 사용 중에도 테스트 가능 |\n\n## 설치\n\n**사전 빌드 바이너리 (권장):**\n\n[GitHub Releases](https://github.com/Kubonsang/testplay-runner/releases)에서 다운로드 — darwin/linux/windows, amd64/arm64.\n\n**소스에서 빌드:**\n\n```bash\ngit clone https://github.com/Kubonsang/testplay-runner.git\ncd testplay-runner\ngo build -o testplay ./cmd/testplay\n```\n\n크로스 컴파일:\n\n```bash\nGOOS=windows GOARCH=amd64 go build -o testplay.exe ./cmd/testplay\n```\n\n## 설정\n\n`testplay init`으로 `testplay.json`을 생성합니다:\n\n```bash\ntestplay init --unity-path /path/to/Unity\n```\n\n또는 프로젝트 루트에 직접 생성합니다:\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"unity_path\": \"/Applications/Unity/Hub/Editor/2022.3.0f1/Unity.app/Contents/MacOS/Unity\",\n  \"project_path\": \"/path/to/your/UnityProject\",\n  \"test_platform\": \"edit_mode\",\n  \"timeout\": {\n    \"total_ms\": 300000,\n    \"compile_ms\": 60000,\n    \"test_ms\": 240000\n  },\n  \"result_dir\": \".testplay/results\",\n  \"retention\": {\n    \"max_runs\": 30\n  }\n}\n```\n\n`unity_path`를 생략하면 `UNITY_PATH` 환경변수로 폴백합니다.\n`project_path`를 생략하면 `testplay.json`이 위치한 디렉터리가 기본값이 됩니다.\n`test_platform`은 `\"edit_mode\"` (기본값) 또는 `\"play_mode\"`를 허용합니다. Unity CLI에 `-testPlatform EditMode|PlayMode`로 전달됩니다.\n`result_dir`는 `testplay result`가 읽는 실행 이력 JSON 저장 위치를 제어합니다.\n반면 run별 아티팩트(`results.xml`, `summary.json`, `manifest.json`, `stdout.log`,\n`stderr.log`, `events.ndjson`)는 항상\n`\u003cproject_path\u003e/.testplay/runs/\u003crun_id\u003e/` 아래에 저장됩니다.\n`retention.max_runs`는 오래된 run 결과/아티팩트의 자동 정리를 제어합니다 (기본값 30). `0`으로 설정하면 정리를 비활성화합니다.\n\n**타임아웃 설정:**\n- `total_ms` (기본값 300000): 전체 실행의 외부 안전망 데드라인.\n- `compile_ms` + `test_ms`: **반드시 둘 다 함께 설정해야** two-phase 실행이 활성화됨 — Unity가 컴파일만 먼저 실행(`compile_ms` 데드라인), 이후 테스트 실행(`test_ms` 데드라인). 단계별 타임아웃이면 `timeout_type: \"compile\"` 또는 `\"test\"`가 나오고, 바깥 `total_ms`가 먼저 만료되면 `\"total\"`이 나올 수 있습니다. 하나만 설정하면 validation error.\n- 둘 다 설정하지 않으면 single-phase 실행 (컴파일+테스트를 Unity 한 번 호출로 처리, `total_ms` 기준).\n\n\u003e **참고:** PlayMode 네트워크 하네스와 NGO 오케스트레이션은 아직 미지원입니다.\n\n## 명령어\n\n### `testplay version`\n\n현재 testplay 버전을 JSON으로 출력합니다.\n\n```bash\ntestplay version\n```\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"version\": \"v0.9.0\"\n}\n```\n\n---\n\n### `testplay init`\n\n`testplay.json` 설정 파일을 합리적인 기본값으로 생성합니다. 새 프로젝트를 시작할 때 한 번 실행합니다.\n\n```bash\ntestplay init --unity-path /path/to/Unity\ntestplay init --test-platform play_mode\ntestplay init --force  # 기존 testplay.json 덮어쓰기\n```\n\n```json\n{\n  \"created\": \"testplay.json\",\n  \"unity_path\": \"/path/to/Unity\",\n  \"project_path\": \"/current/directory\"\n}\n```\n\nUnity 경로 해석 우선순위: `--unity-path` 플래그 \u003e `UNITY_PATH` 환경변수 \u003e 빈 값 (경고 포함).\n`testplay.json`이 이미 있으면 exit 5 (`--force`로 덮어쓰기 가능). `--test-platform`이 유효하지 않으면 exit 5.\n\n---\n\n### `testplay check`\n\nUnity 경로, 프로젝트 경로, 설정 파일을 사전 검증합니다. 가장 먼저 실행하세요.\n\n```bash\ntestplay check\n```\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"ready\": true\n}\n```\n\n실패 시:\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"ready\": false,\n  \"hint\": \"set UNITY_PATH or add unity_path to testplay.json\"\n}\n```\n\n종료코드 0 = 준비됨. 종료코드 1 = 의존성 누락 (`hint` 필드 참조). 종료코드 5 = 설정 파일 오류.\n\n---\n\n### `testplay list`\n\nUnity를 실행하지 않고 `*.cs` 파일에서 `[Test]`, `[UnityTest]`, `[TestCase]`, `[TestCaseSource]`, `[Theory]` 어트리뷰트를 정적으로 스캔합니다.\n\n**이 결과는 완전한 테스트 목록이 아닌 최선의 추정값입니다.** 커스텀 테스트 어트리뷰트(`[NetworkTest]`, `[IntegrationTest]`, 프로젝트 전용 기반 클래스 등)는 조용히 누락됩니다. 출력 결과만으로는 무엇이 빠졌는지 알 수 없습니다.\n\n실용 지침:\n- 이미 존재하는 걸 아는 테스트의 `--filter` 후보 생성에 사용하세요.\n- 전체 커버리지가 중요한 경우 `--filter` 없이 `testplay run`을 실행하세요. Unity가 직접 모든 테스트를 탐색합니다.\n- `list`에 없는 테스트가 실제로 존재하고 실행될 수 있습니다.\n\n```bash\ntestplay list\n```\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"tests\": [\"MyTests.PlayerTests.TestJump\", \"MyTests.PlayerTests.TestRun\"]\n}\n```\n\n---\n\n### `testplay run`\n\n설정된 `test_platform` (`edit_mode` 또는 `play_mode`)으로 Unity 테스트를 실행합니다. 진행 상황은 `testplay-status.json`에 스트리밍됩니다.\n\n```bash\ntestplay run\ntestplay run --filter TestJump\ntestplay run --category Smoke\ntestplay run --compare-run 20250301-102200-a3f8b2c1\ntestplay run --config path/to/testplay.json\ntestplay run --shadow              # 에디터 락 없이 강제로 섀도우 워크스페이스 사용\ntestplay run --clear-cache         # 캐시된 Library 제거 후 섀도우 워크스페이스 생성\ntestplay run --scenario scenario.json  # 멀티 인스턴스 동시 실행\n```\n\n**전체 통과 (exit 0):**\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"run_id\": \"20250325-143000-a3f8b2c1\",\n  \"exit_code\": 0,\n  \"total\": 2,\n  \"passed\": 2,\n  \"failed\": 0,\n  \"skipped\": 0,\n  \"tests\": [\n    {\n      \"name\": \"MyTests.PlayerTests.TestJump\",\n      \"result\": \"Passed\",\n      \"duration_s\": 0.006\n    },\n    {\n      \"name\": \"MyTests.PlayerTests.TestRun\",\n      \"result\": \"Passed\",\n      \"duration_s\": 0.004\n    }\n  ],\n  \"new_failures\": null\n}\n```\n\n**테스트 실패 (exit 3):**\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"run_id\": \"20250325-143000-a3f8b2c1\",\n  \"total\": 10,\n  \"passed\": 9,\n  \"failed\": 1,\n  \"skipped\": 0,\n  \"tests\": [\n    {\n      \"name\": \"MyTests.PlayerTests.TestJump\",\n      \"result\": \"Failed\",\n      \"message\": \"Expected 1 but was 0\",\n      \"excerpt\": \"Expected 1 but was 0 (at PlayerTests.cs:42)\",\n      \"file\": \"Assets/Tests/PlayerTests.cs\",\n      \"absolute_path\": \"/path/to/UnityProject/Assets/Tests/PlayerTests.cs\",\n      \"line\": 42\n    }\n  ],\n  \"new_failures\": null\n}\n```\n\n**컴파일 실패 (exit 2):**\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"run_id\": \"20250325-143000-a3f8b2c1\",\n  \"exit_code\": 2,\n  \"total\": 0,\n  \"passed\": 0,\n  \"failed\": 0,\n  \"skipped\": 0,\n  \"tests\": [],\n  \"errors\": [\n    {\n      \"file\": \"Assets/Scripts/Player.cs\",\n      \"absolute_path\": \"/path/to/UnityProject/Assets/Scripts/Player.cs\",\n      \"line\": 17,\n      \"message\": \"CS0103: The name 'speed' does not exist in the current context\"\n    }\n  ],\n  \"new_failures\": null\n}\n```\n\n---\n\n### `testplay result`\n\n저장된 실행 이력을 조회합니다. Unity를 재실행하지 않습니다.\n\n```bash\ntestplay result\ntestplay result --last 3\n```\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"runs\": [\n    {\"run_id\": \"20250325-143000-a3f8b2c1\", \"exit_code\": 0, \"total\": 10, \"passed\": 10, \"failed\": 0},\n    {\"run_id\": \"20250324-091500-b7d2e4f0\", \"exit_code\": 3, \"total\": 10, \"passed\": 9, \"failed\": 1}\n  ]\n}\n```\n\n## 섀도우 워크스페이스\n\nUnity 에디터가 프로젝트를 열고 있으면 `Temp/UnityLockfile`이 존재하며, Unity 배치 모드가 동일한 프로젝트 디렉터리에서 실행될 수 없습니다. `testplay run`은 이를 자동으로 감지하고 프로젝트 루트 내 `.testplay-shadow-\u003crun_id\u003e/`에 per-run 섀도우 워크스페이스를 생성합니다:\n\n| 디렉터리 | 전략 |\n|---|---|\n| `Assets/` | 매 실행마다 새로 복사 |\n| `ProjectSettings/` | 매 실행마다 새로 복사 |\n| `Packages/` | 심링크(Windows는 Junction) |\n| `Library/` | `.testplay/cache/Library/`에서 seed; 캐시 없으면 cold-start |\n| `Temp/` | 매 실행 전 삭제; Unity가 새로 생성 |\n\n각 실행은 고유한 격리된 섀도우 디렉터리를 사용하므로 병렬 `testplay run` 호출이 안전합니다. 실행 종료 후 `ws.Cleanup()`으로 자동 삭제됩니다.\n\n**Library 웜 캐시:** 첫 번째 성공적인 실행이 `.testplay/cache/Library/`를 생성합니다. 이후 섀도우 실행은 이 캐시에서 `Library/`를 seed하여 cold-start 재임포트를 방지합니다. `ProjectVersion.txt` 또는 `Packages/manifest.json`이 변경되면 캐시가 무효화됩니다. `--clear-cache`로 강제 cold-start가 가능합니다.\n\n**섀도우 모드는 에이전트에게 투명합니다.** JSON 출력의 모든 `absolute_path` 필드는 원본 프로젝트 경로로 재매핑됩니다 — 에이전트는 섀도우 경로를 볼 수 없습니다.\n\n**플래그:**\n- `--shadow` — 에디터가 열려 있지 않아도 강제로 섀도우 워크스페이스를 사용 (섀도우 동작 테스트에 유용)\n- `--reset-shadow` — `--shadow`와 동일 (per-run 격리로 매 실행이 이미 새로 시작됨; API 호환성을 위해 유지)\n- `--clear-cache` — `.testplay/cache/` 제거 후 섀도우 워크스페이스 생성, Unity 강제 재임포트\n\n**`.gitignore`는 최초 사용 시 자동으로 패치**되어 `.testplay-shadow-*/`가 제외됩니다.\n\n## 시나리오 IPC 버스\n\n`testplay run --scenario`로 여러 인스턴스가 동시 실행될 때, 각 인스턴스는 `TESTPLAY_IPC_BUS` 환경변수를 받습니다 — 값은 공유 NDJSON 파일의 절대 경로입니다. 어떤 언어든 그 파일에 메시지를 append하고 폴링으로 읽을 수 있습니다. `depends_on` ready 신호 외 임의 조정 (예: \"client 접속 완료\", \"server가 플레이어 입장 감지\", \"host가 데미지 이벤트 수신\") 에 사용하세요.\n\n**메시지 형식** (한 줄당 JSON 하나):\n\n```json\n{\"seq\": 1, \"ts\": \"2026-04-24T13:00:05Z\", \"from\": \"host\", \"to\": \"*\", \"kind\": \"ready\", \"payload\": {\"port\": 7777}}\n```\n\n- `from` — 자기 역할\n- `to` — 수신 역할, 또는 브로드캐스트는 `\"*\"`\n- `kind` — 애플리케이션 정의 이벤트 이름\n- `payload` — 선택; 메시지는 짧게 (atomic-append 보장은 ~4 KB 이하)\n- `seq` — 자기 단조 카운터; (from, seq) 쌍으로 유일성 확보\n\n**C# 최소 예제 (host 측):**\n\n```csharp\nvar bus = System.Environment.GetEnvironmentVariable(\"TESTPLAY_IPC_BUS\");\nif (!string.IsNullOrEmpty(bus)) {\n    var line = \"{\\\"seq\\\":1,\\\"ts\\\":\\\"\" + DateTime.UtcNow.ToString(\"o\") + \"\\\",\\\"from\\\":\\\"host\\\",\\\"to\\\":\\\"*\\\",\\\"kind\\\":\\\"ready\\\"}\";\n    System.IO.File.AppendAllText(bus, line + \"\\n\");\n}\n```\n\n**testplay가 자동으로 해주는 것:**\n\n- 인스턴스별 폴링 reader가 자기 앞으로 온 메시지(브로드캐스트 포함) 수집\n- 시나리오 출력에 `instances[].ipc_messages` (전체 리스트) + `instances[].ipc_summary` (카운트 + last_sent / last_received) 노출\n- 각 인스턴스의 `events.ndjson`에 `ipc_send` / `ipc_recv`가 Unity 페이즈 이벤트와 섞여 단일 forensic 타임라인 형성\n- 의존성이 ready 도달 전에 종료되면 `orchestrator_errors`에 마지막으로 본 메시지 정보 추가\n- 버스 디렉토리(`.testplay/ipc/\u003cscenario_run_id\u003e/`)는 `retention.max_runs` 정책 자동 적용; `.gitignore` 자동 패치\n\n**v0.9 비목표:** 실시간 push(SSE/websocket), 양방향 RPC, 프레임워크별 헬퍼(NGO/Mirror — v1.0 예정), single-mode(`--scenario` 없는 `testplay run`)에서의 IPC.\n\n## 종료코드\n\n| 코드 | 의미 | 에이전트 조치 |\n|---|---|---|\n| 0 | 모든 테스트 통과 | 진행 |\n| 1 | Unity / 프로젝트 경로 없음 | 환경 수정, `hint` 필드 참조 |\n| 2 | 컴파일 실패 | 소스 수정, `errors[].absolute_path` + `line` 참조 |\n| 3 | 테스트 실패 | 테스트 수정, `tests[].absolute_path` + `line` 참조 |\n| 4 | 타임아웃 | JSON 결과의 `timeout_type` 확인 — 아래 표 참조 |\n| 5 | 설정 오류 | `testplay.json` 수정 또는 생성 |\n| 6 | 빌드 실패 (라이선스 / 빌드 타겟) | Unity 라이선스 활성화 및 빌드 모듈 설치 확인 |\n| 7 | 권한 오류 (섀도우 워크스페이스) | 프로젝트 디렉토리 권한 수정 |\n| 8 | 시그널 중단 | SIGINT/SIGTERM 수신 — 코드 변경 없이 재시도 |\n| 9 | 러너 시스템 오류 | 결과/아티팩트 저장 실패 — 디스크 용량/권한 확인, `warnings` 필드 참조 |\n\n### Exit 4 — timeout_type 값\n\n| `timeout_type` | status의 `phase` | 원인 |\n|---|---|---|\n| `\"compile\"` | `timeout_compile` | 컴파일 단계가 `compile_ms` 데드라인 초과 |\n| `\"test\"` | `timeout_test` | 테스트 단계가 `test_ms` 데드라인 초과 |\n| `\"total\"` | `timeout_total` | 외부 `total_ms` 데드라인 만료 (어느 단계에서든 발생) |\n\n컴파일 단계 타임아웃 JSON 예시:\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"exit_code\": 4,\n  \"timeout_type\": \"compile\",\n  \"tests\": [],\n  \"errors\": []\n}\n```\n\n## 진행 상황 모니터링\n\n**폴링이 유일한 수단입니다.** 푸시 알림, 웹훅, SSE 엔드포인트는 없습니다. 에이전트는 일정 간격으로 `testplay-status.json`을 읽어야 합니다. `seq` 필드(매 Write마다 증가)로 파일이 마지막 읽기 이후 변경됐는지 감지할 수 있습니다 — `updated_at` 파싱 없이도 변경 여부를 판단 가능합니다.\n\n`testplay run` 실행 중 `testplay-status.json`을 폴링하면 진행 상황을 확인할 수 있습니다:\n\n```json\n{\n  \"schema_version\": \"1\",\n  \"phase\": \"running\",\n  \"run_id\": \"20250325-143000-a3f8b2c1\",\n  \"total\": 10,\n  \"passed\": 3,\n  \"failed\": 0,\n  \"updated_at\": \"2025-03-25T14:30:05Z\",\n  \"started_at\": \"2025-03-25T14:29:58Z\",\n  \"last_heartbeat_at\": \"2025-03-25T14:30:03Z\",\n  \"artifact_root\": \"/Users/user/MyProject/.testplay/runs/20250325-143000-a3f8b2c1\",\n  \"pid\": 12345\n}\n```\n\n페이즈 진행 (single-phase): `compiling → done`\n페이즈 진행 (two-phase): `compiling → running → done`\n실패 페이즈: `timeout_compile`, `timeout_test`, `timeout_total`, `interrupted`\n\n## 권장 에이전트 흐름\n\n```\n0. testplay init             # testplay.json 생성 (최초 1회)\n1. testplay check            # 환경 검증\n2. testplay list             # 테스트 이름 탐색\n3. testplay run              # 실행 (testplay-status.json 폴링으로 진행 추적)\n4. testplay result --last 3  # 실행 이력 검토\n```\n\n## 개발\n\n```bash\n# 레이스 감지 포함 전체 테스트\ngo test -race ./...\n\n# 통합 테스트\ngo test -tags=integration ./cmd/testplay/...\n\n# 현재 플랫폼 빌드\ngo build ./cmd/testplay\n```\n\n## Unity Smoke 검증\n\n`fixtures/smoke-project/`에 실제 Unity 설치 환경에서 `testplay run`의 end-to-end 동작을 검증하는 최소 Unity 프로젝트가 포함되어 있습니다. EditMode 테스트 1개와 PlayMode(`[UnityTest]`) 테스트 1개로 구성됩니다.\n\n**로컬 실행:**\n\n```bash\n# 사전 조건: Unity 설치, UNITY_PATH 설정\nexport UNITY_PATH=/Applications/Unity/Hub/Editor/2022.3.0f1/Unity.app/Contents/MacOS/Unity\n./scripts/smoke.sh\n```\n\n스크립트 동작:\n1. EditMode → PlayMode 순으로 각 플랫폼에 맞는 `testplay.json`을 생성\n2. `testplay check` + `testplay run` 실행\n3. 각 run의 아티팩트 디렉터리(`.testplay/runs/\u003crun_id\u003e/`)에 아래 6개 파일이 모두 존재하는지 확인:\n   - `results.xml`, `summary.json`, `manifest.json`, `stdout.log`, `stderr.log`, `events.ndjson`\n4. 프로젝트 루트의 `testplay-status.json`(run 디렉터리 바깥의 스냅샷) 존재 확인\n5. `--shadow` 플래그를 사용한 섀도우 모드 스모크 단계 실행 — 섀도우 워크스페이스 생성 및 예상 서브디렉터리 확인\n\n**CI (opt-in):**\n\n```bash\ngh workflow run smoke.yml\n```\n\n`.github/workflows/smoke.yml` 참조. Unity가 설치된 self-hosted runner와 `UNITY_PATH` 환경변수가 필요합니다.\n\n실제 프로젝트에 재사용할 수 있는 패턴은\n[`docs/05_v0.2.0_playmode_smoke_example.md`](docs/05_v0.2.0_playmode_smoke_example.md)를 참고하세요.\nfixture를 코드로 생성하는 scene-free PlayMode smoke 테스트를 `testplay run`\n기준으로 정리해뒀습니다.\n\n## Known Limitations\n\n현재 존재하는 한계를 솔직하게 기록합니다. 각 항목에는 개선 방향이 있습니다.\n\n**`testplay list` 정적 스캔은 불완전할 수 있습니다.**\n정적 스캐너는 `[Test]`, `[UnityTest]`, `[TestCase]`, `[TestCaseSource]`, `[Theory]`만 감지합니다. 커스텀 어트리뷰트나 추상 기반 클래스 패턴을 사용하는 테스트는 보이지 않습니다. 출력 JSON에 `complete`와 `source` 필드가 포함되어 있어 에이전트가 목록의 완전성 여부를 알 수 있습니다. 첫 번째 `testplay run`이 완료(exit 0 또는 3)된 후에는 `.testplay/cache/list.json`에 실행 캐시가 작성되며, 이후 `testplay list` 호출은 `complete: true, source: \"run_cache\"`로 실제 전체 목록을 반환합니다.\n\n**진행 상황 모니터링은 폴링만 가능합니다.**\n`testplay-status.json`이 실행 중 상태를 확인하는 유일한 채널입니다. SSE, 웹소켓, 네임드 파이프 없음. `seq` 필드(매 Write마다 증가)로 마지막 읽기 이후 파일이 변경됐는지 감지할 수 있습니다. 개선 방향: PlayMode 네트워크 테스트 도입 시 선택적 SSE 엔드포인트.\n\n## 라이선스\n\nApache 2.0 — [LICENSE](LICENSE) 참조.\n서드파티 고지 — [THIRD_PARTY_LICENSES](THIRD_PARTY_LICENSES) 참조.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkubonsang%2Ftestplay-runner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkubonsang%2Ftestplay-runner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkubonsang%2Ftestplay-runner/lists"}