-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: Add configurable init_timeout for PlayMode test initialization #1021
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Scriptwonder
merged 4 commits into
CoplayDev:beta
from
Emerix:feature/configurable-init-timeout
Apr 29, 2026
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
8d51737
Add configurable init_timeout for PlayMode test initialization
e8e3b45
Address code review: input validation and tests
3df5a84
Remove unused field, guard test against compile/update flakiness
233e88e
Clear SessionState in TestJobManagerInitTimeoutTests TearDown
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
168 changes: 168 additions & 0 deletions
168
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Reflection; | ||
| using NUnit.Framework; | ||
| using MCPForUnity.Editor.Services; | ||
|
|
||
| namespace MCPForUnityTests.Editor.Services | ||
| { | ||
| /// <summary> | ||
| /// Tests for TestJobManager's per-job InitTimeoutMs feature. | ||
| /// Uses reflection to manipulate internal state since StartJob triggers a real test run. | ||
| /// </summary> | ||
| public class TestJobManagerInitTimeoutTests | ||
| { | ||
| private FieldInfo _jobsField; | ||
| private FieldInfo _currentJobIdField; | ||
| private MethodInfo _getJobMethod; | ||
| private MethodInfo _persistMethod; | ||
| private MethodInfo _restoreMethod; | ||
| private Type _testJobType; | ||
|
|
||
| private string _originalJobId; | ||
|
|
||
| [SetUp] | ||
| public void SetUp() | ||
| { | ||
| var asm = typeof(MCPServiceLocator).Assembly; | ||
| var managerType = asm.GetType("MCPForUnity.Editor.Services.TestJobManager"); | ||
| Assert.NotNull(managerType, "Could not find TestJobManager"); | ||
|
|
||
| _testJobType = asm.GetType("MCPForUnity.Editor.Services.TestJob"); | ||
| Assert.NotNull(_testJobType, "Could not find TestJob"); | ||
|
|
||
| _jobsField = managerType.GetField("Jobs", BindingFlags.NonPublic | BindingFlags.Static); | ||
| Assert.NotNull(_jobsField, "Could not find Jobs field"); | ||
|
|
||
| _currentJobIdField = managerType.GetField("_currentJobId", BindingFlags.NonPublic | BindingFlags.Static); | ||
| Assert.NotNull(_currentJobIdField, "Could not find _currentJobId field"); | ||
|
|
||
| _getJobMethod = managerType.GetMethod("GetJob", BindingFlags.NonPublic | BindingFlags.Static); | ||
| Assert.NotNull(_getJobMethod, "Could not find GetJob method"); | ||
|
|
||
| _persistMethod = managerType.GetMethod("PersistToSessionState", BindingFlags.NonPublic | BindingFlags.Static); | ||
| Assert.NotNull(_persistMethod, "Could not find PersistToSessionState method"); | ||
|
|
||
| _restoreMethod = managerType.GetMethod("TryRestoreFromSessionState", BindingFlags.NonPublic | BindingFlags.Static); | ||
| Assert.NotNull(_restoreMethod, "Could not find TryRestoreFromSessionState method"); | ||
|
|
||
| // Snapshot original state | ||
| _originalJobId = _currentJobIdField.GetValue(null) as string; | ||
| // We'll restore _currentJobId in TearDown; Jobs dictionary is shared static state | ||
| } | ||
|
|
||
| [TearDown] | ||
| public void TearDown() | ||
| { | ||
| // Restore original state | ||
| _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 }); | ||
| } | ||
|
|
||
| [Test] | ||
| public void GetJob_WithCustomInitTimeout_UsesPerJobTimeout() | ||
| { | ||
| // Arrange: insert a job with a custom init timeout and a start time far enough in the | ||
| // past to exceed the default 15s but within the custom 120s. | ||
| var jobs = _jobsField.GetValue(null) as System.Collections.IDictionary; | ||
| long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); | ||
|
|
||
| var job = Activator.CreateInstance(_testJobType); | ||
| _testJobType.GetProperty("JobId").SetValue(job, "test-init-timeout-job"); | ||
| _testJobType.GetProperty("Status").SetValue(job, TestJobStatus.Running); | ||
| _testJobType.GetProperty("Mode").SetValue(job, "PlayMode"); | ||
| _testJobType.GetProperty("StartedUnixMs").SetValue(job, now - 30_000); // 30s ago | ||
| _testJobType.GetProperty("LastUpdateUnixMs").SetValue(job, now - 30_000); | ||
| _testJobType.GetProperty("TotalTests").SetValue(job, null); // Not initialized yet | ||
| _testJobType.GetProperty("InitTimeoutMs").SetValue(job, 120_000L); // 120s custom timeout | ||
| _testJobType.GetProperty("FailuresSoFar").SetValue(job, new List<TestJobFailure>()); | ||
|
|
||
| jobs["test-init-timeout-job"] = job; | ||
| _currentJobIdField.SetValue(null, "test-init-timeout-job"); | ||
|
|
||
| // Act: GetJob should NOT auto-fail because 30s < 120s custom timeout | ||
| var result = _getJobMethod.Invoke(null, new object[] { "test-init-timeout-job" }); | ||
|
|
||
| // Assert: job should still be running | ||
| var status = (TestJobStatus)_testJobType.GetProperty("Status").GetValue(result); | ||
| Assert.AreEqual(TestJobStatus.Running, status, | ||
| "Job with 120s custom timeout should not auto-fail after 30s"); | ||
| } | ||
|
|
||
| [Test] | ||
| public void GetJob_WithDefaultTimeout_AutoFailsAfter15Seconds() | ||
| { | ||
| // Arrange: insert a job with InitTimeoutMs=0 (use default) and start time 20s ago | ||
| var jobs = _jobsField.GetValue(null) as System.Collections.IDictionary; | ||
| long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); | ||
|
|
||
| var job = Activator.CreateInstance(_testJobType); | ||
| _testJobType.GetProperty("JobId").SetValue(job, "test-init-timeout-default"); | ||
| _testJobType.GetProperty("Status").SetValue(job, TestJobStatus.Running); | ||
| _testJobType.GetProperty("Mode").SetValue(job, "EditMode"); | ||
| _testJobType.GetProperty("StartedUnixMs").SetValue(job, now - 20_000); // 20s ago | ||
| _testJobType.GetProperty("LastUpdateUnixMs").SetValue(job, now - 20_000); | ||
| _testJobType.GetProperty("TotalTests").SetValue(job, null); | ||
| _testJobType.GetProperty("InitTimeoutMs").SetValue(job, 0L); // Use default | ||
| _testJobType.GetProperty("FailuresSoFar").SetValue(job, new List<TestJobFailure>()); | ||
|
|
||
| jobs["test-init-timeout-default"] = job; | ||
| _currentJobIdField.SetValue(null, "test-init-timeout-default"); | ||
|
|
||
| // Act: GetJob should auto-fail because 20s > 15s default | ||
| var result = _getJobMethod.Invoke(null, new object[] { "test-init-timeout-default" }); | ||
|
|
||
| // Assert: job should be failed | ||
| var status = (TestJobStatus)_testJobType.GetProperty("Status").GetValue(result); | ||
| Assert.AreEqual(TestJobStatus.Failed, status, | ||
| "Job with default timeout should auto-fail after 20s"); | ||
| } | ||
|
|
||
| [Test] | ||
| public void InitTimeoutMs_SurvivesPersistAndRestore() | ||
| { | ||
| // Arrange: insert a job with custom InitTimeoutMs | ||
| var jobs = _jobsField.GetValue(null) as System.Collections.IDictionary; | ||
| long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); | ||
|
|
||
| var job = Activator.CreateInstance(_testJobType); | ||
| _testJobType.GetProperty("JobId").SetValue(job, "test-init-timeout-persist"); | ||
| _testJobType.GetProperty("Status").SetValue(job, TestJobStatus.Running); | ||
| _testJobType.GetProperty("Mode").SetValue(job, "PlayMode"); | ||
| _testJobType.GetProperty("StartedUnixMs").SetValue(job, now); | ||
| _testJobType.GetProperty("LastUpdateUnixMs").SetValue(job, now); | ||
| _testJobType.GetProperty("TotalTests").SetValue(job, null); | ||
| _testJobType.GetProperty("InitTimeoutMs").SetValue(job, 90_000L); | ||
| _testJobType.GetProperty("FailuresSoFar").SetValue(job, new List<TestJobFailure>()); | ||
|
|
||
| jobs["test-init-timeout-persist"] = job; | ||
| _currentJobIdField.SetValue(null, "test-init-timeout-persist"); | ||
|
|
||
| // Act: persist then restore (simulates domain reload) | ||
| _persistMethod.Invoke(null, new object[] { true }); | ||
| // Clear in-memory state | ||
| jobs.Remove("test-init-timeout-persist"); | ||
| _currentJobIdField.SetValue(null, null); | ||
| // Restore from SessionState | ||
| _restoreMethod.Invoke(null, null); | ||
|
|
||
| // Assert: restored job should have the same InitTimeoutMs | ||
| var restoredJobs = _jobsField.GetValue(null) as System.Collections.IDictionary; | ||
| Assert.IsTrue(restoredJobs.Contains("test-init-timeout-persist"), | ||
| "Job should be restored from SessionState"); | ||
|
|
||
| var restoredJob = restoredJobs["test-init-timeout-persist"]; | ||
| var restoredTimeout = (long)_testJobType.GetProperty("InitTimeoutMs").GetValue(restoredJob); | ||
| Assert.AreEqual(90_000L, restoredTimeout, | ||
| "InitTimeoutMs should survive persist/restore cycle"); | ||
| } | ||
|
Emerix marked this conversation as resolved.
|
||
| } | ||
| } | ||
2 changes: 2 additions & 0 deletions
2
...jects/UnityMCPTests/Assets/Tests/EditMode/Services/TestJobManagerInitTimeoutTests.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
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.
Uh oh!
There was an error while loading. Please reload this page.