@alpsckr/unitycli 0.4.3 → 0.4.5
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/README.md +5 -5
- package/contracts/command-contract.json +109 -217
- package/contracts/package-files.json +374 -294
- package/contracts/stable-session/direct-call-semantics.json +8 -2
- package/contracts/tool-completion-contract.json +148 -672
- package/dist/bridge/admission.d.ts +17 -1
- package/dist/bridge/admission.js +2 -0
- package/dist/bridge/admission.js.map +1 -1
- package/dist/bridge/domains/asset.js +3 -1
- package/dist/bridge/domains/asset.js.map +1 -1
- package/dist/bridge/domains/camera.js +39 -7
- package/dist/bridge/domains/camera.js.map +1 -1
- package/dist/bridge/domains/console.js +2 -1
- package/dist/bridge/domains/console.js.map +1 -1
- package/dist/bridge/domains/doctor.d.ts +3 -0
- package/dist/bridge/domains/doctor.js +8 -0
- package/dist/bridge/domains/doctor.js.map +1 -0
- package/dist/bridge/domains/editor.js +0 -4
- package/dist/bridge/domains/editor.js.map +1 -1
- package/dist/bridge/domains/game-object.js +4 -12
- package/dist/bridge/domains/game-object.js.map +1 -1
- package/dist/bridge/domains/graphics.js +1 -16
- package/dist/bridge/domains/graphics.js.map +1 -1
- package/dist/bridge/domains/object.js +3 -0
- package/dist/bridge/domains/object.js.map +1 -1
- package/dist/bridge/domains/physics.js +1 -2
- package/dist/bridge/domains/physics.js.map +1 -1
- package/dist/bridge/domains/profiler.js +18 -7
- package/dist/bridge/domains/profiler.js.map +1 -1
- package/dist/bridge/domains/project-tool.js +4 -0
- package/dist/bridge/domains/project-tool.js.map +1 -1
- package/dist/bridge/domains/reflection.js +10 -2
- package/dist/bridge/domains/reflection.js.map +1 -1
- package/dist/bridge/domains/runtime-tool.js +3 -0
- package/dist/bridge/domains/runtime-tool.js.map +1 -1
- package/dist/bridge/domains/scene.js +3 -8
- package/dist/bridge/domains/scene.js.map +1 -1
- package/dist/bridge/domains/screenshot.js +30 -6
- package/dist/bridge/domains/screenshot.js.map +1 -1
- package/dist/bridge/domains/ui.js +3 -1
- package/dist/bridge/domains/ui.js.map +1 -1
- package/dist/bridge/index.d.ts +5 -5
- package/dist/bridge/index.js +6 -9
- package/dist/bridge/index.js.map +1 -1
- package/dist/bridge/registry.js +3 -5
- package/dist/bridge/registry.js.map +1 -1
- package/dist/bridge/session-supervisor.d.ts +6 -3
- package/dist/bridge/session-supervisor.js +145 -39
- package/dist/bridge/session-supervisor.js.map +1 -1
- package/dist/bridge/start-diagnostics.d.ts +19 -0
- package/dist/bridge/start-diagnostics.js +96 -0
- package/dist/bridge/start-diagnostics.js.map +1 -0
- package/dist/bridge/types.d.ts +6 -5
- package/dist/bridge/types.js +16 -2
- package/dist/bridge/types.js.map +1 -1
- package/dist/catalog-protocol.d.ts +64 -0
- package/dist/catalog-protocol.js +460 -0
- package/dist/catalog-protocol.js.map +1 -0
- package/dist/cli-commands/command.js +84 -61
- package/dist/cli-commands/command.js.map +1 -1
- package/dist/cli-commands/extensions.js +70 -50
- package/dist/cli-commands/extensions.js.map +1 -1
- package/dist/cli-commands/instances.js +1 -1
- package/dist/cli-commands/instances.js.map +1 -1
- package/dist/cli-commands/package.js +10 -2
- package/dist/cli-commands/package.js.map +1 -1
- package/dist/cli-process-owners.d.ts +6 -0
- package/dist/cli-process-owners.js +27 -0
- package/dist/cli-process-owners.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +20 -5
- package/dist/cli.js.map +1 -1
- package/dist/contracts/command-contract.js +14 -4
- package/dist/contracts/command-contract.js.map +1 -1
- package/dist/doctor.d.ts +4 -6
- package/dist/doctor.js +451 -398
- package/dist/doctor.js.map +1 -1
- package/dist/editor-blocker-observer.d.ts +2 -1
- package/dist/editor-blocker-observer.js +40 -13
- package/dist/editor-blocker-observer.js.map +1 -1
- package/dist/extension-availability.d.ts +27 -9
- package/dist/extension-availability.js +96 -56
- package/dist/extension-availability.js.map +1 -1
- package/dist/extensions/descriptor-loader.d.ts +2 -2
- package/dist/extensions/descriptor-loader.js +3 -4
- package/dist/extensions/descriptor-loader.js.map +1 -1
- package/dist/extensions/index.d.ts +1 -1
- package/dist/extensions/index.js +1 -1
- package/dist/extensions/index.js.map +1 -1
- package/dist/extensions/installer.d.ts +57 -5
- package/dist/extensions/installer.js +263 -64
- package/dist/extensions/installer.js.map +1 -1
- package/dist/extensions/registry.js +3 -5
- package/dist/extensions/registry.js.map +1 -1
- package/dist/extensions/state-contract.d.ts +11 -0
- package/dist/extensions/state-contract.js +50 -0
- package/dist/extensions/state-contract.js.map +1 -0
- package/dist/instances.d.ts +19 -1
- package/dist/instances.js +39 -0
- package/dist/instances.js.map +1 -1
- package/dist/package-manager.d.ts +0 -24
- package/dist/package-manager.js +5 -58
- package/dist/package-manager.js.map +1 -1
- package/dist/project-tool-discovery.d.ts +5 -2
- package/dist/project-tool-discovery.js +22 -18
- package/dist/project-tool-discovery.js.map +1 -1
- package/dist/runtime-code.js +1 -1
- package/dist/runtime-code.js.map +1 -1
- package/dist/tool-name.d.ts +4 -0
- package/dist/tool-name.js +15 -0
- package/dist/tool-name.js.map +1 -0
- package/dist/tools/.generated-completion-registry.d.ts +2 -0
- package/dist/tools/.generated-completion-registry.js +763 -325
- package/dist/tools/.generated-completion-registry.js.map +1 -1
- package/dist/tools/.generated-registry.d.ts +6 -11
- package/dist/tools/.generated-registry.js +6 -11
- package/dist/tools/.generated-registry.js.map +1 -1
- package/dist/tools/.generated-schemas.js +11 -16
- package/dist/tools/.generated-schemas.js.map +1 -1
- package/dist/tools/asset/refresh-status.d.ts +3 -0
- package/dist/tools/asset/refresh-status.js +12 -0
- package/dist/tools/asset/refresh-status.js.map +1 -0
- package/dist/tools/asset/refresh.js +7 -3
- package/dist/tools/asset/refresh.js.map +1 -1
- package/dist/tools/build/player-status.d.ts +3 -0
- package/dist/tools/build/player-status.js +12 -0
- package/dist/tools/build/player-status.js.map +1 -0
- package/dist/tools/build/run.js +1 -1
- package/dist/tools/build/run.js.map +1 -1
- package/dist/tools/catalog-control.d.ts +22 -0
- package/dist/tools/catalog-control.js +335 -0
- package/dist/tools/catalog-control.js.map +1 -0
- package/dist/tools/catalog.d.ts +7 -9
- package/dist/tools/catalog.js +66 -45
- package/dist/tools/catalog.js.map +1 -1
- package/dist/tools/commands.d.ts +1 -1
- package/dist/tools/commands.js +44 -11
- package/dist/tools/commands.js.map +1 -1
- package/dist/tools/define.js +3 -2
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/dispatcher.d.ts +53 -6
- package/dist/tools/dispatcher.js +277 -108
- package/dist/tools/dispatcher.js.map +1 -1
- package/dist/tools/editor/compile.js +1 -1
- package/dist/tools/editor/compile.js.map +1 -1
- package/dist/tools/editor/play-mode-status.d.ts +3 -0
- package/dist/tools/editor/play-mode-status.js +12 -0
- package/dist/tools/editor/play-mode-status.js.map +1 -0
- package/dist/tools/editor/readiness.d.ts +0 -1
- package/dist/tools/editor/readiness.js +0 -1
- package/dist/tools/editor/readiness.js.map +1 -1
- package/dist/tools/editor/refresh.d.ts +7 -3
- package/dist/tools/editor/refresh.js +20 -15
- package/dist/tools/editor/refresh.js.map +1 -1
- package/dist/tools/editor/state.d.ts +2 -2
- package/dist/tools/editor/state.js +13 -12
- package/dist/tools/editor/state.js.map +1 -1
- package/dist/tools/metadata/derived.js +1 -7
- package/dist/tools/metadata/derived.js.map +1 -1
- package/dist/tools/metadata/status.js +1 -12
- package/dist/tools/metadata/status.js.map +1 -1
- package/dist/tools/metadata/types.d.ts +0 -2
- package/dist/tools/navmesh/live.js +1 -1
- package/dist/tools/navmesh/live.js.map +1 -1
- package/dist/tools/occlusion/live.js +1 -1
- package/dist/tools/occlusion/live.js.map +1 -1
- package/dist/tools/package/deploy.js +2 -2
- package/dist/tools/package/deploy.js.map +1 -1
- package/dist/tools/package/embed.js +1 -1
- package/dist/tools/package/embed.js.map +1 -1
- package/dist/tools/package/install-status.d.ts +3 -0
- package/dist/tools/package/install-status.js +12 -0
- package/dist/tools/package/install-status.js.map +1 -0
- package/dist/tools/package/install.js +3 -3
- package/dist/tools/package/install.js.map +1 -1
- package/dist/tools/package/live-resolve.js +45 -12
- package/dist/tools/package/live-resolve.js.map +1 -1
- package/dist/tools/package/registry-clear.js.map +1 -1
- package/dist/tools/package/registry-set.js.map +1 -1
- package/dist/tools/package/remove-status.d.ts +3 -0
- package/dist/tools/package/remove-status.js +12 -0
- package/dist/tools/package/remove-status.js.map +1 -0
- package/dist/tools/package/remove.js +3 -3
- package/dist/tools/package/remove.js.map +1 -1
- package/dist/tools/package/resolve.js +3 -3
- package/dist/tools/package/resolve.js.map +1 -1
- package/dist/tools/package/restore.js +2 -2
- package/dist/tools/package/restore.js.map +1 -1
- package/dist/tools/package/scoped-registry-add.js.map +1 -1
- package/dist/tools/status/support.d.ts +13 -0
- package/dist/tools/status/support.js +28 -0
- package/dist/tools/status/support.js.map +1 -0
- package/dist/tools/test/cancel.js +2 -2
- package/dist/tools/test/cancel.js.map +1 -1
- package/dist/tools/test/run-status.d.ts +3 -0
- package/dist/tools/test/run-status.js +12 -0
- package/dist/tools/test/run-status.js.map +1 -0
- package/dist/tools/test/run.js +7 -4
- package/dist/tools/test/run.js.map +1 -1
- package/dist/tools/tools/group-summaries.js +10 -14
- package/dist/tools/tools/group-summaries.js.map +1 -1
- package/docs/architecture.md +4 -4
- package/docs/availability-differences.md +4 -4
- package/docs/command-execution.md +4 -3
- package/docs/error-codes.md +13 -5
- package/docs/extensions.md +1 -7
- package/docs/risk-and-side-effects.md +4 -3
- package/docs/safety-boundaries.md +3 -1
- package/docs/target-and-instance.md +1 -1
- package/docs/tool-discovery.md +17 -10
- package/docs/tool-execution.md +18 -7
- package/extensions/cinemachine/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
- package/extensions/hdrp/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
- package/extensions/probuilder/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
- package/extensions/urp/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
- package/extensions/vfx-graph/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
- package/package.json +2 -4
- package/skills/unitycli/SKILL.md +11 -2
- package/skills/unitycli/manifest.json +1 -1
- package/skills/unitycli/references/operation-protocol.md +3 -3
- package/unitypkg/Editor/AssemblyInfo.cs +5 -0
- package/unitypkg/Editor/Bridge/BridgeCommandRegistry.cs +49 -14
- package/unitypkg/Editor/Bridge/BridgeHttpServer.cs +9 -35
- package/unitypkg/Editor/Bridge/BridgeQueueState.cs +672 -0
- package/unitypkg/Editor/Bridge/BridgeWorkQueue.cs +970 -120
- package/unitypkg/Editor/BridgeStartupStateMachine.cs +59 -1
- package/unitypkg/Editor/Commands/ProjectToolCommands.cs +4 -0
- package/unitypkg/Editor/Commands/RuntimeToolCommands.cs +3 -0
- package/unitypkg/Editor/Generated/UnityCliToolCompletionRegistry.g.cs +379 -14
- package/unitypkg/Editor/Infrastructure/CodeExecSupport.cs +28 -13
- package/unitypkg/Editor/Infrastructure/RegistryOwnershipResolver.cs +132 -105
- package/unitypkg/Editor/Infrastructure/UnityCliActiveOperation.cs +138 -29
- package/unitypkg/Editor/Infrastructure/UnityCliAdmission.cs +1856 -237
- package/unitypkg/Editor/Infrastructure/UnityCliDoctorDiagnostics.cs +220 -0
- package/unitypkg/Editor/Infrastructure/UnityCliDurableStatusCoordinator.cs +118 -0
- package/unitypkg/Editor/Infrastructure/UnityCliEditorStateSnapshot.cs +2 -1
- package/unitypkg/Editor/Infrastructure/UnityCliHierarchyPager.cs +203 -0
- package/unitypkg/Editor/Infrastructure/UnityCliInstanceCleanup.cs +2 -1
- package/unitypkg/Editor/Infrastructure/UnityCliInstanceRegistry.cs +201 -63
- package/unitypkg/Editor/Infrastructure/UnityCliJson.cs +2 -3
- package/unitypkg/Editor/Infrastructure/UnityCliLiveBuildCoordinator.cs +289 -46
- package/unitypkg/Editor/Infrastructure/UnityCliLiveRefreshCoordinator.cs +87 -53
- package/unitypkg/Editor/Infrastructure/UnityCliNavMeshBakeCoordinator.cs +4 -4
- package/unitypkg/Editor/Infrastructure/UnityCliObjectRef.cs +128 -25
- package/unitypkg/Editor/Infrastructure/UnityCliObjectSerializer.cs +435 -327
- package/unitypkg/Editor/Infrastructure/UnityCliOcclusionBakeCoordinator.cs +4 -4
- package/unitypkg/Editor/Infrastructure/UnityCliOperationCoordinator.cs +351 -53
- package/unitypkg/Editor/Infrastructure/UnityCliPackageResolveCoordinator.cs +118 -18
- package/unitypkg/Editor/Infrastructure/UnityCliPackageResolveState.cs +20 -1
- package/unitypkg/Editor/Infrastructure/UnityCliReadGeneration.cs +97 -0
- package/unitypkg/Editor/Infrastructure/UnityCliReflection.cs +644 -133
- package/unitypkg/Editor/Infrastructure/UnityCliReloadOperation.cs +128 -41
- package/unitypkg/Editor/Infrastructure/UnityCliRuntimeExecutor.cs +14 -84
- package/unitypkg/Editor/Infrastructure/UnityCliSerializer.cs +220 -0
- package/unitypkg/Editor/Infrastructure/UnityCliSessionMutationLease.cs +69 -29
- package/unitypkg/Editor/Infrastructure/UnityCliTestRunCoordinator.cs +440 -54
- package/unitypkg/Editor/Infrastructure/UnityCliWaiter.cs +128 -35
- package/unitypkg/Editor/ProjectTools/IUnityCliTool.cs +9 -1
- package/unitypkg/Editor/ProjectTools/OfficialAttributeToolRegistry.cs +32 -31
- package/unitypkg/Editor/ProjectTools/ProjectToolRegistry.cs +607 -458
- package/unitypkg/Editor/ProjectTools/ProjectToolSchemaValidator.cs +35 -7
- package/unitypkg/Editor/ProjectTools/RegisteredTool.cs +501 -60
- package/unitypkg/Editor/ProjectTools/UnityCliProjectToolContext.cs +21 -0
- package/unitypkg/Editor/ProjectTools/UnityCliSchemaDeriver.cs +36 -7
- package/unitypkg/Editor/ProjectTools/UnityCliToolName.cs +30 -0
- package/unitypkg/Editor/Properties/AssemblyInfo.cs +3 -0
- package/unitypkg/Editor/Tools/asset/get-data.cs +16 -7
- package/unitypkg/Editor/Tools/build/BuildSceneStateTool.cs +23 -3
- package/unitypkg/Editor/Tools/build/BuildToolDtos.cs +2 -1
- package/unitypkg/Editor/Tools/build/scene/add.cs +6 -3
- package/unitypkg/Editor/Tools/build/scene/disable.cs +1 -1
- package/unitypkg/Editor/Tools/build/scene/enable.cs +1 -1
- package/unitypkg/Editor/Tools/build/scene/list.cs +1 -1
- package/unitypkg/Editor/Tools/build/scene/remove.cs +13 -3
- package/unitypkg/Editor/Tools/camera/CameraToolSupport.cs +38 -38
- package/unitypkg/Editor/Tools/camera/multiview.cs +13 -6
- package/unitypkg/Editor/Tools/camera/screenshot.cs +14 -7
- package/unitypkg/Editor/Tools/component/get.cs +16 -9
- package/unitypkg/Editor/Tools/component/modify.cs +6 -4
- package/unitypkg/Editor/Tools/console/clear.cs +7 -6
- package/unitypkg/Editor/Tools/console/get.cs +48 -16
- package/unitypkg/Editor/Tools/editor/EditorToolSupport.cs +143 -28
- package/unitypkg/Editor/Tools/editor/menu-items.cs +1 -1
- package/unitypkg/Editor/Tools/editor/play.cs +2 -11
- package/unitypkg/Editor/Tools/editor/selection.cs +1 -1
- package/unitypkg/Editor/Tools/editor/stop.cs +2 -11
- package/unitypkg/Editor/Tools/editor/windows.cs +1 -1
- package/unitypkg/Editor/Tools/go/GameObjectToolSupport.cs +54 -4
- package/unitypkg/Editor/Tools/go/delete.cs +1 -1
- package/unitypkg/Editor/Tools/go/find.cs +167 -70
- package/unitypkg/Editor/Tools/graphics/GraphicsToolSupport.cs +25 -0
- package/unitypkg/Editor/Tools/graphics/light-probe-group.cs +9 -3
- package/unitypkg/Editor/Tools/graphics/pipeline-info.cs +1 -1
- package/unitypkg/Editor/Tools/graphics/pipeline-settings.cs +1 -1
- package/unitypkg/Editor/Tools/graphics/quality.cs +2 -2
- package/unitypkg/Editor/Tools/graphics/reflection-probe.cs +3 -3
- package/unitypkg/Editor/Tools/graphics/stats.cs +1 -1
- package/unitypkg/Editor/Tools/object/ObjectToolSupport.cs +4 -4
- package/unitypkg/Editor/Tools/object/get-data.cs +9 -7
- package/unitypkg/Editor/Tools/object/modify.cs +7 -5
- package/unitypkg/Editor/Tools/physics/raycast.cs +24 -6
- package/unitypkg/Editor/Tools/prefab/stage.cs +1 -1
- package/unitypkg/Editor/Tools/profiler/ProfilerFrameDebuggerSupport.cs +94 -21
- package/unitypkg/Editor/Tools/profiler/ProfilerMemorySnapshotSupport.cs +397 -10
- package/unitypkg/Editor/Tools/profiler/ProfilerToolSupport.cs +18 -4
- package/unitypkg/Editor/Tools/profiler/frame-debugger/enable.cs +1 -1
- package/unitypkg/Editor/Tools/profiler/frame-debugger/events.cs +88 -87
- package/unitypkg/Editor/Tools/profiler/hierarchy.cs +278 -112
- package/unitypkg/Editor/Tools/profiler/memory-snapshot/compare.cs +9 -5
- package/unitypkg/Editor/Tools/profiler/memory-snapshot/list.cs +37 -35
- package/unitypkg/Editor/Tools/profiler/memory-snapshot/take.cs +99 -97
- package/unitypkg/Editor/Tools/project/info.cs +1 -1
- package/unitypkg/Editor/Tools/project/layers.cs +1 -1
- package/unitypkg/Editor/Tools/project/tags.cs +1 -1
- package/unitypkg/Editor/Tools/reflection/method/call.cs +9 -7
- package/unitypkg/Editor/Tools/reflection/method/find.cs +27 -37
- package/unitypkg/Editor/Tools/reflection/type/schema.cs +24 -28
- package/unitypkg/Editor/Tools/scene/gameobject/component.cs +8 -5
- package/unitypkg/Editor/Tools/scene/gameobject/components.cs +10 -7
- package/unitypkg/Editor/Tools/scene/gameobject-support.cs +104 -8
- package/unitypkg/Editor/Tools/scene/gameobject.cs +37 -16
- package/unitypkg/Editor/Tools/scene/hierarchy.cs +146 -142
- package/unitypkg/Editor/Tools/screenshot/EditorElementCaptureToolSupport.cs +77 -25
- package/unitypkg/Editor/Tools/screenshot/ScreenshotToolSupport.cs +315 -95
- package/unitypkg/Editor/Tools/screenshot/editor-element.cs +7 -5
- package/unitypkg/Editor/Tools/screenshot/game-view.cs +35 -21
- package/unitypkg/Editor/Tools/screenshot/isolated-object.cs +29 -23
- package/unitypkg/Editor/Tools/screenshot/orbit.cs +37 -25
- package/unitypkg/Editor/Tools/screenshot/scene-view.cs +25 -18
- package/unitypkg/Editor/Tools/screenshot/screen.cs +177 -17
- package/unitypkg/Editor/Tools/screenshot/surround.cs +83 -79
- package/unitypkg/Editor/Tools/ui/render-preview.cs +16 -11
- package/unitypkg/Editor/UnityCliBridge.cs +97 -34
- package/unitypkg/Runtime/Infrastructure/UnityCliRuntimeBridge.cs +753 -122
- package/unitypkg/Runtime/Properties/AssemblyInfo.cs +5 -0
- package/unitypkg/Runtime/UnityCli.Runtime.asmdef +1 -1
- package/unitypkg/Shared/AssemblyInfo.cs +4 -0
- package/unitypkg/Shared/UnityCliAdmissionPool.cs +265 -75
- package/unitypkg/Shared/UnityCliBridgeQueuePolicy.cs +410 -0
- package/unitypkg/Shared/UnityCliBridgeWorkItemState.cs +47 -0
- package/unitypkg/Shared/UnityCliExceptionProjection.cs +151 -0
- package/unitypkg/Shared/UnityCliQueuePublication.cs +30 -0
- package/unitypkg/Shared/UnityCliRequestBudget.cs +352 -0
- package/unitypkg/Shared/UnityCliRuntimeRequestPublication.cs +107 -0
- package/unitypkg/Tests/Editor/UnityCli.Editor.Tests.asmdef +2 -2
- package/unitypkg/Tests/Editor/UnityCliAdmissionCoordinatorIntegrationTests.cs +1329 -0
- package/unitypkg/Tests/Editor/UnityCliAdmissionPoolTests.cs +311 -114
- package/unitypkg/Tests/Editor/UnityCliAdmissionRepairStateTests.cs +94 -0
- package/unitypkg/Tests/Editor/UnityCliBridgeAdmissionIntegrationTests.cs +1229 -0
- package/unitypkg/Tests/Editor/UnityCliBridgeQueuePolicyTests.cs +412 -0
- package/unitypkg/Tests/Editor/UnityCliContinuationIntegrationTests.cs +292 -0
- package/unitypkg/Tests/Editor/UnityCliDiagnosticBoundaryTests.cs +120 -0
- package/unitypkg/Tests/Editor/UnityCliDurableStatusTests.cs +84 -0
- package/unitypkg/Tests/Editor/UnityCliExceptionProjectionTests.cs +72 -0
- package/unitypkg/Tests/Editor/UnityCliInstancePublicationRetryTests.cs +180 -97
- package/unitypkg/Tests/Editor/UnityCliMemoryReflectionIntegrationTests.cs +434 -0
- package/unitypkg/Tests/Editor/UnityCliRequestBudgetTests.cs +615 -0
- package/unitypkg/Tests/Editor/UnityCliRuntimeBridgeIntegrationTests.cs +520 -0
- package/unitypkg/Tests/Editor/UnityCliToolCatalogTests.cs +562 -0
- package/unitypkg/Tests/Runtime/UnityCli.Runtime.Tests.asmdef +23 -0
- package/unitypkg/Tests/Runtime/UnityCliRuntimeCatalogValidationTests.cs +29 -0
- package/unitypkg/package.json +1 -1
- package/contracts/static-capability-catalog.json +0 -48226
- package/dist/bridge/domains/diagnose.d.ts +0 -2
- package/dist/bridge/domains/diagnose.js +0 -11
- package/dist/bridge/domains/diagnose.js.map +0 -1
- package/dist/bridge/domains/extension.d.ts +0 -8
- package/dist/bridge/domains/extension.js +0 -20
- package/dist/bridge/domains/extension.js.map +0 -1
- package/dist/capabilities.d.ts +0 -5
- package/dist/capabilities.js +0 -84
- package/dist/capabilities.js.map +0 -1
- package/dist/static-capability-catalog.d.ts +0 -13
- package/dist/static-capability-catalog.js +0 -41
- package/dist/static-capability-catalog.js.map +0 -1
- package/dist/tools/diagnose/capabilities.d.ts +0 -13
- package/dist/tools/diagnose/capabilities.js +0 -34
- package/dist/tools/diagnose/capabilities.js.map +0 -1
- package/dist/tools/package/info.d.ts +0 -19
- package/dist/tools/package/info.js +0 -27
- package/dist/tools/package/info.js.map +0 -1
- package/dist/tools/package/list.d.ts +0 -27
- package/dist/tools/package/list.js +0 -21
- package/dist/tools/package/list.js.map +0 -1
- package/dist/tools/package/registry-get.d.ts +0 -10
- package/dist/tools/package/registry-get.js +0 -22
- package/dist/tools/package/registry-get.js.map +0 -1
- package/dist/tools/package/scoped-registry-list.d.ts +0 -14
- package/dist/tools/package/scoped-registry-list.js +0 -22
- package/dist/tools/package/scoped-registry-list.js.map +0 -1
- package/dist/tools/package/search.d.ts +0 -17
- package/dist/tools/package/search.js +0 -39
- package/dist/tools/package/search.js.map +0 -1
- package/dist/tools/package/status.d.ts +0 -21
- package/dist/tools/package/status.js +0 -37
- package/dist/tools/package/status.js.map +0 -1
- package/dist/tools/tools/describe.d.ts +0 -27
- package/dist/tools/tools/describe.js +0 -35
- package/dist/tools/tools/describe.js.map +0 -1
- package/dist/tools/tools/group-list.d.ts +0 -16
- package/dist/tools/tools/group-list.js +0 -38
- package/dist/tools/tools/group-list.js.map +0 -1
- package/dist/tools/tools/group.d.ts +0 -13
- package/dist/tools/tools/group.js +0 -34
- package/dist/tools/tools/group.js.map +0 -1
- package/dist/tools/tools/pagination.d.ts +0 -17
- package/dist/tools/tools/pagination.js +0 -28
- package/dist/tools/tools/pagination.js.map +0 -1
- package/dist/tools/tools/search.d.ts +0 -15
- package/dist/tools/tools/search.js +0 -42
- package/dist/tools/tools/search.js.map +0 -1
- package/unitypkg/Editor/Tools/diagnose/preflight.cs +0 -269
- package/unitypkg/Editor/Tools/editor/console.cs +0 -26
- package/unitypkg/Editor/Tools/extension/status.cs +0 -87
- package/unitypkg/Editor/Tools/go/inspect.cs +0 -49
- package/unitypkg/Editor/Tools/graphics/pipeline-quality.cs +0 -24
- package/unitypkg/Editor/Tools/graphics/probe-positions.cs +0 -58
- package/unitypkg/Editor/Tools/graphics/settings.cs +0 -104
- package/unitypkg/Editor/Tools/graphics/stats-counters.cs +0 -28
- package/unitypkg/Editor/Tools/graphics/stats-memory.cs +0 -21
- package/unitypkg/Editor/Tools/instances/list.cs +0 -71
- package/unitypkg/Editor/Tools/physics/raycast-all.cs +0 -67
- package/unitypkg/Editor/Tools/scene/build-settings.cs +0 -69
- package/unitypkg/Editor/Tools/scene/data.cs +0 -125
- package/unitypkg/Editor/Tools/tests/list.cs +0 -98
|
@@ -0,0 +1,1229 @@
|
|
|
1
|
+
#if UNITY_EDITOR
|
|
2
|
+
using System;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.IO;
|
|
5
|
+
using System.Linq;
|
|
6
|
+
using System.Text;
|
|
7
|
+
using System.Threading;
|
|
8
|
+
using System.Threading.Tasks;
|
|
9
|
+
using NUnit.Framework;
|
|
10
|
+
using UnityCli.Protocol;
|
|
11
|
+
|
|
12
|
+
namespace UnityCli.Tests
|
|
13
|
+
{
|
|
14
|
+
public sealed class UnityCliBridgeAdmissionIntegrationTests
|
|
15
|
+
{
|
|
16
|
+
private UnityCliAdmissionPool pool;
|
|
17
|
+
private BridgeQueueState state;
|
|
18
|
+
private string fingerprint;
|
|
19
|
+
private long monotonicNowMs;
|
|
20
|
+
|
|
21
|
+
[SetUp]
|
|
22
|
+
public void SetUp()
|
|
23
|
+
{
|
|
24
|
+
pool = new UnityCliAdmissionPool("test-instance", "test-session", 4, 1);
|
|
25
|
+
fingerprint = new string('a', 64);
|
|
26
|
+
monotonicNowMs = 1000L;
|
|
27
|
+
state = UnityCliBridge.CreateQueueStateForTests(pool, () => monotonicNowMs);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
[TearDown]
|
|
31
|
+
public void TearDown()
|
|
32
|
+
{
|
|
33
|
+
state.AssertEmptyForTests();
|
|
34
|
+
Assert.That(state.Sinks.Production, Is.False);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[Test]
|
|
38
|
+
public void ExactAttachAndCompletionShareOneQueuePoolBoundary()
|
|
39
|
+
{
|
|
40
|
+
var request = Request("queue-race-attach");
|
|
41
|
+
var raw = RequestBytes(request);
|
|
42
|
+
var deadline = Deadline(request);
|
|
43
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
44
|
+
Assert.That(grant.Outcome, Is.EqualTo(UnityCliAdmissionPool.AcquireOutcome.Granted));
|
|
45
|
+
bool firstCreated;
|
|
46
|
+
var first = Enqueue(request, raw, grant.Token, deadline, out firstCreated);
|
|
47
|
+
Assert.That(firstCreated, Is.True);
|
|
48
|
+
var entered = new ManualResetEventSlim(false);
|
|
49
|
+
var release = new ManualResetEventSlim(false);
|
|
50
|
+
state.TestHooks.ExpectOnce("before-existing-lookup", () =>
|
|
51
|
+
{
|
|
52
|
+
entered.Set();
|
|
53
|
+
release.Wait();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
UnityCliBridge.BridgeWorkItem attached = null;
|
|
57
|
+
var attachedCreated = true;
|
|
58
|
+
var attachTask = Task.Run(() => attached = Enqueue(request, raw, grant.Token, deadline, out attachedCreated));
|
|
59
|
+
Task<bool> completionTask = null;
|
|
60
|
+
try
|
|
61
|
+
{
|
|
62
|
+
Assert.That(entered.Wait(5000), Is.True);
|
|
63
|
+
completionTask = Task.Run(() => UnityCliBridge.CompleteWorkItemForTests(first, Success(request.RequestId)));
|
|
64
|
+
}
|
|
65
|
+
finally { release.Set(); }
|
|
66
|
+
|
|
67
|
+
Assert.That(attachTask.Wait(5000), Is.True);
|
|
68
|
+
Assert.That(completionTask.Wait(5000), Is.True);
|
|
69
|
+
Assert.That(completionTask.Result, Is.True);
|
|
70
|
+
Assert.That(attached, Is.SameAs(first));
|
|
71
|
+
Assert.That(attachedCreated, Is.False);
|
|
72
|
+
Assert.That(first.Completed.IsSet, Is.True);
|
|
73
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
[Test]
|
|
77
|
+
public void CompletionFirstRejectsRepublishAndCompletesOnlyOnce()
|
|
78
|
+
{
|
|
79
|
+
var request = Request("queue-race-complete");
|
|
80
|
+
var raw = RequestBytes(request);
|
|
81
|
+
var deadline = Deadline(request);
|
|
82
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
83
|
+
bool ignoredCreated;
|
|
84
|
+
var first = Enqueue(request, raw, grant.Token, deadline, out ignoredCreated);
|
|
85
|
+
var entered = new ManualResetEventSlim(false);
|
|
86
|
+
var release = new ManualResetEventSlim(false);
|
|
87
|
+
state.TestHooks.ExpectOnce("before-completion-memory", () =>
|
|
88
|
+
{
|
|
89
|
+
entered.Set();
|
|
90
|
+
release.Wait();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
var completionTask = Task.Run(() => UnityCliBridge.CompleteWorkItemForTests(first, Success(request.RequestId)));
|
|
94
|
+
UnityCliBridge.BridgeCommandException failure = null;
|
|
95
|
+
Task duplicateTask = null;
|
|
96
|
+
try
|
|
97
|
+
{
|
|
98
|
+
Assert.That(entered.Wait(5000), Is.True);
|
|
99
|
+
duplicateTask = Task.Run(() =>
|
|
100
|
+
{
|
|
101
|
+
try { bool created; Enqueue(request, raw, grant.Token, deadline, out created); }
|
|
102
|
+
catch (UnityCliBridge.BridgeCommandException exception) { failure = exception; }
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
finally { release.Set(); }
|
|
106
|
+
|
|
107
|
+
Assert.That(completionTask.Wait(5000), Is.True);
|
|
108
|
+
Assert.That(duplicateTask.Wait(5000), Is.True);
|
|
109
|
+
Assert.That(completionTask.Result, Is.True);
|
|
110
|
+
Assert.That(UnityCliBridge.CompleteWorkItemForTests(first, Success(request.RequestId)), Is.False);
|
|
111
|
+
Assert.That(failure, Is.Not.Null);
|
|
112
|
+
Assert.That(failure.Code, Is.EqualTo("E_ADMISSION_IDENTITY_MISMATCH"));
|
|
113
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
[Test]
|
|
117
|
+
public void QueueCloseAndSweepPreventOldListenerPublication()
|
|
118
|
+
{
|
|
119
|
+
var request = Request("queue-reload-sweep");
|
|
120
|
+
var raw = RequestBytes(request);
|
|
121
|
+
var deadline = Deadline(request);
|
|
122
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
123
|
+
bool created;
|
|
124
|
+
var first = Enqueue(request, raw, grant.Token, deadline, out created);
|
|
125
|
+
Assert.That(created, Is.True);
|
|
126
|
+
|
|
127
|
+
UnityCliBridge.SweepQueuedWorkForTests(state);
|
|
128
|
+
|
|
129
|
+
Assert.That(first.Completed.IsSet, Is.True);
|
|
130
|
+
Assert.That(first.Response, Does.Contain("E_UNITY_DOMAIN_RELOAD"));
|
|
131
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
132
|
+
var exception = Assert.Throws<UnityCliBridge.BridgeCommandException>(() => Enqueue(request, raw, grant.Token, deadline, out created));
|
|
133
|
+
Assert.That(exception.Code, Is.EqualTo("E_UNITY_DOMAIN_RELOAD"));
|
|
134
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
[Test]
|
|
138
|
+
public void AdmissionAcquireAndShutdownShareQueuePublicationLinearization()
|
|
139
|
+
{
|
|
140
|
+
var entered = new ManualResetEventSlim(false);
|
|
141
|
+
var release = new ManualResetEventSlim(false);
|
|
142
|
+
state.TestHooks.ExpectOnce("before-admission-endpoint-acquire", () =>
|
|
143
|
+
{
|
|
144
|
+
entered.Set();
|
|
145
|
+
release.Wait();
|
|
146
|
+
});
|
|
147
|
+
UnityCliAdmissionPool.AcquireResult acquired = null;
|
|
148
|
+
var acquireTask = Task.Run(() => acquired = UnityCliBridge.AcquireAdmissionForTests(state, "endpoint-stop-race", fingerprint));
|
|
149
|
+
Task shutdownTask = null;
|
|
150
|
+
try
|
|
151
|
+
{
|
|
152
|
+
Assert.That(entered.Wait(5000), Is.True);
|
|
153
|
+
shutdownTask = Task.Run(() => UnityCliBridge.ShutdownQueuedWorkForTests(state));
|
|
154
|
+
}
|
|
155
|
+
finally { release.Set(); }
|
|
156
|
+
|
|
157
|
+
Assert.That(acquireTask.Wait(5000), Is.True);
|
|
158
|
+
Assert.That(shutdownTask.Wait(5000), Is.True);
|
|
159
|
+
Assert.That(acquired.Outcome, Is.EqualTo(UnityCliAdmissionPool.AcquireOutcome.Granted));
|
|
160
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
161
|
+
var stale = Assert.Throws<UnityCliBridge.BridgeCommandException>(() =>
|
|
162
|
+
UnityCliBridge.AcquireAdmissionForTests(state, "endpoint-after-stop", fingerprint));
|
|
163
|
+
Assert.That(stale.Code, Is.EqualTo("E_RUNTIME_SESSION_STALE"));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
[Test]
|
|
167
|
+
public void AdmissionShutdownWinningTheRaceReturnsOnlySessionStale()
|
|
168
|
+
{
|
|
169
|
+
var entered = new ManualResetEventSlim(false);
|
|
170
|
+
var release = new ManualResetEventSlim(false);
|
|
171
|
+
state.TestHooks.ExpectOnce("before-queue-shutdown-close", () =>
|
|
172
|
+
{
|
|
173
|
+
entered.Set();
|
|
174
|
+
release.Wait();
|
|
175
|
+
});
|
|
176
|
+
var shutdownTask = Task.Run(() => UnityCliBridge.ShutdownQueuedWorkForTests(state));
|
|
177
|
+
Task<UnityCliBridge.BridgeCommandException> acquireTask = null;
|
|
178
|
+
try
|
|
179
|
+
{
|
|
180
|
+
Assert.That(entered.Wait(5000), Is.True);
|
|
181
|
+
acquireTask = Task.Run(() =>
|
|
182
|
+
{
|
|
183
|
+
try
|
|
184
|
+
{
|
|
185
|
+
UnityCliBridge.AcquireAdmissionForTests(state, "endpoint-stop-wins", fingerprint);
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
catch (UnityCliBridge.BridgeCommandException exception) { return exception; }
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
finally { release.Set(); }
|
|
192
|
+
|
|
193
|
+
Assert.That(shutdownTask.Wait(5000), Is.True);
|
|
194
|
+
Assert.That(acquireTask.Wait(5000), Is.True);
|
|
195
|
+
Assert.That(acquireTask.Result, Is.Not.Null);
|
|
196
|
+
Assert.That(acquireTask.Result.Code, Is.EqualTo("E_RUNTIME_SESSION_STALE"));
|
|
197
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
[Test]
|
|
201
|
+
public void DistinctCompletionIntentsSettleConcurrentlyWithoutLostUpdateOrResurrection()
|
|
202
|
+
{
|
|
203
|
+
var firstRequest = Request("journal-concurrent-first");
|
|
204
|
+
var secondRequest = Request("journal-concurrent-second");
|
|
205
|
+
var firstRaw = RequestBytes(firstRequest);
|
|
206
|
+
var secondRaw = RequestBytes(secondRequest);
|
|
207
|
+
var firstDigest = UnityCliRequestDigest.Compute(firstRaw, firstRequest.InstanceId);
|
|
208
|
+
var secondDigest = UnityCliRequestDigest.Compute(secondRaw, secondRequest.InstanceId);
|
|
209
|
+
var firstGrant = pool.Acquire(firstRequest.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
210
|
+
var secondGrant = pool.Acquire(secondRequest.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
211
|
+
Assert.That(pool.AuthorizeAndCapture(firstRequest.RequestId, fingerprint, firstDigest, firstGrant.Token, state.ListenerId, state.ListenerGeneration, Deadline(firstRequest)).Outcome,
|
|
212
|
+
Is.EqualTo(UnityCliAdmissionPool.AuthorizeOutcome.Authorized));
|
|
213
|
+
Assert.That(pool.AuthorizeAndCapture(secondRequest.RequestId, fingerprint, secondDigest, secondGrant.Token, state.ListenerId, state.ListenerGeneration, Deadline(secondRequest)).Outcome,
|
|
214
|
+
Is.EqualTo(UnityCliAdmissionPool.AuthorizeOutcome.Authorized));
|
|
215
|
+
var firstOwner = UnityCliBridge.CaptureAdmissionOwner(state, firstRequest.RequestId, firstDigest);
|
|
216
|
+
var secondOwner = UnityCliBridge.CaptureAdmissionOwner(state, secondRequest.RequestId, secondDigest);
|
|
217
|
+
|
|
218
|
+
var firstPersist = Task.Run(() => state.PersistIntent(firstOwner));
|
|
219
|
+
var secondPersist = Task.Run(() => state.PersistIntent(secondOwner));
|
|
220
|
+
Assert.That(Task.WaitAll(new Task[] { firstPersist, secondPersist }, 5000), Is.True);
|
|
221
|
+
Assert.That(firstPersist.Result, Is.True);
|
|
222
|
+
Assert.That(secondPersist.Result, Is.True);
|
|
223
|
+
Assert.That(state.HasIntent(firstOwner), Is.True);
|
|
224
|
+
Assert.That(state.HasIntent(secondOwner), Is.True);
|
|
225
|
+
|
|
226
|
+
var firstComplete = Task.Run(() => UnityCliBridge.CompleteAdmissionRoot(state, firstOwner));
|
|
227
|
+
var secondComplete = Task.Run(() => UnityCliBridge.CompleteAdmissionRoot(state, secondOwner));
|
|
228
|
+
Assert.That(Task.WaitAll(new Task[] { firstComplete, secondComplete }, 5000), Is.True);
|
|
229
|
+
Assert.That(firstComplete.Result, Is.True);
|
|
230
|
+
Assert.That(secondComplete.Result, Is.True);
|
|
231
|
+
Assert.That(state.HasIntent(firstOwner), Is.False);
|
|
232
|
+
Assert.That(state.HasIntent(secondOwner), Is.False);
|
|
233
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
234
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
[Test]
|
|
238
|
+
public void PublicationCompensationPersistsAndLogsOutsideQueueLock()
|
|
239
|
+
{
|
|
240
|
+
var request = Request("queue-publication-fault");
|
|
241
|
+
var raw = RequestBytes(request);
|
|
242
|
+
var deadline = Deadline(request);
|
|
243
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
244
|
+
var persistOutside = false;
|
|
245
|
+
var logOutside = false;
|
|
246
|
+
state.TestHooks.ExpectOnce("before-queue-publication", () => { throw new InvalidOperationException("injected publication fault"); });
|
|
247
|
+
state.TestHooks.ExpectOnce("before-admission-compensation-persist", () => persistOutside = !UnityCliBridge.QueueLockHeldForTests(state));
|
|
248
|
+
state.TestHooks.ExpectOnce("before-admission-compensation-log", () => logOutside = !UnityCliBridge.QueueLockHeldForTests(state));
|
|
249
|
+
|
|
250
|
+
Assert.Throws<InvalidOperationException>(() => { bool created; Enqueue(request, raw, grant.Token, deadline, out created); });
|
|
251
|
+
|
|
252
|
+
Assert.That(persistOutside, Is.True);
|
|
253
|
+
Assert.That(logOutside, Is.True);
|
|
254
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
255
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
[Test]
|
|
259
|
+
public void PublicationFaultTombstoneBlocksDuplicateBeforeDurableCompensation()
|
|
260
|
+
{
|
|
261
|
+
var request = Request("queue-publication-duplicate-race");
|
|
262
|
+
var raw = RequestBytes(request);
|
|
263
|
+
var deadline = Deadline(request);
|
|
264
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
265
|
+
var tombstoneEntered = new ManualResetEventSlim(false);
|
|
266
|
+
var releaseTombstone = new ManualResetEventSlim(false);
|
|
267
|
+
var intentEntered = new ManualResetEventSlim(false);
|
|
268
|
+
var releaseIntent = new ManualResetEventSlim(false);
|
|
269
|
+
var duplicateBeforeLock = new ManualResetEventSlim(false);
|
|
270
|
+
var duplicateEntered = new ManualResetEventSlim(false);
|
|
271
|
+
var tombstoneTrackedUnderLock = false;
|
|
272
|
+
state.TestHooks.ExpectOnce("before-queue-publication", () => { throw new InvalidOperationException("injected publication fault"); });
|
|
273
|
+
state.TestHooks.ExpectOnce("after-publication-compensation-tombstone", () =>
|
|
274
|
+
{
|
|
275
|
+
tombstoneTrackedUnderLock = UnityCliBridge.QueueLockHeldForTests(state);
|
|
276
|
+
tombstoneEntered.Set();
|
|
277
|
+
releaseTombstone.Wait();
|
|
278
|
+
});
|
|
279
|
+
state.TestHooks.ExpectOnce("before-state-persist-intent", () =>
|
|
280
|
+
{
|
|
281
|
+
intentEntered.Set();
|
|
282
|
+
releaseIntent.Wait();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
var publicationTask = Task.Run<Exception>(() =>
|
|
286
|
+
{
|
|
287
|
+
try
|
|
288
|
+
{
|
|
289
|
+
bool created;
|
|
290
|
+
Enqueue(request, raw, grant.Token, deadline, out created);
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
catch (Exception exception) { return exception; }
|
|
294
|
+
});
|
|
295
|
+
Task<UnityCliBridge.BridgeCommandException> duplicateTask = null;
|
|
296
|
+
try
|
|
297
|
+
{
|
|
298
|
+
Assert.That(tombstoneEntered.Wait(5000), Is.True);
|
|
299
|
+
Assert.That(tombstoneTrackedUnderLock, Is.True);
|
|
300
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(1));
|
|
301
|
+
state.TestHooks.ExpectOnce("before-queue-lock", () => duplicateBeforeLock.Set());
|
|
302
|
+
state.TestHooks.ExpectOnce("before-existing-lookup", () => duplicateEntered.Set());
|
|
303
|
+
duplicateTask = Task.Run<UnityCliBridge.BridgeCommandException>(() =>
|
|
304
|
+
{
|
|
305
|
+
try
|
|
306
|
+
{
|
|
307
|
+
bool created;
|
|
308
|
+
Enqueue(request, raw, grant.Token, deadline, out created);
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
catch (UnityCliBridge.BridgeCommandException exception) { return exception; }
|
|
312
|
+
});
|
|
313
|
+
Assert.That(duplicateBeforeLock.Wait(5000), Is.True);
|
|
314
|
+
Assert.That(duplicateEntered.IsSet, Is.False, "duplicate must remain behind QueueLock until the tombstone is visible");
|
|
315
|
+
releaseTombstone.Set();
|
|
316
|
+
Assert.That(intentEntered.Wait(5000), Is.True);
|
|
317
|
+
Assert.That(duplicateEntered.Wait(5000), Is.True);
|
|
318
|
+
Assert.That(duplicateTask.Wait(5000), Is.True);
|
|
319
|
+
Assert.That(duplicateTask.Result, Is.Not.Null);
|
|
320
|
+
Assert.That(duplicateTask.Result.Code, Is.EqualTo("E_EXECUTION_TIMEOUT"));
|
|
321
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(1));
|
|
322
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
323
|
+
Assert.That(state.WorkQueue, Is.Empty);
|
|
324
|
+
}
|
|
325
|
+
finally
|
|
326
|
+
{
|
|
327
|
+
releaseTombstone.Set();
|
|
328
|
+
releaseIntent.Set();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
Assert.That(publicationTask.Wait(5000), Is.True);
|
|
332
|
+
Assert.That(publicationTask.Result, Is.TypeOf<InvalidOperationException>());
|
|
333
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
334
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
335
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
336
|
+
|
|
337
|
+
var successorGrant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
338
|
+
Assert.That(successorGrant.Outcome, Is.EqualTo(UnityCliAdmissionPool.AcquireOutcome.Granted));
|
|
339
|
+
var successor = Enqueue(request, raw, successorGrant.Token, deadline, out var successorCreated);
|
|
340
|
+
Assert.That(successorCreated, Is.True);
|
|
341
|
+
Assert.That(successor, Is.Not.Null);
|
|
342
|
+
UnityCliBridge.ShutdownQueuedWorkForTests(state);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
[Test]
|
|
346
|
+
public void RecoveryBlockedAuthorizationRevokesItsExactGrantBeforeQueueRejection()
|
|
347
|
+
{
|
|
348
|
+
var request = Request("queue-recovery-blocked-authorization");
|
|
349
|
+
var raw = RequestBytes(request);
|
|
350
|
+
var deadline = Deadline(request);
|
|
351
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
352
|
+
Assert.That(grant.Outcome, Is.EqualTo(UnityCliAdmissionPool.AcquireOutcome.Granted));
|
|
353
|
+
pool.BlockRecovery();
|
|
354
|
+
|
|
355
|
+
var failure = Assert.Throws<UnityCliBridge.BridgeCommandException>(() =>
|
|
356
|
+
{
|
|
357
|
+
bool created;
|
|
358
|
+
Enqueue(request, raw, grant.Token, deadline, out created);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
Assert.That(failure.Code, Is.EqualTo("E_ADMISSION_IDENTITY_MISMATCH"));
|
|
362
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
363
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
364
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
365
|
+
pool.Reset();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
[Test]
|
|
369
|
+
public void FirstCompensationIntentWriteFailureRetriesWithoutLosingTheExactOwner()
|
|
370
|
+
{
|
|
371
|
+
var request = Request("queue-compensation-intent-first-write");
|
|
372
|
+
var raw = RequestBytes(request);
|
|
373
|
+
var deadline = Deadline(request);
|
|
374
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
375
|
+
state.TestHooks.ExpectOnce("before-queue-publication", () => { throw new InvalidOperationException("injected publication fault"); });
|
|
376
|
+
state.TestHooks.ExpectOnce("before-state-persist-intent", () => { throw new IOException("injected first intent write fault"); });
|
|
377
|
+
|
|
378
|
+
var failure = Assert.Throws<UnityCliBridge.BridgeCommandException>(() =>
|
|
379
|
+
{
|
|
380
|
+
bool created;
|
|
381
|
+
Enqueue(request, raw, grant.Token, deadline, out created);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
Assert.That(failure.Code, Is.EqualTo("E_EXECUTION_TIMEOUT"));
|
|
385
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(1));
|
|
386
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(1));
|
|
387
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
388
|
+
var logsAfterFailure = state.Sinks.LogCount;
|
|
389
|
+
Assert.Throws<UnityCliBridge.BridgeCommandException>(() =>
|
|
390
|
+
{
|
|
391
|
+
bool created;
|
|
392
|
+
Enqueue(request, raw, grant.Token, deadline, out created);
|
|
393
|
+
});
|
|
394
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(1));
|
|
395
|
+
|
|
396
|
+
monotonicNowMs += 99L;
|
|
397
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
398
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(1));
|
|
399
|
+
Assert.That(state.Sinks.LogCount, Is.EqualTo(logsAfterFailure));
|
|
400
|
+
monotonicNowMs += 1L;
|
|
401
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
402
|
+
|
|
403
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
404
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
405
|
+
Assert.That(state.Sinks.TransactionCount, Is.EqualTo(0));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
[Test]
|
|
409
|
+
public void CompensationRetryDoesNotDeletePublishedSuccessorOwner()
|
|
410
|
+
{
|
|
411
|
+
var request = Request("queue-compensation-successor-owner");
|
|
412
|
+
var raw = RequestBytes(request);
|
|
413
|
+
var deadline = Deadline(request);
|
|
414
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
415
|
+
state.TestHooks.ExpectOnce("before-queue-publication", () => { throw new InvalidOperationException("injected publication fault"); });
|
|
416
|
+
state.TestHooks.ExpectOnce("before-admission-compensation-persist", () => { throw new IOException("injected pool persistence fault"); });
|
|
417
|
+
|
|
418
|
+
var failure = Assert.Throws<UnityCliBridge.BridgeCommandException>(() =>
|
|
419
|
+
{
|
|
420
|
+
bool created;
|
|
421
|
+
Enqueue(request, raw, grant.Token, deadline, out created);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
Assert.That(failure.Code, Is.EqualTo("E_EXECUTION_TIMEOUT"));
|
|
425
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
426
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(1));
|
|
427
|
+
var successorRequest = Request(request.RequestId);
|
|
428
|
+
successorRequest.Input = new Dictionary<string, object> { { "successor", true } };
|
|
429
|
+
var successorRaw = RequestBytes(successorRequest);
|
|
430
|
+
var successorGrant = pool.Acquire(successorRequest.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
431
|
+
Assert.That(successorGrant.Outcome, Is.EqualTo(UnityCliAdmissionPool.AcquireOutcome.Granted));
|
|
432
|
+
var successor = Enqueue(successorRequest, successorRaw, successorGrant.Token, Deadline(successorRequest), out var successorCreated);
|
|
433
|
+
Assert.That(successorCreated, Is.True);
|
|
434
|
+
|
|
435
|
+
monotonicNowMs += 100L;
|
|
436
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
437
|
+
|
|
438
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(1));
|
|
439
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(1));
|
|
440
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(1));
|
|
441
|
+
Assert.That(UnityCliBridge.PreviewQueuedStartRequestIdsForTests(state), Is.EqualTo(new[] { successorRequest.RequestId }));
|
|
442
|
+
Assert.That(UnityCliBridge.CompleteWorkItemForTests(successor, Success(successorRequest.RequestId)), Is.True);
|
|
443
|
+
monotonicNowMs += 100L;
|
|
444
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
445
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
446
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
[Test]
|
|
450
|
+
public void CompletionIntentWaitsForAttachedWorkItemBeforeSettlingOwner()
|
|
451
|
+
{
|
|
452
|
+
var request = Request("queue-completion-intent-attached-work");
|
|
453
|
+
var raw = RequestBytes(request);
|
|
454
|
+
var deadline = Deadline(request);
|
|
455
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
456
|
+
Assert.That(grant.Outcome, Is.EqualTo(UnityCliAdmissionPool.AcquireOutcome.Granted));
|
|
457
|
+
var item = Enqueue(request, raw, grant.Token, deadline, out var created);
|
|
458
|
+
Assert.That(created, Is.True);
|
|
459
|
+
Assert.That(item.AdmissionOwner, Is.Not.Null);
|
|
460
|
+
Assert.That(state.PersistIntent(item.AdmissionOwner), Is.True);
|
|
461
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(1));
|
|
462
|
+
|
|
463
|
+
monotonicNowMs += 100L;
|
|
464
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
465
|
+
|
|
466
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(1));
|
|
467
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(1));
|
|
468
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(1));
|
|
469
|
+
Assert.That(item.Completed.IsSet, Is.False);
|
|
470
|
+
|
|
471
|
+
Assert.That(UnityCliBridge.CompleteWorkItemForTests(item, Success(request.RequestId)), Is.True);
|
|
472
|
+
Assert.That(item.Completed.IsSet, Is.True);
|
|
473
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
474
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
475
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
476
|
+
|
|
477
|
+
monotonicNowMs += 100L;
|
|
478
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
479
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
[Test]
|
|
483
|
+
public void CompletionBoundaryFaultLogsAndPersistsOutsideQueueLock()
|
|
484
|
+
{
|
|
485
|
+
var request = Request("queue-completion-fault");
|
|
486
|
+
var raw = RequestBytes(request);
|
|
487
|
+
var deadline = Deadline(request);
|
|
488
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
489
|
+
bool created;
|
|
490
|
+
var item = Enqueue(request, raw, grant.Token, deadline, out created);
|
|
491
|
+
var faultObservedInside = false;
|
|
492
|
+
var persistOutside = false;
|
|
493
|
+
var logOutside = false;
|
|
494
|
+
state.TestHooks.ExpectOnce("before-completion-memory", () =>
|
|
495
|
+
{
|
|
496
|
+
faultObservedInside = UnityCliBridge.QueueLockHeldForTests(state);
|
|
497
|
+
throw new InvalidOperationException("injected completion boundary fault");
|
|
498
|
+
});
|
|
499
|
+
state.TestHooks.ExpectOnce("before-queue-boundary-fault-log", () => logOutside = !UnityCliBridge.QueueLockHeldForTests(state));
|
|
500
|
+
|
|
501
|
+
Assert.That(UnityCliBridge.CompleteWorkItemForTests(item, Success(request.RequestId)), Is.False);
|
|
502
|
+
|
|
503
|
+
Assert.That(faultObservedInside, Is.True);
|
|
504
|
+
Assert.That(logOutside, Is.True);
|
|
505
|
+
Assert.That(item.Completed.IsSet, Is.False);
|
|
506
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(1));
|
|
507
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(1));
|
|
508
|
+
|
|
509
|
+
state.TestHooks.ExpectOnce("before-admission-persist", () => persistOutside = !UnityCliBridge.QueueLockHeldForTests(state));
|
|
510
|
+
monotonicNowMs += 100L;
|
|
511
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
512
|
+
|
|
513
|
+
Assert.That(persistOutside, Is.True);
|
|
514
|
+
Assert.That(item.Completed.IsSet, Is.True);
|
|
515
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
516
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
[TestCase("persist")]
|
|
520
|
+
[TestCase("marker-clear")]
|
|
521
|
+
public void DurableSettlementFailureRetainsMapAndSignalUntilRetry(string fault)
|
|
522
|
+
{
|
|
523
|
+
var request = Request("queue-durable-" + fault);
|
|
524
|
+
var raw = RequestBytes(request);
|
|
525
|
+
var deadline = Deadline(request);
|
|
526
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
527
|
+
bool created;
|
|
528
|
+
var item = Enqueue(request, raw, grant.Token, deadline, out created);
|
|
529
|
+
Assert.That(created, Is.True);
|
|
530
|
+
state.TestHooks.ExpectOnce(fault == "persist" ? "before-state-persist-pool" : "before-state-clear-markers",
|
|
531
|
+
() => { throw new IOException("injected durable settlement fault"); });
|
|
532
|
+
|
|
533
|
+
Assert.That(UnityCliBridge.CompleteWorkItemForTests(item, Success(request.RequestId)), Is.False);
|
|
534
|
+
|
|
535
|
+
Assert.That(item.Completed.IsSet, Is.False);
|
|
536
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(1));
|
|
537
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
538
|
+
Assert.That(state.Sinks.TransactionCount, Is.EqualTo(0));
|
|
539
|
+
var ioBeforeAttach = state.IoCallbackCount;
|
|
540
|
+
bool duplicateCreated;
|
|
541
|
+
var duplicate = Enqueue(request, raw, grant.Token, deadline, out duplicateCreated);
|
|
542
|
+
Assert.That(state.IoCallbackCount, Is.EqualTo(ioBeforeAttach), "completion-pending attach must not invoke state I/O while QueueLock is held");
|
|
543
|
+
Assert.That(duplicate, Is.SameAs(item));
|
|
544
|
+
Assert.That(duplicateCreated, Is.False);
|
|
545
|
+
Assert.That(item.Completed.IsSet, Is.False);
|
|
546
|
+
|
|
547
|
+
monotonicNowMs += 100L;
|
|
548
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
549
|
+
|
|
550
|
+
Assert.That(item.Completed.IsSet, Is.True);
|
|
551
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
552
|
+
Assert.That(state.Sinks.TransactionCount, Is.EqualTo(1));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
[Test]
|
|
556
|
+
public void CompletionIntentWithoutWorkItemRetriesOnlineAtFixedBackoff()
|
|
557
|
+
{
|
|
558
|
+
var request = Request("queue-intent-online");
|
|
559
|
+
var raw = RequestBytes(request);
|
|
560
|
+
var digest = UnityCliRequestDigest.Compute(raw, request.InstanceId);
|
|
561
|
+
var deadline = Deadline(request);
|
|
562
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
563
|
+
var authorized = pool.AuthorizeAndCapture(request.RequestId, fingerprint, digest, grant.Token, state.ListenerId, state.ListenerGeneration, deadline);
|
|
564
|
+
Assert.That(authorized.Outcome, Is.EqualTo(UnityCliAdmissionPool.AuthorizeOutcome.Authorized));
|
|
565
|
+
var owner = UnityCliBridge.CaptureAdmissionOwner(state, request.RequestId, digest);
|
|
566
|
+
Assert.That(state.PersistIntent(owner), Is.True);
|
|
567
|
+
Assert.That(pool.CompleteTrusted(authorized.Identity), Is.EqualTo(UnityCliAdmissionPool.CompletionOutcome.ExactCurrentCompleted));
|
|
568
|
+
state.TestHooks.ExpectOnce("before-state-clear-intent", () => { throw new IOException("injected intent cleanup fault"); });
|
|
569
|
+
|
|
570
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
571
|
+
|
|
572
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(1));
|
|
573
|
+
var logsAfterFailure = state.Sinks.LogCount;
|
|
574
|
+
monotonicNowMs += 99L;
|
|
575
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
576
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(1));
|
|
577
|
+
Assert.That(state.Sinks.LogCount, Is.EqualTo(logsAfterFailure));
|
|
578
|
+
monotonicNowMs += 1L;
|
|
579
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
580
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
581
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
[Test]
|
|
585
|
+
public void QueuedShutdownUsesCapturedPoolAndSignalsTerminalExactlyOnce()
|
|
586
|
+
{
|
|
587
|
+
var request = Request("queue-shutdown-owner");
|
|
588
|
+
var raw = RequestBytes(request);
|
|
589
|
+
var deadline = Deadline(request);
|
|
590
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
591
|
+
bool created;
|
|
592
|
+
var item = Enqueue(request, raw, grant.Token, deadline, out created);
|
|
593
|
+
Assert.That(created, Is.True);
|
|
594
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(1));
|
|
595
|
+
pool.BlockRecovery();
|
|
596
|
+
|
|
597
|
+
UnityCliBridge.ShutdownQueuedWorkForTests(state);
|
|
598
|
+
|
|
599
|
+
Assert.That(item.Completed.IsSet, Is.True);
|
|
600
|
+
Assert.That(item.Response, Does.Contain("E_UNITY_QUITTING"));
|
|
601
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
602
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
603
|
+
Assert.That(state.PendingSettlementWork, Is.Empty);
|
|
604
|
+
Assert.That(state.Sinks.TransactionCount, Is.EqualTo(1));
|
|
605
|
+
Assert.That(UnityCliBridge.CompleteWorkItemForTests(item, Success(request.RequestId)), Is.False);
|
|
606
|
+
Assert.That(state.Sinks.TransactionCount, Is.EqualTo(1));
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
[TestCase("internal.test.run.cancel.plan", "test-run", "expectedEditorSessionId")]
|
|
610
|
+
[TestCase("internal.test.run.cancel", "test-run", "expectedEditorSessionId")]
|
|
611
|
+
[TestCase("internal.package.files.claim", "package-resolve", "rootRequestId")]
|
|
612
|
+
[TestCase("internal.package.files.commit", "package-resolve", "rootRequestId")]
|
|
613
|
+
[TestCase("internal.package.files.prove", "package-resolve", "rootRequestId")]
|
|
614
|
+
[TestCase("internal.package.files.fail", "package-resolve", "rootRequestId")]
|
|
615
|
+
[TestCase("internal.navmesh.bake.cancel", "navmesh-bake", "operationId")]
|
|
616
|
+
[TestCase("internal.occlusion.bake.cancel", "occlusion-bake", "operationId")]
|
|
617
|
+
public void DurableControlLaneStartsOnlyExactVerifiedParent(string command, string scope, string selectorKey)
|
|
618
|
+
{
|
|
619
|
+
var parentRequestId = "parent-" + Guid.NewGuid().ToString("N");
|
|
620
|
+
var parentDigest = UnityCliRequestDigest.Compute(Encoding.UTF8.GetBytes(parentRequestId), "test-instance");
|
|
621
|
+
var operationId = "operation-" + Guid.NewGuid().ToString("N");
|
|
622
|
+
var root = new Dictionary<string, object>
|
|
623
|
+
{
|
|
624
|
+
{ "requestId", parentRequestId }, { "requestDigest", parentDigest },
|
|
625
|
+
{ "editorSessionId", UnityCliBridge.EditorSessionId() }, { "operationId", operationId }
|
|
626
|
+
};
|
|
627
|
+
if (scope == "test-run") root["testOperation"] = new Dictionary<string, object>
|
|
628
|
+
{
|
|
629
|
+
{ "phase", "running" }, { "runId", Guid.NewGuid().ToString() }
|
|
630
|
+
};
|
|
631
|
+
state.SaveOperation(scope, root);
|
|
632
|
+
Assert.That(state.TryAcquireLease(parentRequestId, parentDigest, out var acquired), Is.True);
|
|
633
|
+
Assert.That(acquired, Is.True);
|
|
634
|
+
var exactValue = selectorKey == "expectedEditorSessionId" ? UnityCliBridge.EditorSessionId()
|
|
635
|
+
: selectorKey == "rootRequestId" ? parentRequestId : operationId;
|
|
636
|
+
var rejected = Request("control-rejected-" + command.Replace('.', '-'));
|
|
637
|
+
rejected.Command = command;
|
|
638
|
+
rejected.Input = new Dictionary<string, object> { { selectorKey, "mismatch" } };
|
|
639
|
+
var exact = Request("control-exact-" + command.Replace('.', '-'));
|
|
640
|
+
exact.Command = command;
|
|
641
|
+
exact.Input = new Dictionary<string, object> { { selectorKey, exactValue } };
|
|
642
|
+
bool created;
|
|
643
|
+
UnityCliBridge.BridgeWorkItem rejectedItem;
|
|
644
|
+
if (command == "internal.test.run.cancel")
|
|
645
|
+
{
|
|
646
|
+
var rejectedRaw = RequestBytes(rejected);
|
|
647
|
+
var rejectedGrant = pool.Acquire(rejected.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
648
|
+
rejectedItem = Enqueue(rejected, rejectedRaw, rejectedGrant.Token, Deadline(rejected), out created);
|
|
649
|
+
Assert.That(created, Is.True);
|
|
650
|
+
var exactRaw = RequestBytes(exact);
|
|
651
|
+
var exactGrant = pool.Acquire(exact.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
652
|
+
Enqueue(exact, exactRaw, exactGrant.Token, Deadline(exact), out created);
|
|
653
|
+
Assert.That(created, Is.True);
|
|
654
|
+
}
|
|
655
|
+
else
|
|
656
|
+
{
|
|
657
|
+
rejectedItem = EnqueueBypass(rejected, out created);
|
|
658
|
+
Assert.That(created, Is.True);
|
|
659
|
+
EnqueueBypass(exact, out created);
|
|
660
|
+
Assert.That(created, Is.True);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
var starts = UnityCliBridge.PreviewQueuedStartRequestIdsForTests(state);
|
|
664
|
+
var rejectedIds = UnityCliBridge.RejectTerminalControlWorkForTests(state);
|
|
665
|
+
|
|
666
|
+
Assert.That(starts, Is.EqualTo(new[] { exact.RequestId }));
|
|
667
|
+
Assert.That(rejectedIds, Is.EqualTo(new[] { rejected.RequestId }));
|
|
668
|
+
Assert.That(rejectedItem.Completed.IsSet, Is.True);
|
|
669
|
+
Assert.That(rejectedItem.NoEffectRejected, Is.True);
|
|
670
|
+
Assert.That(rejectedItem.Response, Does.Contain("E_ADMISSION_IDENTITY_MISMATCH"));
|
|
671
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(command == "internal.test.run.cancel" ? 1 : 0));
|
|
672
|
+
Assert.That(state.PendingSettlementWork, Is.Empty);
|
|
673
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
674
|
+
Assert.That(UnityCliBridge.PreviewQueuedStartRequestIdsForTests(state), Is.EqualTo(new[] { exact.RequestId }));
|
|
675
|
+
Assert.That(state.LastControlNodesExamined, Is.EqualTo(1));
|
|
676
|
+
Assert.That(state.LastFifoNodesExamined, Is.EqualTo(1));
|
|
677
|
+
Assert.That(state.LastQueueNodesExamined, Is.EqualTo(2));
|
|
678
|
+
UnityCliBridge.ShutdownQueuedWorkForTests(state);
|
|
679
|
+
state.ClearOperation(scope);
|
|
680
|
+
state.ReleaseLease(parentRequestId, parentDigest);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
[Test]
|
|
684
|
+
public void RejectedTestCancelIdentityMismatchUsesExactNoEffectSettlement()
|
|
685
|
+
{
|
|
686
|
+
var parentRequestId = "parent-no-effect-" + Guid.NewGuid().ToString("N");
|
|
687
|
+
var parentDigest = UnityCliRequestDigest.Compute(Encoding.UTF8.GetBytes(parentRequestId), "test-instance");
|
|
688
|
+
var root = new Dictionary<string, object>
|
|
689
|
+
{
|
|
690
|
+
{ "requestId", parentRequestId }, { "requestDigest", parentDigest },
|
|
691
|
+
{ "editorSessionId", UnityCliBridge.EditorSessionId() },
|
|
692
|
+
{ "testOperation", new Dictionary<string, object>
|
|
693
|
+
{
|
|
694
|
+
{ "phase", "running" }, { "runId", Guid.NewGuid().ToString() }
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
state.SaveOperation("test-run", root);
|
|
699
|
+
Assert.That(state.TryAcquireLease(parentRequestId, parentDigest, out var acquired), Is.True);
|
|
700
|
+
Assert.That(acquired, Is.True);
|
|
701
|
+
var rejected = Request("control-no-effect-mismatch");
|
|
702
|
+
rejected.Command = "internal.test.run.cancel";
|
|
703
|
+
rejected.Input = new Dictionary<string, object> { { "expectedEditorSessionId", "mismatch" } };
|
|
704
|
+
var raw = RequestBytes(rejected);
|
|
705
|
+
var grant = pool.Acquire(rejected.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
706
|
+
Assert.That(grant.Outcome, Is.EqualTo(UnityCliAdmissionPool.AcquireOutcome.Granted));
|
|
707
|
+
bool created;
|
|
708
|
+
var item = Enqueue(rejected, raw, grant.Token, Deadline(rejected), out created);
|
|
709
|
+
Assert.That(created, Is.True);
|
|
710
|
+
var settlementOrder = new List<string>();
|
|
711
|
+
state.TestHooks.ExpectOnce("before-state-persist-intent", () => settlementOrder.Add("intent"));
|
|
712
|
+
state.TestHooks.ExpectOnce("before-completion-memory", () =>
|
|
713
|
+
{
|
|
714
|
+
Assert.That(state.HasIntentInMemory(item.AdmissionOwner), Is.True);
|
|
715
|
+
Assert.That(pool.TryGetConsumedIdentity(rejected.RequestId, out var exactOwner), Is.True);
|
|
716
|
+
Assert.That(exactOwner.OwnerState, Is.EqualTo(UnityCliAdmissionPool.AdmissionOwnerState.WorkItem));
|
|
717
|
+
settlementOrder.Add("complete");
|
|
718
|
+
});
|
|
719
|
+
state.TestHooks.ExpectOnce("before-admission-persist", () =>
|
|
720
|
+
{
|
|
721
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
722
|
+
settlementOrder.Add("persist");
|
|
723
|
+
});
|
|
724
|
+
state.TestHooks.ExpectOnce("before-state-clear-markers", () =>
|
|
725
|
+
{
|
|
726
|
+
Assert.That(state.HasIntentInMemory(item.AdmissionOwner), Is.True);
|
|
727
|
+
settlementOrder.Add("markers");
|
|
728
|
+
});
|
|
729
|
+
state.TestHooks.ExpectOnce("before-state-clear-intent", () =>
|
|
730
|
+
{
|
|
731
|
+
Assert.That(state.HasIntentInMemory(item.AdmissionOwner), Is.True);
|
|
732
|
+
settlementOrder.Add("intent-cleanup");
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
var rejectedIds = UnityCliBridge.RejectTerminalControlWorkForTests(state);
|
|
736
|
+
|
|
737
|
+
Assert.That(rejectedIds, Is.EqualTo(new[] { rejected.RequestId }));
|
|
738
|
+
Assert.That(settlementOrder, Is.EqualTo(new[] { "intent", "complete", "persist", "markers", "intent-cleanup" }));
|
|
739
|
+
Assert.That(item.NoEffectRejected, Is.True);
|
|
740
|
+
Assert.That(item.HasStarted, Is.False);
|
|
741
|
+
Assert.That(item.Completed.IsSet, Is.True);
|
|
742
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
743
|
+
Assert.That(state.PendingSettlementWork, Is.Empty);
|
|
744
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
745
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
746
|
+
Assert.That(state.OperationExists("test-cancel-admission-owner"), Is.False);
|
|
747
|
+
Assert.That(state.OperationExists("test-run"), Is.True);
|
|
748
|
+
Assert.That(state.TryIsLeaseOwned(parentRequestId, parentDigest, out var parentOwned), Is.True);
|
|
749
|
+
Assert.That(parentOwned, Is.True);
|
|
750
|
+
state.ClearOperation("test-run");
|
|
751
|
+
state.ReleaseLease(parentRequestId, parentDigest);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
[TestCase(UnityCliAdmissionPool.AdmissionOwnerState.Continuation, false)]
|
|
755
|
+
[TestCase(UnityCliAdmissionPool.AdmissionOwnerState.Recovery, false)]
|
|
756
|
+
[TestCase(UnityCliAdmissionPool.AdmissionOwnerState.TerminalCleanup, false)]
|
|
757
|
+
[TestCase(UnityCliAdmissionPool.AdmissionOwnerState.Coordinator, false)]
|
|
758
|
+
[TestCase(UnityCliAdmissionPool.AdmissionOwnerState.Continuation, true)]
|
|
759
|
+
public void RejectedOrExpiredAttachToExistingDurableOwnerOnlyCompletesItsLocalWorkItem(UnityCliAdmissionPool.AdmissionOwnerState durableState, bool expire)
|
|
760
|
+
{
|
|
761
|
+
var request = Request("attached-durable-no-effect-" + durableState.ToString().ToLowerInvariant());
|
|
762
|
+
request.Command = "internal.test.run.cancel";
|
|
763
|
+
request.Input = new Dictionary<string, object> { { "expectedEditorSessionId", "missing-parent" } };
|
|
764
|
+
var raw = RequestBytes(request);
|
|
765
|
+
var digest = UnityCliRequestDigest.Compute(raw, request.InstanceId);
|
|
766
|
+
var deadline = Deadline(request);
|
|
767
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
768
|
+
var authorized = pool.AuthorizeAndCapture(request.RequestId, fingerprint, digest, grant.Token,
|
|
769
|
+
state.ListenerId, state.ListenerGeneration, deadline);
|
|
770
|
+
Assert.That(authorized.Outcome, Is.EqualTo(UnityCliAdmissionPool.AuthorizeOutcome.Authorized));
|
|
771
|
+
var owner = authorized.Identity;
|
|
772
|
+
if (durableState == UnityCliAdmissionPool.AdmissionOwnerState.TerminalCleanup)
|
|
773
|
+
{
|
|
774
|
+
owner = Transfer(owner, UnityCliAdmissionPool.AdmissionOwnerState.Recovery);
|
|
775
|
+
owner = Transfer(owner, UnityCliAdmissionPool.AdmissionOwnerState.TerminalCleanup);
|
|
776
|
+
}
|
|
777
|
+
else owner = Transfer(owner, durableState);
|
|
778
|
+
if (expire) request.Execution.AbsoluteDeadlineUtc = DateTimeOffset.FromUnixTimeMilliseconds(0).ToString("O");
|
|
779
|
+
|
|
780
|
+
for (var attempt = 0; attempt < 2; attempt++)
|
|
781
|
+
{
|
|
782
|
+
bool created;
|
|
783
|
+
var item = Enqueue(request, raw, null, deadline, out created);
|
|
784
|
+
Assert.That(created, Is.True);
|
|
785
|
+
Assert.That(item.AdmissionNewlyConsumed, Is.False);
|
|
786
|
+
Assert.That(item.AdmissionOwnerStateAtPublication, Is.EqualTo(durableState));
|
|
787
|
+
Assert.That(item.AttachedToExistingDurableOwner, Is.True);
|
|
788
|
+
|
|
789
|
+
var rejected = UnityCliBridge.RejectTerminalControlWorkForTests(state);
|
|
790
|
+
|
|
791
|
+
Assert.That(rejected, Is.EqualTo(new[] { request.RequestId }));
|
|
792
|
+
Assert.That(item.Completed.IsSet, Is.True);
|
|
793
|
+
Assert.That(item.NoEffectRejected, Is.True);
|
|
794
|
+
Assert.That(item.Response, Does.Contain(expire ? "deadline elapsed" : "parent is no longer active"));
|
|
795
|
+
Assert.That(item.HasStarted, Is.False);
|
|
796
|
+
Assert.That(state.PendingSettlementWork, Is.Empty);
|
|
797
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
798
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
799
|
+
Assert.That(pool.TryGetConsumedIdentity(request.RequestId, out var retained), Is.True);
|
|
800
|
+
Assert.That(retained.OwnerState, Is.EqualTo(durableState));
|
|
801
|
+
}
|
|
802
|
+
Assert.That(state.Sinks.TransactionCount, Is.EqualTo(2));
|
|
803
|
+
pool.Reset();
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
[TestCase("internal.test.run.cancel")]
|
|
807
|
+
[TestCase("internal.package.files.claim")]
|
|
808
|
+
[TestCase("internal.package.files.commit")]
|
|
809
|
+
[TestCase("internal.package.files.prove")]
|
|
810
|
+
[TestCase("internal.package.files.fail")]
|
|
811
|
+
[TestCase("internal.navmesh.bake.cancel")]
|
|
812
|
+
[TestCase("internal.occlusion.bake.cancel")]
|
|
813
|
+
public void MissingOrExpiredControlReachesTerminalAndAllowsQueueProgress(string command)
|
|
814
|
+
{
|
|
815
|
+
var stale = Request("stale-control-" + command.Replace('.', '-'));
|
|
816
|
+
stale.Command = command;
|
|
817
|
+
stale.Input = new Dictionary<string, object>
|
|
818
|
+
{
|
|
819
|
+
{ command.StartsWith("internal.package.files", StringComparison.Ordinal) ? "rootRequestId"
|
|
820
|
+
: command.StartsWith("internal.test.run", StringComparison.Ordinal) ? "expectedEditorSessionId" : "operationId", "missing-parent" }
|
|
821
|
+
};
|
|
822
|
+
bool created;
|
|
823
|
+
UnityCliBridge.BridgeWorkItem staleItem;
|
|
824
|
+
if (command == "internal.test.run.cancel")
|
|
825
|
+
{
|
|
826
|
+
var raw = RequestBytes(stale);
|
|
827
|
+
var grant = pool.Acquire(stale.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
828
|
+
staleItem = Enqueue(stale, raw, grant.Token, Deadline(stale), out created);
|
|
829
|
+
}
|
|
830
|
+
else staleItem = EnqueueBypass(stale, out created);
|
|
831
|
+
Assert.That(created, Is.True);
|
|
832
|
+
var ordinary = Request("ordinary-after-stale-" + command.Replace('.', '-'));
|
|
833
|
+
ordinary.Command = "project-tool.list";
|
|
834
|
+
EnqueueBypass(ordinary, out created);
|
|
835
|
+
Assert.That(created, Is.True);
|
|
836
|
+
|
|
837
|
+
var rejected = UnityCliBridge.RejectTerminalControlWorkForTests(state);
|
|
838
|
+
|
|
839
|
+
Assert.That(rejected, Is.EqualTo(new[] { stale.RequestId }));
|
|
840
|
+
Assert.That(staleItem.Completed.IsSet, Is.True);
|
|
841
|
+
Assert.That(staleItem.NoEffectRejected, Is.True);
|
|
842
|
+
Assert.That(staleItem.HasStarted, Is.False);
|
|
843
|
+
Assert.That(staleItem.Response, Does.Contain("E_EXECUTION_TIMEOUT"));
|
|
844
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
845
|
+
Assert.That(state.PendingSettlementWork, Is.Empty);
|
|
846
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
847
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(1));
|
|
848
|
+
Assert.That(state.OperationExists("test-cancel-admission-owner"), Is.False);
|
|
849
|
+
Assert.That(UnityCliBridge.PreviewQueuedStartRequestIdsForTests(state), Is.EqualTo(new[] { ordinary.RequestId }));
|
|
850
|
+
UnityCliBridge.ShutdownQueuedWorkForTests(state);
|
|
851
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
[Test]
|
|
855
|
+
public void ExpiredTestCancelSettlesWithoutReadableParentOrPendingSlot()
|
|
856
|
+
{
|
|
857
|
+
var stale = Request("expired-test-cancel-unreadable");
|
|
858
|
+
stale.Command = "internal.test.run.cancel";
|
|
859
|
+
stale.Input = new Dictionary<string, object> { { "expectedEditorSessionId", UnityCliBridge.EditorSessionId() } };
|
|
860
|
+
var raw = RequestBytes(stale);
|
|
861
|
+
var deadline = Deadline(stale);
|
|
862
|
+
var grant = pool.Acquire(stale.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
863
|
+
Assert.That(grant.Outcome, Is.EqualTo(UnityCliAdmissionPool.AcquireOutcome.Granted));
|
|
864
|
+
bool created;
|
|
865
|
+
var item = Enqueue(stale, raw, grant.Token, deadline, out created);
|
|
866
|
+
Assert.That(created, Is.True);
|
|
867
|
+
// publication后只推进测试持有的request时钟视图,确定性模拟排队期间deadline流逝。
|
|
868
|
+
stale.Execution.AbsoluteDeadlineUtc = DateTimeOffset.FromUnixTimeMilliseconds(0).ToString("O");
|
|
869
|
+
state.TestHooks.ExpectOnce("before-operation-load:test-run", () => { throw new IOException("injected unreadable parent"); });
|
|
870
|
+
|
|
871
|
+
var rejected = UnityCliBridge.RejectTerminalControlWorkForTests(state);
|
|
872
|
+
|
|
873
|
+
Assert.That(rejected, Is.EqualTo(new[] { stale.RequestId }));
|
|
874
|
+
Assert.That(item.Response, Does.Contain("deadline elapsed"));
|
|
875
|
+
Assert.That(item.NoEffectRejected, Is.True);
|
|
876
|
+
Assert.That(item.HasStarted, Is.False);
|
|
877
|
+
Assert.That(item.Completed.IsSet, Is.True);
|
|
878
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
879
|
+
Assert.That(state.PendingSettlementWork, Is.Empty);
|
|
880
|
+
Assert.That(state.PendingCompletionIntentCount, Is.EqualTo(0));
|
|
881
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
882
|
+
Assert.That(state.OperationExists("test-cancel-admission-owner"), Is.False);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
[Test]
|
|
886
|
+
public void ExpiredControlTerminatesEvenWhenItsParentStateIsUnreadable()
|
|
887
|
+
{
|
|
888
|
+
var stale = Request("expired-control-unreadable");
|
|
889
|
+
stale.Command = "internal.navmesh.bake.cancel";
|
|
890
|
+
stale.Input = new Dictionary<string, object> { { "operationId", "expired-operation" } };
|
|
891
|
+
stale.Execution.AbsoluteDeadlineUtc = DateTimeOffset.FromUnixTimeMilliseconds(0).ToString("O");
|
|
892
|
+
bool created;
|
|
893
|
+
var staleItem = EnqueueBypass(stale, out created);
|
|
894
|
+
Assert.That(created, Is.True);
|
|
895
|
+
state.TestHooks.ExpectOnce("before-operation-load:navmesh-bake", () => { throw new IOException("injected unreadable parent"); });
|
|
896
|
+
|
|
897
|
+
var rejected = UnityCliBridge.RejectTerminalControlWorkForTests(state);
|
|
898
|
+
|
|
899
|
+
Assert.That(rejected, Is.EqualTo(new[] { stale.RequestId }));
|
|
900
|
+
Assert.That(staleItem.Completed.IsSet, Is.True);
|
|
901
|
+
Assert.That(staleItem.NoEffectRejected, Is.True);
|
|
902
|
+
Assert.That(staleItem.Response, Does.Contain("deadline elapsed"));
|
|
903
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(0));
|
|
904
|
+
Assert.That(state.PendingSettlementWork, Is.Empty);
|
|
905
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
[Test]
|
|
909
|
+
public void ProductionQueueTraversalCountsActualLinkedListNodesWithinHardBound()
|
|
910
|
+
{
|
|
911
|
+
for (var index = 0; index < UnityCliBridgeQueuePolicy.MaxFifoNodesExaminedPerTick + 1; index++)
|
|
912
|
+
{
|
|
913
|
+
var request = Request("actual-queue-scan-" + index.ToString());
|
|
914
|
+
request.Command = "project-tool.list";
|
|
915
|
+
var raw = RequestBytes(request);
|
|
916
|
+
var item = new UnityCliBridge.BridgeWorkItem(state, request, raw,
|
|
917
|
+
UnityCliRequestDigest.Compute(raw, request.InstanceId), state.ListenerId, state.ListenerGeneration,
|
|
918
|
+
null, UnityCliBridgeQueuePolicy.WorkClass.OrdinaryRead, true, false);
|
|
919
|
+
lock (state.QueueLock)
|
|
920
|
+
{
|
|
921
|
+
state.ActiveRequestWork.Add(request.RequestId, item);
|
|
922
|
+
item.QueueNode = state.WorkQueue.AddLast(item);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
var starts = UnityCliBridge.PreviewQueuedStartRequestIdsForTests(state);
|
|
927
|
+
|
|
928
|
+
Assert.That(state.LastSafeStatusNodesExamined, Is.EqualTo(0));
|
|
929
|
+
Assert.That(state.LastControlNodesExamined, Is.EqualTo(0));
|
|
930
|
+
Assert.That(state.LastFifoNodesExamined, Is.EqualTo(UnityCliBridgeQueuePolicy.MaxFifoNodesExaminedPerTick));
|
|
931
|
+
Assert.That(state.LastQueueNodesExamined, Is.EqualTo(UnityCliBridgeQueuePolicy.MaxFifoNodesExaminedPerTick));
|
|
932
|
+
Assert.That(starts.Length, Is.EqualTo(UnityCliBridgeQueuePolicy.MaxStartsPerTick));
|
|
933
|
+
Assert.That(starts, Does.Not.Contain("actual-queue-scan-128"));
|
|
934
|
+
UnityCliBridge.ShutdownQueuedWorkForTests(state);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
[Test]
|
|
938
|
+
public void SafeStatusBeyondBlockedPrefixStartsWithinBoundAndIndexSurvivesAttachAndClose()
|
|
939
|
+
{
|
|
940
|
+
PublishQueueItem("blocked-mutation", "project-tool.run", UnityCliBridgeQueuePolicy.WorkClass.Mutation);
|
|
941
|
+
for (var index = 0; index < 127; index++)
|
|
942
|
+
PublishQueueItem("pending-control-" + index.ToString(), "internal.navmesh.bake.cancel", UnityCliBridgeQueuePolicy.WorkClass.Control);
|
|
943
|
+
var safeRequest = Request("safe-status-129");
|
|
944
|
+
safeRequest.Command = "internal.editor.compile.status";
|
|
945
|
+
var safeRaw = RequestBytes(safeRequest);
|
|
946
|
+
var safeGrant = pool.Acquire(safeRequest.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
947
|
+
var safe = Enqueue(safeRequest, safeRaw, safeGrant.Token, Deadline(safeRequest), out var safeCreated);
|
|
948
|
+
Assert.That(safeCreated, Is.True);
|
|
949
|
+
|
|
950
|
+
var firstTick = UnityCliBridge.TakeQueuedStartsForTests(state, UnityCliBridgeQueuePolicy.DurableMutationBarrier.Unreadable);
|
|
951
|
+
|
|
952
|
+
Assert.That(firstTick.Select(item => item.Request.RequestId), Is.EqualTo(new[] { safe.Request.RequestId }));
|
|
953
|
+
Assert.That(state.LastSafeStatusNodesExamined, Is.EqualTo(1));
|
|
954
|
+
Assert.That(state.LastControlNodesExamined, Is.EqualTo(127));
|
|
955
|
+
Assert.That(state.LastFifoNodesExamined, Is.EqualTo(UnityCliBridgeQueuePolicy.MaxFifoNodesExaminedPerTick));
|
|
956
|
+
Assert.That(state.LastQueueNodesExamined, Is.EqualTo(256));
|
|
957
|
+
Assert.That(state.SafeStatusQueue, Is.Empty);
|
|
958
|
+
Assert.That(UnityCliBridge.CompleteWorkItemForTests(safe, Success(safe.Request.RequestId)), Is.True);
|
|
959
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(128));
|
|
960
|
+
|
|
961
|
+
var attachedRequest = Request("safe-status-attach");
|
|
962
|
+
attachedRequest.Command = "internal.editor.compile.status";
|
|
963
|
+
var attachedRaw = RequestBytes(attachedRequest);
|
|
964
|
+
var attachedGrant = pool.Acquire(attachedRequest.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
965
|
+
var attachedSafe = Enqueue(attachedRequest, attachedRaw, attachedGrant.Token, Deadline(attachedRequest), out var attachedCreated);
|
|
966
|
+
Assert.That(attachedCreated, Is.True);
|
|
967
|
+
var duplicate = Enqueue(attachedRequest, attachedRaw, attachedGrant.Token, Deadline(attachedRequest), out var created);
|
|
968
|
+
Assert.That(created, Is.False);
|
|
969
|
+
Assert.That(duplicate, Is.SameAs(attachedSafe));
|
|
970
|
+
Assert.That(state.SafeStatusQueue.Count, Is.EqualTo(1));
|
|
971
|
+
|
|
972
|
+
UnityCliBridge.ShutdownQueuedWorkForTests(state);
|
|
973
|
+
|
|
974
|
+
Assert.That(state.SafeStatusQueue, Is.Empty);
|
|
975
|
+
Assert.That(state.ControlQueue, Is.Empty);
|
|
976
|
+
Assert.That(state.WorkQueue, Is.Empty);
|
|
977
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
[Test]
|
|
981
|
+
public void ContinuousSafeStatusCannotHideBoundaryAuthorizedControlFromItsIndependentBudget()
|
|
982
|
+
{
|
|
983
|
+
var parentRequestId = "control-budget-parent-" + Guid.NewGuid().ToString("N");
|
|
984
|
+
var parentDigest = UnityCliRequestDigest.Compute(Encoding.UTF8.GetBytes(parentRequestId), "test-instance");
|
|
985
|
+
var operationId = "control-budget-operation-" + Guid.NewGuid().ToString("N");
|
|
986
|
+
state.SaveOperation("navmesh-bake", new Dictionary<string, object>
|
|
987
|
+
{
|
|
988
|
+
{ "requestId", parentRequestId }, { "requestDigest", parentDigest },
|
|
989
|
+
{ "editorSessionId", UnityCliBridge.EditorSessionId() }, { "operationId", operationId }
|
|
990
|
+
});
|
|
991
|
+
Assert.That(state.TryAcquireLease(parentRequestId, parentDigest, out var acquired), Is.True);
|
|
992
|
+
Assert.That(acquired, Is.True);
|
|
993
|
+
for (var index = 0; index < UnityCliBridgeQueuePolicy.MaxSafeStatusNodesExaminedPerTick; index++)
|
|
994
|
+
PublishQueueItem("control-budget-safe-" + index.ToString(), "internal.editor.compile.status", UnityCliBridgeQueuePolicy.WorkClass.SafeStatus);
|
|
995
|
+
for (var index = 0; index < UnityCliBridgeQueuePolicy.MaxControlNodesExaminedPerTick - 1; index++)
|
|
996
|
+
PublishQueueItem("control-budget-pending-" + index.ToString(), "internal.navmesh.bake.cancel", UnityCliBridgeQueuePolicy.WorkClass.Control);
|
|
997
|
+
var boundary = PublishQueueItem("control-budget-boundary", "internal.navmesh.bake.cancel", UnityCliBridgeQueuePolicy.WorkClass.Control);
|
|
998
|
+
boundary.Request.Input = new Dictionary<string, object> { { "operationId", operationId } };
|
|
999
|
+
|
|
1000
|
+
var firstTick = UnityCliBridge.PreviewQueuedStartRequestIdsForTests(state);
|
|
1001
|
+
for (var index = 0; index < UnityCliBridgeQueuePolicy.MaxSafeStatusNodesExaminedPerTick; index++)
|
|
1002
|
+
PublishQueueItem("control-budget-refill-" + index.ToString(), "internal.editor.compile.status", UnityCliBridgeQueuePolicy.WorkClass.SafeStatus);
|
|
1003
|
+
var secondTick = UnityCliBridge.PreviewQueuedStartRequestIdsForTests(state);
|
|
1004
|
+
|
|
1005
|
+
Assert.That(firstTick, Does.Contain(boundary.Request.RequestId));
|
|
1006
|
+
Assert.That(secondTick, Does.Contain(boundary.Request.RequestId));
|
|
1007
|
+
Assert.That(state.LastSafeStatusNodesExamined,
|
|
1008
|
+
Is.EqualTo(UnityCliBridgeQueuePolicy.MaxSafeStatusNodesExaminedPerTick));
|
|
1009
|
+
Assert.That(state.LastControlNodesExamined,
|
|
1010
|
+
Is.EqualTo(UnityCliBridgeQueuePolicy.MaxControlNodesExaminedPerTick));
|
|
1011
|
+
Assert.That(state.LastFifoNodesExamined,
|
|
1012
|
+
Is.EqualTo(UnityCliBridgeQueuePolicy.MaxFifoNodesExaminedPerTick));
|
|
1013
|
+
Assert.That(state.LastQueueNodesExamined,
|
|
1014
|
+
Is.EqualTo(UnityCliBridgeQueuePolicy.MaxNodesExaminedPerTick));
|
|
1015
|
+
UnityCliBridge.ShutdownQueuedWorkForTests(state);
|
|
1016
|
+
state.ClearOperation("navmesh-bake");
|
|
1017
|
+
state.ReleaseLease(parentRequestId, parentDigest);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
[Test]
|
|
1021
|
+
public void SuccessorIdentityMismatchBlocksFrozenCompletionUntilSuccessorEnds()
|
|
1022
|
+
{
|
|
1023
|
+
var request = Request("queue-successor-mismatch");
|
|
1024
|
+
var raw = RequestBytes(request);
|
|
1025
|
+
var digest = UnityCliRequestDigest.Compute(raw, request.InstanceId);
|
|
1026
|
+
var deadline = Deadline(request);
|
|
1027
|
+
var grant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
1028
|
+
bool created;
|
|
1029
|
+
var item = Enqueue(request, raw, grant.Token, deadline, out created);
|
|
1030
|
+
Assert.That(created, Is.True);
|
|
1031
|
+
UnityCliAdmissionPool.ConsumedIdentity successor = null;
|
|
1032
|
+
state.TestHooks.ExpectOnce("before-completion-memory", () =>
|
|
1033
|
+
{
|
|
1034
|
+
UnityCliAdmissionPool.ConsumedIdentity current;
|
|
1035
|
+
Assert.That(pool.TryGetConsumedIdentity(request.RequestId, out current), Is.True);
|
|
1036
|
+
Assert.That(pool.CompleteTrusted(current), Is.EqualTo(UnityCliAdmissionPool.CompletionOutcome.ExactCurrentCompleted));
|
|
1037
|
+
var nextGrant = pool.Acquire(request.RequestId, fingerprint, state.ListenerId, state.ListenerGeneration);
|
|
1038
|
+
Assert.That(nextGrant.Outcome, Is.EqualTo(UnityCliAdmissionPool.AcquireOutcome.Granted));
|
|
1039
|
+
var authorized = pool.AuthorizeAndCapture(request.RequestId, fingerprint, digest, nextGrant.Token, state.ListenerId, state.ListenerGeneration, deadline);
|
|
1040
|
+
Assert.That(authorized.Outcome, Is.EqualTo(UnityCliAdmissionPool.AuthorizeOutcome.Authorized));
|
|
1041
|
+
successor = authorized.Identity;
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
Assert.That(UnityCliBridge.CompleteWorkItemForTests(item, Success(request.RequestId)), Is.False);
|
|
1045
|
+
|
|
1046
|
+
Assert.That(successor, Is.Not.Null);
|
|
1047
|
+
Assert.That(item.Completed.IsSet, Is.False);
|
|
1048
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(1));
|
|
1049
|
+
Assert.That(pool.Capture().Active, Is.EqualTo(1));
|
|
1050
|
+
Assert.That(pool.CompleteTrusted(successor), Is.EqualTo(UnityCliAdmissionPool.CompletionOutcome.ExactCurrentCompleted));
|
|
1051
|
+
monotonicNowMs += 100L;
|
|
1052
|
+
UnityCliBridge.RetryPendingWorkItemSettlementsForTests(state);
|
|
1053
|
+
|
|
1054
|
+
Assert.That(item.Completed.IsSet, Is.True);
|
|
1055
|
+
Assert.That(UnityCliBridge.ActiveRequestWorkCountForTests(state), Is.EqualTo(0));
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
[Test]
|
|
1059
|
+
public void WorkerClassificationUsesOnlyImmutableInput()
|
|
1060
|
+
{
|
|
1061
|
+
var requests = new[]
|
|
1062
|
+
{
|
|
1063
|
+
ProjectToolRequest("worker-rigidbody", "physics.rigidbody", new Dictionary<string, object> { { "target", "MissingTarget" } }),
|
|
1064
|
+
ProjectToolRequest("worker-reflection", "graphics.reflection-probe", new Dictionary<string, object> { { "target", "MissingTarget" }, { "create", true } }),
|
|
1065
|
+
ProjectToolRequest("worker-light-probes", "graphics.light-probe-group", new Dictionary<string, object> { { "target", "MissingTarget" }, { "create", true } }),
|
|
1066
|
+
ProjectToolRequest("worker-scene-read", "scene.active", new Dictionary<string, object>())
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
var task = Task.Run(() => requests.Select(UnityCliBridge.QueueRequestWorkClass).ToArray());
|
|
1070
|
+
|
|
1071
|
+
Assert.That(task.Wait(5000), Is.True);
|
|
1072
|
+
Assert.That(task.Result, Is.All.EqualTo(UnityCliBridgeQueuePolicy.WorkClass.Mutation));
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
[Test]
|
|
1076
|
+
public void ListenerGenerationNeverStartsAtZeroAndDoesNotFork()
|
|
1077
|
+
{
|
|
1078
|
+
Assert.That(UnityCliBridge.ComputeListenerGenerationForTests(0, 0), Is.EqualTo(1));
|
|
1079
|
+
Assert.That(UnityCliBridge.ComputeListenerGenerationForTests(0, 7), Is.EqualTo(8));
|
|
1080
|
+
Assert.That(UnityCliBridge.ComputeListenerGenerationForTests(9, 99), Is.EqualTo(9));
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
[Test]
|
|
1084
|
+
public void RegisteredInternalCommandsUseTheClosedProductionClassification()
|
|
1085
|
+
{
|
|
1086
|
+
var classes = UnityCliBridge.InternalQueueClassesForTests();
|
|
1087
|
+
foreach (var command in new[]
|
|
1088
|
+
{
|
|
1089
|
+
"internal.editor.compile.start", "internal.editor.refresh.start", "internal.asset.refresh.start",
|
|
1090
|
+
"internal.package.embed.start", "internal.package.resolve.start", "internal.test.run.start",
|
|
1091
|
+
"internal.navmesh.bake.start", "internal.navmesh.bake.plan",
|
|
1092
|
+
"internal.occlusion.bake.start", "internal.occlusion.bake.plan",
|
|
1093
|
+
"internal.build.run.start", "internal.instances.prune"
|
|
1094
|
+
}) AssertClass(classes, command, UnityCliBridgeQueuePolicy.WorkClass.Mutation);
|
|
1095
|
+
foreach (var command in new[]
|
|
1096
|
+
{
|
|
1097
|
+
"internal.test.run.cancel.plan", "internal.test.run.cancel", "internal.package.files.claim",
|
|
1098
|
+
"internal.package.files.commit", "internal.package.files.prove", "internal.package.files.fail",
|
|
1099
|
+
"internal.navmesh.bake.cancel", "internal.occlusion.bake.cancel"
|
|
1100
|
+
}) AssertClass(classes, command, UnityCliBridgeQueuePolicy.WorkClass.Control);
|
|
1101
|
+
foreach (var command in new[] { "internal.navmesh.bake.snapshot", "internal.occlusion.bake.snapshot" })
|
|
1102
|
+
AssertClass(classes, command, UnityCliBridgeQueuePolicy.WorkClass.OrdinaryRead);
|
|
1103
|
+
foreach (var command in new[]
|
|
1104
|
+
{
|
|
1105
|
+
"internal.editor.compile.status", "internal.editor.refresh.status", "internal.editor.state.snapshot",
|
|
1106
|
+
"internal.asset.refresh.status", "internal.package.embed.status", "internal.package.resolve.status",
|
|
1107
|
+
"internal.test.run.status", "internal.test.run.cancel.status", "internal.navmesh.bake.status",
|
|
1108
|
+
"internal.occlusion.bake.status"
|
|
1109
|
+
}) AssertClass(classes, command, UnityCliBridgeQueuePolicy.WorkClass.SafeStatus);
|
|
1110
|
+
|
|
1111
|
+
var unknown = Request("classification-unknown");
|
|
1112
|
+
unknown.Command = "internal.unknown.effect";
|
|
1113
|
+
Assert.That(UnityCliBridge.QueueRequestWorkClass(unknown), Is.EqualTo(UnityCliBridgeQueuePolicy.WorkClass.Mutation));
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
private static void AssertClass(IReadOnlyDictionary<string, UnityCliBridgeQueuePolicy.WorkClass> classes, string command, UnityCliBridgeQueuePolicy.WorkClass expected)
|
|
1117
|
+
{
|
|
1118
|
+
Assert.That(classes.ContainsKey(command), Is.True, command);
|
|
1119
|
+
Assert.That(classes[command], Is.EqualTo(expected), command);
|
|
1120
|
+
var request = Request("classification-" + command.Replace('.', '-'));
|
|
1121
|
+
request.Command = command;
|
|
1122
|
+
Assert.That(UnityCliBridge.QueueRequestWorkClass(request), Is.EqualTo(expected), command);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
private UnityCliAdmissionPool.ConsumedIdentity Transfer(UnityCliAdmissionPool.ConsumedIdentity owner, UnityCliAdmissionPool.AdmissionOwnerState target)
|
|
1126
|
+
{
|
|
1127
|
+
Assert.That(pool.PrepareOwnerTransfer(owner, target), Is.True);
|
|
1128
|
+
Assert.That(pool.CommitOwnerTransfer(owner, target, out var successor), Is.True);
|
|
1129
|
+
return successor;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
private UnityCliBridge.BridgeWorkItem PublishQueueItem(string requestId, string command, UnityCliBridgeQueuePolicy.WorkClass workClass)
|
|
1133
|
+
{
|
|
1134
|
+
var request = Request(requestId);
|
|
1135
|
+
request.Command = command;
|
|
1136
|
+
if (workClass == UnityCliBridgeQueuePolicy.WorkClass.Control)
|
|
1137
|
+
request.Input = new Dictionary<string, object> { { "operationId", requestId } };
|
|
1138
|
+
var raw = RequestBytes(request);
|
|
1139
|
+
var item = new UnityCliBridge.BridgeWorkItem(state, request, raw,
|
|
1140
|
+
UnityCliRequestDigest.Compute(raw, request.InstanceId), state.ListenerId, state.ListenerGeneration,
|
|
1141
|
+
null, workClass, true, false);
|
|
1142
|
+
lock (state.QueueLock)
|
|
1143
|
+
{
|
|
1144
|
+
state.ActiveRequestWork.Add(request.RequestId, item);
|
|
1145
|
+
item.QueueNode = state.WorkQueue.AddLast(item);
|
|
1146
|
+
if (workClass == UnityCliBridgeQueuePolicy.WorkClass.SafeStatus)
|
|
1147
|
+
item.SafeStatusNode = state.SafeStatusQueue.AddLast(item);
|
|
1148
|
+
if (workClass == UnityCliBridgeQueuePolicy.WorkClass.Control)
|
|
1149
|
+
{
|
|
1150
|
+
item.ControlNode = state.ControlQueue.AddLast(item);
|
|
1151
|
+
if (state.ControlScanCursor == null) state.ControlScanCursor = item.ControlNode;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return item;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
private UnityCliBridge.BridgeWorkItem Enqueue(BridgeRequest request, byte[] raw, string token, long deadline, out bool created)
|
|
1158
|
+
{
|
|
1159
|
+
var reservation = new UnityCliBridge.AdmissionQueueReservation
|
|
1160
|
+
{
|
|
1161
|
+
RequestId = request.RequestId,
|
|
1162
|
+
RequestDigest = UnityCliRequestDigest.Compute(raw, request.InstanceId),
|
|
1163
|
+
RootFingerprint = fingerprint,
|
|
1164
|
+
Token = token,
|
|
1165
|
+
ExecutionDeadlineMs = deadline,
|
|
1166
|
+
HasAdmissionHeaders = true,
|
|
1167
|
+
IsBypass = false
|
|
1168
|
+
};
|
|
1169
|
+
int depth;
|
|
1170
|
+
return UnityCliBridge.EnqueuePreparedForTests(request, raw, reservation, state, out created, out depth);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
private UnityCliBridge.BridgeWorkItem EnqueueBypass(BridgeRequest request, out bool created)
|
|
1174
|
+
{
|
|
1175
|
+
var raw = RequestBytes(request);
|
|
1176
|
+
var reservation = new UnityCliBridge.AdmissionQueueReservation
|
|
1177
|
+
{
|
|
1178
|
+
RequestId = request.RequestId,
|
|
1179
|
+
RequestDigest = UnityCliRequestDigest.Compute(raw, request.InstanceId),
|
|
1180
|
+
HasAdmissionHeaders = false,
|
|
1181
|
+
IsBypass = true
|
|
1182
|
+
};
|
|
1183
|
+
int depth;
|
|
1184
|
+
return UnityCliBridge.EnqueuePreparedForTests(request, raw, reservation, state, out created, out depth);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
private static BridgeRequest ProjectToolRequest(string requestId, string toolName, Dictionary<string, object> input)
|
|
1188
|
+
{
|
|
1189
|
+
var request = Request(requestId);
|
|
1190
|
+
request.Command = "project-tool.run";
|
|
1191
|
+
request.Input = new Dictionary<string, object> { { "name", toolName }, { "input", input } };
|
|
1192
|
+
return request;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
private static BridgeRequest Request(string requestId)
|
|
1196
|
+
{
|
|
1197
|
+
return new BridgeRequest
|
|
1198
|
+
{
|
|
1199
|
+
ProtocolVersion = 2,
|
|
1200
|
+
InstanceId = "test-instance",
|
|
1201
|
+
Command = "internal.editor.state.snapshot",
|
|
1202
|
+
RequestId = requestId,
|
|
1203
|
+
Input = new Dictionary<string, object>(),
|
|
1204
|
+
Execution = new BridgeExecution
|
|
1205
|
+
{
|
|
1206
|
+
AbsoluteDeadlineUtc = DateTimeOffset.UtcNow.AddMinutes(1).ToString("O"),
|
|
1207
|
+
CompletionContractVersion = 1,
|
|
1208
|
+
RequestDigestVersion = 1
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
private static byte[] RequestBytes(BridgeRequest request)
|
|
1214
|
+
{
|
|
1215
|
+
return Encoding.UTF8.GetBytes(UnityCliSerializer.Serialize(request));
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
private static long Deadline(BridgeRequest request)
|
|
1219
|
+
{
|
|
1220
|
+
return DateTimeOffset.Parse(request.Execution.AbsoluteDeadlineUtc).ToUnixTimeMilliseconds();
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
private static string Success(string requestId)
|
|
1224
|
+
{
|
|
1225
|
+
return UnityCliSerializer.Serialize(ProtocolEnvelopeFactory.Success(requestId, new Dictionary<string, object> { { "terminal", true } }));
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
#endif
|