using System; using System.Threading.Tasks; using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Resources.Tests; using MCPForUnity.Editor.Services; using Newtonsoft.Json.Linq; using UnityEditor.TestTools.TestRunner.Api; namespace MCPForUnity.Editor.Tools { /// /// Starts a Unity Test Runner run asynchronously and returns a job id immediately. /// Use get_test_job(job_id) to poll status/results. /// [McpForUnityTool("run_tests", AutoRegister = false, Group = "testing")] public static class RunTests { public static Task HandleCommand(JObject @params) { try { // Check for clear_stuck action first if (ParamCoercion.CoerceBool(@params?["clear_stuck"], false)) { bool wasCleared = TestJobManager.ClearStuckJob(); return Task.FromResult(new SuccessResponse( wasCleared ? "Stuck job cleared." : "No running job to clear.", new { cleared = wasCleared } )); } string modeStr = @params?["mode"]?.ToString(); if (string.IsNullOrWhiteSpace(modeStr)) { modeStr = "EditMode"; } if (!ModeParser.TryParse(modeStr, out var parsedMode, out var parseError)) { return Task.FromResult(new ErrorResponse(parseError)); } var p = new ToolParams(@params); bool includeDetails = p.GetBool("includeDetails"); bool includeFailedTests = p.GetBool("includeFailedTests"); var filterOptions = GetFilterOptions(@params); long initTimeoutMs = p.GetInt("initTimeout") ?? 0; string jobId = TestJobManager.StartJob(parsedMode.Value, filterOptions, initTimeoutMs); return Task.FromResult(new SuccessResponse("Test job started.", new { job_id = jobId, status = "running", mode = parsedMode.Value.ToString(), include_details = includeDetails, include_failed_tests = includeFailedTests })); } catch (Exception ex) { // Normalize the already-running case to a stable error token. if (ex.Message != null && ex.Message.IndexOf("already in progress", StringComparison.OrdinalIgnoreCase) >= 0) { return Task.FromResult(new ErrorResponse("tests_running", new { reason = "tests_running", retry_after_ms = 5000 })); } return Task.FromResult(new ErrorResponse($"Failed to start test job: {ex.Message}")); } } private static TestFilterOptions GetFilterOptions(JObject @params) { if (@params == null) { return null; } var p = new ToolParams(@params); var testNames = p.GetStringArray("testNames"); var groupNames = p.GetStringArray("groupNames"); var categoryNames = p.GetStringArray("categoryNames"); var assemblyNames = p.GetStringArray("assemblyNames"); if (testNames == null && groupNames == null && categoryNames == null && assemblyNames == null) { return null; } return new TestFilterOptions { TestNames = testNames, GroupNames = groupNames, CategoryNames = categoryNames, AssemblyNames = assemblyNames }; } } }