@atelierai/uco 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +7 -8
- package/bin/server.mjs +1 -1
- package/bin/stub-plugin.mjs +3 -3
- package/dist/codegen/coerce.js +1 -1
- package/dist/codegen/coerce.js.map +1 -1
- package/dist/codegen/emit.js +2 -2
- package/dist/codegen/emit.js.map +1 -1
- package/dist/codegen/refs.js +1 -1
- package/dist/codegen/refs.js.map +1 -1
- package/dist/codegen/types.js +1 -1
- package/dist/codegen/types.js.map +1 -1
- package/dist/commands/call.js +1 -1
- package/dist/commands/call.js.map +1 -1
- package/dist/commands/devops/configure.js +1 -1
- package/dist/commands/devops/configure.js.map +1 -1
- package/dist/commands/devops/index.js +0 -4
- package/dist/commands/devops/index.js.map +1 -1
- package/dist/commands/devops/install-plugin.js +1 -1
- package/dist/commands/devops/install-plugin.js.map +1 -1
- package/dist/commands/devops/open.js +7 -7
- package/dist/commands/devops/open.js.map +1 -1
- package/dist/commands/devops/status.js +3 -3
- package/dist/commands/devops/status.js.map +1 -1
- package/dist/commands/exec.js +1 -1
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/install.js +2 -2
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/prompt.js +2 -2
- package/dist/commands/prompt.js.map +1 -1
- package/dist/commands/resource.js +2 -2
- package/dist/commands/resource.js.map +1 -1
- package/dist/commands/update.js +1 -26
- package/dist/commands/update.js.map +1 -1
- package/dist/config/port.js +2 -2
- package/dist/config/port.js.map +1 -1
- package/dist/config/resolve.d.ts +1 -2
- package/dist/config/resolve.js +0 -12
- package/dist/config/resolve.js.map +1 -1
- package/dist/devops/lib/configure.d.ts +1 -1
- package/dist/devops/lib/configure.js +2 -2
- package/dist/devops/lib/configure.js.map +1 -1
- package/dist/devops/lib/install-plugin.d.ts +1 -1
- package/dist/devops/lib/install-plugin.js +1 -1
- package/dist/devops/lib/install-plugin.js.map +1 -1
- package/dist/devops/lib/install.d.ts +2 -2
- package/dist/devops/lib/install.js +5 -5
- package/dist/devops/lib/install.js.map +1 -1
- package/dist/devops/lib/open.js +11 -32
- package/dist/devops/lib/open.js.map +1 -1
- package/dist/devops/lib/remove-plugin.d.ts +1 -1
- package/dist/devops/lib/remove-plugin.js +3 -3
- package/dist/devops/lib/remove-plugin.js.map +1 -1
- package/dist/devops/lib/run-tool.d.ts +2 -2
- package/dist/devops/lib/run-tool.js +2 -2
- package/dist/devops/lib/types.d.ts +17 -54
- package/dist/devops/lib/types.js +2 -2
- package/dist/devops/lib/types.js.map +1 -1
- package/dist/devops/lib/update.d.ts +0 -15
- package/dist/devops/lib/update.js +2 -91
- package/dist/devops/lib/update.js.map +1 -1
- package/dist/devops/utils/agents.d.ts +0 -28
- package/dist/devops/utils/agents.js +0 -462
- package/dist/devops/utils/agents.js.map +1 -1
- package/dist/devops/utils/config.d.ts +9 -18
- package/dist/devops/utils/config.js +7 -20
- package/dist/devops/utils/config.js.map +1 -1
- package/dist/devops/utils/editor-cache.js +1 -1
- package/dist/devops/utils/editor-cache.js.map +1 -1
- package/dist/devops/utils/launch-error-dismiss.d.ts +2 -2
- package/dist/devops/utils/launch-error-dismiss.js +5 -5
- package/dist/devops/utils/manifest.d.ts +2 -2
- package/dist/devops/utils/manifest.js +3 -3
- package/dist/devops/utils/manifest.js.map +1 -1
- package/dist/devops/utils/port.d.ts +1 -1
- package/dist/devops/utils/port.js +1 -1
- package/dist/devops/utils/probe.d.ts +1 -1
- package/dist/devops/utils/probe.js +1 -1
- package/dist/devops/utils/ui.js +2 -2
- package/dist/devops/utils/ui.js.map +1 -1
- package/dist/devops/utils/unity-editor.js +4 -4
- package/dist/devops/utils/unity-editor.js.map +1 -1
- package/dist/devops/utils/unity-hub.js +1 -1
- package/dist/devops/utils/unity-hub.js.map +1 -1
- package/dist/devops/utils/unity-shutdown.d.ts +1 -1
- package/dist/devops/utils/unity-shutdown.js +1 -1
- package/dist/devops/utils/update-check.js +3 -3
- package/dist/devops/utils/update-check.js.map +1 -1
- package/dist/devops/utils/vendor.d.ts +2 -2
- package/dist/devops/utils/vendor.js +4 -4
- package/dist/devops/utils/vendor.js.map +1 -1
- package/dist/generated/tools.js +1 -1
- package/dist/generated/tools.js.map +1 -1
- package/dist/program.js +3 -3
- package/dist/program.js.map +1 -1
- package/dist/server/app.d.ts +5 -5
- package/dist/server/app.js +3 -3
- package/dist/server/app.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +2 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/rest/auth.d.ts +1 -1
- package/dist/server/rest/auth.js +3 -3
- package/dist/server/rest/auth.js.map +1 -1
- package/dist/server/rest/context.d.ts +2 -2
- package/dist/server/rest/forward.d.ts +2 -2
- package/dist/server/session/store.d.ts +3 -3
- package/dist/server/session/store.js +4 -4
- package/dist/server/session/store.js.map +1 -1
- package/dist/server/stub-client/scenarios.js +8 -8
- package/dist/server/stub-client/scenarios.js.map +1 -1
- package/dist/server/types.d.ts +11 -11
- package/dist/server/types.js +9 -9
- package/dist/server/types.js.map +1 -1
- package/dist/server/ws/hub.d.ts +8 -8
- package/dist/server/ws/hub.js +13 -13
- package/dist/server/ws/hub.js.map +1 -1
- package/dist/server/ws/rpc.d.ts +3 -3
- package/dist/server/ws/rpc.js +4 -4
- package/dist/server/ws/rpc.js.map +1 -1
- package/dist/transport/index.d.ts +1 -1
- package/dist/transport/index.js +3 -3
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/rest.js +1 -1
- package/dist/transport/rest.js.map +1 -1
- package/docs/INSTALL.zh-CN.md +2 -3
- package/package.json +1 -1
- package/skills/uco-setup/SKILL.md +1 -1
- package/skills/unity-editor/references/diagnostics.md +24 -24
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetConfig.cs +132 -132
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDependencyResolver.cs +237 -237
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDownloader.cs +82 -82
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/MenuItems.cs +247 -247
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/ToolGroups.cs +153 -153
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/SystemTool/Skills.Create.SkillBody.cs +155 -155
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Console.GetLogs.cs +95 -95
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Docs.HttpFetcher.cs +139 -139
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Instance.ListAll.cs +72 -72
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/PathReadHelper.cs +106 -106
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Script.Validate.cs +252 -252
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests/TestResultCollector.cs +537 -537
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests.cs +400 -400
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Branding/ProductInfo.cs +62 -62
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/CopilotServerManager.cs +1034 -1034
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Skills/Skill_InitialSetup.cs +197 -197
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.Editor.cs +201 -201
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.cs +67 -67
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Components/ExtensionPanel.cs +379 -379
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/MenuItems.cs +72 -72
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/UnityCopilotProjectSettingsProvider.cs +64 -64
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotListWindowBase.cs +393 -393
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotWindowBase.cs +113 -113
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.Connection.cs +3 -3
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotFeatures.cs +195 -195
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotServer.cs +372 -372
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CreateGUI.cs +354 -354
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.cs +78 -78
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowInitializer.cs +47 -47
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/SerializationCheckWindow.cs +190 -190
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Build.cs +91 -91
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Config.cs +298 -298
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Static.cs +296 -296
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.cs +69 -69
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PackageUtils.cs +168 -168
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PrefsKeyMigration.cs +119 -119
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/SafeDefaultsGuard.cs +180 -180
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ScriptUtils.cs +229 -229
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ToolsManifestGenerator.cs +200 -200
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringAdapters.cs +1467 -1467
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringSafetyReloadGuard.cs +65 -65
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityCopilotUpdateProjectSettings.cs +66 -66
- package/vendor/plugin/com.atelierai.unity.copilot/Plugins/ReflectorNet.dll +0 -0
- package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.Common.dll +0 -0
- package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.dll +0 -0
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Converter/Json/Structures/EntityIdConverter.cs +120 -120
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.cs +110 -110
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.pre-Unity.6.5.cs +110 -110
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Unity/Logs/FileLogStorage.cs +547 -547
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Build.cs +243 -243
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Config.cs +175 -175
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.cs +295 -295
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginBuilder.cs +105 -105
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.Static.cs +155 -155
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.cs +59 -59
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Utils/EnvironmentUtils.cs +368 -368
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/AtomicApiTestFixtures.cs +99 -99
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/TryModifyAtTests.cs +227 -227
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AuthoringSafetyPilotTests.cs +1050 -1050
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/DependencyResolver/NuGetLegacyMigrationTests.cs +304 -304
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Environment/EnvironmentUtilsTests.cs +4 -4
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/BlacklistTypeTests.cs +416 -416
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.cs +172 -172
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.pre-Unity.6.5.cs +169 -169
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.cs +182 -182
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.pre-Unity.6.5.cs +179 -179
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.cs +175 -175
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.pre-Unity.6.5.cs +174 -174
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.cs +171 -171
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.pre-Unity.6.5.cs +170 -170
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsCreateFolderTests.cs +243 -243
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsMaterialTests.cs +101 -101
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.cs +348 -348
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.pre-Unity.6.5.cs +348 -348
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsShaderTests.cs +194 -194
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.cs +573 -573
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.pre-Unity.6.5.cs +513 -513
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.cs +154 -154
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.pre-Unity.6.5.cs +154 -154
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotGameViewTests.cs +164 -164
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotSceneViewTests.cs +155 -155
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolParameterTests.cs +56 -56
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.cs +651 -651
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.pre-Unity.6.5.cs +651 -651
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ToolBatchExecutionIntegrationTests.cs +407 -407
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/{McpPluginTests.cs → UcoPluginTests.cs} +105 -105
- package/vendor/plugin/com.atelierai.unity.copilot/package.json +1 -1
- package/dist/commands/devops/login.d.ts +0 -2
- package/dist/commands/devops/login.js +0 -29
- package/dist/commands/devops/login.js.map +0 -1
- package/dist/commands/devops/setup-mcp.d.ts +0 -2
- package/dist/commands/devops/setup-mcp.js +0 -41
- package/dist/commands/devops/setup-mcp.js.map +0 -1
- package/dist/devops/commands-upstream/bootstrap-local.d.ts +0 -17
- package/dist/devops/commands-upstream/bootstrap-local.js +0 -87
- package/dist/devops/commands-upstream/bootstrap-local.js.map +0 -1
- package/dist/devops/commands-upstream/close.d.ts +0 -57
- package/dist/devops/commands-upstream/close.js +0 -188
- package/dist/devops/commands-upstream/close.js.map +0 -1
- package/dist/devops/commands-upstream/configure.d.ts +0 -2
- package/dist/devops/commands-upstream/configure.js +0 -87
- package/dist/devops/commands-upstream/configure.js.map +0 -1
- package/dist/devops/commands-upstream/create-project.d.ts +0 -2
- package/dist/devops/commands-upstream/create-project.js +0 -43
- package/dist/devops/commands-upstream/create-project.js.map +0 -1
- package/dist/devops/commands-upstream/install-plugin.d.ts +0 -2
- package/dist/devops/commands-upstream/install-plugin.js +0 -64
- package/dist/devops/commands-upstream/install-plugin.js.map +0 -1
- package/dist/devops/commands-upstream/install-unity.d.ts +0 -2
- package/dist/devops/commands-upstream/install-unity.js +0 -72
- package/dist/devops/commands-upstream/install-unity.js.map +0 -1
- package/dist/devops/commands-upstream/login.d.ts +0 -2
- package/dist/devops/commands-upstream/login.js +0 -33
- package/dist/devops/commands-upstream/login.js.map +0 -1
- package/dist/devops/commands-upstream/open.d.ts +0 -32
- package/dist/devops/commands-upstream/open.js +0 -282
- package/dist/devops/commands-upstream/open.js.map +0 -1
- package/dist/devops/commands-upstream/remove-plugin.d.ts +0 -2
- package/dist/devops/commands-upstream/remove-plugin.js +0 -41
- package/dist/devops/commands-upstream/remove-plugin.js.map +0 -1
- package/dist/devops/commands-upstream/run-system-tool.d.ts +0 -1
- package/dist/devops/commands-upstream/run-system-tool.js +0 -13
- package/dist/devops/commands-upstream/run-system-tool.js.map +0 -1
- package/dist/devops/commands-upstream/run-tool-builder.d.ts +0 -25
- package/dist/devops/commands-upstream/run-tool-builder.js +0 -125
- package/dist/devops/commands-upstream/run-tool-builder.js.map +0 -1
- package/dist/devops/commands-upstream/run-tool.d.ts +0 -1
- package/dist/devops/commands-upstream/run-tool.js +0 -13
- package/dist/devops/commands-upstream/run-tool.js.map +0 -1
- package/dist/devops/commands-upstream/setup-mcp.d.ts +0 -2
- package/dist/devops/commands-upstream/setup-mcp.js +0 -68
- package/dist/devops/commands-upstream/setup-mcp.js.map +0 -1
- package/dist/devops/commands-upstream/setup-skills.d.ts +0 -2
- package/dist/devops/commands-upstream/setup-skills.js +0 -167
- package/dist/devops/commands-upstream/setup-skills.js.map +0 -1
- package/dist/devops/commands-upstream/status.d.ts +0 -2
- package/dist/devops/commands-upstream/status.js +0 -87
- package/dist/devops/commands-upstream/status.js.map +0 -1
- package/dist/devops/commands-upstream/update.d.ts +0 -2
- package/dist/devops/commands-upstream/update.js +0 -77
- package/dist/devops/commands-upstream/update.js.map +0 -1
- package/dist/devops/commands-upstream/wait-for-ready.d.ts +0 -2
- package/dist/devops/commands-upstream/wait-for-ready.js +0 -93
- package/dist/devops/commands-upstream/wait-for-ready.js.map +0 -1
- package/dist/devops/lib/setup-mcp.d.ts +0 -35
- package/dist/devops/lib/setup-mcp.js +0 -177
- package/dist/devops/lib/setup-mcp.js.map +0 -1
- package/dist/devops/utils/cloud-login.d.ts +0 -7
- package/dist/devops/utils/cloud-login.js +0 -56
- package/dist/devops/utils/cloud-login.js.map +0 -1
- /package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/{McpPluginTests.cs.meta → UcoPluginTests.cs.meta} +0 -0
|
@@ -1,537 +1,537 @@
|
|
|
1
|
-
/*
|
|
2
|
-
┌──────────────────────────────────────────────────────────────────┐
|
|
3
|
-
│ Author: Ivan Murzak (https://github.com/IvanMurzak) │
|
|
4
|
-
│ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
|
|
5
|
-
│ Copyright (c) 2025 Ivan Murzak │
|
|
6
|
-
│ Licensed under the Apache License, Version 2.0. │
|
|
7
|
-
│ See the LICENSE file in the project root for more information. │
|
|
8
|
-
└──────────────────────────────────────────────────────────────────┘
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
#nullable enable
|
|
12
|
-
using System;
|
|
13
|
-
using System.Collections.Generic;
|
|
14
|
-
using System.Linq;
|
|
15
|
-
using System.Text.Json;
|
|
16
|
-
using com.AtelierAI.Uco.Framework.Common.Model;
|
|
17
|
-
using com.AtelierAI.Unity.Copilot.Editor.Utils;
|
|
18
|
-
using com.AtelierAI.Unity.Copilot.Runtime.Utils;
|
|
19
|
-
using UnityEditor.TestTools.TestRunner.Api;
|
|
20
|
-
using UnityEngine;
|
|
21
|
-
|
|
22
|
-
namespace com.AtelierAI.Unity.Copilot.Editor.API.TestRunner
|
|
23
|
-
{
|
|
24
|
-
internal sealed class TestRunCallbackOwnership
|
|
25
|
-
{
|
|
26
|
-
internal sealed class Context
|
|
27
|
-
{
|
|
28
|
-
public string OperationId = string.Empty;
|
|
29
|
-
public string RequestId = string.Empty;
|
|
30
|
-
public bool IncludePassingTests;
|
|
31
|
-
public bool IncludeMessage;
|
|
32
|
-
public bool IncludeMessageStacktrace;
|
|
33
|
-
public bool IncludeLogs;
|
|
34
|
-
public int IncludeLogsMinLevel;
|
|
35
|
-
public bool IncludeLogsStacktrace;
|
|
36
|
-
/// <summary>
|
|
37
|
-
/// Filtered leaf count reported by RunStarted for the owning run.
|
|
38
|
-
/// RunFinished prefers it over counting the whole Unity result
|
|
39
|
-
/// tree when the persisted ExpectedMatchedTests is stale/zero
|
|
40
|
-
/// (COCli-12: "total=1319 while executed=487").
|
|
41
|
-
/// </summary>
|
|
42
|
-
public int RunStartedTestCount;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
readonly object _gate = new();
|
|
46
|
-
Context? _active;
|
|
47
|
-
|
|
48
|
-
internal bool HasUnsettledRun
|
|
49
|
-
{
|
|
50
|
-
get
|
|
51
|
-
{
|
|
52
|
-
lock (_gate)
|
|
53
|
-
return _active != null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
internal string ActiveOperationId
|
|
58
|
-
{
|
|
59
|
-
get
|
|
60
|
-
{
|
|
61
|
-
lock (_gate)
|
|
62
|
-
return _active?.OperationId ?? string.Empty;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
internal bool CaptureCurrentRun()
|
|
67
|
-
{
|
|
68
|
-
var context = new Context
|
|
69
|
-
{
|
|
70
|
-
OperationId = TestResultCollector.TestOperationId.Value,
|
|
71
|
-
RequestId = TestResultCollector.TestCallRequestID.Value,
|
|
72
|
-
IncludePassingTests = TestResultCollector.IncludePassingTests.Value,
|
|
73
|
-
IncludeMessage = TestResultCollector.IncludeMessage.Value,
|
|
74
|
-
IncludeMessageStacktrace = TestResultCollector.IncludeMessageStacktrace.Value,
|
|
75
|
-
IncludeLogs = TestResultCollector.IncludeLogs.Value,
|
|
76
|
-
IncludeLogsMinLevel = TestResultCollector.IncludeLogsMinLevel.Value,
|
|
77
|
-
IncludeLogsStacktrace = TestResultCollector.IncludeLogsStacktrace.Value
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
lock (_gate)
|
|
81
|
-
{
|
|
82
|
-
if (_active != null)
|
|
83
|
-
return false;
|
|
84
|
-
_active = context;
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
internal void SetRunStartedTestCount(int count)
|
|
90
|
-
{
|
|
91
|
-
lock (_gate)
|
|
92
|
-
{
|
|
93
|
-
if (_active != null)
|
|
94
|
-
_active.RunStartedTestCount = count;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
internal void UpdateFromTestFinished(int executedTests, int matchedTests)
|
|
99
|
-
{
|
|
100
|
-
string operationId;
|
|
101
|
-
lock (_gate)
|
|
102
|
-
operationId = _active?.OperationId ?? string.Empty;
|
|
103
|
-
if (string.IsNullOrEmpty(operationId))
|
|
104
|
-
return;
|
|
105
|
-
|
|
106
|
-
var operation = EditorOperationRegistry.Get(operationId);
|
|
107
|
-
if (operation == null || operation.IsTerminal)
|
|
108
|
-
return;
|
|
109
|
-
var total = Math.Max(1, matchedTests);
|
|
110
|
-
EditorOperationRegistry.Update(operationId, "executing",
|
|
111
|
-
Math.Min(1d, (double)executedTests / total));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
internal Context? ReleaseForRunFinished()
|
|
115
|
-
{
|
|
116
|
-
lock (_gate)
|
|
117
|
-
{
|
|
118
|
-
var released = _active;
|
|
119
|
-
_active = null;
|
|
120
|
-
return released;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
internal bool CompleteFromRunFinished(
|
|
125
|
-
Context? context,
|
|
126
|
-
int failedTests,
|
|
127
|
-
int executedTests,
|
|
128
|
-
string resultJson)
|
|
129
|
-
{
|
|
130
|
-
if (context == null)
|
|
131
|
-
return false;
|
|
132
|
-
|
|
133
|
-
var completed = TestResultCollector.CompleteOperationFromCallback(
|
|
134
|
-
context.OperationId, failedTests, executedTests, resultJson);
|
|
135
|
-
ClearPersistedOwnershipIfMatching(context);
|
|
136
|
-
return completed;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
static void ClearPersistedOwnershipIfMatching(Context context)
|
|
140
|
-
{
|
|
141
|
-
if (!string.IsNullOrEmpty(context.OperationId)
|
|
142
|
-
&& string.Equals(TestResultCollector.TestOperationId.Value,
|
|
143
|
-
context.OperationId, StringComparison.Ordinal))
|
|
144
|
-
{
|
|
145
|
-
TestResultCollector.TestOperationId.Value = string.Empty;
|
|
146
|
-
TestResultCollector.ExpectedDiscoveredTests.Value = 0;
|
|
147
|
-
TestResultCollector.ExpectedMatchedTests.Value = 0;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (!string.IsNullOrEmpty(context.RequestId)
|
|
151
|
-
&& string.Equals(TestResultCollector.TestCallRequestID.Value,
|
|
152
|
-
context.RequestId, StringComparison.Ordinal))
|
|
153
|
-
{
|
|
154
|
-
TestResultCollector.TestCallRequestID.Value = string.Empty;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
public class TestResultCollector : ICallbacks
|
|
160
|
-
{
|
|
161
|
-
static volatile int counter = 0;
|
|
162
|
-
static readonly TestRunCallbackOwnership s_callbackOwnership = new();
|
|
163
|
-
|
|
164
|
-
readonly TestRunCallbackOwnership _callbackOwnership;
|
|
165
|
-
readonly object _logsMutex = new();
|
|
166
|
-
readonly List<TestResultData> _results = new();
|
|
167
|
-
readonly TestSummaryData _summary = new();
|
|
168
|
-
readonly List<TestLogEntry> _logs = new();
|
|
169
|
-
readonly TestMode _testMode;
|
|
170
|
-
|
|
171
|
-
DateTime startTime;
|
|
172
|
-
|
|
173
|
-
public List<TestResultData> GetResults() => _results;
|
|
174
|
-
public TestSummaryData GetSummary() => _summary;
|
|
175
|
-
public List<TestLogEntry> GetLogs()
|
|
176
|
-
{
|
|
177
|
-
lock (_logsMutex)
|
|
178
|
-
{
|
|
179
|
-
return _logs.ToList();
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
public TestMode GetTestMode() => _testMode;
|
|
183
|
-
|
|
184
|
-
public string TestModeAsString => _testMode switch
|
|
185
|
-
{
|
|
186
|
-
TestMode.EditMode => "EditMode",
|
|
187
|
-
TestMode.PlayMode => "PlayMode",
|
|
188
|
-
_ => "Unknown"
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
public static PlayerPrefsString TestCallRequestID = new PlayerPrefsString("
|
|
192
|
-
public static PlayerPrefsString TestOperationId = new PlayerPrefsString("
|
|
193
|
-
public static PlayerPrefsInt ExpectedDiscoveredTests = new PlayerPrefsInt("
|
|
194
|
-
public static PlayerPrefsInt ExpectedMatchedTests = new PlayerPrefsInt("
|
|
195
|
-
|
|
196
|
-
internal static int ExpectedDiscoveredTestCount
|
|
197
|
-
{
|
|
198
|
-
get => ExpectedDiscoveredTests.Value;
|
|
199
|
-
set => ExpectedDiscoveredTests.Value = value;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
internal static int ExpectedMatchedTestCount
|
|
203
|
-
{
|
|
204
|
-
get => ExpectedMatchedTests.Value;
|
|
205
|
-
set => ExpectedMatchedTests.Value = value;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
public static PlayerPrefsBool IncludePassingTests = new PlayerPrefsBool("
|
|
209
|
-
public static PlayerPrefsBool IncludeMessage = new PlayerPrefsBool("
|
|
210
|
-
public static PlayerPrefsBool IncludeMessageStacktrace = new PlayerPrefsBool("
|
|
211
|
-
|
|
212
|
-
public static PlayerPrefsBool IncludeLogs = new PlayerPrefsBool("
|
|
213
|
-
public static PlayerPrefsInt IncludeLogsMinLevel = new PlayerPrefsInt("
|
|
214
|
-
public static PlayerPrefsBool IncludeLogsStacktrace = new PlayerPrefsBool("
|
|
215
|
-
|
|
216
|
-
internal static bool HasUnsettledRun => s_callbackOwnership.HasUnsettledRun;
|
|
217
|
-
internal static string ActiveOperationId => s_callbackOwnership.ActiveOperationId;
|
|
218
|
-
|
|
219
|
-
public TestResultCollector() : this(s_callbackOwnership, enforceSingleton: true)
|
|
220
|
-
{
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
TestResultCollector(
|
|
224
|
-
TestRunCallbackOwnership callbackOwnership,
|
|
225
|
-
bool enforceSingleton)
|
|
226
|
-
{
|
|
227
|
-
_callbackOwnership = callbackOwnership
|
|
228
|
-
?? throw new ArgumentNullException(nameof(callbackOwnership));
|
|
229
|
-
if (!enforceSingleton)
|
|
230
|
-
return;
|
|
231
|
-
|
|
232
|
-
int newCount = System.Threading.Interlocked.Increment(ref counter);
|
|
233
|
-
|
|
234
|
-
UnityCopilotPluginEditor.Instance.LogTrace("Ctor", typeof(TestResultCollector));
|
|
235
|
-
|
|
236
|
-
if (newCount > 1)
|
|
237
|
-
throw new InvalidOperationException($"Only one instance of {nameof(TestResultCollector)} is allowed. Current count: {newCount}");
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
internal static TestResultCollector CreateIsolatedForTests()
|
|
241
|
-
=> new(new TestRunCallbackOwnership(), enforceSingleton: false);
|
|
242
|
-
|
|
243
|
-
public void RunStarted(ITestAdaptor testsToRun)
|
|
244
|
-
{
|
|
245
|
-
UnityCopilotPluginEditor.Instance.LogInfo("RunStarted", typeof(TestResultCollector));
|
|
246
|
-
|
|
247
|
-
if (!_callbackOwnership.CaptureCurrentRun())
|
|
248
|
-
{
|
|
249
|
-
UnityCopilotPluginEditor.Instance.LogError(
|
|
250
|
-
"Ignoring overlapping test RunStarted callback while operation '{operationId}' still owns the runner.",
|
|
251
|
-
typeof(TestResultCollector), _callbackOwnership.ActiveOperationId);
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
startTime = DateTime.Now;
|
|
256
|
-
var testCount = CountTests(testsToRun);
|
|
257
|
-
_callbackOwnership.SetRunStartedTestCount(testCount);
|
|
258
|
-
|
|
259
|
-
lock (_logsMutex)
|
|
260
|
-
{
|
|
261
|
-
_logs.Clear();
|
|
262
|
-
}
|
|
263
|
-
_results.Clear();
|
|
264
|
-
_summary.Clear();
|
|
265
|
-
_summary.TotalTests = testCount;
|
|
266
|
-
_summary.DiscoveredTests = ExpectedDiscoveredTests.Value;
|
|
267
|
-
_summary.MatchedTests = ExpectedMatchedTests.Value;
|
|
268
|
-
|
|
269
|
-
// Subscribe to log messages (using threaded version to catch logs from all threads)
|
|
270
|
-
Application.logMessageReceivedThreaded -= OnLogMessageReceived;
|
|
271
|
-
Application.logMessageReceivedThreaded += OnLogMessageReceived;
|
|
272
|
-
|
|
273
|
-
UnityCopilotPluginEditor.Instance.LogInfo("Run {testMode} started: {testCount} tests.",
|
|
274
|
-
typeof(TestResultCollector), TestModeAsString, testCount);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
public void RunFinished(ITestResultAdaptor result)
|
|
278
|
-
{
|
|
279
|
-
UnityCopilotPluginEditor.Instance.LogInfo("RunFinished", typeof(TestResultCollector));
|
|
280
|
-
|
|
281
|
-
// Unsubscribe from log messages
|
|
282
|
-
Application.logMessageReceivedThreaded -= OnLogMessageReceived;
|
|
283
|
-
|
|
284
|
-
// Release the ownership context up front: its captured members
|
|
285
|
-
// (including RunStartedTestCount) feed the summary fallback below
|
|
286
|
-
// and the structured response afterwards.
|
|
287
|
-
var ownership = _callbackOwnership.ReleaseForRunFinished();
|
|
288
|
-
|
|
289
|
-
var duration = DateTime.Now - startTime;
|
|
290
|
-
_summary.Duration = DateTime.Now - startTime;
|
|
291
|
-
_summary.ExecutedTests = _results.Count;
|
|
292
|
-
// COCli-12: TotalTests reports the filtered execution scope, never
|
|
293
|
-
// the whole-project discovery count. Preference order: the
|
|
294
|
-
// discovery-matched count, the RunStarted filtered count, then
|
|
295
|
-
// (only for runs that bypassed discovery) the executed count
|
|
296
|
-
// before falling back to counting the Unity result tree.
|
|
297
|
-
_summary.TotalTests = _summary.MatchedTests > 0
|
|
298
|
-
? _summary.MatchedTests
|
|
299
|
-
: ownership is { RunStartedTestCount: > 0 }
|
|
300
|
-
? ownership.RunStartedTestCount
|
|
301
|
-
: Math.Max(_summary.ExecutedTests, CountTests(result.Test));
|
|
302
|
-
if (_summary.FailedTests > 0)
|
|
303
|
-
{
|
|
304
|
-
_summary.Status = TestRunStatus.Failed;
|
|
305
|
-
}
|
|
306
|
-
else if (_summary.PassedTests > 0)
|
|
307
|
-
{
|
|
308
|
-
_summary.Status = TestRunStatus.Passed;
|
|
309
|
-
}
|
|
310
|
-
else
|
|
311
|
-
{
|
|
312
|
-
_summary.Status = TestRunStatus.Unknown;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
UnityCopilotPluginEditor.Instance.LogInfo("Run {testMode} finished with {totalTests} test results. Result status: {status}",
|
|
316
|
-
typeof(TestResultCollector), TestModeAsString, _summary.TotalTests, result.TestStatus);
|
|
317
|
-
UnityCopilotPluginEditor.Instance.LogInfo("Final duration: {duration:mm\\:ss\\.fff}. Completed: {completed}/{total}",
|
|
318
|
-
typeof(TestResultCollector), duration, _results.Count, _summary.TotalTests);
|
|
319
|
-
|
|
320
|
-
UnityCopilotPluginEditor.Instance.
|
|
321
|
-
|
|
322
|
-
if (!EnvironmentUtils.IsCi() && !EnvironmentUtils.IsBatchMode())
|
|
323
|
-
UnityCopilotPluginEditor.ConnectIfNeeded();
|
|
324
|
-
|
|
325
|
-
var structuredResponse = CreateStructuredResponse(
|
|
326
|
-
includePassingTests: ownership?.IncludePassingTests ?? IncludePassingTests.Value,
|
|
327
|
-
includeMessage: ownership?.IncludeMessage ?? IncludeMessage.Value,
|
|
328
|
-
includeLogs: ownership?.IncludeLogs ?? IncludeLogs.Value,
|
|
329
|
-
includeLogsMinLevel: ownership?.IncludeLogsMinLevel ?? IncludeLogsMinLevel.Value,
|
|
330
|
-
includeMessageStacktrace: ownership?.IncludeMessageStacktrace ?? IncludeMessageStacktrace.Value,
|
|
331
|
-
includeLogsStacktrace: ownership?.IncludeLogsStacktrace ?? IncludeLogsStacktrace.Value);
|
|
332
|
-
|
|
333
|
-
// Scene hygiene (COCli-06): restore a sandboxed run and report the
|
|
334
|
-
// observed mutation against the persisted pre-run baseline.
|
|
335
|
-
var sceneOutcome = TestRunSceneSandbox.RestoreForOperation(ownership?.OperationId);
|
|
336
|
-
structuredResponse.Mutated = sceneOutcome.Mutation.Mutated;
|
|
337
|
-
structuredResponse.MutatedScenes = sceneOutcome.Mutation.MutatedScenes;
|
|
338
|
-
structuredResponse.SandboxRestored = sceneOutcome.SandboxRestored;
|
|
339
|
-
structuredResponse.SandboxRestoreCause = sceneOutcome.SandboxRestoreCause;
|
|
340
|
-
|
|
341
|
-
var resultJson = JsonSerializer.Serialize(structuredResponse);
|
|
342
|
-
var completedOwnedOperation = _callbackOwnership.CompleteFromRunFinished(
|
|
343
|
-
ownership, _summary.FailedTests, _summary.ExecutedTests, resultJson);
|
|
344
|
-
|
|
345
|
-
var requestId = ownership?.RequestId ?? string.Empty;
|
|
346
|
-
if (completedOwnedOperation && string.IsNullOrEmpty(requestId) == false)
|
|
347
|
-
{
|
|
348
|
-
var
|
|
349
|
-
|
|
350
|
-
var response = ResponseCallValueTool<TestRunResponse>
|
|
351
|
-
.SuccessStructured(
|
|
352
|
-
.SetRequestID(requestId);
|
|
353
|
-
|
|
354
|
-
_ = UnityCopilotPluginEditor.NotifyToolRequestCompleted(new RequestToolCompletedData
|
|
355
|
-
{
|
|
356
|
-
RequestId = requestId,
|
|
357
|
-
OperationId = ownership?.OperationId,
|
|
358
|
-
Result = response
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
internal static bool CompleteOperationFromCallback(
|
|
364
|
-
string operationId,
|
|
365
|
-
int failedTests,
|
|
366
|
-
int executedTests,
|
|
367
|
-
string resultJson)
|
|
368
|
-
{
|
|
369
|
-
var operation = EditorOperationRegistry.Get(operationId);
|
|
370
|
-
if (operation == null || operation.IsTerminal)
|
|
371
|
-
return false;
|
|
372
|
-
|
|
373
|
-
if (operation.CancellationRequested)
|
|
374
|
-
EditorOperationRegistry.CancelRunning(operationId, "test-run-cancelled", resultJson);
|
|
375
|
-
else if (failedTests > 0)
|
|
376
|
-
EditorOperationRegistry.Fail(operationId, "tests-failed",
|
|
377
|
-
$"{failedTests} test(s) failed.", "completed-with-failures", resultJson);
|
|
378
|
-
else if (executedTests == 0)
|
|
379
|
-
EditorOperationRegistry.Fail(operationId, "tests-no-results",
|
|
380
|
-
"Unity Test Runner completed without executing a matched test.", "completed-without-results", resultJson);
|
|
381
|
-
else
|
|
382
|
-
EditorOperationRegistry.Succeed(operationId, resultJson);
|
|
383
|
-
return true;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
public void TestStarted(ITestAdaptor test)
|
|
387
|
-
{
|
|
388
|
-
// Test started - could log this if needed
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
public void TestFinished(ITestResultAdaptor result)
|
|
392
|
-
{
|
|
393
|
-
// Only count actual tests, not test suites
|
|
394
|
-
if (!result.Test.IsSuite)
|
|
395
|
-
{
|
|
396
|
-
var testResult = new TestResultData
|
|
397
|
-
{
|
|
398
|
-
Name = result.Test.FullName,
|
|
399
|
-
Status = ConvertTestStatus(result.TestStatus),
|
|
400
|
-
Duration = TimeSpan.FromSeconds(result.Duration),
|
|
401
|
-
Message = result.Message,
|
|
402
|
-
StackTrace = result.StackTrace
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
_results.Add(testResult);
|
|
406
|
-
|
|
407
|
-
var statusEmoji = result.TestStatus switch
|
|
408
|
-
{
|
|
409
|
-
TestStatus.Passed => "<color=green>✅</color>",
|
|
410
|
-
TestStatus.Failed => "<color=red>❌</color>",
|
|
411
|
-
TestStatus.Skipped => "<color=yellow>⚠️</color>",
|
|
412
|
-
_ => string.Empty
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
UnityCopilotPluginEditor.Instance.LogInfo("{emoji} Test finished ({counter}/{total}): {testName} - {testStatus}",
|
|
416
|
-
typeof(TestResultCollector), statusEmoji, _results.Count, _summary.TotalTests, result.Test.FullName, result.TestStatus);
|
|
417
|
-
|
|
418
|
-
// Update summary counts
|
|
419
|
-
switch (result.TestStatus)
|
|
420
|
-
{
|
|
421
|
-
case TestStatus.Passed:
|
|
422
|
-
_summary.PassedTests++;
|
|
423
|
-
break;
|
|
424
|
-
case TestStatus.Failed:
|
|
425
|
-
_summary.FailedTests++;
|
|
426
|
-
break;
|
|
427
|
-
case TestStatus.Skipped:
|
|
428
|
-
_summary.SkippedTests++;
|
|
429
|
-
break;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// Update duration as tests complete
|
|
433
|
-
_summary.Duration = DateTime.Now - startTime;
|
|
434
|
-
_summary.ExecutedTests = _results.Count;
|
|
435
|
-
|
|
436
|
-
_callbackOwnership.UpdateFromTestFinished(
|
|
437
|
-
_results.Count, _summary.MatchedTests);
|
|
438
|
-
|
|
439
|
-
// Check if all tests are complete
|
|
440
|
-
if (_results.Count >= _summary.TotalTests)
|
|
441
|
-
{
|
|
442
|
-
UnityCopilotPluginEditor.Instance.LogInfo("All tests completed via TestFinished. Final duration: {duration:mm\\:ss\\.fff}",
|
|
443
|
-
typeof(TestResultCollector), _summary.Duration);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
void OnLogMessageReceived(string condition, string stackTrace, LogType type)
|
|
449
|
-
{
|
|
450
|
-
var entry = new TestLogEntry(type, condition, stackTrace);
|
|
451
|
-
lock (_logsMutex)
|
|
452
|
-
{
|
|
453
|
-
_logs.Add(entry);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
TestRunResponse CreateStructuredResponse(
|
|
458
|
-
bool includePassingTests,
|
|
459
|
-
bool includeMessage,
|
|
460
|
-
bool includeMessageStacktrace,
|
|
461
|
-
bool includeLogs,
|
|
462
|
-
int includeLogsMinLevel,
|
|
463
|
-
bool includeLogsStacktrace)
|
|
464
|
-
{
|
|
465
|
-
var results = GetResults();
|
|
466
|
-
var summary = GetSummary();
|
|
467
|
-
var logs = GetLogs();
|
|
468
|
-
|
|
469
|
-
var response = new TestRunResponse
|
|
470
|
-
{
|
|
471
|
-
Summary = summary,
|
|
472
|
-
Results = new List<TestResultData>()
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
// Filter test results based on includePassingTests, includeMessage and includeMessageStacktrace
|
|
476
|
-
foreach (var result in results)
|
|
477
|
-
{
|
|
478
|
-
// Skip passing tests if includePassingTests is false
|
|
479
|
-
if (!includePassingTests && result.Status == TestResultStatus.Passed)
|
|
480
|
-
continue;
|
|
481
|
-
|
|
482
|
-
var filteredResult = new TestResultData
|
|
483
|
-
{
|
|
484
|
-
Name = result.Name,
|
|
485
|
-
Status = result.Status,
|
|
486
|
-
Duration = result.Duration,
|
|
487
|
-
Message = includeMessage ? result.Message : null,
|
|
488
|
-
StackTrace = includeMessageStacktrace ? result.StackTrace : null
|
|
489
|
-
};
|
|
490
|
-
response.Results.Add(filteredResult);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// Include logs if requested
|
|
494
|
-
if (includeLogs && logs.Any())
|
|
495
|
-
{
|
|
496
|
-
var minLogLevel = TestLogEntry.ToLogLevel((LogType)includeLogsMinLevel);
|
|
497
|
-
response.Logs = logs
|
|
498
|
-
.Where(log => log.LogLevel >= minLogLevel)
|
|
499
|
-
.Select(log => includeLogsStacktrace
|
|
500
|
-
? log
|
|
501
|
-
: new TestLogEntry(log.Type, log.Condition, null, log.Timestamp))
|
|
502
|
-
.ToList();
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
return response;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
public static int CountTests(ITestAdaptor test)
|
|
509
|
-
{
|
|
510
|
-
try
|
|
511
|
-
{
|
|
512
|
-
if (test == null)
|
|
513
|
-
return 0;
|
|
514
|
-
|
|
515
|
-
if (test.HasChildren && test.Children != null)
|
|
516
|
-
return test.Children.Sum(CountTests);
|
|
517
|
-
|
|
518
|
-
return test.IsSuite ? 0 : 1;
|
|
519
|
-
}
|
|
520
|
-
catch
|
|
521
|
-
{
|
|
522
|
-
return 0;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
static TestResultStatus ConvertTestStatus(TestStatus testStatus)
|
|
527
|
-
{
|
|
528
|
-
return testStatus switch
|
|
529
|
-
{
|
|
530
|
-
TestStatus.Passed => TestResultStatus.Passed,
|
|
531
|
-
TestStatus.Failed => TestResultStatus.Failed,
|
|
532
|
-
TestStatus.Skipped => TestResultStatus.Skipped,
|
|
533
|
-
_ => TestResultStatus.Skipped
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
3
|
+
│ Author: Ivan Murzak (https://github.com/IvanMurzak) │
|
|
4
|
+
│ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
|
|
5
|
+
│ Copyright (c) 2025 Ivan Murzak │
|
|
6
|
+
│ Licensed under the Apache License, Version 2.0. │
|
|
7
|
+
│ See the LICENSE file in the project root for more information. │
|
|
8
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#nullable enable
|
|
12
|
+
using System;
|
|
13
|
+
using System.Collections.Generic;
|
|
14
|
+
using System.Linq;
|
|
15
|
+
using System.Text.Json;
|
|
16
|
+
using com.AtelierAI.Uco.Framework.Common.Model;
|
|
17
|
+
using com.AtelierAI.Unity.Copilot.Editor.Utils;
|
|
18
|
+
using com.AtelierAI.Unity.Copilot.Runtime.Utils;
|
|
19
|
+
using UnityEditor.TestTools.TestRunner.Api;
|
|
20
|
+
using UnityEngine;
|
|
21
|
+
|
|
22
|
+
namespace com.AtelierAI.Unity.Copilot.Editor.API.TestRunner
|
|
23
|
+
{
|
|
24
|
+
internal sealed class TestRunCallbackOwnership
|
|
25
|
+
{
|
|
26
|
+
internal sealed class Context
|
|
27
|
+
{
|
|
28
|
+
public string OperationId = string.Empty;
|
|
29
|
+
public string RequestId = string.Empty;
|
|
30
|
+
public bool IncludePassingTests;
|
|
31
|
+
public bool IncludeMessage;
|
|
32
|
+
public bool IncludeMessageStacktrace;
|
|
33
|
+
public bool IncludeLogs;
|
|
34
|
+
public int IncludeLogsMinLevel;
|
|
35
|
+
public bool IncludeLogsStacktrace;
|
|
36
|
+
/// <summary>
|
|
37
|
+
/// Filtered leaf count reported by RunStarted for the owning run.
|
|
38
|
+
/// RunFinished prefers it over counting the whole Unity result
|
|
39
|
+
/// tree when the persisted ExpectedMatchedTests is stale/zero
|
|
40
|
+
/// (COCli-12: "total=1319 while executed=487").
|
|
41
|
+
/// </summary>
|
|
42
|
+
public int RunStartedTestCount;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
readonly object _gate = new();
|
|
46
|
+
Context? _active;
|
|
47
|
+
|
|
48
|
+
internal bool HasUnsettledRun
|
|
49
|
+
{
|
|
50
|
+
get
|
|
51
|
+
{
|
|
52
|
+
lock (_gate)
|
|
53
|
+
return _active != null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
internal string ActiveOperationId
|
|
58
|
+
{
|
|
59
|
+
get
|
|
60
|
+
{
|
|
61
|
+
lock (_gate)
|
|
62
|
+
return _active?.OperationId ?? string.Empty;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
internal bool CaptureCurrentRun()
|
|
67
|
+
{
|
|
68
|
+
var context = new Context
|
|
69
|
+
{
|
|
70
|
+
OperationId = TestResultCollector.TestOperationId.Value,
|
|
71
|
+
RequestId = TestResultCollector.TestCallRequestID.Value,
|
|
72
|
+
IncludePassingTests = TestResultCollector.IncludePassingTests.Value,
|
|
73
|
+
IncludeMessage = TestResultCollector.IncludeMessage.Value,
|
|
74
|
+
IncludeMessageStacktrace = TestResultCollector.IncludeMessageStacktrace.Value,
|
|
75
|
+
IncludeLogs = TestResultCollector.IncludeLogs.Value,
|
|
76
|
+
IncludeLogsMinLevel = TestResultCollector.IncludeLogsMinLevel.Value,
|
|
77
|
+
IncludeLogsStacktrace = TestResultCollector.IncludeLogsStacktrace.Value
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
lock (_gate)
|
|
81
|
+
{
|
|
82
|
+
if (_active != null)
|
|
83
|
+
return false;
|
|
84
|
+
_active = context;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
internal void SetRunStartedTestCount(int count)
|
|
90
|
+
{
|
|
91
|
+
lock (_gate)
|
|
92
|
+
{
|
|
93
|
+
if (_active != null)
|
|
94
|
+
_active.RunStartedTestCount = count;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
internal void UpdateFromTestFinished(int executedTests, int matchedTests)
|
|
99
|
+
{
|
|
100
|
+
string operationId;
|
|
101
|
+
lock (_gate)
|
|
102
|
+
operationId = _active?.OperationId ?? string.Empty;
|
|
103
|
+
if (string.IsNullOrEmpty(operationId))
|
|
104
|
+
return;
|
|
105
|
+
|
|
106
|
+
var operation = EditorOperationRegistry.Get(operationId);
|
|
107
|
+
if (operation == null || operation.IsTerminal)
|
|
108
|
+
return;
|
|
109
|
+
var total = Math.Max(1, matchedTests);
|
|
110
|
+
EditorOperationRegistry.Update(operationId, "executing",
|
|
111
|
+
Math.Min(1d, (double)executedTests / total));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
internal Context? ReleaseForRunFinished()
|
|
115
|
+
{
|
|
116
|
+
lock (_gate)
|
|
117
|
+
{
|
|
118
|
+
var released = _active;
|
|
119
|
+
_active = null;
|
|
120
|
+
return released;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
internal bool CompleteFromRunFinished(
|
|
125
|
+
Context? context,
|
|
126
|
+
int failedTests,
|
|
127
|
+
int executedTests,
|
|
128
|
+
string resultJson)
|
|
129
|
+
{
|
|
130
|
+
if (context == null)
|
|
131
|
+
return false;
|
|
132
|
+
|
|
133
|
+
var completed = TestResultCollector.CompleteOperationFromCallback(
|
|
134
|
+
context.OperationId, failedTests, executedTests, resultJson);
|
|
135
|
+
ClearPersistedOwnershipIfMatching(context);
|
|
136
|
+
return completed;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static void ClearPersistedOwnershipIfMatching(Context context)
|
|
140
|
+
{
|
|
141
|
+
if (!string.IsNullOrEmpty(context.OperationId)
|
|
142
|
+
&& string.Equals(TestResultCollector.TestOperationId.Value,
|
|
143
|
+
context.OperationId, StringComparison.Ordinal))
|
|
144
|
+
{
|
|
145
|
+
TestResultCollector.TestOperationId.Value = string.Empty;
|
|
146
|
+
TestResultCollector.ExpectedDiscoveredTests.Value = 0;
|
|
147
|
+
TestResultCollector.ExpectedMatchedTests.Value = 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!string.IsNullOrEmpty(context.RequestId)
|
|
151
|
+
&& string.Equals(TestResultCollector.TestCallRequestID.Value,
|
|
152
|
+
context.RequestId, StringComparison.Ordinal))
|
|
153
|
+
{
|
|
154
|
+
TestResultCollector.TestCallRequestID.Value = string.Empty;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public class TestResultCollector : ICallbacks
|
|
160
|
+
{
|
|
161
|
+
static volatile int counter = 0;
|
|
162
|
+
static readonly TestRunCallbackOwnership s_callbackOwnership = new();
|
|
163
|
+
|
|
164
|
+
readonly TestRunCallbackOwnership _callbackOwnership;
|
|
165
|
+
readonly object _logsMutex = new();
|
|
166
|
+
readonly List<TestResultData> _results = new();
|
|
167
|
+
readonly TestSummaryData _summary = new();
|
|
168
|
+
readonly List<TestLogEntry> _logs = new();
|
|
169
|
+
readonly TestMode _testMode;
|
|
170
|
+
|
|
171
|
+
DateTime startTime;
|
|
172
|
+
|
|
173
|
+
public List<TestResultData> GetResults() => _results;
|
|
174
|
+
public TestSummaryData GetSummary() => _summary;
|
|
175
|
+
public List<TestLogEntry> GetLogs()
|
|
176
|
+
{
|
|
177
|
+
lock (_logsMutex)
|
|
178
|
+
{
|
|
179
|
+
return _logs.ToList();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
public TestMode GetTestMode() => _testMode;
|
|
183
|
+
|
|
184
|
+
public string TestModeAsString => _testMode switch
|
|
185
|
+
{
|
|
186
|
+
TestMode.EditMode => "EditMode",
|
|
187
|
+
TestMode.PlayMode => "PlayMode",
|
|
188
|
+
_ => "Unknown"
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
public static PlayerPrefsString TestCallRequestID = new PlayerPrefsString("Unity_COPILOT_TestRunner_TestCallRequestID");
|
|
192
|
+
public static PlayerPrefsString TestOperationId = new PlayerPrefsString("Unity_COPILOT_TestRunner_TestOperationId");
|
|
193
|
+
public static PlayerPrefsInt ExpectedDiscoveredTests = new PlayerPrefsInt("Unity_COPILOT_TestRunner_DiscoveredTests");
|
|
194
|
+
public static PlayerPrefsInt ExpectedMatchedTests = new PlayerPrefsInt("Unity_COPILOT_TestRunner_MatchedTests");
|
|
195
|
+
|
|
196
|
+
internal static int ExpectedDiscoveredTestCount
|
|
197
|
+
{
|
|
198
|
+
get => ExpectedDiscoveredTests.Value;
|
|
199
|
+
set => ExpectedDiscoveredTests.Value = value;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
internal static int ExpectedMatchedTestCount
|
|
203
|
+
{
|
|
204
|
+
get => ExpectedMatchedTests.Value;
|
|
205
|
+
set => ExpectedMatchedTests.Value = value;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
public static PlayerPrefsBool IncludePassingTests = new PlayerPrefsBool("Unity_COPILOT_TestRunner_IncludePassingTests");
|
|
209
|
+
public static PlayerPrefsBool IncludeMessage = new PlayerPrefsBool("Unity_COPILOT_TestRunner_IncludeMessage", true);
|
|
210
|
+
public static PlayerPrefsBool IncludeMessageStacktrace = new PlayerPrefsBool("Unity_COPILOT_TestRunner_IncludeStacktrace");
|
|
211
|
+
|
|
212
|
+
public static PlayerPrefsBool IncludeLogs = new PlayerPrefsBool("Unity_COPILOT_TestRunner_IncludeLogs");
|
|
213
|
+
public static PlayerPrefsInt IncludeLogsMinLevel = new PlayerPrefsInt("Unity_COPILOT_TestRunner_IncludeLogsMinLevel", (int)LogType.Warning);
|
|
214
|
+
public static PlayerPrefsBool IncludeLogsStacktrace = new PlayerPrefsBool("Unity_COPILOT_TestRunner_IncludeLogsStacktrace");
|
|
215
|
+
|
|
216
|
+
internal static bool HasUnsettledRun => s_callbackOwnership.HasUnsettledRun;
|
|
217
|
+
internal static string ActiveOperationId => s_callbackOwnership.ActiveOperationId;
|
|
218
|
+
|
|
219
|
+
public TestResultCollector() : this(s_callbackOwnership, enforceSingleton: true)
|
|
220
|
+
{
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
TestResultCollector(
|
|
224
|
+
TestRunCallbackOwnership callbackOwnership,
|
|
225
|
+
bool enforceSingleton)
|
|
226
|
+
{
|
|
227
|
+
_callbackOwnership = callbackOwnership
|
|
228
|
+
?? throw new ArgumentNullException(nameof(callbackOwnership));
|
|
229
|
+
if (!enforceSingleton)
|
|
230
|
+
return;
|
|
231
|
+
|
|
232
|
+
int newCount = System.Threading.Interlocked.Increment(ref counter);
|
|
233
|
+
|
|
234
|
+
UnityCopilotPluginEditor.Instance.LogTrace("Ctor", typeof(TestResultCollector));
|
|
235
|
+
|
|
236
|
+
if (newCount > 1)
|
|
237
|
+
throw new InvalidOperationException($"Only one instance of {nameof(TestResultCollector)} is allowed. Current count: {newCount}");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
internal static TestResultCollector CreateIsolatedForTests()
|
|
241
|
+
=> new(new TestRunCallbackOwnership(), enforceSingleton: false);
|
|
242
|
+
|
|
243
|
+
public void RunStarted(ITestAdaptor testsToRun)
|
|
244
|
+
{
|
|
245
|
+
UnityCopilotPluginEditor.Instance.LogInfo("RunStarted", typeof(TestResultCollector));
|
|
246
|
+
|
|
247
|
+
if (!_callbackOwnership.CaptureCurrentRun())
|
|
248
|
+
{
|
|
249
|
+
UnityCopilotPluginEditor.Instance.LogError(
|
|
250
|
+
"Ignoring overlapping test RunStarted callback while operation '{operationId}' still owns the runner.",
|
|
251
|
+
typeof(TestResultCollector), _callbackOwnership.ActiveOperationId);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
startTime = DateTime.Now;
|
|
256
|
+
var testCount = CountTests(testsToRun);
|
|
257
|
+
_callbackOwnership.SetRunStartedTestCount(testCount);
|
|
258
|
+
|
|
259
|
+
lock (_logsMutex)
|
|
260
|
+
{
|
|
261
|
+
_logs.Clear();
|
|
262
|
+
}
|
|
263
|
+
_results.Clear();
|
|
264
|
+
_summary.Clear();
|
|
265
|
+
_summary.TotalTests = testCount;
|
|
266
|
+
_summary.DiscoveredTests = ExpectedDiscoveredTests.Value;
|
|
267
|
+
_summary.MatchedTests = ExpectedMatchedTests.Value;
|
|
268
|
+
|
|
269
|
+
// Subscribe to log messages (using threaded version to catch logs from all threads)
|
|
270
|
+
Application.logMessageReceivedThreaded -= OnLogMessageReceived;
|
|
271
|
+
Application.logMessageReceivedThreaded += OnLogMessageReceived;
|
|
272
|
+
|
|
273
|
+
UnityCopilotPluginEditor.Instance.LogInfo("Run {testMode} started: {testCount} tests.",
|
|
274
|
+
typeof(TestResultCollector), TestModeAsString, testCount);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
public void RunFinished(ITestResultAdaptor result)
|
|
278
|
+
{
|
|
279
|
+
UnityCopilotPluginEditor.Instance.LogInfo("RunFinished", typeof(TestResultCollector));
|
|
280
|
+
|
|
281
|
+
// Unsubscribe from log messages
|
|
282
|
+
Application.logMessageReceivedThreaded -= OnLogMessageReceived;
|
|
283
|
+
|
|
284
|
+
// Release the ownership context up front: its captured members
|
|
285
|
+
// (including RunStartedTestCount) feed the summary fallback below
|
|
286
|
+
// and the structured response afterwards.
|
|
287
|
+
var ownership = _callbackOwnership.ReleaseForRunFinished();
|
|
288
|
+
|
|
289
|
+
var duration = DateTime.Now - startTime;
|
|
290
|
+
_summary.Duration = DateTime.Now - startTime;
|
|
291
|
+
_summary.ExecutedTests = _results.Count;
|
|
292
|
+
// COCli-12: TotalTests reports the filtered execution scope, never
|
|
293
|
+
// the whole-project discovery count. Preference order: the
|
|
294
|
+
// discovery-matched count, the RunStarted filtered count, then
|
|
295
|
+
// (only for runs that bypassed discovery) the executed count
|
|
296
|
+
// before falling back to counting the Unity result tree.
|
|
297
|
+
_summary.TotalTests = _summary.MatchedTests > 0
|
|
298
|
+
? _summary.MatchedTests
|
|
299
|
+
: ownership is { RunStartedTestCount: > 0 }
|
|
300
|
+
? ownership.RunStartedTestCount
|
|
301
|
+
: Math.Max(_summary.ExecutedTests, CountTests(result.Test));
|
|
302
|
+
if (_summary.FailedTests > 0)
|
|
303
|
+
{
|
|
304
|
+
_summary.Status = TestRunStatus.Failed;
|
|
305
|
+
}
|
|
306
|
+
else if (_summary.PassedTests > 0)
|
|
307
|
+
{
|
|
308
|
+
_summary.Status = TestRunStatus.Passed;
|
|
309
|
+
}
|
|
310
|
+
else
|
|
311
|
+
{
|
|
312
|
+
_summary.Status = TestRunStatus.Unknown;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
UnityCopilotPluginEditor.Instance.LogInfo("Run {testMode} finished with {totalTests} test results. Result status: {status}",
|
|
316
|
+
typeof(TestResultCollector), TestModeAsString, _summary.TotalTests, result.TestStatus);
|
|
317
|
+
UnityCopilotPluginEditor.Instance.LogInfo("Final duration: {duration:mm\\:ss\\.fff}. Completed: {completed}/{total}",
|
|
318
|
+
typeof(TestResultCollector), duration, _results.Count, _summary.TotalTests);
|
|
319
|
+
|
|
320
|
+
UnityCopilotPluginEditor.Instance.BuildUcoPluginIfNeeded();
|
|
321
|
+
|
|
322
|
+
if (!EnvironmentUtils.IsCi() && !EnvironmentUtils.IsBatchMode())
|
|
323
|
+
UnityCopilotPluginEditor.ConnectIfNeeded();
|
|
324
|
+
|
|
325
|
+
var structuredResponse = CreateStructuredResponse(
|
|
326
|
+
includePassingTests: ownership?.IncludePassingTests ?? IncludePassingTests.Value,
|
|
327
|
+
includeMessage: ownership?.IncludeMessage ?? IncludeMessage.Value,
|
|
328
|
+
includeLogs: ownership?.IncludeLogs ?? IncludeLogs.Value,
|
|
329
|
+
includeLogsMinLevel: ownership?.IncludeLogsMinLevel ?? IncludeLogsMinLevel.Value,
|
|
330
|
+
includeMessageStacktrace: ownership?.IncludeMessageStacktrace ?? IncludeMessageStacktrace.Value,
|
|
331
|
+
includeLogsStacktrace: ownership?.IncludeLogsStacktrace ?? IncludeLogsStacktrace.Value);
|
|
332
|
+
|
|
333
|
+
// Scene hygiene (COCli-06): restore a sandboxed run and report the
|
|
334
|
+
// observed mutation against the persisted pre-run baseline.
|
|
335
|
+
var sceneOutcome = TestRunSceneSandbox.RestoreForOperation(ownership?.OperationId);
|
|
336
|
+
structuredResponse.Mutated = sceneOutcome.Mutation.Mutated;
|
|
337
|
+
structuredResponse.MutatedScenes = sceneOutcome.Mutation.MutatedScenes;
|
|
338
|
+
structuredResponse.SandboxRestored = sceneOutcome.SandboxRestored;
|
|
339
|
+
structuredResponse.SandboxRestoreCause = sceneOutcome.SandboxRestoreCause;
|
|
340
|
+
|
|
341
|
+
var resultJson = JsonSerializer.Serialize(structuredResponse);
|
|
342
|
+
var completedOwnedOperation = _callbackOwnership.CompleteFromRunFinished(
|
|
343
|
+
ownership, _summary.FailedTests, _summary.ExecutedTests, resultJson);
|
|
344
|
+
|
|
345
|
+
var requestId = ownership?.RequestId ?? string.Empty;
|
|
346
|
+
if (completedOwnedOperation && string.IsNullOrEmpty(requestId) == false)
|
|
347
|
+
{
|
|
348
|
+
var ucoPlugin = UnityCopilotPluginEditor.Instance.UcoPluginInstance ?? throw new InvalidOperationException("Uco Plugin instance is not available.");
|
|
349
|
+
|
|
350
|
+
var response = ResponseCallValueTool<TestRunResponse>
|
|
351
|
+
.SuccessStructured(ucoPlugin.UcoManager.Reflector.JsonSerializer.SerializeToNode(structuredResponse))
|
|
352
|
+
.SetRequestID(requestId);
|
|
353
|
+
|
|
354
|
+
_ = UnityCopilotPluginEditor.NotifyToolRequestCompleted(new RequestToolCompletedData
|
|
355
|
+
{
|
|
356
|
+
RequestId = requestId,
|
|
357
|
+
OperationId = ownership?.OperationId,
|
|
358
|
+
Result = response
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
internal static bool CompleteOperationFromCallback(
|
|
364
|
+
string operationId,
|
|
365
|
+
int failedTests,
|
|
366
|
+
int executedTests,
|
|
367
|
+
string resultJson)
|
|
368
|
+
{
|
|
369
|
+
var operation = EditorOperationRegistry.Get(operationId);
|
|
370
|
+
if (operation == null || operation.IsTerminal)
|
|
371
|
+
return false;
|
|
372
|
+
|
|
373
|
+
if (operation.CancellationRequested)
|
|
374
|
+
EditorOperationRegistry.CancelRunning(operationId, "test-run-cancelled", resultJson);
|
|
375
|
+
else if (failedTests > 0)
|
|
376
|
+
EditorOperationRegistry.Fail(operationId, "tests-failed",
|
|
377
|
+
$"{failedTests} test(s) failed.", "completed-with-failures", resultJson);
|
|
378
|
+
else if (executedTests == 0)
|
|
379
|
+
EditorOperationRegistry.Fail(operationId, "tests-no-results",
|
|
380
|
+
"Unity Test Runner completed without executing a matched test.", "completed-without-results", resultJson);
|
|
381
|
+
else
|
|
382
|
+
EditorOperationRegistry.Succeed(operationId, resultJson);
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
public void TestStarted(ITestAdaptor test)
|
|
387
|
+
{
|
|
388
|
+
// Test started - could log this if needed
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
public void TestFinished(ITestResultAdaptor result)
|
|
392
|
+
{
|
|
393
|
+
// Only count actual tests, not test suites
|
|
394
|
+
if (!result.Test.IsSuite)
|
|
395
|
+
{
|
|
396
|
+
var testResult = new TestResultData
|
|
397
|
+
{
|
|
398
|
+
Name = result.Test.FullName,
|
|
399
|
+
Status = ConvertTestStatus(result.TestStatus),
|
|
400
|
+
Duration = TimeSpan.FromSeconds(result.Duration),
|
|
401
|
+
Message = result.Message,
|
|
402
|
+
StackTrace = result.StackTrace
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
_results.Add(testResult);
|
|
406
|
+
|
|
407
|
+
var statusEmoji = result.TestStatus switch
|
|
408
|
+
{
|
|
409
|
+
TestStatus.Passed => "<color=green>✅</color>",
|
|
410
|
+
TestStatus.Failed => "<color=red>❌</color>",
|
|
411
|
+
TestStatus.Skipped => "<color=yellow>⚠️</color>",
|
|
412
|
+
_ => string.Empty
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
UnityCopilotPluginEditor.Instance.LogInfo("{emoji} Test finished ({counter}/{total}): {testName} - {testStatus}",
|
|
416
|
+
typeof(TestResultCollector), statusEmoji, _results.Count, _summary.TotalTests, result.Test.FullName, result.TestStatus);
|
|
417
|
+
|
|
418
|
+
// Update summary counts
|
|
419
|
+
switch (result.TestStatus)
|
|
420
|
+
{
|
|
421
|
+
case TestStatus.Passed:
|
|
422
|
+
_summary.PassedTests++;
|
|
423
|
+
break;
|
|
424
|
+
case TestStatus.Failed:
|
|
425
|
+
_summary.FailedTests++;
|
|
426
|
+
break;
|
|
427
|
+
case TestStatus.Skipped:
|
|
428
|
+
_summary.SkippedTests++;
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Update duration as tests complete
|
|
433
|
+
_summary.Duration = DateTime.Now - startTime;
|
|
434
|
+
_summary.ExecutedTests = _results.Count;
|
|
435
|
+
|
|
436
|
+
_callbackOwnership.UpdateFromTestFinished(
|
|
437
|
+
_results.Count, _summary.MatchedTests);
|
|
438
|
+
|
|
439
|
+
// Check if all tests are complete
|
|
440
|
+
if (_results.Count >= _summary.TotalTests)
|
|
441
|
+
{
|
|
442
|
+
UnityCopilotPluginEditor.Instance.LogInfo("All tests completed via TestFinished. Final duration: {duration:mm\\:ss\\.fff}",
|
|
443
|
+
typeof(TestResultCollector), _summary.Duration);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
void OnLogMessageReceived(string condition, string stackTrace, LogType type)
|
|
449
|
+
{
|
|
450
|
+
var entry = new TestLogEntry(type, condition, stackTrace);
|
|
451
|
+
lock (_logsMutex)
|
|
452
|
+
{
|
|
453
|
+
_logs.Add(entry);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
TestRunResponse CreateStructuredResponse(
|
|
458
|
+
bool includePassingTests,
|
|
459
|
+
bool includeMessage,
|
|
460
|
+
bool includeMessageStacktrace,
|
|
461
|
+
bool includeLogs,
|
|
462
|
+
int includeLogsMinLevel,
|
|
463
|
+
bool includeLogsStacktrace)
|
|
464
|
+
{
|
|
465
|
+
var results = GetResults();
|
|
466
|
+
var summary = GetSummary();
|
|
467
|
+
var logs = GetLogs();
|
|
468
|
+
|
|
469
|
+
var response = new TestRunResponse
|
|
470
|
+
{
|
|
471
|
+
Summary = summary,
|
|
472
|
+
Results = new List<TestResultData>()
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// Filter test results based on includePassingTests, includeMessage and includeMessageStacktrace
|
|
476
|
+
foreach (var result in results)
|
|
477
|
+
{
|
|
478
|
+
// Skip passing tests if includePassingTests is false
|
|
479
|
+
if (!includePassingTests && result.Status == TestResultStatus.Passed)
|
|
480
|
+
continue;
|
|
481
|
+
|
|
482
|
+
var filteredResult = new TestResultData
|
|
483
|
+
{
|
|
484
|
+
Name = result.Name,
|
|
485
|
+
Status = result.Status,
|
|
486
|
+
Duration = result.Duration,
|
|
487
|
+
Message = includeMessage ? result.Message : null,
|
|
488
|
+
StackTrace = includeMessageStacktrace ? result.StackTrace : null
|
|
489
|
+
};
|
|
490
|
+
response.Results.Add(filteredResult);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Include logs if requested
|
|
494
|
+
if (includeLogs && logs.Any())
|
|
495
|
+
{
|
|
496
|
+
var minLogLevel = TestLogEntry.ToLogLevel((LogType)includeLogsMinLevel);
|
|
497
|
+
response.Logs = logs
|
|
498
|
+
.Where(log => log.LogLevel >= minLogLevel)
|
|
499
|
+
.Select(log => includeLogsStacktrace
|
|
500
|
+
? log
|
|
501
|
+
: new TestLogEntry(log.Type, log.Condition, null, log.Timestamp))
|
|
502
|
+
.ToList();
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return response;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
public static int CountTests(ITestAdaptor test)
|
|
509
|
+
{
|
|
510
|
+
try
|
|
511
|
+
{
|
|
512
|
+
if (test == null)
|
|
513
|
+
return 0;
|
|
514
|
+
|
|
515
|
+
if (test.HasChildren && test.Children != null)
|
|
516
|
+
return test.Children.Sum(CountTests);
|
|
517
|
+
|
|
518
|
+
return test.IsSuite ? 0 : 1;
|
|
519
|
+
}
|
|
520
|
+
catch
|
|
521
|
+
{
|
|
522
|
+
return 0;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
static TestResultStatus ConvertTestStatus(TestStatus testStatus)
|
|
527
|
+
{
|
|
528
|
+
return testStatus switch
|
|
529
|
+
{
|
|
530
|
+
TestStatus.Passed => TestResultStatus.Passed,
|
|
531
|
+
TestStatus.Failed => TestResultStatus.Failed,
|
|
532
|
+
TestStatus.Skipped => TestResultStatus.Skipped,
|
|
533
|
+
_ => TestResultStatus.Skipped
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|