Skip to content

feat: Add configurable init_timeout for PlayMode test initialization#1021

Merged
Scriptwonder merged 4 commits intoCoplayDev:betafrom
Emerix:feature/configurable-init-timeout
Apr 29, 2026
Merged

feat: Add configurable init_timeout for PlayMode test initialization#1021
Scriptwonder merged 4 commits intoCoplayDev:betafrom
Emerix:feature/configurable-init-timeout

Conversation

@Emerix
Copy link
Copy Markdown

@Emerix Emerix commented Apr 2, 2026

Description

PlayMode tests require entering play mode which triggers a domain reload. On large Unity projects this can take >15s, causing the hardcoded 15s init timeout to
auto-fail the test job before tests actually start. This PR adds a configurable init_timeout parameter to run_tests so callers can specify a longer
initialization timeout for PlayMode tests.

Type of Change

  • New feature (non-breaking change that adds functionality)

Changes Made

  • Python (Server/src/services/tools/run_tests.py): Added init_timeout parameter to run_tests() function signature. When set, forwarded to Unity as
    initTimeout in the params dict.
  • C# RunTests (MCPForUnity/Editor/Tools/RunTests.cs): Reads initTimeout param from the request and passes it to TestJobManager.StartJob().
  • C# TestJobManager (MCPForUnity/Editor/Services/TestJobManager.cs):
    • Added InitTimeoutMs field to TestJob for per-job timeout override
    • Renamed InitializationTimeoutMs to DefaultInitializationTimeoutMs (15s, unchanged default)
    • StartJob() accepts optional initTimeoutMs parameter
    • GetJob() uses per-job timeout when set, falls back to 15s default
    • InitTimeoutMs persisted/restored across domain reloads via SessionState

Testing/Screenshots/Recordings

Tested on a large Unity project (~18s domain reload time for PlayMode):

  • Verified PlayMode tests fail with default 15s timeout (auto-fail before tests start)
  • Verified PlayMode tests succeed with init_timeout=120000
  • Verified EditMode tests still work without specifying init_timeout (default 15s is sufficient)
  • Verified per-job timeout survives domain reload (SessionState persistence)

Documentation Updates

  • I have added/removed/modified tools or resources
  • If yes, I have updated all documentation files using:
    • The LLM prompt at tools/UPDATE_DOCS_PROMPT.md (recommended)
    • Manual updates following the guide at tools/UPDATE_DOCS.md

Related Issues

None

Additional Notes

The default timeout remains 15s (no breaking change). The init_timeout parameter is optional and only needed for projects where PlayMode domain reload exceeds
15s. Recommended value for PlayMode: 120000 (120s).

Summary by Sourcery

Add configurable per-job initialization timeout for Unity test runs and propagate it from the Python run_tests tool through the editor pipeline.

New Features:

  • Allow callers of the Python run_tests tool to specify an init_timeout parameter to control Unity test initialization timeouts on a per-run basis.

Enhancements:

  • Introduce a per-job initialization timeout in TestJobManager with a default value, using the override when provided and persisting it across domain reloads.
  • Extend the editor RunTests command handler to read the initTimeout parameter and pass it through when starting test jobs.

Documentation:

  • Update developer documentation to demonstrate using init_timeout for PlayMode runs that require longer initialization due to domain reloads.

Summary by CodeRabbit

  • New Features

    • Per-job initialization timeout for test runs, forwarded to the test runner and persisted across sessions.
  • Bug Fixes

    • Validation rejects non-positive init_timeout inputs and clamps overly large values; timeout checks now honor per-job settings and report the resolved timeout.
  • Documentation

    • Developer docs updated with a PlayMode example using an extended init_timeout.
  • Tests

    • New integration and edit-mode tests covering forwarding, validation, persistence, and timeout behavior.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Apr 2, 2026

Reviewer's Guide

Adds a configurable initialization timeout for Unity PlayMode test jobs, threading an optional init_timeout parameter from the Python run_tests tool through the C# RunTests handler into TestJobManager, which now supports per-job init timeouts with persistence across domain reloads while keeping the default at 15s.

Sequence diagram for configurable init_timeout through test job lifecycle

sequenceDiagram
    actor Developer
    participant PythonRunTests as Python_run_tests_tool
    participant MCPServer as MCP_Server
    participant RunTestsHandler as RunTests_HandleCommand
    participant TestJobManager as TestJobManager

    Developer->>PythonRunTests: run_tests(mode=PlayMode, init_timeout=120000)
    PythonRunTests->>MCPServer: Request with init_timeout
    MCPServer->>RunTestsHandler: HandleCommand(params)
    RunTestsHandler->>RunTestsHandler: initTimeoutMs = Params.GetInt(initTimeout) ?? 0
    RunTestsHandler->>TestJobManager: StartJob(mode, filterOptions, initTimeoutMs)
    TestJobManager->>TestJobManager: Create TestJob with InitTimeoutMs = initTimeoutMs
    TestJobManager->>TestJobManager: PersistToSessionState(init_timeout_ms)

    loop Poll job status
        MCPServer->>TestJobManager: GetJob(jobId)
        TestJobManager->>TestJobManager: initTimeout = InitTimeoutMs > 0 ? InitTimeoutMs : DefaultInitializationTimeoutMs
        TestJobManager->>TestJobManager: Check now - StartedUnixMs > initTimeout
        alt Exceeded initTimeout
            TestJobManager->>TestJobManager: Status = Failed, Error = initialization timeout
        else Within initTimeout
            TestJobManager-->>MCPServer: Running or Completed status
        end
    end

    TestJobManager->>TestJobManager: PersistToSessionState(init_timeout_ms)
Loading

Class diagram for updated TestJobManager and RunTests init timeout handling

classDiagram
    class TestJob {
        string JobId
        TestJobStatus Status
        TestMode Mode
        long StartedUnixMs
        long FinishedUnixMs
        long LastProgressUnixMs
        long? LastFinishedUnixMs
        List~TestJobFailure~ FailuresSoFar
        string Error
        TestRunResult Result
        long InitTimeoutMs
    }

    class PersistedJob {
        string job_id
        string status
        string mode
        long started_unix_ms
        long? finished_unix_ms
        string last_finished_test_full_name
        long? last_finished_unix_ms
        List~TestJobFailure~ failures_so_far
        string error
        long init_timeout_ms
    }

    class TestJobManager {
        <<static>>
        const int FailureCap
        const long StuckThresholdMs
        const long DefaultInitializationTimeoutMs
        const int MaxJobsToKeep
        const long MinPersistIntervalMs

        static string StartJob(TestMode mode, TestFilterOptions filterOptions, long initTimeoutMs)
        static TestJob GetJob(string jobId)
        static void TryRestoreFromSessionState()
        static void PersistToSessionState(bool force)
    }

    class RunTests {
        static Task~object~ HandleCommand(JObject params)
    }

    class ParamsHelper {
        bool GetBool(string key)
        int? GetInt(string key)
        string GetString(string key)
    }

    RunTests --> TestJobManager : calls StartJob(mode, filterOptions, initTimeoutMs)
    TestJobManager o-- TestJob : manages
    TestJobManager o-- PersistedJob : serializes to SessionState
    RunTests --> ParamsHelper : uses to read initTimeout
    PersistedJob --> TestJob : maps init_timeout_ms to InitTimeoutMs
Loading

File-Level Changes

Change Details Files
Introduce per-job configurable initialization timeout in TestJobManager with persistence and default fallback.
  • Add InitTimeoutMs field to TestJob and include it when creating new jobs
  • Rename InitializationTimeoutMs to DefaultInitializationTimeoutMs and keep its value at 15s
  • Extend PersistedJob with init_timeout_ms, and persist/restore InitTimeoutMs via SessionState
  • Update StartJob to accept an optional initTimeoutMs parameter and store it on the job
  • In GetJob, compute an effective init timeout from per-job value or default and use it for initialization auto-fail logic and logging
MCPForUnity/Editor/Services/TestJobManager.cs
Plumb init_timeout from the Python run_tests tool into Unity as initTimeout, then into TestJobManager.
  • Extend run_tests() signature with optional init_timeout and document its purpose in the parameter annotation
  • When init_timeout is provided and >0, add initTimeout to the Unity command params sent over the transport
  • In the C# RunTests handler, read initTimeout from the request and pass it as initTimeoutMs into TestJobManager.StartJob
Server/src/services/tools/run_tests.py
MCPForUnity/Editor/Tools/RunTests.cs
Document usage of init_timeout for PlayMode tests in development docs.
  • Update English dev README to show run_tests PlayMode example with init_timeout=120000 and a brief comment
  • Update Chinese dev README with the same example and localized explanation
docs/development/README-DEV.md
docs/development/README-DEV-zh.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 2, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Per-job initialization timeout support added: TestJob.InitTimeoutMs persisted as init_timeout_ms; StartJob accepts/clamps initTimeoutMs; stuck-job detection in GetJob uses per-job timeout when set. Server, editor tooling, docs, and tests updated to accept, forward, validate, and cover init_timeout.

Changes

Cohort / File(s) Summary
Test job manager & editor
MCPForUnity/Editor/Services/TestJobManager.cs
Add TestJob.InitTimeoutMs; StartJob accepts initTimeoutMs, clamps to [0, Max], persists init_timeout_ms to SessionState, restores on load, and uses per-job timeout in stuck-job auto-fail logic and warning text.
Editor tooling
MCPForUnity/Editor/Tools/RunTests.cs
Read optional initTimeout param from tool args and forward it to TestJobManager.StartJob.
Server tool API
Server/src/services/tools/run_tests.py
Add `init_timeout: int
Documentation
docs/development/README-DEV.md, docs/development/README-DEV-zh.md
Examples updated to show init_timeout=120000 for PlayMode invocation.
Integration & unit tests
Server/tests/integration/test_run_tests_async.py, TestProjects/.../TestJobManagerInitTimeoutTests.cs, ...TestJobManagerInitTimeoutTests.cs.meta
Add integration tests for init_timeout forwarding and validation; add NUnit edit-mode tests verifying per-job InitTimeoutMs, stuck-job behavior, and persistence/restore; add Unity .meta for the test asset.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(220,240,255,0.5)
    Client->>Server: POST /run_tests (mode, filter, init_timeout?)
    end
    rect rgba(240,255,220,0.5)
    Server->>MCP Tool: send "run_tests" command (params include initTimeout if >0)
    end
    rect rgba(255,240,220,0.5)
    MCP Tool / Unity Editor->>TestJobManager: StartJob(mode, filterOptions, initTimeoutMs)
    Note right of TestJobManager: clamp initTimeoutMs -> set TestJob.InitTimeoutMs -> Persist to SessionState
    TestJobManager->>TestJobManager: Periodic GetJob checks init timeout using per-job InitTimeoutMs (or default)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • dsarno
  • msanatan

Poem

"A rabbit hops through code at night,
I set timeouts so tests take flight,
Fields tucked safe in session's nest,
Timers watched until they're best,
🐇⏱️ Tests start, persist, and rest."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main feature: adding a configurable initialization timeout for PlayMode test initialization, which is the primary change across multiple files.
Description check ✅ Passed The description comprehensively covers all required sections: clear problem statement, type of change, detailed changes made across Python/C#, testing verification, documentation updates, and notes on backwards compatibility.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Consider validating/sanitizing the initTimeoutMs value in RunTests.HandleCommand/TestJobManager.StartJob (e.g., clamp to a reasonable min/max and treat non-positive values as 0) so that non-Python callers or malformed input can't set an accidentally huge or negative initialization timeout.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider validating/sanitizing the `initTimeoutMs` value in `RunTests.HandleCommand`/`TestJobManager.StartJob` (e.g., clamp to a reasonable min/max and treat non-positive values as `0`) so that non-Python callers or malformed input can't set an accidentally huge or negative initialization timeout.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
MCPForUnity/Editor/Services/TestJobManager.cs (1)

301-325: 🛠️ Refactor suggestion | 🟠 Major

Add Unity tests for initTimeoutMs override and domain-reload restore functionality.

The feature is implemented (parameter passing, persistence, restoration, timeout enforcement) but completely lacks test coverage. Tests should verify:

  • Override applied when initTimeoutMs > 0 is passed
  • Default fallback to DefaultInitializationTimeoutMs (15 seconds) when initTimeoutMs == 0
  • Persistence and restoration of initTimeoutMs after domain reload

Add tests in TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/RunTestsTests.cs or create a dedicated TestJobManagerTests.cs file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Services/TestJobManager.cs` around lines 301 - 325, Add
EditMode unit tests exercising StartJob's initTimeoutMs behavior: one test calls
TestJobManager.StartJob(mode, filterOptions, initTimeoutMs>0) and asserts the
returned/created TestJob has InitTimeoutMs equal to the passed value; a second
test calls StartJob with initTimeoutMs==0 and asserts the TestJob.InitTimeoutMs
equals TestJobManager.DefaultInitializationTimeoutMs (15s). Add a third test for
persistence by starting a job with a nonzero initTimeoutMs, forcing the
manager's persistence path to run, then simulate a domain-reload restore by
invoking the TestJobManager restoration method (the same method used on domain
reload) and assert the restored TestJob still has the original InitTimeoutMs.
Reference StartJob, TestJob.InitTimeoutMs, DefaultInitializationTimeoutMs and
the manager's persistence/restore methods in your new tests.
🧹 Nitpick comments (1)
Server/src/services/tools/run_tests.py (1)

170-172: Validate explicit invalid init_timeout values instead of silently dropping them.

If callers pass init_timeout <= 0, the current path ignores it and proceeds. Returning a clear error makes misconfiguration obvious.

Proposed adjustment
-    if init_timeout is not None and init_timeout > 0:
-        params["initTimeout"] = init_timeout
+    if init_timeout is not None:
+        if init_timeout <= 0:
+            return MCPResponse(
+                success=False,
+                error="init_timeout must be a positive integer (milliseconds)",
+            )
+        params["initTimeout"] = init_timeout

Also applies to: 203-204

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Server/src/services/tools/run_tests.py` around lines 170 - 172, The
init_timeout parameter should be validated so invalid values (<= 0) are not
silently ignored; in the function signature/parameter handling for init_timeout
(and the other occurrence around lines 203-204 where init_timeout is used) add a
guard that if init_timeout is not None and init_timeout <= 0 you raise a
ValueError (or return an explicit error) with a clear message like "init_timeout
must be > 0 or None" instead of dropping the value—update validation in the
run_tests entrypoint/handler that declares init_timeout to enforce this rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@MCPForUnity/Editor/Services/TestJobManager.cs`:
- Around line 301-325: Add EditMode unit tests exercising StartJob's
initTimeoutMs behavior: one test calls TestJobManager.StartJob(mode,
filterOptions, initTimeoutMs>0) and asserts the returned/created TestJob has
InitTimeoutMs equal to the passed value; a second test calls StartJob with
initTimeoutMs==0 and asserts the TestJob.InitTimeoutMs equals
TestJobManager.DefaultInitializationTimeoutMs (15s). Add a third test for
persistence by starting a job with a nonzero initTimeoutMs, forcing the
manager's persistence path to run, then simulate a domain-reload restore by
invoking the TestJobManager restoration method (the same method used on domain
reload) and assert the restored TestJob still has the original InitTimeoutMs.
Reference StartJob, TestJob.InitTimeoutMs, DefaultInitializationTimeoutMs and
the manager's persistence/restore methods in your new tests.

---

Nitpick comments:
In `@Server/src/services/tools/run_tests.py`:
- Around line 170-172: The init_timeout parameter should be validated so invalid
values (<= 0) are not silently ignored; in the function signature/parameter
handling for init_timeout (and the other occurrence around lines 203-204 where
init_timeout is used) add a guard that if init_timeout is not None and
init_timeout <= 0 you raise a ValueError (or return an explicit error) with a
clear message like "init_timeout must be > 0 or None" instead of dropping the
value—update validation in the run_tests entrypoint/handler that declares
init_timeout to enforce this rule.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1f29e7e8-145c-4496-887a-6f3b43f421bc

📥 Commits

Reviewing files that changed from the base of the PR and between 359f2fd and 6c17b69.

📒 Files selected for processing (5)
  • MCPForUnity/Editor/Services/TestJobManager.cs
  • MCPForUnity/Editor/Tools/RunTests.cs
  • Server/src/services/tools/run_tests.py
  • docs/development/README-DEV-zh.md
  • docs/development/README-DEV.md

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs (2)

23-24: Remove unused field _originalJobs.

This field is declared but never assigned or used anywhere in the class.

🧹 Remove dead code
         private string _originalJobId;
-        private object _originalJobs;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs`
around lines 23 - 24, The private field _originalJobs in the
TestJobManagerInitTimeoutTests class is declared but never used; remove the
declaration "private object _originalJobs;" from the class to eliminate dead
code and any unused field warning, ensuring you only change the field
declaration and not other test logic or setup/teardown methods.

97-124: Potential test flakiness if Editor is compiling or updating.

The GetJob auto-fail logic in TestJobManager.cs (line 505) checks !EditorApplication.isCompiling && !EditorApplication.isUpdating before applying the timeout. If either condition is true during test execution, the job won't auto-fail and this test will fail unexpectedly.

This is unlikely in a controlled EditMode test environment, but worth noting. If flakiness is observed, consider adding a brief delay or explicit check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs`
around lines 97 - 124, The test can be flaky because TestJobManager.GetJob
checks EditorApplication.isCompiling and EditorApplication.isUpdating before
applying the timeout; modify the test
GetJob_WithDefaultTimeout_AutoFailsAfter15Seconds to wait until
EditorApplication.isCompiling and EditorApplication.isUpdating are false before
invoking _getJobMethod (or explicitly poll with a short timeout and small
delays), so the auto-fail path in TestJobManager.GetJob will be exercised
deterministically.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs`:
- Around line 23-24: The private field _originalJobs in the
TestJobManagerInitTimeoutTests class is declared but never used; remove the
declaration "private object _originalJobs;" from the class to eliminate dead
code and any unused field warning, ensuring you only change the field
declaration and not other test logic or setup/teardown methods.
- Around line 97-124: The test can be flaky because TestJobManager.GetJob checks
EditorApplication.isCompiling and EditorApplication.isUpdating before applying
the timeout; modify the test GetJob_WithDefaultTimeout_AutoFailsAfter15Seconds
to wait until EditorApplication.isCompiling and EditorApplication.isUpdating are
false before invoking _getJobMethod (or explicitly poll with a short timeout and
small delays), so the auto-fail path in TestJobManager.GetJob will be exercised
deterministically.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 27b7cef6-d43c-4a0a-b4df-05acf680c2a7

📥 Commits

Reviewing files that changed from the base of the PR and between 6c17b69 and 311aa66.

📒 Files selected for processing (5)
  • MCPForUnity/Editor/Services/TestJobManager.cs
  • Server/src/services/tools/run_tests.py
  • Server/tests/integration/test_run_tests_async.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs.meta
✅ Files skipped from review due to trivial changes (1)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs.meta
🚧 Files skipped from review as they are similar to previous changes (1)
  • Server/src/services/tools/run_tests.py

@Emerix Emerix changed the title Add configurable init_timeout for PlayMode test initialization feat: Add configurable init_timeout for PlayMode test initialization Apr 28, 2026
PaulLubos and others added 3 commits April 29, 2026 08:29
PlayMode tests require entering play mode which triggers a domain reload.
On large projects this can take >15s, causing the hardcoded 15s init
timeout to auto-fail the test job before tests actually start.

This adds an `init_timeout` parameter to `run_tests` that flows through
the Python server → C# RunTests handler → TestJobManager. When set, the
per-job timeout overrides the 15s default. The value is persisted across
domain reloads via SessionState.

Changes:
- Python: Add `init_timeout` param to `run_tests()` function signature
- C# RunTests: Read `initTimeout` param and pass to `StartJob()`
- C# TestJobManager: Per-job `InitTimeoutMs` field with fallback to
  `DefaultInitializationTimeoutMs` (15s), persisted in SessionState

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Clamp initTimeoutMs in StartJob: negative values → 0, cap at 600s
- Python: reject init_timeout <= 0 with explicit error before calling Unity
- Add 3 C# EditMode tests for per-job InitTimeoutMs behavior
  (custom timeout, default timeout auto-fail, persist/restore)
- Add 4 Python tests for init_timeout forwarding and validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove unused _originalJobs field
- Add Assume.That guards for EditorApplication.isCompiling/isUpdating
  so the test is skipped (inconclusive) rather than producing misleading
  results when the editor is mid-compilation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Emerix Emerix force-pushed the feature/configurable-init-timeout branch from 4475218 to 3df5a84 Compare April 29, 2026 06:34
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
MCPForUnity/Editor/Services/TestJobManager.cs (1)

498-507: ⚠️ Potential issue | 🟡 Minor

Update the timeout comment to match the per-job behavior.

The code now resolves either the per-job timeout or the default, but the comment still says the job auto-fails after 15 seconds. That will mislead anyone debugging the stuck-job path.

✏️ Suggested comment fix
-                // After 15 seconds without initialization, auto-fail the job to prevent hanging.
+                // After the resolved initialization timeout without initialization, auto-fail the job to prevent hanging.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Services/TestJobManager.cs` around lines 498 - 507, The
existing comment in TestJobManager incorrectly states a fixed 15-second timeout
while the code uses a per-job timeout (job.InitTimeoutMs) falling back to
DefaultInitializationTimeoutMs; update the comment above the check that uses
job.Status, job.TotalTests, job.InitTimeoutMs and DefaultInitializationTimeoutMs
to explain that the initialization timeout is per-job (job.InitTimeoutMs) or the
default (DefaultInitializationTimeoutMs) and that the job will be auto-failed
after whichever timeout value is resolved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs`:
- Around line 66-162: Tests that assert auto-fail behavior are flaking because
GetJob suppresses timeouts while the editor is compiling/updating; add a guard
at the start of the three tests (GetJob_WithCustomInitTimeout_UsesPerJobTimeout,
GetJob_WithDefaultTimeout_AutoFailsAfter15Seconds,
InitTimeoutMs_SurvivesPersistAndRestore) to skip or defer timeout assertions if
EditorApplication.isCompiling or EditorApplication.isUpdating is true (or re-run
the check after a short wait), so the assertions about TestJobStatus (the
Assert.AreEqual checks against TestJobStatus.Running/Failed) only run when the
editor is idle and GetJob's timeout logic will behave deterministically.
- Around line 24-64: The TearDown currently clears only the in-memory Jobs
dictionary but leaves the editor SessionState populated; after removing the test
keys from the _jobsField dictionary and restoring _currentJobIdField, invoke the
manager's PersistToSessionState (use the _persistMethod) so the cleaned/restored
state is written back to SessionState (alternatively call
TryRestoreFromSessionState if intended) to ensure no synthetic jobs remain in
the shared editor SessionState across tests.

---

Outside diff comments:
In `@MCPForUnity/Editor/Services/TestJobManager.cs`:
- Around line 498-507: The existing comment in TestJobManager incorrectly states
a fixed 15-second timeout while the code uses a per-job timeout
(job.InitTimeoutMs) falling back to DefaultInitializationTimeoutMs; update the
comment above the check that uses job.Status, job.TotalTests, job.InitTimeoutMs
and DefaultInitializationTimeoutMs to explain that the initialization timeout is
per-job (job.InitTimeoutMs) or the default (DefaultInitializationTimeoutMs) and
that the job will be auto-failed after whichever timeout value is resolved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 86061baf-f56a-49a9-9aa6-a19406939fea

📥 Commits

Reviewing files that changed from the base of the PR and between 4475218 and 3df5a84.

📒 Files selected for processing (8)
  • MCPForUnity/Editor/Services/TestJobManager.cs
  • MCPForUnity/Editor/Tools/RunTests.cs
  • Server/src/services/tools/run_tests.py
  • Server/tests/integration/test_run_tests_async.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs.meta
  • docs/development/README-DEV-zh.md
  • docs/development/README-DEV.md
✅ Files skipped from review due to trivial changes (3)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs.meta
  • docs/development/README-DEV.md
  • docs/development/README-DEV-zh.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • MCPForUnity/Editor/Tools/RunTests.cs

The persist/restore test writes synthetic jobs to SessionState, which
survives domain reloads and would be re-hydrated by TestJobManager's
[InitializeOnLoadMethod] hook. Flush the cleaned in-memory state back
to SessionState in TearDown so test artifacts don't leak across runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs (2)

54-68: ⚠️ Potential issue | 🟠 Major

Restore the original SessionState, not just the in-memory jobs.

Persisting the cleaned dictionary still overwrites whatever was already in editor SessionState before the test ran. Snapshot the SessionState strings in SetUp and write them back here after removing the synthetic jobs.

🧹 Suggested cleanup
     private string _originalJobId;
+    private string _originalJobsSessionState;
+    private string _originalCurrentJobIdSessionState;
@@
             _originalJobId = _currentJobIdField.GetValue(null) as string;
+            _originalJobsSessionState = UnityEditor.SessionState.GetString("MCPForUnity.TestJobsV1", string.Empty);
+            _originalCurrentJobIdSessionState = UnityEditor.SessionState.GetString("MCPForUnity.CurrentTestJobIdV1", string.Empty);
@@
             _currentJobIdField.SetValue(null, _originalJobId);
             // Clean up any test jobs we inserted
             var jobs = _jobsField.GetValue(null) as System.Collections.IDictionary;
             jobs?.Remove("test-init-timeout-job");
             jobs?.Remove("test-init-timeout-default");
             jobs?.Remove("test-init-timeout-persist");
-            // Flush cleaned state to SessionState so synthetic jobs don't survive domain reloads.
-            // The persist test writes to SessionState; without this, the stub job would be
-            // restored on the next [InitializeOnLoadMethod] and pollute later test runs.
-            _persistMethod.Invoke(null, new object[] { true });
+            UnityEditor.SessionState.SetString("MCPForUnity.TestJobsV1", _originalJobsSessionState);
+            UnityEditor.SessionState.SetString("MCPForUnity.CurrentTestJobIdV1", _originalCurrentJobIdSessionState);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs`
around lines 54 - 68, TearDown currently restores only in-memory fields
(_currentJobIdField, _jobsField) and calls _persistMethod, which overwrites any
pre-existing editor SessionState; modify the test to snapshot the relevant
SessionState string(s) during SetUp and restore that snapshot in TearDown
instead of blindly persisting the cleaned dictionary: in SetUp capture the
original SessionState value(s) that the tested code reads/writes, store them on
a private field (e.g. _originalSessionStateString), and in TearDown invoke
_persistMethod or the appropriate SessionState API to write back that original
string after removing the synthetic jobs and resetting _currentJobIdField so the
editor SessionState is exactly restored.

100-127: ⚠️ Potential issue | 🟡 Minor

Scope the auto-fail assertion to an idle editor.

GetJob suppresses init-timeout failures while Unity is compiling or updating, so this test can still be flaky unless it only runs when the editor is idle.

🧪 Suggested guard
     [Test]
     public void GetJob_WithDefaultTimeout_AutoFailsAfter15Seconds()
     {
+        Assume.That(
+            !UnityEditor.EditorApplication.isCompiling && !UnityEditor.EditorApplication.isUpdating,
+            "Timeout auto-fail is undefined while the editor is compiling or updating.");
+
         // Arrange: insert a job with InitTimeoutMs=0 (use default) and start time 20s ago
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs`
around lines 100 - 127, The test
GetJob_WithDefaultTimeout_AutoFailsAfter15Seconds calls GetJob which suppresses
init-timeout failures while the editor is busy; scope the assertion to an idle
editor by checking EditorApplication.isCompiling and
EditorApplication.isUpdating (or EditorApplication.isPlaying/other relevant busy
flags) inside the
TestJobManagerInitTimeoutTests.GetJob_WithDefaultTimeout_AutoFailsAfter15Seconds
test and either skip the assertion (Assert.Ignore) when the editor is busy or
wait/poll until both are false before invoking GetJob, so the auto-fail check
runs only when the editor is idle.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs`:
- Around line 54-68: TearDown currently restores only in-memory fields
(_currentJobIdField, _jobsField) and calls _persistMethod, which overwrites any
pre-existing editor SessionState; modify the test to snapshot the relevant
SessionState string(s) during SetUp and restore that snapshot in TearDown
instead of blindly persisting the cleaned dictionary: in SetUp capture the
original SessionState value(s) that the tested code reads/writes, store them on
a private field (e.g. _originalSessionStateString), and in TearDown invoke
_persistMethod or the appropriate SessionState API to write back that original
string after removing the synthetic jobs and resetting _currentJobIdField so the
editor SessionState is exactly restored.
- Around line 100-127: The test
GetJob_WithDefaultTimeout_AutoFailsAfter15Seconds calls GetJob which suppresses
init-timeout failures while the editor is busy; scope the assertion to an idle
editor by checking EditorApplication.isCompiling and
EditorApplication.isUpdating (or EditorApplication.isPlaying/other relevant busy
flags) inside the
TestJobManagerInitTimeoutTests.GetJob_WithDefaultTimeout_AutoFailsAfter15Seconds
test and either skip the assertion (Assert.Ignore) when the editor is busy or
wait/poll until both are false before invoking GetJob, so the auto-fail check
runs only when the editor is idle.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59c4bedf-6a1a-43cb-8d2e-1631af993300

📥 Commits

Reviewing files that changed from the base of the PR and between 3df5a84 and 233e88e.

📒 Files selected for processing (1)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs

@Scriptwonder Scriptwonder merged commit dfc3485 into CoplayDev:beta Apr 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants