ue-mcp 1.2.4 → 1.3.0
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 +4 -3
- package/dist/action-class.js +56 -3
- package/dist/action-class.js.map +1 -1
- package/dist/bridge-timeouts.d.ts +64 -0
- package/dist/bridge-timeouts.js +118 -0
- package/dist/bridge-timeouts.js.map +1 -0
- package/dist/bridge.js +10 -2
- package/dist/bridge.js.map +1 -1
- package/dist/editor-control.d.ts +72 -5
- package/dist/editor-control.js +164 -140
- package/dist/editor-control.js.map +1 -1
- package/dist/editor-target.d.ts +55 -1
- package/dist/editor-target.js +107 -10
- package/dist/editor-target.js.map +1 -1
- package/dist/engine-observer.d.ts +59 -5
- package/dist/engine-observer.js +249 -29
- package/dist/engine-observer.js.map +1 -1
- package/dist/engine-root.d.ts +91 -0
- package/dist/engine-root.js +448 -0
- package/dist/engine-root.js.map +1 -0
- package/dist/epic-enrich.js +16 -1
- package/dist/epic-enrich.js.map +1 -1
- package/dist/feedback-cli.js +4 -0
- package/dist/feedback-cli.js.map +1 -1
- package/dist/feedback-deferred.d.ts +9 -0
- package/dist/feedback-deferred.js +2 -0
- package/dist/feedback-deferred.js.map +1 -1
- package/dist/feedback-fallback.d.ts +99 -0
- package/dist/feedback-fallback.js +173 -0
- package/dist/feedback-fallback.js.map +1 -0
- package/dist/flow/write-methods.js +20 -0
- package/dist/flow/write-methods.js.map +1 -1
- package/dist/lean-context.js +8 -3
- package/dist/lean-context.js.map +1 -1
- package/dist/locking.js +11 -2
- package/dist/locking.js.map +1 -1
- package/dist/project.d.ts +11 -0
- package/dist/project.js +23 -2
- package/dist/project.js.map +1 -1
- package/dist/python-gate.d.ts +95 -0
- package/dist/python-gate.js +175 -0
- package/dist/python-gate.js.map +1 -0
- package/dist/tool-counts.json +16 -16
- package/dist/tools/animation.js +288 -24
- package/dist/tools/animation.js.map +1 -1
- package/dist/tools/asset.js +79 -14
- package/dist/tools/asset.js.map +1 -1
- package/dist/tools/audio.js +59 -5
- package/dist/tools/audio.js.map +1 -1
- package/dist/tools/blueprint.js +29 -12
- package/dist/tools/blueprint.js.map +1 -1
- package/dist/tools/chooser.js +4 -1
- package/dist/tools/chooser.js.map +1 -1
- package/dist/tools/editor.js +81 -43
- package/dist/tools/editor.js.map +1 -1
- package/dist/tools/feedback.js +214 -48
- package/dist/tools/feedback.js.map +1 -1
- package/dist/tools/foliage.js +27 -1
- package/dist/tools/foliage.js.map +1 -1
- package/dist/tools/gameplay.js +38 -11
- package/dist/tools/gameplay.js.map +1 -1
- package/dist/tools/gas.js +17 -5
- package/dist/tools/gas.js.map +1 -1
- package/dist/tools/landscape.js +22 -7
- package/dist/tools/landscape.js.map +1 -1
- package/dist/tools/level.js +322 -51
- package/dist/tools/level.js.map +1 -1
- package/dist/tools/material.js +10 -4
- package/dist/tools/material.js.map +1 -1
- package/dist/tools/niagara.js +3 -2
- package/dist/tools/niagara.js.map +1 -1
- package/dist/tools/pcg.js +6 -5
- package/dist/tools/pcg.js.map +1 -1
- package/dist/tools/project.js +47 -20
- package/dist/tools/project.js.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/types.js +46 -3
- package/dist/types.js.map +1 -1
- package/dist/ue-mcp.default.yml +493 -125
- package/package.json +2 -2
- package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
- package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
- package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
- package/skills/ue-mcp-animation/SKILL.md +154 -0
- package/skills/ue-mcp-blueprint/SKILL.md +71 -0
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
2
|
+
|
|
3
|
+
#include "Tests/MCPInstancedStructPathTestTypes.h"
|
|
4
|
+
|
|
5
|
+
#include "HandlerRegistry.h"
|
|
6
|
+
#include "Handlers/EditorHandlers.h"
|
|
7
|
+
#include "Dom/JsonObject.h"
|
|
8
|
+
#include "Dom/JsonValue.h"
|
|
9
|
+
#include "Misc/AutomationTest.h"
|
|
10
|
+
#include "Misc/Guid.h"
|
|
11
|
+
#include "UObject/Package.h"
|
|
12
|
+
#include "UObject/StrongObjectPtr.h"
|
|
13
|
+
|
|
14
|
+
namespace
|
|
15
|
+
{
|
|
16
|
+
TStrongObjectPtr<UUEMCPInstancedStructPathTestObject> MakeFixture()
|
|
17
|
+
{
|
|
18
|
+
const FString FixtureId = FGuid::NewGuid().ToString(EGuidFormats::Digits);
|
|
19
|
+
UPackage* FixturePackage = CreatePackage(*FString::Printf(
|
|
20
|
+
TEXT("/Temp/UEMCPInstancedStructPathTests/%s"),
|
|
21
|
+
*FixtureId));
|
|
22
|
+
FixturePackage->SetDirtyFlag(false);
|
|
23
|
+
TStrongObjectPtr<UUEMCPInstancedStructPathTestObject> Fixture(NewObject<UUEMCPInstancedStructPathTestObject>(
|
|
24
|
+
FixturePackage,
|
|
25
|
+
TEXT("Fixture")));
|
|
26
|
+
|
|
27
|
+
Fixture->Payload.InitializeAs<FUEMCPInstancedStructPathPayload>();
|
|
28
|
+
FUEMCPInstancedStructPathPayload& Payload = Fixture->Payload.GetMutable<FUEMCPInstancedStructPathPayload>();
|
|
29
|
+
Payload.Scalar = 11;
|
|
30
|
+
Payload.Sibling = TEXT("payload-sibling");
|
|
31
|
+
Payload.Elements.SetNum(2);
|
|
32
|
+
Payload.Elements[0].Scalar = 21;
|
|
33
|
+
Payload.Elements[0].Sibling = TEXT("first-sibling");
|
|
34
|
+
Payload.Elements[1].Scalar = 31;
|
|
35
|
+
Payload.Elements[1].Sibling = TEXT("second-sibling");
|
|
36
|
+
|
|
37
|
+
Fixture->OpStack.SetNum(2);
|
|
38
|
+
for (int32 Index = 0; Index < Fixture->OpStack.Num(); ++Index)
|
|
39
|
+
{
|
|
40
|
+
Fixture->OpStack[Index].InitializeAs<FUEMCPInstancedStructPathPayload>();
|
|
41
|
+
FUEMCPInstancedStructPathPayload& Op = Fixture->OpStack[Index].GetMutable<FUEMCPInstancedStructPathPayload>();
|
|
42
|
+
Op.Scalar = 101 + (Index * 100);
|
|
43
|
+
Op.Sibling = FString::Printf(TEXT("op-%d-sibling"), Index);
|
|
44
|
+
Op.Elements.SetNum(2);
|
|
45
|
+
Op.Elements[0].Scalar = 111 + (Index * 100);
|
|
46
|
+
Op.Elements[0].Sibling = FString::Printf(TEXT("op-%d-first-sibling"), Index);
|
|
47
|
+
Op.Elements[1].Scalar = 121 + (Index * 100);
|
|
48
|
+
Op.Elements[1].Sibling = FString::Printf(TEXT("op-%d-second-sibling"), Index);
|
|
49
|
+
}
|
|
50
|
+
return Fixture;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
TSharedPtr<FJsonObject> MakeSetRequest(
|
|
54
|
+
const UObject& Target,
|
|
55
|
+
const TCHAR* PropertyPath,
|
|
56
|
+
const TSharedPtr<FJsonValue>& Value)
|
|
57
|
+
{
|
|
58
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
59
|
+
Request->SetStringField(TEXT("objectPath"), Target.GetPathName());
|
|
60
|
+
Request->SetStringField(TEXT("propertyName"), PropertyPath);
|
|
61
|
+
Request->SetField(TEXT("value"), Value);
|
|
62
|
+
return Request;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
TSharedPtr<FJsonObject> ExecuteSet(
|
|
66
|
+
FMCPHandlerRegistry& Registry,
|
|
67
|
+
const UObject& Target,
|
|
68
|
+
const TCHAR* PropertyPath,
|
|
69
|
+
const TSharedPtr<FJsonValue>& Value)
|
|
70
|
+
{
|
|
71
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(
|
|
72
|
+
TEXT("set_object_property"),
|
|
73
|
+
MakeSetRequest(Target, PropertyPath, Value));
|
|
74
|
+
if (!Response.IsValid() || Response->Type != EJson::Object)
|
|
75
|
+
{
|
|
76
|
+
return nullptr;
|
|
77
|
+
}
|
|
78
|
+
return Response->AsObject();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
TSharedPtr<FJsonObject> ExecuteAssetSet(
|
|
82
|
+
FMCPHandlerRegistry& Registry,
|
|
83
|
+
const UObject& Target,
|
|
84
|
+
const TCHAR* PropertyPath,
|
|
85
|
+
const TSharedPtr<FJsonValue>& Value)
|
|
86
|
+
{
|
|
87
|
+
TSharedPtr<FJsonObject> Request = MakeSetRequest(Target, PropertyPath, Value);
|
|
88
|
+
Request->SetBoolField(TEXT("save"), false);
|
|
89
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("set_property"), Request);
|
|
90
|
+
if (!Response.IsValid() || Response->Type != EJson::Object)
|
|
91
|
+
{
|
|
92
|
+
return nullptr;
|
|
93
|
+
}
|
|
94
|
+
return Response->AsObject();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
TSharedPtr<FJsonObject> ExecuteAssetGet(
|
|
98
|
+
FMCPHandlerRegistry& Registry,
|
|
99
|
+
const UObject& Target,
|
|
100
|
+
const TCHAR* PropertyPath)
|
|
101
|
+
{
|
|
102
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
103
|
+
Request->SetStringField(TEXT("objectPath"), Target.GetPathName());
|
|
104
|
+
Request->SetStringField(TEXT("propertyName"), PropertyPath);
|
|
105
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("get_property"), Request);
|
|
106
|
+
if (!Response.IsValid() || Response->Type != EJson::Object)
|
|
107
|
+
{
|
|
108
|
+
return nullptr;
|
|
109
|
+
}
|
|
110
|
+
return Response->AsObject();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
bool IsSuccess(const TSharedPtr<FJsonObject>& Response)
|
|
114
|
+
{
|
|
115
|
+
bool bSuccess = false;
|
|
116
|
+
return Response.IsValid() && Response->TryGetBoolField(TEXT("success"), bSuccess) && bSuccess;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
121
|
+
FMCPInstancedStructScalarPathTest,
|
|
122
|
+
"UE.MCP.Editor.SetObjectProperty.InstancedStruct.ScalarLeaf",
|
|
123
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
124
|
+
|
|
125
|
+
bool FMCPInstancedStructScalarPathTest::RunTest(const FString& Parameters)
|
|
126
|
+
{
|
|
127
|
+
FMCPHandlerRegistry Registry;
|
|
128
|
+
FEditorHandlers::RegisterHandlers(Registry);
|
|
129
|
+
TestTrue(TEXT("set_object_property is registered"), Registry.HasHandler(TEXT("set_object_property")));
|
|
130
|
+
|
|
131
|
+
TStrongObjectPtr<UUEMCPInstancedStructPathTestObject> Fixture = MakeFixture();
|
|
132
|
+
FUEMCPInstancedStructPathPayload& Payload = Fixture->Payload.GetMutable<FUEMCPInstancedStructPathPayload>();
|
|
133
|
+
const bool bPackageWasDirty = Fixture->GetOutermost()->IsDirty();
|
|
134
|
+
|
|
135
|
+
const TSharedPtr<FJsonObject> Response = ExecuteSet(
|
|
136
|
+
Registry,
|
|
137
|
+
*Fixture,
|
|
138
|
+
TEXT("Payload.Scalar"),
|
|
139
|
+
MakeShared<FJsonValueNumber>(47));
|
|
140
|
+
TestTrue(TEXT("scalar payload leaf write succeeds"), IsSuccess(Response));
|
|
141
|
+
TestEqual(TEXT("scalar payload leaf is updated"), Payload.Scalar, 47);
|
|
142
|
+
TestEqual(TEXT("payload sibling is unchanged"), Payload.Sibling, FString(TEXT("payload-sibling")));
|
|
143
|
+
if (Response.IsValid())
|
|
144
|
+
{
|
|
145
|
+
FString LeafPropertyName;
|
|
146
|
+
FString CurrentValue;
|
|
147
|
+
FString PreviousValue;
|
|
148
|
+
TestTrue(TEXT("handler reports a scalar leaf field"), Response->TryGetStringField(TEXT("leafPropertyName"), LeafPropertyName));
|
|
149
|
+
TestTrue(TEXT("handler reports a scalar value field"), Response->TryGetStringField(TEXT("value"), CurrentValue));
|
|
150
|
+
TestTrue(TEXT("handler reports a previous value field"), Response->TryGetStringField(TEXT("previousValue"), PreviousValue));
|
|
151
|
+
TestEqual(TEXT("handler reports the scalar leaf"), LeafPropertyName, FString(TEXT("Scalar")));
|
|
152
|
+
TestEqual(TEXT("handler reads back the current value"), CurrentValue, FString(TEXT("47")));
|
|
153
|
+
TestEqual(TEXT("handler reports the previous value"), PreviousValue, FString(TEXT("11")));
|
|
154
|
+
}
|
|
155
|
+
TestEqual(TEXT("live-object write does not dirty the isolated package"), Fixture->GetOutermost()->IsDirty(), bPackageWasDirty);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
160
|
+
FMCPInstancedStructArrayPathTest,
|
|
161
|
+
"UE.MCP.Editor.SetObjectProperty.InstancedStruct.NestedArrayLeaf",
|
|
162
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
163
|
+
|
|
164
|
+
bool FMCPInstancedStructArrayPathTest::RunTest(const FString& Parameters)
|
|
165
|
+
{
|
|
166
|
+
FMCPHandlerRegistry Registry;
|
|
167
|
+
FEditorHandlers::RegisterHandlers(Registry);
|
|
168
|
+
TStrongObjectPtr<UUEMCPInstancedStructPathTestObject> Fixture = MakeFixture();
|
|
169
|
+
FUEMCPInstancedStructPathPayload& Payload = Fixture->Payload.GetMutable<FUEMCPInstancedStructPathPayload>();
|
|
170
|
+
const bool bPackageWasDirty = Fixture->GetOutermost()->IsDirty();
|
|
171
|
+
|
|
172
|
+
const TSharedPtr<FJsonObject> Response = ExecuteSet(
|
|
173
|
+
Registry,
|
|
174
|
+
*Fixture,
|
|
175
|
+
TEXT("Payload.Elements[1].Scalar"),
|
|
176
|
+
MakeShared<FJsonValueNumber>(99));
|
|
177
|
+
TestTrue(TEXT("nested array payload leaf write succeeds"), IsSuccess(Response));
|
|
178
|
+
TestEqual(TEXT("selected array element is updated"), Payload.Elements[1].Scalar, 99);
|
|
179
|
+
TestEqual(TEXT("other array element is unchanged"), Payload.Elements[0].Scalar, 21);
|
|
180
|
+
TestEqual(TEXT("selected element sibling is unchanged"), Payload.Elements[1].Sibling, FString(TEXT("second-sibling")));
|
|
181
|
+
TestEqual(TEXT("payload sibling is unchanged"), Payload.Sibling, FString(TEXT("payload-sibling")));
|
|
182
|
+
if (Response.IsValid())
|
|
183
|
+
{
|
|
184
|
+
FString ResolvedPropertyName;
|
|
185
|
+
FString CurrentValue;
|
|
186
|
+
TestTrue(TEXT("handler reports an indexed resolved path"), Response->TryGetStringField(TEXT("resolvedPropertyName"), ResolvedPropertyName));
|
|
187
|
+
TestTrue(TEXT("handler reports an indexed leaf value"), Response->TryGetStringField(TEXT("value"), CurrentValue));
|
|
188
|
+
TestEqual(TEXT("indexed path is preserved in response"), ResolvedPropertyName, FString(TEXT("Payload.Elements[1].Scalar")));
|
|
189
|
+
TestEqual(TEXT("indexed leaf readback is current"), CurrentValue, FString(TEXT("99")));
|
|
190
|
+
}
|
|
191
|
+
TestEqual(TEXT("indexed live-object write does not dirty the isolated package"), Fixture->GetOutermost()->IsDirty(), bPackageWasDirty);
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
196
|
+
FMCPInstancedStructWrapperArrayPathTest,
|
|
197
|
+
"UE.MCP.Editor.Property.InstancedStruct.WrapperArrayPaths",
|
|
198
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
199
|
+
|
|
200
|
+
bool FMCPInstancedStructWrapperArrayPathTest::RunTest(const FString& Parameters)
|
|
201
|
+
{
|
|
202
|
+
FMCPHandlerRegistry Registry;
|
|
203
|
+
FEditorHandlers::RegisterHandlers(Registry);
|
|
204
|
+
TestTrue(TEXT("live setter is registered"), Registry.HasHandler(TEXT("set_object_property")));
|
|
205
|
+
TestTrue(TEXT("asset setter is registered"), Registry.HasHandler(TEXT("set_property")));
|
|
206
|
+
TestTrue(TEXT("asset getter is registered"), Registry.HasHandler(TEXT("get_property")));
|
|
207
|
+
|
|
208
|
+
TStrongObjectPtr<UUEMCPInstancedStructPathTestObject> Fixture = MakeFixture();
|
|
209
|
+
UPackage* FixturePackage = Fixture->GetOutermost();
|
|
210
|
+
TestFalse(TEXT("isolated fixture package starts clean"), FixturePackage->IsDirty());
|
|
211
|
+
FUEMCPInstancedStructPathPayload& Op0 = Fixture->OpStack[0].GetMutable<FUEMCPInstancedStructPathPayload>();
|
|
212
|
+
FUEMCPInstancedStructPathPayload& Op1 = Fixture->OpStack[1].GetMutable<FUEMCPInstancedStructPathPayload>();
|
|
213
|
+
|
|
214
|
+
const TSharedPtr<FJsonObject> LiveResponse = ExecuteSet(
|
|
215
|
+
Registry,
|
|
216
|
+
*Fixture,
|
|
217
|
+
TEXT("OpStack[0].Scalar"),
|
|
218
|
+
MakeShared<FJsonValueNumber>(151));
|
|
219
|
+
TestTrue(TEXT("live setter reaches OpStack[0] payload scalar"), IsSuccess(LiveResponse));
|
|
220
|
+
TestEqual(TEXT("selected wrapper scalar changes"), Op0.Scalar, 151);
|
|
221
|
+
TestEqual(TEXT("unselected wrapper scalar stays unchanged"), Op1.Scalar, 201);
|
|
222
|
+
TestEqual(TEXT("selected wrapper sibling stays unchanged"), Op0.Sibling, FString(TEXT("op-0-sibling")));
|
|
223
|
+
TestEqual(TEXT("selected wrapper nested data stays unchanged"), Op0.Elements[1].Scalar, 121);
|
|
224
|
+
TestFalse(TEXT("live setter leaves isolated package clean"), FixturePackage->IsDirty());
|
|
225
|
+
|
|
226
|
+
const TSharedPtr<FJsonObject> AssetResponse = ExecuteAssetSet(
|
|
227
|
+
Registry,
|
|
228
|
+
*Fixture,
|
|
229
|
+
TEXT("OpStack[1].Elements[1].Scalar"),
|
|
230
|
+
MakeShared<FJsonValueNumber>(299));
|
|
231
|
+
TestTrue(TEXT("asset setter reaches nested OpStack payload leaf"), IsSuccess(AssetResponse));
|
|
232
|
+
if (AssetResponse.IsValid())
|
|
233
|
+
{
|
|
234
|
+
bool bSaved = true;
|
|
235
|
+
FString PropertyName;
|
|
236
|
+
TestTrue(TEXT("asset setter reports saved state"), AssetResponse->TryGetBoolField(TEXT("saved"), bSaved));
|
|
237
|
+
TestFalse(TEXT("asset setter honors save=false"), bSaved);
|
|
238
|
+
TestTrue(TEXT("asset setter reports property path"), AssetResponse->TryGetStringField(TEXT("propertyName"), PropertyName));
|
|
239
|
+
TestEqual(TEXT("asset setter preserves exact property path"), PropertyName, FString(TEXT("OpStack[1].Elements[1].Scalar")));
|
|
240
|
+
}
|
|
241
|
+
TestEqual(TEXT("nested selected leaf changes"), Op1.Elements[1].Scalar, 299);
|
|
242
|
+
TestEqual(TEXT("same wrapper other element stays unchanged"), Op1.Elements[0].Scalar, 211);
|
|
243
|
+
TestEqual(TEXT("nested selected element sibling stays unchanged"), Op1.Elements[1].Sibling, FString(TEXT("op-1-second-sibling")));
|
|
244
|
+
TestEqual(TEXT("same wrapper top-level sibling stays unchanged"), Op1.Sibling, FString(TEXT("op-1-sibling")));
|
|
245
|
+
TestEqual(TEXT("other wrapper retains prior selected write"), Op0.Scalar, 151);
|
|
246
|
+
TestTrue(TEXT("asset setter leaves save=false package dirty in memory"), FixturePackage->IsDirty());
|
|
247
|
+
|
|
248
|
+
const TSharedPtr<FJsonObject> ReadResponse = ExecuteAssetGet(
|
|
249
|
+
Registry,
|
|
250
|
+
*Fixture,
|
|
251
|
+
TEXT("OpStack[1].Elements[1].Scalar"));
|
|
252
|
+
TestTrue(TEXT("asset getter reads nested OpStack payload leaf"), IsSuccess(ReadResponse));
|
|
253
|
+
if (ReadResponse.IsValid())
|
|
254
|
+
{
|
|
255
|
+
double ReadValue = 0.0;
|
|
256
|
+
FString LeafPropertyName;
|
|
257
|
+
TestTrue(TEXT("asset getter reports numeric leaf"), ReadResponse->TryGetNumberField(TEXT("value"), ReadValue));
|
|
258
|
+
TestEqual(TEXT("asset getter returns updated leaf"), ReadValue, 299.0);
|
|
259
|
+
TestTrue(TEXT("asset getter reports leaf name"), ReadResponse->TryGetStringField(TEXT("leafPropertyName"), LeafPropertyName));
|
|
260
|
+
TestEqual(TEXT("asset getter resolves scalar leaf"), LeafPropertyName, FString(TEXT("Scalar")));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
FixturePackage->SetDirtyFlag(false);
|
|
264
|
+
TestFalse(TEXT("test clears its isolated package dirtiness"), FixturePackage->IsDirty());
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
269
|
+
FMCPInstancedStructPathRefusalTest,
|
|
270
|
+
"UE.MCP.Editor.SetObjectProperty.InstancedStruct.PathRefusals",
|
|
271
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
272
|
+
|
|
273
|
+
bool FMCPInstancedStructPathRefusalTest::RunTest(const FString& Parameters)
|
|
274
|
+
{
|
|
275
|
+
FMCPHandlerRegistry Registry;
|
|
276
|
+
FEditorHandlers::RegisterHandlers(Registry);
|
|
277
|
+
TStrongObjectPtr<UUEMCPInstancedStructPathTestObject> Fixture = MakeFixture();
|
|
278
|
+
FUEMCPInstancedStructPathPayload& Payload = Fixture->Payload.GetMutable<FUEMCPInstancedStructPathPayload>();
|
|
279
|
+
const bool bPackageWasDirty = Fixture->GetOutermost()->IsDirty();
|
|
280
|
+
|
|
281
|
+
const TSharedPtr<FJsonObject> EmptyResponse = ExecuteSet(
|
|
282
|
+
Registry,
|
|
283
|
+
*Fixture,
|
|
284
|
+
TEXT("EmptyPayload.Scalar"),
|
|
285
|
+
MakeShared<FJsonValueNumber>(7));
|
|
286
|
+
TestTrue(TEXT("empty wrapper returns a response"), EmptyResponse.IsValid());
|
|
287
|
+
TestFalse(TEXT("empty wrapper write fails"), IsSuccess(EmptyResponse));
|
|
288
|
+
if (EmptyResponse.IsValid())
|
|
289
|
+
{
|
|
290
|
+
FString Error;
|
|
291
|
+
TestTrue(TEXT("empty wrapper response reports an error"), EmptyResponse->TryGetStringField(TEXT("error"), Error));
|
|
292
|
+
TestTrue(TEXT("empty wrapper failure identifies the wrapper"), Error.Contains(TEXT("EmptyPayload")));
|
|
293
|
+
TestTrue(TEXT("empty wrapper failure is deterministic"), Error.Contains(TEXT("payload is empty - cannot descend")));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
Fixture->OpStack.AddDefaulted();
|
|
297
|
+
const TSharedPtr<FJsonObject> EmptyIndexedResponse = ExecuteSet(
|
|
298
|
+
Registry,
|
|
299
|
+
*Fixture,
|
|
300
|
+
TEXT("OpStack[2].Scalar"),
|
|
301
|
+
MakeShared<FJsonValueNumber>(7));
|
|
302
|
+
TestTrue(TEXT("empty indexed wrapper returns a response"), EmptyIndexedResponse.IsValid());
|
|
303
|
+
TestFalse(TEXT("empty indexed wrapper write fails"), IsSuccess(EmptyIndexedResponse));
|
|
304
|
+
if (EmptyIndexedResponse.IsValid())
|
|
305
|
+
{
|
|
306
|
+
FString Error;
|
|
307
|
+
TestTrue(TEXT("empty indexed wrapper response reports an error"), EmptyIndexedResponse->TryGetStringField(TEXT("error"), Error));
|
|
308
|
+
TestTrue(TEXT("empty indexed wrapper failure preserves the index"), Error.Contains(TEXT("OpStack[2]")));
|
|
309
|
+
TestTrue(TEXT("empty indexed wrapper failure is deterministic"), Error.Contains(TEXT("payload is empty - cannot descend")));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const TSharedPtr<FJsonObject> UnknownFieldResponse = ExecuteSet(
|
|
313
|
+
Registry,
|
|
314
|
+
*Fixture,
|
|
315
|
+
TEXT("Payload.UnknownField"),
|
|
316
|
+
MakeShared<FJsonValueNumber>(8));
|
|
317
|
+
TestTrue(TEXT("unknown payload field returns a response"), UnknownFieldResponse.IsValid());
|
|
318
|
+
TestFalse(TEXT("unknown payload field fails"), IsSuccess(UnknownFieldResponse));
|
|
319
|
+
if (UnknownFieldResponse.IsValid())
|
|
320
|
+
{
|
|
321
|
+
FString Error;
|
|
322
|
+
TestTrue(TEXT("unknown payload response reports an error"), UnknownFieldResponse->TryGetStringField(TEXT("error"), Error));
|
|
323
|
+
TestTrue(TEXT("unknown payload failure names the field"), Error.Contains(TEXT("UnknownField")));
|
|
324
|
+
TestTrue(TEXT("unknown payload failure names the full path"), Error.Contains(TEXT("Payload.UnknownField")));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const TSharedPtr<FJsonObject> UnknownNestedResponse = ExecuteSet(
|
|
328
|
+
Registry,
|
|
329
|
+
*Fixture,
|
|
330
|
+
TEXT("Payload.Elements[0].MissingLeaf"),
|
|
331
|
+
MakeShared<FJsonValueNumber>(9));
|
|
332
|
+
TestTrue(TEXT("unknown nested path returns a response"), UnknownNestedResponse.IsValid());
|
|
333
|
+
TestFalse(TEXT("unknown nested path fails"), IsSuccess(UnknownNestedResponse));
|
|
334
|
+
if (UnknownNestedResponse.IsValid())
|
|
335
|
+
{
|
|
336
|
+
FString Error;
|
|
337
|
+
TestTrue(TEXT("unknown nested response reports an error"), UnknownNestedResponse->TryGetStringField(TEXT("error"), Error));
|
|
338
|
+
TestTrue(
|
|
339
|
+
TEXT("unknown nested path failure names the missing leaf"),
|
|
340
|
+
Error.Contains(TEXT("MissingLeaf")));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
TSharedPtr<FJsonObject> ValidFirstElement = MakeShared<FJsonObject>();
|
|
344
|
+
ValidFirstElement->SetNumberField(TEXT("Scalar"), 777);
|
|
345
|
+
TSharedPtr<FJsonObject> InvalidSecondElement = MakeShared<FJsonObject>();
|
|
346
|
+
InvalidSecondElement->SetBoolField(TEXT("UnknownField"), true);
|
|
347
|
+
TArray<TSharedPtr<FJsonValue>> PartiallyInvalidElements;
|
|
348
|
+
PartiallyInvalidElements.Add(MakeShared<FJsonValueObject>(ValidFirstElement));
|
|
349
|
+
PartiallyInvalidElements.Add(MakeShared<FJsonValueObject>(InvalidSecondElement));
|
|
350
|
+
const TSharedPtr<FJsonObject> PartialArrayResponse = ExecuteSet(
|
|
351
|
+
Registry,
|
|
352
|
+
*Fixture,
|
|
353
|
+
TEXT("OpStack[0].Elements"),
|
|
354
|
+
MakeShared<FJsonValueArray>(PartiallyInvalidElements));
|
|
355
|
+
TestTrue(TEXT("partially invalid array write returns a response"), PartialArrayResponse.IsValid());
|
|
356
|
+
TestFalse(TEXT("partially invalid array write fails"), IsSuccess(PartialArrayResponse));
|
|
357
|
+
if (PartialArrayResponse.IsValid())
|
|
358
|
+
{
|
|
359
|
+
FString Error;
|
|
360
|
+
TestTrue(TEXT("partially invalid array response reports an error"), PartialArrayResponse->TryGetStringField(TEXT("error"), Error));
|
|
361
|
+
TestTrue(TEXT("partially invalid array error identifies item one"), Error.Contains(TEXT("[1]")));
|
|
362
|
+
TestTrue(TEXT("partially invalid array error names the bad field"), Error.Contains(TEXT("UnknownField")));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
TestEqual(TEXT("failed writes leave scalar unchanged"), Payload.Scalar, 11);
|
|
366
|
+
TestEqual(TEXT("failed writes leave nested scalar unchanged"), Payload.Elements[0].Scalar, 21);
|
|
367
|
+
TestEqual(TEXT("failed writes leave payload sibling unchanged"), Payload.Sibling, FString(TEXT("payload-sibling")));
|
|
368
|
+
TestEqual(TEXT("failed writes leave nested sibling unchanged"), Payload.Elements[0].Sibling, FString(TEXT("first-sibling")));
|
|
369
|
+
const FUEMCPInstancedStructPathPayload& Op0 = Fixture->OpStack[0].Get<FUEMCPInstancedStructPathPayload>();
|
|
370
|
+
TestEqual(TEXT("failed array conversion restores element count"), Op0.Elements.Num(), 2);
|
|
371
|
+
TestEqual(TEXT("failed array conversion rolls first scalar back"), Op0.Elements[0].Scalar, 111);
|
|
372
|
+
TestEqual(TEXT("failed array conversion preserves first sibling"), Op0.Elements[0].Sibling, FString(TEXT("op-0-first-sibling")));
|
|
373
|
+
TestEqual(TEXT("failed array conversion restores second scalar"), Op0.Elements[1].Scalar, 121);
|
|
374
|
+
TestEqual(TEXT("failed array conversion preserves second sibling"), Op0.Elements[1].Sibling, FString(TEXT("op-0-second-sibling")));
|
|
375
|
+
TestEqual(TEXT("failed writes do not dirty the isolated package"), Fixture->GetOutermost()->IsDirty(), bPackageWasDirty);
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
#endif
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// Coverage for the material handlers that is safe to run anywhere.
|
|
2
|
+
//
|
|
3
|
+
// run_automation_tests dispatches every EditorContext/EngineFilter test in the
|
|
4
|
+
// process when it is called without a filter, against whatever project the
|
|
5
|
+
// bridge is attached to. A test that created materials or wrote to textures
|
|
6
|
+
// would therefore mutate a user's live project as a side effect of asking for a
|
|
7
|
+
// test run. Everything below is confined to registration and to argument
|
|
8
|
+
// validation, all of which are specified to write nothing; the build, sampler
|
|
9
|
+
// selection and mesh assignment behaviour is exercised by the smoke suite
|
|
10
|
+
// against the dedicated test project instead.
|
|
11
|
+
|
|
12
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
13
|
+
|
|
14
|
+
#include "HandlerRegistry.h"
|
|
15
|
+
#include "Handlers/MaterialHandlers.h"
|
|
16
|
+
#include "Misc/AutomationTest.h"
|
|
17
|
+
#include "UObject/Class.h"
|
|
18
|
+
#include "Engine/EngineTypes.h"
|
|
19
|
+
|
|
20
|
+
namespace
|
|
21
|
+
{
|
|
22
|
+
TSharedPtr<FJsonObject> MaterialTestsRunHandler(
|
|
23
|
+
FMCPHandlerRegistry& Registry,
|
|
24
|
+
const FString& Method,
|
|
25
|
+
const TSharedPtr<FJsonObject>& Request)
|
|
26
|
+
{
|
|
27
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(Method, Request);
|
|
28
|
+
if (Response.IsValid() && Response->Type == EJson::Object)
|
|
29
|
+
{
|
|
30
|
+
return Response->AsObject();
|
|
31
|
+
}
|
|
32
|
+
return nullptr;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
37
|
+
FMaterialBuildRegistrationTest,
|
|
38
|
+
"UE.MCP.Material.BuildMaterial.RegistrationAndValidation",
|
|
39
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
40
|
+
|
|
41
|
+
bool FMaterialBuildRegistrationTest::RunTest(const FString& Parameters)
|
|
42
|
+
{
|
|
43
|
+
FMCPHandlerRegistry Registry;
|
|
44
|
+
FMaterialHandlers::RegisterHandlers(Registry);
|
|
45
|
+
|
|
46
|
+
TestTrue(TEXT("build_material is registered"), Registry.HasHandler(TEXT("build_material")));
|
|
47
|
+
|
|
48
|
+
// No texture set at all.
|
|
49
|
+
if (TSharedPtr<FJsonObject> Missing = MaterialTestsRunHandler(Registry, TEXT("build_material"), MakeShared<FJsonObject>()))
|
|
50
|
+
{
|
|
51
|
+
TestFalse(TEXT("a missing texture set is rejected"), Missing->GetBoolField(TEXT("success")));
|
|
52
|
+
TestTrue(TEXT("the rejection names the field"), Missing->GetStringField(TEXT("error")).Contains(TEXT("textures")));
|
|
53
|
+
}
|
|
54
|
+
else
|
|
55
|
+
{
|
|
56
|
+
AddError(TEXT("build_material did not return an object for a missing texture set"));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// An empty texture set is a different mistake and gets its own message.
|
|
60
|
+
{
|
|
61
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
62
|
+
Request->SetObjectField(TEXT("textures"), MakeShared<FJsonObject>());
|
|
63
|
+
if (TSharedPtr<FJsonObject> Empty = MaterialTestsRunHandler(Registry, TEXT("build_material"), Request))
|
|
64
|
+
{
|
|
65
|
+
TestFalse(TEXT("an empty texture set is rejected"), Empty->GetBoolField(TEXT("success")));
|
|
66
|
+
TestTrue(TEXT("the rejection says the set is empty"), Empty->GetStringField(TEXT("error")).Contains(TEXT("empty")));
|
|
67
|
+
}
|
|
68
|
+
else
|
|
69
|
+
{
|
|
70
|
+
AddError(TEXT("build_material did not return an object for an empty texture set"));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// A key that is not a material property is rejected before anything is
|
|
75
|
+
// created, and the message lists what is accepted.
|
|
76
|
+
{
|
|
77
|
+
TSharedPtr<FJsonObject> Textures = MakeShared<FJsonObject>();
|
|
78
|
+
Textures->SetStringField(TEXT("notAProperty"), TEXT("/Game/DoesNotExist"));
|
|
79
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
80
|
+
Request->SetObjectField(TEXT("textures"), Textures);
|
|
81
|
+
Request->SetStringField(TEXT("name"), TEXT("M_UEMCP_BuildValidationOnly"));
|
|
82
|
+
if (TSharedPtr<FJsonObject> Bad = MaterialTestsRunHandler(Registry, TEXT("build_material"), Request))
|
|
83
|
+
{
|
|
84
|
+
TestFalse(TEXT("an unknown property key is rejected"), Bad->GetBoolField(TEXT("success")));
|
|
85
|
+
TestTrue(TEXT("the rejection names the offending key"), Bad->GetStringField(TEXT("error")).Contains(TEXT("notAProperty")));
|
|
86
|
+
TestTrue(TEXT("the rejection lists the packed keys"), Bad->GetStringField(TEXT("error")).Contains(TEXT("orm")));
|
|
87
|
+
}
|
|
88
|
+
else
|
|
89
|
+
{
|
|
90
|
+
AddError(TEXT("build_material did not return an object for an unknown property key"));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// A known key whose texture does not exist fails on the texture, not on the
|
|
95
|
+
// key, and still creates nothing.
|
|
96
|
+
{
|
|
97
|
+
TSharedPtr<FJsonObject> Textures = MakeShared<FJsonObject>();
|
|
98
|
+
Textures->SetStringField(TEXT("baseColor"), TEXT("/Game/UEMCPTests/DoesNotExist_UEMCP"));
|
|
99
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
100
|
+
Request->SetObjectField(TEXT("textures"), Textures);
|
|
101
|
+
Request->SetStringField(TEXT("name"), TEXT("M_UEMCP_BuildValidationOnly"));
|
|
102
|
+
if (TSharedPtr<FJsonObject> Bad = MaterialTestsRunHandler(Registry, TEXT("build_material"), Request))
|
|
103
|
+
{
|
|
104
|
+
TestFalse(TEXT("a missing texture is rejected"), Bad->GetBoolField(TEXT("success")));
|
|
105
|
+
TestTrue(TEXT("the rejection names the texture path"), Bad->GetStringField(TEXT("error")).Contains(TEXT("DoesNotExist_UEMCP")));
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
{
|
|
109
|
+
AddError(TEXT("build_material did not return an object for a missing texture"));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
117
|
+
FMaterialVirtualSamplerPairingTest,
|
|
118
|
+
"UE.MCP.Material.BuildMaterial.VirtualSamplerTypesArePaired",
|
|
119
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
120
|
+
|
|
121
|
+
bool FMaterialVirtualSamplerPairingTest::RunTest(const FString& Parameters)
|
|
122
|
+
{
|
|
123
|
+
// The whole point of build_material is picking the virtual variant of a
|
|
124
|
+
// sampler type for a virtual (UDIM) texture. That correction is only
|
|
125
|
+
// possible while every plain type the builder can produce has a virtual
|
|
126
|
+
// counterpart in the engine enum, so assert the pairing the builder relies
|
|
127
|
+
// on rather than trusting it to stay true across engine versions.
|
|
128
|
+
const EMaterialSamplerType PlainTypes[] =
|
|
129
|
+
{
|
|
130
|
+
SAMPLERTYPE_Color,
|
|
131
|
+
SAMPLERTYPE_Grayscale,
|
|
132
|
+
SAMPLERTYPE_Alpha,
|
|
133
|
+
SAMPLERTYPE_Normal,
|
|
134
|
+
SAMPLERTYPE_Masks,
|
|
135
|
+
SAMPLERTYPE_LinearColor,
|
|
136
|
+
SAMPLERTYPE_LinearGrayscale,
|
|
137
|
+
};
|
|
138
|
+
const EMaterialSamplerType VirtualTypes[] =
|
|
139
|
+
{
|
|
140
|
+
SAMPLERTYPE_VirtualColor,
|
|
141
|
+
SAMPLERTYPE_VirtualGrayscale,
|
|
142
|
+
SAMPLERTYPE_VirtualAlpha,
|
|
143
|
+
SAMPLERTYPE_VirtualNormal,
|
|
144
|
+
SAMPLERTYPE_VirtualMasks,
|
|
145
|
+
SAMPLERTYPE_VirtualLinearColor,
|
|
146
|
+
SAMPLERTYPE_VirtualLinearGrayscale,
|
|
147
|
+
};
|
|
148
|
+
static_assert(UE_ARRAY_COUNT(PlainTypes) == UE_ARRAY_COUNT(VirtualTypes), "sampler pairing table is lopsided");
|
|
149
|
+
|
|
150
|
+
for (int32 Index = 0; Index < (int32)UE_ARRAY_COUNT(PlainTypes); ++Index)
|
|
151
|
+
{
|
|
152
|
+
TestFalse(
|
|
153
|
+
FString::Printf(TEXT("plain sampler %d does not read as virtual"), Index),
|
|
154
|
+
IsVirtualSamplerType(PlainTypes[Index]));
|
|
155
|
+
TestTrue(
|
|
156
|
+
FString::Printf(TEXT("virtual sampler %d reads as virtual"), Index),
|
|
157
|
+
IsVirtualSamplerType(VirtualTypes[Index]));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
UEnum* Enum = StaticEnum<EMaterialSamplerType>();
|
|
161
|
+
TestNotNull(TEXT("EMaterialSamplerType is reflected, so sampler types can be named in results"), Enum);
|
|
162
|
+
if (Enum)
|
|
163
|
+
{
|
|
164
|
+
TestEqual(
|
|
165
|
+
TEXT("the virtual colour sampler still carries the name the builder reports"),
|
|
166
|
+
Enum->GetNameStringByValue((int64)SAMPLERTYPE_VirtualColor),
|
|
167
|
+
FString(TEXT("SAMPLERTYPE_VirtualColor")));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
174
|
+
FMaterialInstanceReadRegistrationTest,
|
|
175
|
+
"UE.MCP.Material.Instance.ReadAndParameterValidation",
|
|
176
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
177
|
+
|
|
178
|
+
bool FMaterialInstanceReadRegistrationTest::RunTest(const FString& Parameters)
|
|
179
|
+
{
|
|
180
|
+
FMCPHandlerRegistry Registry;
|
|
181
|
+
FMaterialHandlers::RegisterHandlers(Registry);
|
|
182
|
+
|
|
183
|
+
TestTrue(TEXT("read_material is registered"), Registry.HasHandler(TEXT("read_material")));
|
|
184
|
+
TestTrue(TEXT("list_material_parameters is registered"), Registry.HasHandler(TEXT("list_material_parameters")));
|
|
185
|
+
TestTrue(TEXT("set_material_parameter is registered"), Registry.HasHandler(TEXT("set_material_parameter")));
|
|
186
|
+
|
|
187
|
+
// #952: a path that is neither a Material nor a MaterialInstance now says so
|
|
188
|
+
// in one message, rather than claiming the asset is not a material when the
|
|
189
|
+
// real cause was that only a base Material was ever accepted.
|
|
190
|
+
{
|
|
191
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
192
|
+
Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCPTests/DoesNotExist_UEMCP"));
|
|
193
|
+
const TCHAR* ReadMethods[] = { TEXT("read_material"), TEXT("list_material_parameters") };
|
|
194
|
+
for (const TCHAR* Method : ReadMethods)
|
|
195
|
+
{
|
|
196
|
+
if (TSharedPtr<FJsonObject> Response = MaterialTestsRunHandler(Registry, Method, Request))
|
|
197
|
+
{
|
|
198
|
+
TestFalse(FString::Printf(TEXT("%s rejects a missing asset"), Method), Response->GetBoolField(TEXT("success")));
|
|
199
|
+
TestTrue(
|
|
200
|
+
FString::Printf(TEXT("%s says both kinds were tried"), Method),
|
|
201
|
+
Response->GetStringField(TEXT("error")).Contains(TEXT("material instance")));
|
|
202
|
+
}
|
|
203
|
+
else
|
|
204
|
+
{
|
|
205
|
+
AddError(FString::Printf(TEXT("%s did not return an object for a missing asset"), Method));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// set_material_parameter still names its required arguments.
|
|
211
|
+
{
|
|
212
|
+
if (TSharedPtr<FJsonObject> Response = MaterialTestsRunHandler(Registry, TEXT("set_material_parameter"), MakeShared<FJsonObject>()))
|
|
213
|
+
{
|
|
214
|
+
TestFalse(TEXT("a parameter write with no asset path is rejected"), Response->GetBoolField(TEXT("success")));
|
|
215
|
+
TestTrue(TEXT("the rejection names assetPath"), Response->GetStringField(TEXT("error")).Contains(TEXT("assetPath")));
|
|
216
|
+
}
|
|
217
|
+
else
|
|
218
|
+
{
|
|
219
|
+
AddError(TEXT("set_material_parameter did not return an object for an empty request"));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
223
|
+
Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCPTests/DoesNotExist_UEMCP"));
|
|
224
|
+
if (TSharedPtr<FJsonObject> Response = MaterialTestsRunHandler(Registry, TEXT("set_material_parameter"), Request))
|
|
225
|
+
{
|
|
226
|
+
TestFalse(TEXT("a parameter write with no parameter name is rejected"), Response->GetBoolField(TEXT("success")));
|
|
227
|
+
TestTrue(TEXT("the rejection names parameterName"), Response->GetStringField(TEXT("error")).Contains(TEXT("parameterName")));
|
|
228
|
+
}
|
|
229
|
+
else
|
|
230
|
+
{
|
|
231
|
+
AddError(TEXT("set_material_parameter did not return an object for a request with no parameter name"));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
#endif
|