|
1 | | -using System; |
2 | | -using System.Collections.Generic; |
3 | | -using System.Threading; |
4 | | -using System.Threading.Tasks; |
5 | | -using McpUnity.Unity; |
6 | | -using UnityEngine; |
7 | | -using UnityEditor; |
8 | | -using UnityEditor.TestTools.TestRunner.Api; |
9 | | -using Newtonsoft.Json.Linq; |
10 | | - |
11 | | -namespace McpUnity.Services |
12 | | -{ |
13 | | - /// <summary> |
14 | | - /// Service for accessing Unity Test Runner functionality |
15 | | - /// </summary> |
16 | | - public class TestRunnerService : ITestRunnerService |
17 | | - { |
18 | | - private readonly TestRunnerApi _testRunnerApi; |
19 | | - |
20 | | - /// <summary> |
21 | | - /// Get the TestRunnerApi instance |
22 | | - /// </summary> |
23 | | - public TestRunnerApi TestRunnerApi => _testRunnerApi; |
24 | | - |
25 | | - /// <summary> |
26 | | - /// Constructor |
27 | | - /// </summary> |
28 | | - public TestRunnerService() |
29 | | - { |
30 | | - _testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>(); |
31 | | - } |
32 | | - |
33 | | - [MenuItem("Tools/MCP Unity/Debug call path")] |
34 | | - public static async void DebugCallGetAllTests() |
35 | | - { |
36 | | - var service = new TestRunnerService(); |
37 | | - var tests = await service.GetAllTestsAsync(); |
38 | | - Debug.Log($"Retrieved {tests.Count} tests:"); |
39 | | - foreach (var t in tests) |
40 | | - Debug.Log($"Test: {t.FullName} ({t.TestMode}) - State: {t.RunState}"); |
41 | | - } |
42 | | - |
43 | | - /// <summary> |
44 | | - /// Async retrieval of all tests using TestRunnerApi callbacks |
45 | | - /// </summary> |
46 | | - /// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param> |
47 | | - /// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns> |
48 | | - public async Task<List<TestItemInfo>> GetAllTestsAsync(string testMode = "") |
49 | | - { |
50 | | - var tests = new List<TestItemInfo>(); |
51 | | - var tcs = new TaskCompletionSource<bool>(); |
52 | | - int pending = 0; |
53 | | - |
54 | | - if (string.IsNullOrEmpty(testMode) || testMode.Equals("EditMode", StringComparison.OrdinalIgnoreCase)) |
55 | | - { |
56 | | - Interlocked.Increment(ref pending); |
57 | | - _testRunnerApi.RetrieveTestList(TestMode.EditMode, adaptor => |
58 | | - { |
59 | | - CollectTestItems(adaptor, tests); |
60 | | - CheckDone(); |
61 | | - }); |
62 | | - } |
63 | | - if (string.IsNullOrEmpty(testMode) || testMode.Equals("PlayMode", StringComparison.OrdinalIgnoreCase)) |
64 | | - { |
65 | | - Interlocked.Increment(ref pending); |
66 | | - _testRunnerApi.RetrieveTestList(TestMode.PlayMode, adaptor => |
67 | | - { |
68 | | - CollectTestItems(adaptor, tests); |
69 | | - CheckDone(); |
70 | | - }); |
71 | | - } |
72 | | - |
73 | | - if (pending == 0) |
74 | | - tcs.SetResult(true); |
75 | | - |
76 | | - await tcs.Task; |
77 | | - |
78 | | - return tests; |
79 | | - |
80 | | - void CheckDone() |
81 | | - { |
82 | | - if (Interlocked.Decrement(ref pending) == 0) |
83 | | - tcs.TrySetResult(true); |
84 | | - } |
85 | | - } |
86 | | - |
87 | | - /// <summary> |
88 | | - /// Execute tests with the provided parameters |
89 | | - /// </summary> |
90 | | - /// <param name="testMode">Test mode to run</param> |
91 | | - /// <param name="testFilter">Optional test filter</param> |
92 | | - /// <param name="completionSource">TaskCompletionSource to resolve when tests are complete</param> |
93 | | - /// <returns>Task that resolves with test results when tests are complete</returns> |
94 | | - public async void ExecuteTests( |
95 | | - TestMode testMode, |
96 | | - string testFilter, |
97 | | - TaskCompletionSource<JObject> completionSource) |
98 | | - { |
99 | | - // Create filter |
100 | | - var filter = new Filter |
101 | | - { |
102 | | - testMode = testMode |
103 | | - }; |
104 | | - |
105 | | - // Apply name filter if provided |
106 | | - if (!string.IsNullOrEmpty(testFilter)) |
107 | | - { |
108 | | - filter.testNames = new[] { testFilter }; |
109 | | - } |
110 | | - |
111 | | - // Execute tests |
112 | | - _testRunnerApi.Execute(new ExecutionSettings(filter)); |
113 | | - |
114 | | - // Use timeout from settings if not specified |
115 | | - var timeoutSeconds = McpUnitySettings.Instance.RequestTimeoutSeconds; |
116 | | - |
117 | | - Task completedTask = await Task.WhenAny( |
118 | | - completionSource.Task, |
119 | | - Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)) |
120 | | - ); |
121 | | - |
122 | | - if (completedTask != completionSource.Task) |
123 | | - { |
124 | | - completionSource.SetResult(McpUnitySocketHandler.CreateErrorResponse( |
125 | | - $"Test run timed out after {timeoutSeconds} seconds", |
126 | | - "test_runner_timeout" |
127 | | - )); |
128 | | - } |
129 | | - } |
130 | | - |
131 | | - /// <summary> |
132 | | - /// Recursively collect test items from test adaptors |
133 | | - /// </summary> |
134 | | - private void CollectTestItems(ITestAdaptor testAdaptor, List<TestItemInfo> tests, string parentPath = "") |
135 | | - { |
136 | | - if (testAdaptor.IsSuite) |
137 | | - { |
138 | | - // For suites (namespaces, classes), collect all children |
139 | | - foreach (var child in testAdaptor.Children) |
140 | | - { |
141 | | - string currentPath = string.IsNullOrEmpty(parentPath) ? testAdaptor.Name : $"{parentPath}.{testAdaptor.Name}"; |
142 | | - CollectTestItems(child, tests, currentPath); |
143 | | - } |
144 | | - } |
145 | | - else |
146 | | - { |
147 | | - // For individual tests, add to the list |
148 | | - string fullPath = string.IsNullOrEmpty(parentPath) ? testAdaptor.Name : $"{parentPath}.{testAdaptor.Name}"; |
149 | | - |
150 | | - tests.Add(new TestItemInfo |
151 | | - { |
152 | | - Name = testAdaptor.Name, |
153 | | - FullName = testAdaptor.FullName, |
154 | | - Path = fullPath, |
155 | | - TestMode = testAdaptor.TestMode.ToString(), |
156 | | - RunState = testAdaptor.RunState.ToString() |
157 | | - }); |
158 | | - } |
159 | | - } |
160 | | - } |
161 | | - |
162 | | - /// <summary> |
163 | | - /// Information about a test item |
164 | | - /// </summary> |
165 | | - public class TestItemInfo |
166 | | - { |
167 | | - public string Name { get; set; } |
168 | | - public string FullName { get; set; } |
169 | | - public string Path { get; set; } |
170 | | - public string TestMode { get; set; } |
171 | | - public string RunState { get; set; } |
172 | | - } |
173 | | -} |
0 commit comments