feat: @game-ci/runtime-test-framework - test the built player, not the Editor - #129
Merged
Merged
Conversation
…l yet) Structural skeleton for a GPU-free runtime test framework plugin, testing the actual built player rather than the Editor. Not wired into core's default load list; test-runtime is not yet a core command. See plugins/runtime-test-framework/README.md.
|
Warning Review limit reachedNext included review available in 9 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (16)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Turns the structural skeleton into a real, working plugin:
`game-ci test-runtime <buildPath>` launches the actual built player
(not the Editor, and not Unity's own Test Framework's specialized test
player - a genuinely different artifact) and reports on tests its own
in-game harness ran, via a small results-file contract this plugin
defines.
Real implementation:
- resolvePlayerExecutable: per-platform executable discovery (Windows
single .exe, macOS single .app bundle -> Contents/MacOS/<bundle
name>, Linux single executable-bit file), erroring clearly on zero
or multiple candidates rather than guessing.
- launchAndCollectResults: spawns the player with
GAME_CI_RUNTIME_TEST_MODE=1 and GAME_CI_RUNTIME_TEST_RESULTS_PATH
set, deletes any stale results file from a previous run first,
handles a timeout by killing the process and failing loudly, and
treats the results file - not the exit code - as authoritative (a
player that writes valid results but exits non-zero for an unrelated
reason still gets its real results honored).
- parseRuntimeTestResults / summarizeRuntimeTestResults: schema
validation and pass/fail summarization for the results contract
(schemaVersion 1: { tests: [{ name, passed, durationMs?, message? }] }).
- RuntimeTestCommand: ties it together, registers --timeout/--resultsPath/
--args, prints a pass/fail summary, fails the CI step (throws) on
any failed test or on a results file that never appeared.
Core wiring, matching the precedent from steam-deploy (#123) exactly:
- CommandFactory: test-runtime added alongside deploy to the
engine-detection bypass list - a built player carries no
Unity/Godot/Unreal project markers of its own.
- CliCommands: registered `test-runtime [buildPath]` (no target
sub-dispatch needed, unlike deploy - one plugin handles it or none
does).
- cli.ts: loaded via PluginLoader.load('@game-ci/runtime-test-framework'),
never a static import, now in the default load list alongside
orchestrator and steam-deploy since it's real.
Verification:
- tsc --noEmit: 737 errors, matching baseline exactly (git stash -u
comparison).
- plugins/runtime-test-framework's own tsc --noEmit: clean.
- bun test ./src: 203 pass, 0 fail (was 202 before this commit),
including a new integration test confirming the plugin loads and
`test-runtime` resolves without engine detection.
- plugins/runtime-test-framework's own vitest: 17 pass, 1 skipped
(the Linux executable-bit test is skipped on Windows, where NTFS
doesn't represent POSIX mode bits - the logic itself is
platform-independent, only that one assertion needs a real POSIX
filesystem, which CI's Linux runners provide).
- bun run build: succeeds, new code confirmed present in the bundle.
- Real functional smoke test end-to-end: ran `test-runtime` against
both a nonexistent path and a real empty directory, both producing
exactly the expected error - option parsing, command dispatch, and
the executable-resolution logic all confirmed wired correctly, not
just type-checked.
- oxfmt --check: clean (including a markdown-list-misparse fix in the
README where a line-wrapped " - " read as a list start).
frostebite
marked this pull request as ready for review
August 24, 2026 19:17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fully implemented, not a draft anymore.
game-ci test-runtime <buildPath>launches the actual built player — not the Editor, and not Unity's own Test Framework's specialized test player (a genuinely different artifact) — and reports on whatever tests its in-game harness ran, via a small results-file contract this plugin defines.Why this is distinct from
game-ci testgame-ci test's-runTestspath (Docker or local) runs Unity's own official Test Framework — editmode/playmode/standalone-test-mode assemblies, executed by a specialized test player Unity's own tooling builds. It never exercises a project's real shipped build.test-runtimetargets the actual player executable a build step produced instead — real assertions against real runtime behavior, on the real artifact a player would download and run.What's implemented
resolvePlayerExecutable— per-platform executable discovery: single.exeon Windows, single.appbundle on macOS (resolving toContents/MacOS/<bundle name>), single executable-bit file on Linux. Errors clearly on zero or multiple candidates rather than guessing.launchAndCollectResults— spawns the player withGAME_CI_RUNTIME_TEST_MODE=1andGAME_CI_RUNTIME_TEST_RESULTS_PATHset, deletes any stale results file from a previous run first, kills the process and fails loudly on timeout, and treats the results file — not the exit code — as authoritative.parseRuntimeTestResults/summarizeRuntimeTestResults— schema validation and pass/fail summarization for the results contract (schemaVersion: 1,{ tests: [{ name, passed, durationMs?, message? }] }).RuntimeTestCommand— ties it together:--timeout/--resultsPath/--argsoptions, a pass/fail summary printed to CI, fails the step on any failed test or a results file that never appeared.Core wiring (matching the precedent from #123 exactly)
CommandFactory:test-runtimeadded alongsidedeployto the engine-detection bypass list — a built player carries no Unity/Godot/Unreal project markers of its own.CliCommands: registeredtest-runtime [buildPath](no target sub-dispatch needed, unlikedeploy).cli.ts: loaded viaPluginLoader.load('@game-ci/runtime-test-framework'), never a static import — now in the default load list alongside orchestrator and steam-deploy, since it's real.Verification
tsc --noEmit: 737 errors, matching baseline exactly (git stash -ucomparison).plugins/runtime-test-framework's owntsc --noEmit: clean.bun test ./src: 203 pass, 0 fail (was 202 before this PR), including a new integration test confirming the plugin loads andtest-runtimeresolves without engine detection.plugins/runtime-test-framework's own vitest: 17 pass, 1 skipped (the Linux executable-bit test is skipped on Windows, where NTFS doesn't represent POSIX mode bits — the logic is platform-independent; only that one assertion needs a real POSIX filesystem, which CI's Linux runners provide).bun run build: succeeds, new code confirmed present in the bundle.test-runtimeagainst both a nonexistent path and a real empty directory, both producing exactly the expected error — option parsing, command dispatch, and executable-resolution logic all confirmed wired correctly, not just type-checked.oxfmt --check: clean.Test plan
PluginLoader,test-runtimeresolves without engine detectiontsc --noEmit/oxfmt --checkcleanbun test ./srcpassesbun run buildsucceeds