@mflrevan/ucp 0.5.1 → 0.6.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 +1 -1
- package/bridge/com.ucp.bridge/Editor/AssemblyInfo.cs +3 -0
- package/bridge/com.ucp.bridge/Editor/AssemblyInfo.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Bridge/BridgeServer.cs +16 -3
- package/bridge/com.ucp.bridge/Editor/Compatibility/UnityObjectCompat.cs +26 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/AssetController.cs +172 -2
- package/bridge/com.ucp.bridge/Editor/Controllers/CompilationController.cs +88 -1
- package/bridge/com.ucp.bridge/Editor/Controllers/EditorModalGuard.cs +60 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/EditorModalGuard.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/HierarchyController.cs +56 -5
- package/bridge/com.ucp.bridge/Editor/Controllers/LogsController.cs +325 -13
- package/bridge/com.ucp.bridge/Editor/Controllers/MaterialController.cs +2 -2
- package/bridge/com.ucp.bridge/Editor/Controllers/ObjectLocator.cs +207 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/ObjectLocator.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/ObjectReferenceResolver.cs +1 -1
- package/bridge/com.ucp.bridge/Editor/Controllers/PlayModeController.cs +14 -35
- package/bridge/com.ucp.bridge/Editor/Controllers/PrefabController.cs +3 -3
- package/bridge/com.ucp.bridge/Editor/Controllers/PropertyController.cs +1 -1
- package/bridge/com.ucp.bridge/Editor/Controllers/ReferenceController.cs +1 -1
- package/bridge/com.ucp.bridge/Editor/Controllers/SceneChangeTracker.cs +6 -6
- package/bridge/com.ucp.bridge/Editor/Controllers/SceneController.cs +2 -34
- package/bridge/com.ucp.bridge/Editor/Controllers/ShaderController.cs +151 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/ShaderController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/SnapshotController.cs +304 -9
- package/bridge/com.ucp.bridge/Editor/Controllers/SpatialController.cs +322 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/SpatialController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/TransformController.cs +249 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/TransformController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/ViewController.cs +415 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/ViewController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Tests/Editor/ControllerSmokeTests.cs +135 -7
- package/bridge/com.ucp.bridge/Tests/Editor/SpatialVisualControllerTests.cs +252 -0
- package/bridge/com.ucp.bridge/Tests/Editor/SpatialVisualControllerTests.cs.meta +2 -0
- package/bridge/com.ucp.bridge/package.json +1 -1
- package/package.json +1 -1
|
@@ -435,6 +435,78 @@ namespace UCP.Bridge.Tests
|
|
|
435
435
|
Assert.That(Convert.ToInt32(byLevel["error"]), Is.EqualTo(1));
|
|
436
436
|
}
|
|
437
437
|
|
|
438
|
+
[Test]
|
|
439
|
+
public void LogsStatus_BackfillsConsoleEntriesWhenHistoryStartsEmpty()
|
|
440
|
+
{
|
|
441
|
+
LogsController.SetConsoleBackfillProviderForTests(() => new List<LogsController.ConsoleBackfillEntry>
|
|
442
|
+
{
|
|
443
|
+
new LogsController.ConsoleBackfillEntry { Level = "warning", Message = "Compiler warning" },
|
|
444
|
+
new LogsController.ConsoleBackfillEntry { Level = "error", Message = "Compiler error" }
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
var response = _router.Dispatch("logs/status", 1, "{}");
|
|
448
|
+
|
|
449
|
+
Assert.That(response.error, Is.Null);
|
|
450
|
+
|
|
451
|
+
var result = (Dictionary<string, object>)response.result;
|
|
452
|
+
Assert.That(Convert.ToInt32(result["total"]), Is.EqualTo(2));
|
|
453
|
+
|
|
454
|
+
var byLevel = (Dictionary<string, object>)result["byLevel"];
|
|
455
|
+
Assert.That(Convert.ToInt32(byLevel["warning"]), Is.EqualTo(1));
|
|
456
|
+
Assert.That(Convert.ToInt32(byLevel["error"]), Is.EqualTo(1));
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
[Test]
|
|
460
|
+
public void LogsTail_BackfillIgnoresUcpBridgeNoise()
|
|
461
|
+
{
|
|
462
|
+
LogsController.SetConsoleBackfillProviderForTests(() => new List<LogsController.ConsoleBackfillEntry>
|
|
463
|
+
{
|
|
464
|
+
new LogsController.ConsoleBackfillEntry { Level = "info", Message = "[UCP] Bridge server started on port 21342" },
|
|
465
|
+
new LogsController.ConsoleBackfillEntry { Level = "warning", Message = "User-facing warning" }
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
var response = _router.Dispatch("logs/tail", 1, "{\"count\":20}");
|
|
469
|
+
|
|
470
|
+
Assert.That(response.error, Is.Null);
|
|
471
|
+
|
|
472
|
+
var result = (Dictionary<string, object>)response.result;
|
|
473
|
+
Assert.That(Convert.ToInt32(result["total"]), Is.EqualTo(1));
|
|
474
|
+
|
|
475
|
+
var logs = (List<object>)result["logs"];
|
|
476
|
+
var only = (Dictionary<string, object>)logs[0];
|
|
477
|
+
Assert.That(only["messagePreview"], Is.EqualTo("User-facing warning"));
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
[Test]
|
|
481
|
+
public void LogsGet_CanReadBackfilledConsoleEntries()
|
|
482
|
+
{
|
|
483
|
+
LogsController.SetConsoleBackfillProviderForTests(() => new List<LogsController.ConsoleBackfillEntry>
|
|
484
|
+
{
|
|
485
|
+
new LogsController.ConsoleBackfillEntry
|
|
486
|
+
{
|
|
487
|
+
Level = "exception",
|
|
488
|
+
Message = "Backfilled exception",
|
|
489
|
+
StackTrace = "stack line 1\nstack line 2"
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
var tail = _router.Dispatch("logs/tail", 1, "{\"count\":20}");
|
|
494
|
+
Assert.That(tail.error, Is.Null);
|
|
495
|
+
|
|
496
|
+
var tailResult = (Dictionary<string, object>)tail.result;
|
|
497
|
+
var logs = (List<object>)tailResult["logs"];
|
|
498
|
+
var first = (Dictionary<string, object>)logs[0];
|
|
499
|
+
var id = Convert.ToInt64(first["id"]);
|
|
500
|
+
|
|
501
|
+
var get = _router.Dispatch("logs/get", 1, "{\"id\":" + id + "}");
|
|
502
|
+
Assert.That(get.error, Is.Null);
|
|
503
|
+
|
|
504
|
+
var getResult = (Dictionary<string, object>)get.result;
|
|
505
|
+
Assert.That(getResult["level"], Is.EqualTo("exception"));
|
|
506
|
+
Assert.That(getResult["message"], Is.EqualTo("Backfilled exception"));
|
|
507
|
+
Assert.That(getResult["stackTrace"], Is.EqualTo("stack line 1\nstack line 2"));
|
|
508
|
+
}
|
|
509
|
+
|
|
438
510
|
[Test]
|
|
439
511
|
public void ObjectLifecycle_CreateMutateAndDelete_WorksEndToEnd()
|
|
440
512
|
{
|
|
@@ -472,7 +544,7 @@ namespace UCP.Bridge.Tests
|
|
|
472
544
|
);
|
|
473
545
|
Assert.That(getPosition.error, Is.Null);
|
|
474
546
|
|
|
475
|
-
var updated =
|
|
547
|
+
var updated = UnityObjectCompat.ResolveByInstanceId(instanceId) as GameObject;
|
|
476
548
|
Assert.That(updated, Is.Not.Null);
|
|
477
549
|
var localPosition = updated.transform.localPosition;
|
|
478
550
|
Assert.That(localPosition.x, Is.EqualTo(1f).Within(0.001f));
|
|
@@ -484,7 +556,7 @@ namespace UCP.Bridge.Tests
|
|
|
484
556
|
|
|
485
557
|
var delete = _router.Dispatch("object/delete", 1, "{\"instanceId\":" + instanceId + "}");
|
|
486
558
|
Assert.That(delete.error, Is.Null);
|
|
487
|
-
Assert.That(
|
|
559
|
+
Assert.That(UnityObjectCompat.ResolveByInstanceId(instanceId), Is.Null);
|
|
488
560
|
}
|
|
489
561
|
|
|
490
562
|
[Test]
|
|
@@ -501,7 +573,7 @@ namespace UCP.Bridge.Tests
|
|
|
501
573
|
var response = _router.Dispatch(
|
|
502
574
|
"object/set-property",
|
|
503
575
|
1,
|
|
504
|
-
"{\"instanceId\":" + go.
|
|
576
|
+
"{\"instanceId\":" + go.GetId() + ",\"component\":\"ReferenceComponent\",\"property\":\"referenceAsset\",\"value\":{\"path\":\"" + TempReferenceAssetPath + "\"}}"
|
|
505
577
|
);
|
|
506
578
|
|
|
507
579
|
Assert.That(response.error, Is.Null);
|
|
@@ -526,7 +598,7 @@ namespace UCP.Bridge.Tests
|
|
|
526
598
|
var response = _router.Dispatch(
|
|
527
599
|
"object/set-property",
|
|
528
600
|
1,
|
|
529
|
-
"{\"instanceId\":" + cube.
|
|
601
|
+
"{\"instanceId\":" + cube.GetId() + ",\"component\":\"MeshRenderer\",\"property\":\"m_Materials\",\"value\":[{\"path\":\"" + TempMaterialPath + "\"}]}"
|
|
530
602
|
);
|
|
531
603
|
|
|
532
604
|
Assert.That(response.error, Is.Null);
|
|
@@ -544,7 +616,7 @@ namespace UCP.Bridge.Tests
|
|
|
544
616
|
var response = _router.Dispatch(
|
|
545
617
|
"object/set-property",
|
|
546
618
|
1,
|
|
547
|
-
"{\"instanceId\":" + go.
|
|
619
|
+
"{\"instanceId\":" + go.GetId() + ",\"component\":\"ReferenceComponent\",\"property\":\"referenceAsset\",\"value\":{\"path\":\"Assets/Missing.asset\"}}"
|
|
548
620
|
);
|
|
549
621
|
|
|
550
622
|
Assert.That(response.error, Is.Not.Null);
|
|
@@ -918,6 +990,62 @@ namespace UCP.Bridge.Tests
|
|
|
918
990
|
Assert.That(UnityEngine.SceneManagement.SceneManager.GetSceneByPath(TempSceneBPath).isLoaded, Is.True);
|
|
919
991
|
}
|
|
920
992
|
|
|
993
|
+
[Test]
|
|
994
|
+
public void ModalGuard_AutoSavesDirtyTitledScene_WithoutPrompting()
|
|
995
|
+
{
|
|
996
|
+
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
|
997
|
+
Assert.That(EditorSceneManager.SaveScene(scene, TempScenePath), Is.True);
|
|
998
|
+
new GameObject("ModalGuardDirtyMaker");
|
|
999
|
+
EditorSceneManager.MarkSceneDirty(scene);
|
|
1000
|
+
Assert.That(scene.isDirty, Is.True);
|
|
1001
|
+
|
|
1002
|
+
EditorModalGuard.SaveOpenDirtyScenes(true, true);
|
|
1003
|
+
|
|
1004
|
+
Assert.That(scene.isDirty, Is.False);
|
|
1005
|
+
Assert.That(string.IsNullOrEmpty(scene.path), Is.False);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
[Test]
|
|
1009
|
+
public void ModalGuard_DiscardsDirtyUntitledScene_WhenDiscardAllowed()
|
|
1010
|
+
{
|
|
1011
|
+
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
|
1012
|
+
new GameObject("ModalGuardUntitledDirtyMaker");
|
|
1013
|
+
EditorSceneManager.MarkSceneDirty(scene);
|
|
1014
|
+
Assert.That(string.IsNullOrEmpty(scene.path), Is.True);
|
|
1015
|
+
Assert.That(scene.isDirty, Is.True);
|
|
1016
|
+
|
|
1017
|
+
EditorModalGuard.SaveOpenDirtyScenes(true, true);
|
|
1018
|
+
|
|
1019
|
+
var active = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
|
1020
|
+
Assert.That(string.IsNullOrEmpty(active.path), Is.True);
|
|
1021
|
+
Assert.That(active.isDirty, Is.False);
|
|
1022
|
+
Assert.That(GameObject.Find("ModalGuardUntitledDirtyMaker"), Is.Null);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
[Test]
|
|
1026
|
+
public void ModalGuard_ThrowsOnDirtyUntitledScene_WhenDiscardDisallowed()
|
|
1027
|
+
{
|
|
1028
|
+
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
|
1029
|
+
new GameObject("ModalGuardUntitledDirtyMaker");
|
|
1030
|
+
EditorSceneManager.MarkSceneDirty(scene);
|
|
1031
|
+
|
|
1032
|
+
Assert.That(
|
|
1033
|
+
() => EditorModalGuard.SaveOpenDirtyScenes(true, false),
|
|
1034
|
+
Throws.TypeOf<System.InvalidOperationException>());
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
[Test]
|
|
1038
|
+
public void ModalGuard_LeavesSceneUntouched_WhenSavingDisabled()
|
|
1039
|
+
{
|
|
1040
|
+
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
|
1041
|
+
new GameObject("ModalGuardUntitledDirtyMaker");
|
|
1042
|
+
EditorSceneManager.MarkSceneDirty(scene);
|
|
1043
|
+
Assert.That(scene.isDirty, Is.True);
|
|
1044
|
+
|
|
1045
|
+
Assert.That(() => EditorModalGuard.SaveOpenDirtyScenes(false, false), Throws.Nothing);
|
|
1046
|
+
Assert.That(UnityEngine.SceneManagement.SceneManager.GetActiveScene().isDirty, Is.True);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
921
1049
|
[Test]
|
|
922
1050
|
public void PackagesController_DependencySetInfoAndRemove_LocalFilePackage()
|
|
923
1051
|
{
|
|
@@ -999,7 +1127,7 @@ namespace UCP.Bridge.Tests
|
|
|
999
1127
|
var response = _router.Dispatch(
|
|
1000
1128
|
"scene/focus",
|
|
1001
1129
|
1,
|
|
1002
|
-
"{\"instanceId\":" + cube.
|
|
1130
|
+
"{\"instanceId\":" + cube.GetId() + ",\"axis\":[1,0,1]}"
|
|
1003
1131
|
);
|
|
1004
1132
|
|
|
1005
1133
|
Assert.That(response.error, Is.Null);
|
|
@@ -1030,7 +1158,7 @@ namespace UCP.Bridge.Tests
|
|
|
1030
1158
|
var response = _router.Dispatch(
|
|
1031
1159
|
"scene/focus",
|
|
1032
1160
|
1,
|
|
1033
|
-
"{\"instanceId\":" + cube.
|
|
1161
|
+
"{\"instanceId\":" + cube.GetId() + ",\"axis\":[0,0,0]}"
|
|
1034
1162
|
);
|
|
1035
1163
|
|
|
1036
1164
|
Assert.That(response.error, Is.Not.Null);
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using NUnit.Framework;
|
|
4
|
+
using UnityEditor.SceneManagement;
|
|
5
|
+
using UnityEngine;
|
|
6
|
+
using UnityEngine.Rendering;
|
|
7
|
+
using UnityEngine.SceneManagement;
|
|
8
|
+
|
|
9
|
+
namespace UCP.Bridge.Tests
|
|
10
|
+
{
|
|
11
|
+
/// <summary>
|
|
12
|
+
/// Edit-mode coverage for the spatial/visual controllers added for in-scene authoring:
|
|
13
|
+
/// TransformController, SpatialController, ViewController, and the shared ObjectLocator.
|
|
14
|
+
/// </summary>
|
|
15
|
+
public class SpatialVisualControllerTests
|
|
16
|
+
{
|
|
17
|
+
private CommandRouter _router;
|
|
18
|
+
private readonly List<GameObject> _spawned = new();
|
|
19
|
+
|
|
20
|
+
[SetUp]
|
|
21
|
+
public void SetUp()
|
|
22
|
+
{
|
|
23
|
+
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
|
24
|
+
_router = new CommandRouter();
|
|
25
|
+
HierarchyController.Register(_router);
|
|
26
|
+
TransformController.Register(_router);
|
|
27
|
+
SpatialController.Register(_router);
|
|
28
|
+
ViewController.Register(_router);
|
|
29
|
+
_spawned.Clear();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
[TearDown]
|
|
33
|
+
public void TearDown()
|
|
34
|
+
{
|
|
35
|
+
foreach (var go in _spawned)
|
|
36
|
+
if (go != null) UnityEngine.Object.DestroyImmediate(go);
|
|
37
|
+
_spawned.Clear();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private GameObject Spawn(PrimitiveType type, string name, Vector3 pos)
|
|
41
|
+
{
|
|
42
|
+
var go = GameObject.CreatePrimitive(type);
|
|
43
|
+
go.name = name;
|
|
44
|
+
go.transform.position = pos;
|
|
45
|
+
_spawned.Add(go);
|
|
46
|
+
return go;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private static Dictionary<string, object> Result(JsonRpcResponse r)
|
|
50
|
+
{
|
|
51
|
+
Assert.That(r.error, Is.Null, "RPC returned an error");
|
|
52
|
+
return (Dictionary<string, object>)r.result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private static float F(object o) => Convert.ToSingle(o);
|
|
56
|
+
private static List<object> Vec(Dictionary<string, object> d, string key) => (List<object>)d[key];
|
|
57
|
+
|
|
58
|
+
// --- Primitive builder ---------------------------------------------
|
|
59
|
+
|
|
60
|
+
[Test]
|
|
61
|
+
public void Object_CreatePrimitive_AddsMeshAndCollider()
|
|
62
|
+
{
|
|
63
|
+
var res = Result(_router.Dispatch("object/create", 1,
|
|
64
|
+
"{\"name\":\"Greybox\",\"primitive\":\"Cube\"}"));
|
|
65
|
+
var id = Convert.ToInt32(res["instanceId"]);
|
|
66
|
+
var go = ObjectLocator.FindByInstanceId(id);
|
|
67
|
+
_spawned.Add(go);
|
|
68
|
+
|
|
69
|
+
Assert.That(go, Is.Not.Null);
|
|
70
|
+
Assert.That(go.name, Is.EqualTo("Greybox"));
|
|
71
|
+
Assert.That(go.GetComponent<MeshFilter>(), Is.Not.Null, "primitive should have a mesh");
|
|
72
|
+
Assert.That(go.GetComponent<MeshFilter>().sharedMesh, Is.Not.Null);
|
|
73
|
+
Assert.That(go.GetComponent<Collider>(), Is.Not.Null, "primitive should have a collider");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
[Test]
|
|
77
|
+
public void Object_CreatePrimitive_RejectsUnknownType()
|
|
78
|
+
{
|
|
79
|
+
var res = _router.Dispatch("object/create", 1,
|
|
80
|
+
"{\"name\":\"Bad\",\"primitive\":\"Dodecahedron\"}");
|
|
81
|
+
Assert.That(res.error, Is.Not.Null, "unknown primitive must be an InvalidParams error, not a crash");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
[Test]
|
|
85
|
+
public void Object_Instantiate_PrimitiveName_RedirectsToPrimitiveFlag()
|
|
86
|
+
{
|
|
87
|
+
// The exact wrong path a weak agent took: treating "PrimitiveType.Cube" as a prefab.
|
|
88
|
+
var res = _router.Dispatch("object/instantiate", 1,
|
|
89
|
+
"{\"prefab\":\"PrimitiveType.Cube\"}");
|
|
90
|
+
Assert.That(res.error, Is.Not.Null);
|
|
91
|
+
Assert.That(res.error.message, Does.Contain("--primitive"),
|
|
92
|
+
"the not-found error should redirect to object create --primitive");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// --- Transform -----------------------------------------------------
|
|
96
|
+
|
|
97
|
+
[Test]
|
|
98
|
+
public void Transform_Move_WorldAbsoluteAndRelative()
|
|
99
|
+
{
|
|
100
|
+
var go = Spawn(PrimitiveType.Cube, "Mover", Vector3.zero);
|
|
101
|
+
var id = go.GetId();
|
|
102
|
+
|
|
103
|
+
var abs = Result(_router.Dispatch("transform/move", 1,
|
|
104
|
+
"{\"instanceId\":" + id + ",\"position\":[5,0,0],\"space\":\"world\"}"));
|
|
105
|
+
Assert.That(go.transform.position.x, Is.EqualTo(5f).Within(0.001f));
|
|
106
|
+
Assert.That(F(Vec(abs, "position")[0]), Is.EqualTo(5f).Within(0.001f));
|
|
107
|
+
|
|
108
|
+
_router.Dispatch("transform/move", 1,
|
|
109
|
+
"{\"instanceId\":" + id + ",\"position\":[1,0,0],\"relative\":true}");
|
|
110
|
+
Assert.That(go.transform.position.x, Is.EqualTo(6f).Within(0.001f));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
[Test]
|
|
114
|
+
public void Transform_Rotate_AbsoluteEulerWorld()
|
|
115
|
+
{
|
|
116
|
+
var go = Spawn(PrimitiveType.Cube, "Rotor", Vector3.zero);
|
|
117
|
+
_router.Dispatch("transform/rotate", 1,
|
|
118
|
+
"{\"instanceId\":" + go.GetId() + ",\"euler\":[0,90,0]}");
|
|
119
|
+
Assert.That(go.transform.eulerAngles.y, Is.EqualTo(90f).Within(0.01f));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
[Test]
|
|
123
|
+
public void Transform_Scale_UniformAndNonUniform()
|
|
124
|
+
{
|
|
125
|
+
var go = Spawn(PrimitiveType.Cube, "Scaler", Vector3.zero);
|
|
126
|
+
_router.Dispatch("transform/scale", 1, "{\"instanceId\":" + go.GetId() + ",\"uniform\":2}");
|
|
127
|
+
Assert.That(go.transform.localScale, Is.EqualTo(Vector3.one * 2f));
|
|
128
|
+
|
|
129
|
+
_router.Dispatch("transform/scale", 1, "{\"instanceId\":" + go.GetId() + ",\"scale\":[1,3,1],\"relative\":true}");
|
|
130
|
+
Assert.That(go.transform.localScale.y, Is.EqualTo(6f).Within(0.001f));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
[Test]
|
|
134
|
+
public void Transform_LookAt_FacesWorldPoint()
|
|
135
|
+
{
|
|
136
|
+
var go = Spawn(PrimitiveType.Cube, "Looker", Vector3.zero);
|
|
137
|
+
_router.Dispatch("transform/look-at", 1,
|
|
138
|
+
"{\"instanceId\":" + go.GetId() + ",\"target\":[10,0,0]}");
|
|
139
|
+
// forward should point along +X
|
|
140
|
+
Assert.That(Vector3.Dot(go.transform.forward, Vector3.right), Is.GreaterThan(0.99f));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
[Test]
|
|
144
|
+
public void Transform_Get_BulkReadByIds()
|
|
145
|
+
{
|
|
146
|
+
var a = Spawn(PrimitiveType.Cube, "A", new Vector3(1, 0, 0));
|
|
147
|
+
var b = Spawn(PrimitiveType.Cube, "B", new Vector3(2, 0, 0));
|
|
148
|
+
var res = Result(_router.Dispatch("transform/get", 1,
|
|
149
|
+
"{\"ids\":[" + a.GetId() + "," + b.GetId() + "]}"));
|
|
150
|
+
Assert.That(Convert.ToInt32(res["count"]), Is.EqualTo(2));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// --- ObjectLocator -------------------------------------------------
|
|
154
|
+
|
|
155
|
+
[Test]
|
|
156
|
+
public void Locator_ResolvesByNameAndPath()
|
|
157
|
+
{
|
|
158
|
+
var root = Spawn(PrimitiveType.Cube, "Root", Vector3.zero);
|
|
159
|
+
var child = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
160
|
+
child.name = "Child";
|
|
161
|
+
child.transform.SetParent(root.transform, false);
|
|
162
|
+
_spawned.Add(child);
|
|
163
|
+
|
|
164
|
+
_router.Dispatch("transform/move", 1, "{\"name\":\"Child\",\"position\":[0,4,0]}");
|
|
165
|
+
Assert.That(child.transform.position.y, Is.EqualTo(4f).Within(0.001f));
|
|
166
|
+
|
|
167
|
+
_router.Dispatch("transform/move", 1, "{\"path\":\"Root/Child\",\"position\":[0,7,0]}");
|
|
168
|
+
Assert.That(child.transform.position.y, Is.EqualTo(7f).Within(0.001f));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// --- Spatial -------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
[Test]
|
|
174
|
+
public void Spatial_Raycast_HitsColliderBelow()
|
|
175
|
+
{
|
|
176
|
+
var ground = Spawn(PrimitiveType.Cube, "Ground", Vector3.zero);
|
|
177
|
+
ground.transform.localScale = new Vector3(20, 1, 20);
|
|
178
|
+
|
|
179
|
+
var res = Result(_router.Dispatch("physics/raycast", 1,
|
|
180
|
+
"{\"origin\":[0,5,0],\"direction\":[0,-1,0]}"));
|
|
181
|
+
Assert.That(Convert.ToBoolean(res["hit"]), Is.True);
|
|
182
|
+
Assert.That(Convert.ToInt32(res["instanceId"]), Is.EqualTo(ground.GetId()));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
[Test]
|
|
186
|
+
public void Spatial_Overlap_FindsSphereOverlap()
|
|
187
|
+
{
|
|
188
|
+
var box = Spawn(PrimitiveType.Cube, "Box", Vector3.zero);
|
|
189
|
+
var res = Result(_router.Dispatch("physics/overlap", 1,
|
|
190
|
+
"{\"shape\":\"sphere\",\"center\":[0,0,0],\"radius\":2}"));
|
|
191
|
+
Assert.That(Convert.ToInt32(res["count"]), Is.GreaterThanOrEqualTo(1));
|
|
192
|
+
_ = box;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
[Test]
|
|
196
|
+
public void Spatial_Bounds_ReturnsWorldAabb()
|
|
197
|
+
{
|
|
198
|
+
var go = Spawn(PrimitiveType.Cube, "Bounded", new Vector3(3, 0, 0));
|
|
199
|
+
var res = Result(_router.Dispatch("object/bounds", 1, "{\"instanceId\":" + go.GetId() + "}"));
|
|
200
|
+
Assert.That(F(Vec(res, "center")[0]), Is.EqualTo(3f).Within(0.01f));
|
|
201
|
+
Assert.That(Convert.ToBoolean(res["empty"]), Is.False);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
[Test]
|
|
205
|
+
public void Spatial_Ground_DropsObjectOntoSurface()
|
|
206
|
+
{
|
|
207
|
+
var ground = Spawn(PrimitiveType.Cube, "Floor", Vector3.zero);
|
|
208
|
+
ground.transform.localScale = new Vector3(20, 1, 20); // top surface at y=0.5
|
|
209
|
+
var cube = Spawn(PrimitiveType.Cube, "Falling", new Vector3(0, 8, 0));
|
|
210
|
+
|
|
211
|
+
var res = Result(_router.Dispatch("spatial/ground", 1,
|
|
212
|
+
"{\"instanceId\":" + cube.GetId() + ",\"apply\":true}"));
|
|
213
|
+
Assert.That(Convert.ToBoolean(res["hit"]), Is.True);
|
|
214
|
+
// Cube (half-height 0.5) should rest with its centre at surface(0.5) + 0.5 = 1.0
|
|
215
|
+
Assert.That(cube.transform.position.y, Is.EqualTo(1f).Within(0.05f));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
[Test]
|
|
219
|
+
public void Spatial_Nearest_SortsByDistance()
|
|
220
|
+
{
|
|
221
|
+
Spawn(PrimitiveType.Cube, "Near", new Vector3(1, 0, 0));
|
|
222
|
+
Spawn(PrimitiveType.Cube, "Mid", new Vector3(5, 0, 0));
|
|
223
|
+
Spawn(PrimitiveType.Cube, "Far", new Vector3(20, 0, 0));
|
|
224
|
+
|
|
225
|
+
var res = Result(_router.Dispatch("spatial/nearest", 1,
|
|
226
|
+
"{\"point\":[0,0,0],\"max\":2}"));
|
|
227
|
+
var objects = (List<object>)res["objects"];
|
|
228
|
+
Assert.That(objects.Count, Is.EqualTo(2));
|
|
229
|
+
var first = (Dictionary<string, object>)objects[0];
|
|
230
|
+
Assert.That(first["name"].ToString(), Is.EqualTo("Near"));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// --- View ----------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
[Test]
|
|
236
|
+
public void View_Isolate_ProducesPngForSingleView()
|
|
237
|
+
{
|
|
238
|
+
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null)
|
|
239
|
+
Assert.Ignore("No graphics device (headless -nographics); skipping render test.");
|
|
240
|
+
|
|
241
|
+
var go = Spawn(PrimitiveType.Cube, "Hero", Vector3.zero);
|
|
242
|
+
var res = Result(_router.Dispatch("view/isolate", 1,
|
|
243
|
+
"{\"instanceId\":" + go.GetId() + ",\"views\":[\"front\"],\"maxEdge\":128}"));
|
|
244
|
+
|
|
245
|
+
Assert.That(res["encoding"].ToString(), Is.EqualTo("base64"));
|
|
246
|
+
Assert.That(res["data"].ToString().Length, Is.GreaterThan(0));
|
|
247
|
+
Assert.That(Convert.ToInt32(res["width"]), Is.EqualTo(128));
|
|
248
|
+
// Isolation must restore the object's original layer.
|
|
249
|
+
Assert.That(go.layer, Is.EqualTo(0));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|