@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,615 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.IO;
|
|
5
|
+
using System.Linq;
|
|
6
|
+
using System.Reflection;
|
|
7
|
+
using System.Text;
|
|
8
|
+
using System.Threading;
|
|
9
|
+
using NUnit.Framework;
|
|
10
|
+
using UnityEditor.SceneManagement;
|
|
11
|
+
using UnityEngine;
|
|
12
|
+
using UnityEngine.SceneManagement;
|
|
13
|
+
|
|
14
|
+
namespace UnityCli.Tests
|
|
15
|
+
{
|
|
16
|
+
public sealed class UnityCliRequestBudgetTests
|
|
17
|
+
{
|
|
18
|
+
[Test]
|
|
19
|
+
public void SparseHundredThousandConsoleShapeStopsBeforeMaterialization()
|
|
20
|
+
{
|
|
21
|
+
var clock = new ManualClock();
|
|
22
|
+
var budget = Budget(clock, scans: 1000);
|
|
23
|
+
var matches = 0;
|
|
24
|
+
for (var index = 99999; index >= 0; index--)
|
|
25
|
+
{
|
|
26
|
+
if (!budget.TryConsumeScannedRecord()) break;
|
|
27
|
+
if (index % 10000 == 0) matches++;
|
|
28
|
+
}
|
|
29
|
+
Assert.That(budget.Usage().ScannedRecords, Is.EqualTo(1000));
|
|
30
|
+
Assert.That(matches, Is.EqualTo(0));
|
|
31
|
+
Assert.That(budget.ExhaustedDimension, Is.EqualTo("scannedRecords"));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
[Test]
|
|
35
|
+
public void ZeroNegativeAndOverMaximumBudgetValuesFailBeforeReads()
|
|
36
|
+
{
|
|
37
|
+
Assert.Throws<ArgumentOutOfRangeException>(() => new UnityCliRequestBudget(new UnityCliReadBudgetInput { MaxScannedRecords = 0 }, 1000, CancellationToken.None, new ManualClock()));
|
|
38
|
+
Assert.Throws<ArgumentOutOfRangeException>(() => new UnityCliRequestBudget(new UnityCliReadBudgetInput { MaxNodes = -1 }, 1000, CancellationToken.None, new ManualClock()));
|
|
39
|
+
Assert.Throws<ArgumentOutOfRangeException>(() => new UnityCliRequestBudget(new UnityCliReadBudgetInput { MaxGetters = UnityCliRequestBudget.AbsoluteMaxGetters + 1 }, 1000, CancellationToken.None, new ManualClock()));
|
|
40
|
+
Assert.Throws<ArgumentOutOfRangeException>(() => new UnityCliRequestBudget(new UnityCliReadBudgetInput { MaxReflectionCalls = UnityCliRequestBudget.AbsoluteMaxReflectionCalls + 1 }, 1000, CancellationToken.None, new ManualClock()));
|
|
41
|
+
Assert.Throws<ArgumentOutOfRangeException>(() => new UnityCliRequestBudget(new UnityCliReadBudgetInput { MaxMainThreadMilliseconds = UnityCliRequestBudget.AbsoluteMaxMainThreadMilliseconds + 1 }, 1000, CancellationToken.None, new ManualClock()));
|
|
42
|
+
Assert.Throws<ArgumentOutOfRangeException>(() => new UnityCliRequestBudget(new UnityCliReadBudgetInput { MaxSerializedUtf8Bytes = UnityCliRequestBudget.AbsoluteMaxSerializedUtf8Bytes + 1 }, 1000, CancellationToken.None, new ManualClock()));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
[Test]
|
|
46
|
+
public void SerializedUtf8BudgetCountsEncodedBytesAndStructureReservations()
|
|
47
|
+
{
|
|
48
|
+
var budget = new UnityCliRequestBudget(new UnityCliReadBudgetInput { MaxSerializedUtf8Bytes = 8 }, 1000, CancellationToken.None, new ManualClock());
|
|
49
|
+
Assert.That(budget.TryReserveSerializedUtf8("é"), Is.True);
|
|
50
|
+
Assert.That(budget.TryReserveSerializedUtf8Bytes(6), Is.True);
|
|
51
|
+
Assert.That(budget.TryReserveSerializedUtf8("x"), Is.False);
|
|
52
|
+
Assert.That(budget.ExhaustedDimension, Is.EqualTo("serializedUtf8Bytes"));
|
|
53
|
+
Assert.That(budget.Usage().EstimatedSerializedUtf8Bytes, Is.EqualTo(8));
|
|
54
|
+
Assert.That(budget.Usage().ActualSerializedUtf8Bytes, Is.Zero);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
[Test]
|
|
58
|
+
public void ManualMonotonicClockAndCancellationShareEveryCounter()
|
|
59
|
+
{
|
|
60
|
+
var clock = new ManualClock();
|
|
61
|
+
var cancellation = new CancellationTokenSource();
|
|
62
|
+
var budget = new UnityCliRequestBudget(new UnityCliReadBudgetInput { MaxMainThreadMilliseconds = 5 }, 1000, cancellation.Token, clock);
|
|
63
|
+
Assert.That(budget.TryConsumeNode(), Is.True);
|
|
64
|
+
clock.Advance(5);
|
|
65
|
+
Assert.That(budget.TryConsumeGetter(), Is.False);
|
|
66
|
+
Assert.That(budget.ExhaustedDimension, Is.EqualTo("mainThreadMilliseconds"));
|
|
67
|
+
|
|
68
|
+
var cancelled = new UnityCliRequestBudget(null, 1000, cancellation.Token, clock);
|
|
69
|
+
cancellation.Cancel();
|
|
70
|
+
Assert.Throws<OperationCanceledException>(() => cancelled.TryConsumeReflectionCall());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
[Test]
|
|
74
|
+
public void CyclicDeepWideEnumerableAndMultiplePathsUseOneSerializerBudget()
|
|
75
|
+
{
|
|
76
|
+
var cycle = new Node { Name = "root" };
|
|
77
|
+
cycle.Next = cycle;
|
|
78
|
+
var cycleJson = UnityCliSerializer.Serialize(UnityCliBridge.ObjectSerializer.Serialize(cycle,
|
|
79
|
+
new UnityCliBridge.ObjectSerializer.Options { MaxDepth = 16, MaxArrayItems = 1000 }, Budget(new ManualClock(), nodes: 32)));
|
|
80
|
+
StringAssert.Contains("cycle-detected", cycleJson);
|
|
81
|
+
|
|
82
|
+
var enumerable = new CountingEnumerable(100000);
|
|
83
|
+
var enumerableBudget = Budget(new ManualClock(), scans: 17, nodes: 64);
|
|
84
|
+
var enumerableJson = UnityCliSerializer.Serialize(UnityCliBridge.ObjectSerializer.Serialize(enumerable,
|
|
85
|
+
new UnityCliBridge.ObjectSerializer.Options { MaxDepth = 4, MaxArrayItems = 1000 }, enumerableBudget));
|
|
86
|
+
Assert.That(enumerable.MoveNextCount, Is.EqualTo(17));
|
|
87
|
+
StringAssert.Contains("non-resumable-enumerable", enumerableJson);
|
|
88
|
+
StringAssert.DoesNotContain("nextPath", enumerableJson);
|
|
89
|
+
|
|
90
|
+
var multi = new GetterFixture();
|
|
91
|
+
var pathBudget = Budget(new ManualClock(), getters: 1, reflections: 32, nodes: 32);
|
|
92
|
+
var pathJson = UnityCliSerializer.Serialize(UnityCliBridge.ObjectSerializer.Serialize(multi,
|
|
93
|
+
new UnityCliBridge.ObjectSerializer.Options { Paths = new List<string> { "First", "Second" }, MaxDepth = 2 }, pathBudget));
|
|
94
|
+
Assert.That(multi.GetterCount, Is.EqualTo(1));
|
|
95
|
+
StringAssert.Contains("request-budget-exhausted", pathJson);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[Test]
|
|
99
|
+
public void OversizedAndRepeatedCapturePlansFailBeforeTextureAllocation()
|
|
100
|
+
{
|
|
101
|
+
var requestBudget = Budget(new ManualClock());
|
|
102
|
+
Assert.Throws<UnityCliBridge.BridgeCommandException>(() => ScreenshotToolSupport.PrepareCaptureBudget(8192, 8192, 1, false, null, requestBudget));
|
|
103
|
+
var captures = ScreenshotToolSupport.PrepareCaptureBudget(512, 512, 2, false, null, Budget(new ManualClock()));
|
|
104
|
+
captures.BeginCapture(512, 512);
|
|
105
|
+
captures.BeginCapture(512, 512);
|
|
106
|
+
Assert.Throws<UnityCliBridge.BridgeCommandException>(() => captures.BeginCapture(512, 512));
|
|
107
|
+
Assert.That(captures.Plan.PeakTemporaryBytes, Is.LessThanOrEqualTo(ScreenshotToolSupport.DefaultMaxTemporaryBytes));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[Test]
|
|
111
|
+
public void HighBranchProfilerAndLargeSceneOrPrefabShapesStopAtGlobalNodeBudget()
|
|
112
|
+
{
|
|
113
|
+
var profiler = SyntheticTree.Wide(depth: 2, width: 100);
|
|
114
|
+
var profilerBudget = Budget(new ManualClock(), scans: 250, nodes: 200);
|
|
115
|
+
Assert.That(Traverse(profiler, profilerBudget), Is.EqualTo(200));
|
|
116
|
+
Assert.That(profilerBudget.ExhaustedDimension, Is.EqualTo("nodes"));
|
|
117
|
+
|
|
118
|
+
var scene = SyntheticTree.Wide(depth: 4, width: 12);
|
|
119
|
+
var sceneBudget = Budget(new ManualClock(), scans: 101, nodes: 101);
|
|
120
|
+
Assert.That(Traverse(scene, sceneBudget), Is.EqualTo(101));
|
|
121
|
+
Assert.That(sceneBudget.Usage().ScannedRecords, Is.EqualTo(101));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
[Test]
|
|
125
|
+
public void ProfilerHierarchyTraversesUnity2022FlatPreorderWithoutChildAt()
|
|
126
|
+
{
|
|
127
|
+
var budget = Budget(new ManualClock(), scans: 64, nodes: 64, getters: 64, reflections: 128);
|
|
128
|
+
using (var accessor = ProfilerHierarchyTool.RawHierarchyAccessor.InitializeForTests(new Unity2022RawFrameDataViewProbe(), budget))
|
|
129
|
+
{
|
|
130
|
+
var stack = new List<ProfilerHierarchyTool.TraversalFrame>
|
|
131
|
+
{
|
|
132
|
+
new ProfilerHierarchyTool.TraversalFrame { ParentId = 0, NextSample = 1, Depth = 0 }
|
|
133
|
+
};
|
|
134
|
+
var items = new List<ProfilerHierarchyTool.HierarchyItem>();
|
|
135
|
+
ProfilerHierarchyTool.HierarchyItem item;
|
|
136
|
+
while (ProfilerHierarchyTool.TryNext(accessor, stack, 8, budget, out item)) items.Add(item);
|
|
137
|
+
|
|
138
|
+
CollectionAssert.AreEqual(new long[] { 1, 2, 3, 4 }, items.Select(value => value.Id).ToArray());
|
|
139
|
+
CollectionAssert.AreEqual(new long[] { 0, 1, 0, 3 }, items.Select(value => value.ParentId).ToArray());
|
|
140
|
+
CollectionAssert.AreEqual(new long[] { 0, 1, 0, 1 }, items.Select(value => value.Depth).ToArray());
|
|
141
|
+
CollectionAssert.AreEqual(new long[] { 1, 0, 1, 0 }, items.Select(value => value.ChildCount).ToArray());
|
|
142
|
+
Assert.That(accessor.SubtreeEndExclusive(0, budget), Is.EqualTo(5));
|
|
143
|
+
Assert.That(stack, Is.Empty);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
[Test]
|
|
148
|
+
public void ContinuationBindsScopeGenerationAndRawPosition()
|
|
149
|
+
{
|
|
150
|
+
var token = UnityCliContinuationToken.Create("console.get", "generation-a", "99999:99000:sentinel");
|
|
151
|
+
string position;
|
|
152
|
+
Assert.That(UnityCliContinuationToken.TryParse(token, "console.get", "generation-a", out position), Is.True);
|
|
153
|
+
Assert.That(position, Is.EqualTo("99999:99000:sentinel"));
|
|
154
|
+
Assert.That(UnityCliContinuationToken.TryParse(token, "console.get", "generation-b", out position), Is.False);
|
|
155
|
+
Assert.That(UnityCliContinuationToken.TryParse(token, "scene.hierarchy", "generation-a", out position), Is.False);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
[Test]
|
|
159
|
+
public void SnapshotRetentionKeepsLatestHundredAndHonorsBytePressure()
|
|
160
|
+
{
|
|
161
|
+
var sizes = Enumerable.Repeat(10L, 101).ToArray();
|
|
162
|
+
var byCount = UnityCliRetentionPlanner.RetainedIndices(sizes, 100, 10000);
|
|
163
|
+
Assert.That(byCount.Count, Is.EqualTo(100));
|
|
164
|
+
Assert.That(byCount.Contains(100), Is.False);
|
|
165
|
+
var byBytes = UnityCliRetentionPlanner.RetainedIndices(sizes, 100, 35);
|
|
166
|
+
CollectionAssert.AreEqual(new[] { 0, 1, 2 }, byBytes.OrderBy(value => value).ToArray());
|
|
167
|
+
var contiguousNewest = UnityCliRetentionPlanner.RetainedIndices(new[] { 20L, 40L, 1L }, 100, 35);
|
|
168
|
+
CollectionAssert.AreEqual(new[] { 0 }, contiguousNewest.OrderBy(value => value).ToArray());
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
[Test]
|
|
172
|
+
public void BroadReflectionReturnsStableContinuationWithinCallBudget()
|
|
173
|
+
{
|
|
174
|
+
var budget = Budget(new ManualClock(), scans: 8, nodes: 64, reflections: 128);
|
|
175
|
+
var page = UnityCliBridge.ReflectionInvoker.FindMethods("System.String", null,
|
|
176
|
+
BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, 100, null, budget);
|
|
177
|
+
Assert.That(page.Methods.Count, Is.LessThanOrEqualTo(8));
|
|
178
|
+
Assert.That(page.Truncated, Is.True);
|
|
179
|
+
Assert.That(page.NextCursor, Is.Not.Null.And.Not.Empty);
|
|
180
|
+
string position;
|
|
181
|
+
Assert.That(UnityCliContinuationToken.TryParse(page.NextCursor, "reflection.method.find", page.Generation, out position), Is.True);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
[Test]
|
|
185
|
+
public void CallbackPublicationOwnsFactoryOnceAndRejectsLateCallbacks()
|
|
186
|
+
{
|
|
187
|
+
var abandoned = new UnityCliCallbackPublication<string>();
|
|
188
|
+
var lateFactoryCalls = 0;
|
|
189
|
+
Assert.That(abandoned.TryAbandon(), Is.True);
|
|
190
|
+
Assert.That(abandoned.TryComplete(() => { lateFactoryCalls++; return "late"; }), Is.False);
|
|
191
|
+
Assert.That(lateFactoryCalls, Is.Zero);
|
|
192
|
+
|
|
193
|
+
var completed = new UnityCliCallbackPublication<string>();
|
|
194
|
+
Assert.That(completed.TryComplete(() => "published"), Is.True);
|
|
195
|
+
Assert.That(completed.TryFail(new InvalidOperationException("late")), Is.False);
|
|
196
|
+
bool isCompleted;
|
|
197
|
+
bool isFailed;
|
|
198
|
+
string value;
|
|
199
|
+
Exception error;
|
|
200
|
+
completed.Read(out isCompleted, out isFailed, out value, out error);
|
|
201
|
+
Assert.That(isCompleted, Is.True);
|
|
202
|
+
Assert.That(isFailed, Is.False);
|
|
203
|
+
Assert.That(value, Is.EqualTo("published"));
|
|
204
|
+
Assert.That(error, Is.Null);
|
|
205
|
+
|
|
206
|
+
var faulted = new UnityCliCallbackPublication<string>();
|
|
207
|
+
Assert.That(faulted.TryComplete(() => { throw new IOException("write fault"); }), Is.True);
|
|
208
|
+
faulted.Read(out isCompleted, out isFailed, out value, out error);
|
|
209
|
+
Assert.That(isCompleted, Is.False);
|
|
210
|
+
Assert.That(isFailed, Is.True);
|
|
211
|
+
Assert.That(error, Is.TypeOf<IOException>());
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
[Test]
|
|
215
|
+
public void RealScenePagerAndComponentGetterFailClosedBeforeBatchMaterialization()
|
|
216
|
+
{
|
|
217
|
+
var scene = EditorSceneManager.NewPreviewScene();
|
|
218
|
+
try
|
|
219
|
+
{
|
|
220
|
+
var first = NewRoot(scene, "issue-042-first");
|
|
221
|
+
var second = NewRoot(scene, "issue-042-second");
|
|
222
|
+
var third = NewRoot(scene, "issue-042-third");
|
|
223
|
+
first.AddComponent<BoxCollider>();
|
|
224
|
+
first.AddComponent<SphereCollider>();
|
|
225
|
+
|
|
226
|
+
var rootBudget = Budget(new ManualClock(), scans: 2);
|
|
227
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => UnityCliBridge.ObjectRefResolver.BoundedSceneRoots(scene, rootBudget));
|
|
228
|
+
Assert.That(rootBudget.Usage().ScannedRecords, Is.Zero);
|
|
229
|
+
|
|
230
|
+
var componentBudget = Budget(new ManualClock(), scans: 2);
|
|
231
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => UnityCliBridge.ObjectRefResolver.BoundedComponents(first, componentBudget));
|
|
232
|
+
Assert.That(componentBudget.Usage().ScannedRecords, Is.Zero);
|
|
233
|
+
|
|
234
|
+
var pagerBudget = Budget(new ManualClock(), scans: 32, nodes: 32, getters: 128);
|
|
235
|
+
var traversal = new UnityCliHierarchyPager.Traversal(scene, null, 8, pagerBudget);
|
|
236
|
+
Assert.That(traversal.First(pagerBudget), Is.SameAs(first));
|
|
237
|
+
Assert.That(traversal.Next(first, pagerBudget), Is.SameAs(second));
|
|
238
|
+
var generation = UnityCliReadGeneration.Hierarchy(scene);
|
|
239
|
+
var cursor = UnityCliHierarchyPager.Cursor("issue-042", generation, "scene", third, pagerBudget);
|
|
240
|
+
string phase;
|
|
241
|
+
Assert.That(UnityCliHierarchyPager.ResolveCursor(cursor, "issue-042", generation, pagerBudget, out phase), Is.SameAs(third));
|
|
242
|
+
Assert.That(phase, Is.EqualTo("scene"));
|
|
243
|
+
|
|
244
|
+
var handler = new UnityCliBridge.SceneGameObjectComponentsTool();
|
|
245
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => handler.ExecuteAsync(
|
|
246
|
+
new UnityCliBridge.SceneGameObjectComponentsTool.Request
|
|
247
|
+
{
|
|
248
|
+
Id = first.GetInstanceID(),
|
|
249
|
+
IncludeProperties = false,
|
|
250
|
+
Budget = new UnityCliReadBudgetInput { MaxScannedRecords = 2 }
|
|
251
|
+
},
|
|
252
|
+
new UnityCliProjectToolContext { RequestId = "issue-042-over-budget", TimeoutMs = 30000, CancellationToken = CancellationToken.None }).GetAwaiter().GetResult());
|
|
253
|
+
var handlerResult = handler.ExecuteAsync(
|
|
254
|
+
new UnityCliBridge.SceneGameObjectComponentsTool.Request { Id = first.GetInstanceID(), IncludeProperties = false },
|
|
255
|
+
new UnityCliProjectToolContext { RequestId = "issue-042", TimeoutMs = 30000, CancellationToken = CancellationToken.None }).GetAwaiter().GetResult();
|
|
256
|
+
Assert.That(handlerResult.Count, Is.EqualTo(3));
|
|
257
|
+
Assert.That(handlerResult.Components.Count, Is.EqualTo(3));
|
|
258
|
+
}
|
|
259
|
+
finally
|
|
260
|
+
{
|
|
261
|
+
Assert.That(EditorSceneManager.ClosePreviewScene(scene), Is.True);
|
|
262
|
+
Assert.That(scene.IsValid(), Is.False);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
[Test]
|
|
267
|
+
public void DictionaryAndEnumTruncationAreExplicitlyNonResumable()
|
|
268
|
+
{
|
|
269
|
+
var dictionary = new Dictionary<string, int> { { "alpha", 1 }, { "beta", 2 } };
|
|
270
|
+
var dictionaryJson = UnityCliSerializer.Serialize(UnityCliBridge.ObjectSerializer.Serialize(
|
|
271
|
+
dictionary,
|
|
272
|
+
new UnityCliBridge.ObjectSerializer.Options { MaxDepth = 4, MaxArrayItems = 1 },
|
|
273
|
+
Budget(new ManualClock(), scans: 32, nodes: 32, getters: 32, reflections: 32)));
|
|
274
|
+
StringAssert.Contains("non-resumable-dictionary-truncation", dictionaryJson);
|
|
275
|
+
StringAssert.DoesNotContain("nextPath", dictionaryJson);
|
|
276
|
+
|
|
277
|
+
var enumPage = UnityCliBridge.TypeSchemaGenerator.Generate(
|
|
278
|
+
typeof(Issue042Enum).FullName,
|
|
279
|
+
4,
|
|
280
|
+
1,
|
|
281
|
+
16,
|
|
282
|
+
16,
|
|
283
|
+
null,
|
|
284
|
+
Budget(new ManualClock(), scans: 4096, nodes: 64, getters: 64, reflections: 4096));
|
|
285
|
+
var schema = (Dictionary<string, object>)enumPage.Data["schema"];
|
|
286
|
+
Assert.That(schema["continuationSupported"], Is.False);
|
|
287
|
+
Assert.That(schema["truncationReason"], Is.EqualTo("non-resumable-enum-truncation"));
|
|
288
|
+
Assert.That(enumPage.NextCursor, Is.Null);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
[Test]
|
|
292
|
+
public void ConsoleCoreFailsOnZeroProgressWithoutRescanningUnboundedInput()
|
|
293
|
+
{
|
|
294
|
+
var adapter = new EmptyConsoleSnapshot(100000);
|
|
295
|
+
var budget = Budget(new ManualClock(), scans: 3, nodes: 32, getters: 32, reflections: 32);
|
|
296
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => EditorToolSupport.ReadConsolePage("error", null, 10, budget, adapter));
|
|
297
|
+
Assert.That(adapter.ReadCount, Is.EqualTo(3));
|
|
298
|
+
Assert.That(budget.ExhaustedDimension, Is.EqualTo("scannedRecords"));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
[Test]
|
|
302
|
+
public void WideReflectionSnapshotsAndInvocationPrebudgetFailBeforeEffect()
|
|
303
|
+
{
|
|
304
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => UnityCliReflectionSnapshot.Methods(
|
|
305
|
+
typeof(string),
|
|
306
|
+
BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static,
|
|
307
|
+
Budget(new ManualClock(), scans: 64, nodes: 64, getters: 64, reflections: 64),
|
|
308
|
+
1));
|
|
309
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => UnityCliReflectionSnapshot.Interfaces(
|
|
310
|
+
typeof(Dictionary<string, string>),
|
|
311
|
+
Budget(new ManualClock(), scans: 64, nodes: 64, getters: 64, reflections: 64),
|
|
312
|
+
1));
|
|
313
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => UnityCliReflectionSnapshot.GenericArguments(
|
|
314
|
+
typeof(Dictionary<string, List<Dictionary<int, string>>>),
|
|
315
|
+
Budget(new ManualClock(), scans: 64, nodes: 64, getters: 64, reflections: 64),
|
|
316
|
+
1));
|
|
317
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => UnityCliBridge.TypeSchemaGenerator.Generate(
|
|
318
|
+
typeof(Dictionary<string, List<Dictionary<int, string>>>).AssemblyQualifiedName,
|
|
319
|
+
8,
|
|
320
|
+
64,
|
|
321
|
+
64,
|
|
322
|
+
64,
|
|
323
|
+
null,
|
|
324
|
+
Budget(new ManualClock(), scans: 128, nodes: 128, getters: 128, reflections: 128, serialized: 64)));
|
|
325
|
+
|
|
326
|
+
Issue042InvocationFixture.InvocationCount = 0;
|
|
327
|
+
var callBudget = Budget(new ManualClock(), scans: 128, nodes: 128, getters: 128, reflections: 128, serialized: 1024);
|
|
328
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => UnityCliBridge.ReflectionInvoker.PrepareCall(
|
|
329
|
+
typeof(Issue042InvocationFixture).FullName,
|
|
330
|
+
"Invoke",
|
|
331
|
+
new List<object> { 1L, 2L, 3L },
|
|
332
|
+
null,
|
|
333
|
+
null,
|
|
334
|
+
BindingFlags.Public | BindingFlags.Static,
|
|
335
|
+
callBudget));
|
|
336
|
+
Assert.That(Issue042InvocationFixture.InvocationCount, Is.Zero);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
[Test]
|
|
340
|
+
public void TerminalSerializationReportsExactUtf8AndUsesManualDeadline()
|
|
341
|
+
{
|
|
342
|
+
var clock = new ManualClock();
|
|
343
|
+
var budget = Budget(clock);
|
|
344
|
+
var data = new Dictionary<string, object>
|
|
345
|
+
{
|
|
346
|
+
{ "value", "é" },
|
|
347
|
+
{ "budget", budget.Usage() }
|
|
348
|
+
};
|
|
349
|
+
var json = UnityCliBridge.SerializeTerminalSuccess("issue-042", data, budget, CancellationToken.None);
|
|
350
|
+
Assert.That(budget.Usage().ActualSerializedUtf8Bytes, Is.EqualTo(Encoding.UTF8.GetByteCount(json)));
|
|
351
|
+
StringAssert.Contains("actualSerializedUtf8Bytes", json);
|
|
352
|
+
|
|
353
|
+
var deadlineClock = new ManualClock();
|
|
354
|
+
var deadlineBudget = Budget(deadlineClock, milliseconds: 5);
|
|
355
|
+
deadlineClock.Advance(5);
|
|
356
|
+
Assert.Throws<UnityCliRequestBudgetExceededException>(() => UnityCliBridge.SerializeTerminalSuccess(
|
|
357
|
+
"issue-042-deadline", new { Value = "late" }, deadlineBudget, CancellationToken.None));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
[Test]
|
|
361
|
+
public void MemoryRetentionLimitsAndOrphanCleanupOperateOnRealFiles()
|
|
362
|
+
{
|
|
363
|
+
var root = CreateTestRoot("memory");
|
|
364
|
+
try
|
|
365
|
+
{
|
|
366
|
+
ProfilerMemorySnapshotSupport.ResetTestOverrides();
|
|
367
|
+
ProfilerMemorySnapshotSupport.RootOverrideForTests = root;
|
|
368
|
+
string newest = null;
|
|
369
|
+
for (var index = 0; index < 101; index++)
|
|
370
|
+
{
|
|
371
|
+
var path = Path.Combine(root, "snapshot-" + index.ToString("D3") + ".snap");
|
|
372
|
+
File.WriteAllBytes(path, new[] { (byte)index });
|
|
373
|
+
File.SetLastWriteTimeUtc(path, new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(index));
|
|
374
|
+
newest = path;
|
|
375
|
+
}
|
|
376
|
+
ProfilerMemorySnapshotSupport.ApplyRetention(newest, Budget(new ManualClock(), scans: 10000, nodes: 4096, getters: 4096));
|
|
377
|
+
Assert.That(Directory.GetFiles(root, "*.snap", SearchOption.TopDirectoryOnly).Length, Is.EqualTo(ProfilerMemorySnapshotSupport.RetainedSnapshotCount));
|
|
378
|
+
Assert.That(File.Exists(newest), Is.True);
|
|
379
|
+
Assert.That(File.Exists(Path.Combine(root, "snapshot-000.snap")), Is.False);
|
|
380
|
+
|
|
381
|
+
CollectionAssert.AreEqual(
|
|
382
|
+
new[] { 0, 1 },
|
|
383
|
+
UnityCliRetentionPlanner.RetainedIndices(
|
|
384
|
+
new[] { ProfilerMemorySnapshotSupport.AbsoluteSnapshotBytes, ProfilerMemorySnapshotSupport.AbsoluteSnapshotBytes, 1L },
|
|
385
|
+
ProfilerMemorySnapshotSupport.RetainedSnapshotCount,
|
|
386
|
+
ProfilerMemorySnapshotSupport.RetainedSnapshotBytes).OrderBy(value => value).ToArray());
|
|
387
|
+
Assert.DoesNotThrow(() => ProfilerMemorySnapshotSupport.ValidateSnapshotLength(
|
|
388
|
+
ProfilerMemorySnapshotSupport.AbsoluteSnapshotBytes,
|
|
389
|
+
ProfilerMemorySnapshotSupport.AbsoluteSnapshotBytes));
|
|
390
|
+
Assert.Throws<UnityCliBridge.BridgeCommandException>(() => ProfilerMemorySnapshotSupport.ValidateSnapshotLength(
|
|
391
|
+
ProfilerMemorySnapshotSupport.AbsoluteSnapshotBytes + 1L,
|
|
392
|
+
ProfilerMemorySnapshotSupport.AbsoluteSnapshotBytes));
|
|
393
|
+
|
|
394
|
+
for (var index = 0; index < 10; index++)
|
|
395
|
+
File.WriteAllBytes(Path.Combine(root, ".pending-orphan-" + index.ToString("D2") + ".snap"), new byte[] { 1 });
|
|
396
|
+
Assert.That(ProfilerMemorySnapshotSupport.CleanupOrphanPass(), Is.True);
|
|
397
|
+
Assert.That(Directory.GetFiles(root, ".pending-*.snap", SearchOption.TopDirectoryOnly), Is.Empty);
|
|
398
|
+
}
|
|
399
|
+
finally
|
|
400
|
+
{
|
|
401
|
+
ProfilerMemorySnapshotSupport.ResetTestOverrides();
|
|
402
|
+
DeleteTestRoot(root);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
[Test]
|
|
407
|
+
public void ScreenshotPendingPublicationIsUniqueAtomicAndFaultOwned()
|
|
408
|
+
{
|
|
409
|
+
var root = CreateTestRoot("screen");
|
|
410
|
+
try
|
|
411
|
+
{
|
|
412
|
+
var output = Path.Combine(root, "capture.png");
|
|
413
|
+
using (var first = new ScreenshotScreenTool.PendingCapturePublication(output))
|
|
414
|
+
using (var second = new ScreenshotScreenTool.PendingCapturePublication(output))
|
|
415
|
+
{
|
|
416
|
+
Assert.That(first.PendingPath, Is.Not.EqualTo(second.PendingPath));
|
|
417
|
+
File.WriteAllText(first.PendingPath, "first");
|
|
418
|
+
File.WriteAllText(second.PendingPath, "second");
|
|
419
|
+
first.Publish();
|
|
420
|
+
Assert.That(File.ReadAllText(output), Is.EqualTo("first"));
|
|
421
|
+
Assert.That(first.TryFault(), Is.False);
|
|
422
|
+
Assert.That(second.TryFault(), Is.True);
|
|
423
|
+
Assert.That(File.Exists(second.PendingPath), Is.False);
|
|
424
|
+
Assert.Throws<InvalidOperationException>(() => second.Publish());
|
|
425
|
+
Assert.That(File.ReadAllText(output), Is.EqualTo("first"));
|
|
426
|
+
}
|
|
427
|
+
string latePath;
|
|
428
|
+
using (var late = new ScreenshotScreenTool.PendingCapturePublication(output))
|
|
429
|
+
{
|
|
430
|
+
latePath = late.PendingPath;
|
|
431
|
+
late.BeginWrite();
|
|
432
|
+
Assert.That(late.TryFault(), Is.True);
|
|
433
|
+
}
|
|
434
|
+
File.WriteAllText(latePath, "late");
|
|
435
|
+
using (var sweep = new ScreenshotScreenTool.PendingCapturePublication(output))
|
|
436
|
+
{
|
|
437
|
+
Assert.That(File.Exists(latePath), Is.False);
|
|
438
|
+
Assert.That(sweep.TryFault(), Is.True);
|
|
439
|
+
}
|
|
440
|
+
Assert.That(Directory.GetFiles(root, "*.unitycli.pending", SearchOption.TopDirectoryOnly), Is.Empty);
|
|
441
|
+
}
|
|
442
|
+
finally
|
|
443
|
+
{
|
|
444
|
+
DeleteTestRoot(root);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
[Test]
|
|
449
|
+
public void TextureAndRawViewOwnershipReleaseInitializationFailures()
|
|
450
|
+
{
|
|
451
|
+
Texture2D texture = null;
|
|
452
|
+
var destroyed = false;
|
|
453
|
+
Assert.Throws<InvalidOperationException>(() => ScreenshotToolSupport.CreateTextureWithOwnership(
|
|
454
|
+
() => texture = new Texture2D(2, 2, TextureFormat.RGB24, false),
|
|
455
|
+
value => { throw new InvalidOperationException("populate failed"); },
|
|
456
|
+
value => { destroyed = true; UnityEngine.Object.DestroyImmediate(value); }));
|
|
457
|
+
Assert.That(destroyed, Is.True);
|
|
458
|
+
Assert.That(texture == null, Is.True);
|
|
459
|
+
|
|
460
|
+
var rawView = new DisposableProbe();
|
|
461
|
+
Assert.Throws<InvalidOperationException>(() => ProfilerHierarchyTool.RawHierarchyAccessor.InitializeOwnedView<object>(
|
|
462
|
+
rawView,
|
|
463
|
+
value => { throw new InvalidOperationException("binding failed"); }));
|
|
464
|
+
Assert.That(rawView.Disposed, Is.True);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
private static GameObject NewRoot(Scene scene, string name)
|
|
468
|
+
{
|
|
469
|
+
var gameObject = new GameObject(name);
|
|
470
|
+
SceneManager.MoveGameObjectToScene(gameObject, scene);
|
|
471
|
+
return gameObject;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
private static string CreateTestRoot(string suffix)
|
|
475
|
+
{
|
|
476
|
+
var parent = Path.GetFullPath(Path.Combine("Library", "UnityCli", "tests"));
|
|
477
|
+
var root = Path.Combine(parent, "issue-042-" + suffix + "-" + Guid.NewGuid().ToString("N"));
|
|
478
|
+
Directory.CreateDirectory(root);
|
|
479
|
+
Assert.That(Directory.Exists(root), Is.True);
|
|
480
|
+
return root;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
private static void DeleteTestRoot(string root)
|
|
484
|
+
{
|
|
485
|
+
var parent = Path.GetFullPath(Path.Combine("Library", "UnityCli", "tests")) + Path.DirectorySeparatorChar;
|
|
486
|
+
var canonical = Path.GetFullPath(root);
|
|
487
|
+
Assert.That(canonical.StartsWith(parent, StringComparison.OrdinalIgnoreCase), Is.True);
|
|
488
|
+
if (Directory.Exists(canonical)) Directory.Delete(canonical, true);
|
|
489
|
+
Assert.That(Directory.Exists(canonical), Is.False);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private static int Traverse(SyntheticTree root, UnityCliRequestBudget budget)
|
|
493
|
+
{
|
|
494
|
+
var count = 0;
|
|
495
|
+
var stack = new Stack<SyntheticTree>();
|
|
496
|
+
stack.Push(root);
|
|
497
|
+
while (stack.Count > 0)
|
|
498
|
+
{
|
|
499
|
+
if (!budget.TryConsumeScannedRecord() || !budget.TryConsumeNode()) break;
|
|
500
|
+
var current = stack.Pop();
|
|
501
|
+
count++;
|
|
502
|
+
for (var index = current.Children.Count - 1; index >= 0; index--) stack.Push(current.Children[index]);
|
|
503
|
+
}
|
|
504
|
+
return count;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
private static UnityCliRequestBudget Budget(ManualClock clock, long scans = 10000, long nodes = 4096, long getters = 2048, long reflections = 4096, long serialized = 8 * 1024 * 1024, long milliseconds = 2000)
|
|
508
|
+
{
|
|
509
|
+
return new UnityCliRequestBudget(new UnityCliReadBudgetInput
|
|
510
|
+
{
|
|
511
|
+
MaxScannedRecords = scans,
|
|
512
|
+
MaxNodes = nodes,
|
|
513
|
+
MaxGetters = getters,
|
|
514
|
+
MaxReflectionCalls = reflections,
|
|
515
|
+
MaxMainThreadMilliseconds = milliseconds,
|
|
516
|
+
MaxSerializedUtf8Bytes = serialized
|
|
517
|
+
}, 30000, CancellationToken.None, clock);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
private sealed class ManualClock : IUnityCliMonotonicClock
|
|
521
|
+
{
|
|
522
|
+
public long ElapsedMilliseconds { get; private set; }
|
|
523
|
+
public void Advance(long milliseconds) { ElapsedMilliseconds += milliseconds; }
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
private sealed class Node { public string Name { get; set; } public Node Next { get; set; } }
|
|
527
|
+
|
|
528
|
+
private sealed class GetterFixture
|
|
529
|
+
{
|
|
530
|
+
public int GetterCount;
|
|
531
|
+
public string First { get { GetterCount++; return "first"; } }
|
|
532
|
+
public string Second { get { GetterCount++; return "second"; } }
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
private sealed class DisposableProbe : IDisposable
|
|
536
|
+
{
|
|
537
|
+
internal bool Disposed { get; private set; }
|
|
538
|
+
public void Dispose() { Disposed = true; }
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
private sealed class Unity2022RawFrameDataViewProbe
|
|
542
|
+
{
|
|
543
|
+
private static readonly int[] DirectChildren = { 2, 1, 0, 1, 0 };
|
|
544
|
+
private static readonly int[] RecursiveChildren = { 4, 1, 0, 1, 0 };
|
|
545
|
+
|
|
546
|
+
public bool valid { get { return true; } }
|
|
547
|
+
public int sampleCount { get { return DirectChildren.Length; } }
|
|
548
|
+
public int frameIndex { get { return 17; } }
|
|
549
|
+
public int threadIndex { get { return 0; } }
|
|
550
|
+
public double frameStartTimeMs { get { return 100.0; } }
|
|
551
|
+
public float frameTimeMs { get { return 16.0f; } }
|
|
552
|
+
public int GetSampleChildrenCount(int sampleIndex) { return DirectChildren[sampleIndex]; }
|
|
553
|
+
public int GetSampleChildrenCountRecursive(int sampleIndex) { return RecursiveChildren[sampleIndex]; }
|
|
554
|
+
public string GetSampleName(int sampleIndex) { return "sample-" + sampleIndex.ToString(); }
|
|
555
|
+
public float GetSampleTimeMs(int sampleIndex) { return sampleIndex; }
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private sealed class EmptyConsoleSnapshot : EditorToolSupport.IConsoleSnapshotAdapter
|
|
559
|
+
{
|
|
560
|
+
internal EmptyConsoleSnapshot(int count) { Count = count; }
|
|
561
|
+
public int Count { get; private set; }
|
|
562
|
+
internal int ReadCount { get; private set; }
|
|
563
|
+
public bool TryRead(int rawIndex, UnityCliRequestBudget budget, out ConsoleEntry result)
|
|
564
|
+
{
|
|
565
|
+
result = null;
|
|
566
|
+
if (!budget.TryConsumeScannedRecord()) return false;
|
|
567
|
+
ReadCount++;
|
|
568
|
+
result = new ConsoleEntry { Type = "log", Message = string.Empty, File = string.Empty };
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
private enum Issue042Enum { First, Second, Third }
|
|
574
|
+
|
|
575
|
+
private static class Issue042InvocationFixture
|
|
576
|
+
{
|
|
577
|
+
internal static int InvocationCount;
|
|
578
|
+
public static int Invoke(int first, int second, int third)
|
|
579
|
+
{
|
|
580
|
+
InvocationCount++;
|
|
581
|
+
return first + second + third;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
private sealed class CountingEnumerable : IEnumerable
|
|
586
|
+
{
|
|
587
|
+
private readonly int count;
|
|
588
|
+
internal CountingEnumerable(int count) { this.count = count; }
|
|
589
|
+
internal int MoveNextCount { get; private set; }
|
|
590
|
+
public IEnumerator GetEnumerator() { return new Counter(this); }
|
|
591
|
+
|
|
592
|
+
private sealed class Counter : IEnumerator
|
|
593
|
+
{
|
|
594
|
+
private readonly CountingEnumerable owner;
|
|
595
|
+
private int index = -1;
|
|
596
|
+
internal Counter(CountingEnumerable owner) { this.owner = owner; }
|
|
597
|
+
public object Current { get { return index; } }
|
|
598
|
+
public bool MoveNext() { owner.MoveNextCount++; index++; return index < owner.count; }
|
|
599
|
+
public void Reset() { index = -1; }
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
private sealed class SyntheticTree
|
|
604
|
+
{
|
|
605
|
+
internal readonly List<SyntheticTree> Children = new List<SyntheticTree>();
|
|
606
|
+
internal static SyntheticTree Wide(int depth, int width)
|
|
607
|
+
{
|
|
608
|
+
var root = new SyntheticTree();
|
|
609
|
+
if (depth == 0) return root;
|
|
610
|
+
for (var index = 0; index < width; index++) root.Children.Add(Wide(depth - 1, width));
|
|
611
|
+
return root;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|