com.wallstop-studios.unity-helpers 2.0.0 → 2.0.1

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.
Files changed (133) hide show
  1. package/.github/workflows/format-on-demand.yml +2 -2
  2. package/.github/workflows/markdown-json.yml +1 -1
  3. package/.github/workflows/npm-publish.yml +1 -1
  4. package/.github/workflows/prettier-autofix.yml +4 -4
  5. package/.github/workflows/yaml-format-lint.yml +1 -1
  6. package/Docs/EFFECTS_SYSTEM.md +1316 -0
  7. package/{EFFECTS_SYSTEM_TUTORIAL.md → Docs/EFFECTS_SYSTEM_TUTORIAL.md} +1 -1
  8. package/{GETTING_STARTED.md → Docs/GETTING_STARTED.md} +10 -8
  9. package/{GLOSSARY.md → Docs/GLOSSARY.md} +4 -4
  10. package/Docs/HELPER_UTILITIES.md +885 -0
  11. package/Docs/HELPER_UTILITIES.md.meta +7 -0
  12. package/{INDEX.md → Docs/INDEX.md} +107 -62
  13. package/Docs/MATH_AND_EXTENSIONS.md +1039 -0
  14. package/{RANDOM_PERFORMANCE.md → Docs/RANDOM_PERFORMANCE.md} +15 -15
  15. package/{RELATIONAL_COMPONENTS.md → Docs/RELATIONAL_COMPONENTS.md} +21 -3
  16. package/{SPATIAL_TREES_2D_GUIDE.md → Docs/SPATIAL_TREES_2D_GUIDE.md} +2 -2
  17. package/{SPATIAL_TREES_3D_GUIDE.md → Docs/SPATIAL_TREES_3D_GUIDE.md} +1 -1
  18. package/{SPATIAL_TREE_2D_PERFORMANCE.md → Docs/SPATIAL_TREE_2D_PERFORMANCE.md} +64 -64
  19. package/{SPATIAL_TREE_3D_PERFORMANCE.md → Docs/SPATIAL_TREE_3D_PERFORMANCE.md} +64 -64
  20. package/Docs/UTILITY_COMPONENTS.md +906 -0
  21. package/Docs/UTILITY_COMPONENTS.md.meta +7 -0
  22. package/Docs/VISUAL_COMPONENTS.md +337 -0
  23. package/Docs/VISUAL_COMPONENTS.md.meta +7 -0
  24. package/Editor/Sprites/AnimationCopier.cs +3 -3
  25. package/README.md +69 -62
  26. package/Runtime/AssemblyInfo.cs +2 -0
  27. package/Runtime/Core/DataStructure/KDTree3D.cs +1 -1
  28. package/Runtime/Core/DataStructure/OctTree3D.cs +1 -1
  29. package/Runtime/Core/Extension/AsyncOperationExtensions.cs +122 -0
  30. package/Runtime/Core/Serialization/ProtobufUnitySurrogates.cs +24 -29
  31. package/Runtime/Integrations/Reflex/AssemblyInfo.cs +7 -0
  32. package/Runtime/Integrations/Reflex/AssemblyInfo.cs.meta +11 -0
  33. package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs +198 -0
  34. package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs.meta +11 -0
  35. package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs +86 -0
  36. package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs.meta +11 -0
  37. package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs +316 -0
  38. package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs.meta +11 -0
  39. package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs +86 -0
  40. package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs.meta +11 -0
  41. package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef +20 -0
  42. package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef.meta +7 -0
  43. package/Runtime/Integrations/Reflex.meta +8 -0
  44. package/Runtime/Utils/ScriptableObjectSingleton.cs +1 -1
  45. package/Samples~/DI - Reflex/README.md +527 -0
  46. package/Samples~/DI - Reflex/README.md.meta +7 -0
  47. package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs +36 -0
  48. package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs.meta +11 -0
  49. package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs +79 -0
  50. package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs.meta +11 -0
  51. package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs +30 -0
  52. package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs.meta +11 -0
  53. package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs +79 -0
  54. package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs.meta +11 -0
  55. package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef +26 -0
  56. package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef.meta +9 -0
  57. package/Samples~/DI - Reflex/Scripts.meta +8 -0
  58. package/Samples~/DI - Reflex.meta +8 -0
  59. package/Samples~/DI - VContainer/README.md +6 -5
  60. package/Samples~/DI - Zenject/README.md +6 -5
  61. package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +29 -31
  62. package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs +41 -0
  63. package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs.meta +11 -0
  64. package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef +27 -0
  65. package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef.meta +7 -0
  66. package/Tests/Editor/Integrations/Reflex.meta +8 -0
  67. package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +15 -16
  68. package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +7 -13
  69. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +7 -11
  70. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +19 -21
  71. package/Tests/Editor/PersistentDirectorySettingsTests.cs +0 -1
  72. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +0 -1
  73. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -2
  74. package/Tests/Editor/Tools/ImageBlurToolTests.cs +1 -1
  75. package/Tests/Editor/Utils/CommonTestBase.cs +17 -0
  76. package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +1 -1
  77. package/Tests/Runtime/Extensions/AsyncOperationExtensionsTests.cs +179 -0
  78. package/Tests/Runtime/Extensions/RandomExtensionTests.cs +55 -0
  79. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +445 -0
  80. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs.meta +11 -0
  81. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef +28 -0
  82. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef.meta +7 -0
  83. package/Tests/Runtime/Integrations/Reflex.meta +8 -0
  84. package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +24 -29
  85. package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +8 -3
  86. package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +10 -20
  87. package/Tests/Runtime/Performance/RandomPerformanceTests.cs +1 -1
  88. package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +1 -1
  89. package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +1 -1
  90. package/Tests/Runtime/Serialization/JsonRoundtripComprehensiveTests.cs +4 -9
  91. package/Tests/Runtime/Serialization/ProtoRoundtripComprehensiveTests.cs +13 -13
  92. package/Tests/Runtime/TestUtils/CommonTestBase.cs +11 -0
  93. package/Tests/Runtime/TestUtils/ReflexTestSupport.cs +111 -0
  94. package/Tests/Runtime/TestUtils/ReflexTestSupport.cs.meta +12 -0
  95. package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +4 -4
  96. package/Tests/TestUtils.meta +8 -0
  97. package/package.json +6 -1
  98. package/EFFECTS_SYSTEM.md +0 -242
  99. package/MATH_AND_EXTENSIONS.md +0 -316
  100. /package/{CHANGELOG.md → Docs/CHANGELOG.md} +0 -0
  101. /package/{CHANGELOG.md.meta → Docs/CHANGELOG.md.meta} +0 -0
  102. /package/{CONTRIBUTING.md → Docs/CONTRIBUTING.md} +0 -0
  103. /package/{CONTRIBUTING.md.meta → Docs/CONTRIBUTING.md.meta} +0 -0
  104. /package/{DATA_STRUCTURES.md → Docs/DATA_STRUCTURES.md} +0 -0
  105. /package/{DATA_STRUCTURES.md.meta → Docs/DATA_STRUCTURES.md.meta} +0 -0
  106. /package/{EDITOR_TOOLS_GUIDE.md → Docs/EDITOR_TOOLS_GUIDE.md} +0 -0
  107. /package/{EDITOR_TOOLS_GUIDE.md.meta → Docs/EDITOR_TOOLS_GUIDE.md.meta} +0 -0
  108. /package/{EFFECTS_SYSTEM.md.meta → Docs/EFFECTS_SYSTEM.md.meta} +0 -0
  109. /package/{EFFECTS_SYSTEM_TUTORIAL.md.meta → Docs/EFFECTS_SYSTEM_TUTORIAL.md.meta} +0 -0
  110. /package/{GETTING_STARTED.md.meta → Docs/GETTING_STARTED.md.meta} +0 -0
  111. /package/{GLOSSARY.md.meta → Docs/GLOSSARY.md.meta} +0 -0
  112. /package/{HULLS.md → Docs/HULLS.md} +0 -0
  113. /package/{HULLS.md.meta → Docs/HULLS.md.meta} +0 -0
  114. /package/{INDEX.md.meta → Docs/INDEX.md.meta} +0 -0
  115. /package/{LICENSE.md → Docs/LICENSE.md} +0 -0
  116. /package/{LICENSE.md.meta → Docs/LICENSE.md.meta} +0 -0
  117. /package/{MATH_AND_EXTENSIONS.md.meta → Docs/MATH_AND_EXTENSIONS.md.meta} +0 -0
  118. /package/{RANDOM_PERFORMANCE.md.meta → Docs/RANDOM_PERFORMANCE.md.meta} +0 -0
  119. /package/{REFLECTION_HELPERS.md → Docs/REFLECTION_HELPERS.md} +0 -0
  120. /package/{REFLECTION_HELPERS.md.meta → Docs/REFLECTION_HELPERS.md.meta} +0 -0
  121. /package/{RELATIONAL_COMPONENTS.md.meta → Docs/RELATIONAL_COMPONENTS.md.meta} +0 -0
  122. /package/{SERIALIZATION.md → Docs/SERIALIZATION.md} +0 -0
  123. /package/{SERIALIZATION.md.meta → Docs/SERIALIZATION.md.meta} +0 -0
  124. /package/{SINGLETONS.md → Docs/SINGLETONS.md} +0 -0
  125. /package/{SINGLETONS.md.meta → Docs/SINGLETONS.md.meta} +0 -0
  126. /package/{SPATIAL_TREES_2D_GUIDE.md.meta → Docs/SPATIAL_TREES_2D_GUIDE.md.meta} +0 -0
  127. /package/{SPATIAL_TREES_3D_GUIDE.md.meta → Docs/SPATIAL_TREES_3D_GUIDE.md.meta} +0 -0
  128. /package/{SPATIAL_TREE_2D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_2D_PERFORMANCE.md.meta} +0 -0
  129. /package/{SPATIAL_TREE_3D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_3D_PERFORMANCE.md.meta} +0 -0
  130. /package/{SPATIAL_TREE_SEMANTICS.md → Docs/SPATIAL_TREE_SEMANTICS.md} +0 -0
  131. /package/{SPATIAL_TREE_SEMANTICS.md.meta → Docs/SPATIAL_TREE_SEMANTICS.md.meta} +0 -0
  132. /package/{THIRD_PARTY_NOTICES.md → Docs/THIRD_PARTY_NOTICES.md} +0 -0
  133. /package/{THIRD_PARTY_NOTICES.md.meta → Docs/THIRD_PARTY_NOTICES.md.meta} +0 -0
@@ -0,0 +1,445 @@
1
+ #if REFLEX_PRESENT
2
+ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Reflex
3
+ {
4
+ using System;
5
+ using System.Collections.Generic;
6
+ using global::Reflex.Core;
7
+ using NUnit.Framework;
8
+ using UnityEngine;
9
+ using UnityEngine.SceneManagement;
10
+ using UnityEngine.TestTools;
11
+ using WallstopStudios.UnityHelpers.Core.Attributes;
12
+ using WallstopStudios.UnityHelpers.Integrations.Reflex;
13
+ using WallstopStudios.UnityHelpers.Tags;
14
+ using WallstopStudios.UnityHelpers.Tests.TestUtils;
15
+
16
+ public sealed class RelationalComponentsReflexTests : CommonTestBase
17
+ {
18
+ [Test]
19
+ public void ContainerExtensionsUseBoundAssigner()
20
+ {
21
+ ContainerBuilder builder = new();
22
+ RecordingAssigner assigner = new();
23
+ builder.AddSingleton(assigner, typeof(IRelationalComponentAssigner));
24
+ builder.AddSingleton(CreateCacheFor(typeof(ReflexRelationalTester)));
25
+ Container container = builder.Build();
26
+
27
+ ReflexRelationalTester tester = CreateHierarchy();
28
+
29
+ container.AssignRelationalComponents(tester);
30
+
31
+ Assert.That(assigner.CallCount, Is.EqualTo(1), "Assigner should be invoked once.");
32
+ Assert.That(
33
+ assigner.LastComponent,
34
+ Is.SameAs(tester),
35
+ "Assigner should target the supplied component."
36
+ );
37
+ Assert.IsTrue(tester.parentBody != null, "ParentComponent field should be assigned.");
38
+ Assert.IsTrue(tester.childCollider != null, "ChildComponent field should be assigned.");
39
+ }
40
+
41
+ [Test]
42
+ public void ContainerExtensionsFallbackWithoutAssigner()
43
+ {
44
+ Container container = new ContainerBuilder().Build();
45
+
46
+ ReflexRelationalTester tester = CreateHierarchy();
47
+
48
+ container.AssignRelationalComponents(tester);
49
+
50
+ Assert.IsTrue(
51
+ tester.parentBody != null,
52
+ "Fallback path should assign parentBody using reflection."
53
+ );
54
+ Assert.IsTrue(
55
+ tester.childCollider != null,
56
+ "Fallback path should assign childCollider using reflection."
57
+ );
58
+ }
59
+
60
+ [UnityTest]
61
+ public System.Collections.IEnumerator AssignSceneHydratesComponents()
62
+ {
63
+ AttributeMetadataCache cache = CreateCacheFor(typeof(ReflexRelationalTester));
64
+ RecordingAssigner assigner = new();
65
+ Scene scene = CreateTempScene("ReflexAssignScene");
66
+ ReflexRelationalTester tester = CreateHierarchy();
67
+ GameObject root = tester.transform.root.gameObject;
68
+ SceneManager.MoveGameObjectToScene(root, scene);
69
+ yield return null;
70
+
71
+ RelationalReflexSceneBootstrapper.AssignScene(
72
+ scene,
73
+ assigner,
74
+ cache,
75
+ RelationalSceneAssignmentOptions.Default
76
+ );
77
+
78
+ Assert.That(assigner.CallCount, Is.EqualTo(1), "AssignScene should use the assigner.");
79
+ Assert.That(
80
+ assigner.LastComponent,
81
+ Is.SameAs(tester),
82
+ "AssignScene should hydrate relational tester component."
83
+ );
84
+ Assert.IsTrue(tester.parentBody != null, "ParentBody should be assigned after scan.");
85
+ Assert.IsTrue(
86
+ tester.childCollider != null,
87
+ "ChildCollider should be assigned after scan."
88
+ );
89
+ }
90
+
91
+ [UnityTest]
92
+ public System.Collections.IEnumerator InstallerBindsAssignerAndOptions()
93
+ {
94
+ AttributeMetadataCache cache = CreateCacheFor(typeof(ReflexRelationalTester));
95
+ System.Lazy<AttributeMetadataCache> previousLazy = AttributeMetadataCache.LazyInstance;
96
+ AttributeMetadataCache.LazyInstance = new System.Lazy<AttributeMetadataCache>(() =>
97
+ cache
98
+ );
99
+
100
+ Scene scene = CreateTempScene("ReflexInstallerScene");
101
+ GameObject installerObject = Track(new GameObject("ReflexInstaller"));
102
+ RelationalComponentsInstaller installer =
103
+ installerObject.AddComponent<RelationalComponentsInstaller>();
104
+ SceneManager.MoveGameObjectToScene(installerObject, scene);
105
+
106
+ ReflexRelationalTester tester = CreateHierarchy();
107
+ GameObject root = tester.transform.root.gameObject;
108
+ SceneManager.MoveGameObjectToScene(root, scene);
109
+ yield return null;
110
+
111
+ ContainerBuilder builder = new();
112
+ builder.SetName("ReflexTesterContainer");
113
+ installer.InstallBindings(builder);
114
+ try
115
+ {
116
+ Container container = builder.Build();
117
+ yield return null;
118
+
119
+ Assert.IsTrue(
120
+ container.HasBinding<IRelationalComponentAssigner>(),
121
+ "Installer should bind IRelationalComponentAssigner."
122
+ );
123
+
124
+ Assert.IsTrue(
125
+ tester.parentBody != null,
126
+ "Installer should hydrate parentBody via scene assignment."
127
+ );
128
+ Assert.IsTrue(
129
+ tester.childCollider != null,
130
+ "Installer should hydrate childCollider via scene assignment."
131
+ );
132
+ }
133
+ finally
134
+ {
135
+ AttributeMetadataCache.LazyInstance = previousLazy;
136
+ }
137
+ }
138
+
139
+ [UnityTest]
140
+ public System.Collections.IEnumerator InstantiateComponentWithRelationsUsesAssigner()
141
+ {
142
+ ContainerBuilder builder = new();
143
+ RecordingAssigner assigner = new();
144
+ builder.AddSingleton(assigner, typeof(IRelationalComponentAssigner));
145
+ builder.AddSingleton(CreateCacheFor(typeof(ReflexRelationalTester)));
146
+ Container container = builder.Build();
147
+
148
+ GameObject parent = Track(new GameObject("ReflexComponentParent"));
149
+ Rigidbody parentBody = parent.AddComponent<Rigidbody>();
150
+ parentBody.useGravity = false;
151
+
152
+ ReflexRelationalTester prefab = CreateComponentPrefabTester();
153
+ ReflexRelationalTester instance = container.InstantiateComponentWithRelations(
154
+ prefab,
155
+ parent.transform
156
+ );
157
+ Track(instance.gameObject);
158
+ instance.gameObject.SetActive(true);
159
+
160
+ yield return null;
161
+
162
+ Assert.That(
163
+ assigner.CallCount,
164
+ Is.GreaterThanOrEqualTo(1),
165
+ "InstantiateComponentWithRelations should invoke the assigner."
166
+ );
167
+ Assert.That(
168
+ assigner.AssignedComponents,
169
+ Does.Contain(instance),
170
+ "Assigner should receive the instantiated component."
171
+ );
172
+ Assert.That(
173
+ instance.parentBody,
174
+ Is.SameAs(parentBody),
175
+ "ParentComponent attribute should resolve the injected parent Rigidbody."
176
+ );
177
+ Assert.IsNotNull(
178
+ instance.childCollider,
179
+ "ChildComponent attribute should resolve the child collider on instantiation."
180
+ );
181
+ }
182
+
183
+ [UnityTest]
184
+ public System.Collections.IEnumerator InstantiateComponentWithRelationsFallsBackWithoutAssigner()
185
+ {
186
+ Container container = new ContainerBuilder().Build();
187
+
188
+ GameObject parent = Track(new GameObject("ReflexComponentParentFallback"));
189
+ Rigidbody parentBody = parent.AddComponent<Rigidbody>();
190
+ parentBody.useGravity = false;
191
+
192
+ ReflexRelationalTester prefab = CreateComponentPrefabTester();
193
+ ReflexRelationalTester instance = container.InstantiateComponentWithRelations(
194
+ prefab,
195
+ parent.transform
196
+ );
197
+ Track(instance.gameObject);
198
+ instance.gameObject.SetActive(true);
199
+
200
+ yield return null;
201
+
202
+ Assert.That(
203
+ instance.parentBody,
204
+ Is.SameAs(parentBody),
205
+ "Fallback path should assign the parent Rigidbody."
206
+ );
207
+ Assert.IsNotNull(
208
+ instance.childCollider,
209
+ "Fallback path should assign the child collider."
210
+ );
211
+ }
212
+
213
+ [UnityTest]
214
+ public System.Collections.IEnumerator InstantiateGameObjectWithRelationsUsesAssigner()
215
+ {
216
+ ContainerBuilder builder = new();
217
+ RecordingAssigner assigner = new();
218
+ builder.AddSingleton(assigner, typeof(IRelationalComponentAssigner));
219
+ builder.AddSingleton(CreateCacheFor(typeof(ReflexRelationalTester)));
220
+ Container container = builder.Build();
221
+
222
+ GameObject parent = Track(new GameObject("ReflexGameObjectParent"));
223
+ Rigidbody parentBody = parent.AddComponent<Rigidbody>();
224
+ parentBody.useGravity = false;
225
+
226
+ GameObject prefabRoot = CreateGameObjectPrefab();
227
+ GameObject instanceRoot = container.InstantiateGameObjectWithRelations(
228
+ prefabRoot,
229
+ parent.transform,
230
+ includeInactiveChildren: true
231
+ );
232
+ Track(instanceRoot);
233
+ instanceRoot.SetActive(true);
234
+
235
+ yield return null;
236
+
237
+ ReflexRelationalTester instanceTester =
238
+ instanceRoot.GetComponentInChildren<ReflexRelationalTester>(true);
239
+ Assert.NotNull(
240
+ instanceTester,
241
+ "Instantiated hierarchy should include the tester component."
242
+ );
243
+
244
+ Assert.That(
245
+ assigner.CallCount,
246
+ Is.GreaterThanOrEqualTo(1),
247
+ "InstantiateGameObjectWithRelations should invoke the assigner for hierarchy hydration."
248
+ );
249
+ Assert.That(
250
+ assigner.AssignedComponents,
251
+ Does.Contain(instanceTester),
252
+ "Assigner should hydrate the tester component inside the instantiated hierarchy."
253
+ );
254
+ Assert.That(
255
+ instanceTester.parentBody,
256
+ Is.SameAs(parentBody),
257
+ "ParentComponent attribute should bind to the supplied parent hierarchy."
258
+ );
259
+ Assert.IsNotNull(
260
+ instanceTester.childCollider,
261
+ "ChildComponent attribute should bind to the prefab child after instantiation."
262
+ );
263
+ }
264
+
265
+ [UnityTest]
266
+ public System.Collections.IEnumerator InstantiateGameObjectWithRelationsFallsBackWithoutAssigner()
267
+ {
268
+ Container container = new ContainerBuilder().Build();
269
+
270
+ GameObject parent = Track(new GameObject("ReflexGameObjectParentFallback"));
271
+ Rigidbody parentBody = parent.AddComponent<Rigidbody>();
272
+ parentBody.useGravity = false;
273
+
274
+ GameObject prefabRoot = CreateGameObjectPrefab();
275
+ GameObject instanceRoot = container.InstantiateGameObjectWithRelations(
276
+ prefabRoot,
277
+ parent.transform,
278
+ includeInactiveChildren: true
279
+ );
280
+ Track(instanceRoot);
281
+ instanceRoot.SetActive(true);
282
+
283
+ yield return null;
284
+
285
+ ReflexRelationalTester instanceTester =
286
+ instanceRoot.GetComponentInChildren<ReflexRelationalTester>(true);
287
+ Assert.NotNull(
288
+ instanceTester,
289
+ "Instantiated hierarchy should include the tester component."
290
+ );
291
+ Assert.That(
292
+ instanceTester.parentBody,
293
+ Is.SameAs(parentBody),
294
+ "Fallback path should assign the parent Rigidbody inside the instantiated hierarchy."
295
+ );
296
+ Assert.IsNotNull(
297
+ instanceTester.childCollider,
298
+ "Fallback path should assign the child collider inside the instantiated hierarchy."
299
+ );
300
+ }
301
+
302
+ private ReflexRelationalTester CreateHierarchy()
303
+ {
304
+ GameObject parent = Track(new GameObject("ReflexParent"));
305
+ Rigidbody parentBody = parent.AddComponent<Rigidbody>();
306
+ parentBody.useGravity = false;
307
+
308
+ GameObject middle = Track(new GameObject("ReflexMiddle"));
309
+ middle.transform.SetParent(parent.transform);
310
+ ReflexRelationalTester tester = middle.AddComponent<ReflexRelationalTester>();
311
+
312
+ GameObject child = Track(new GameObject("ReflexChild"));
313
+ child.transform.SetParent(middle.transform);
314
+ child.AddComponent<CapsuleCollider>();
315
+
316
+ return tester;
317
+ }
318
+
319
+ private ReflexRelationalTester CreateComponentPrefabTester()
320
+ {
321
+ GameObject root = Track(new GameObject("ReflexComponentPrefab"));
322
+ ReflexRelationalTester tester = root.AddComponent<ReflexRelationalTester>();
323
+
324
+ GameObject child = new GameObject("ReflexComponentPrefabChild");
325
+ child.AddComponent<CapsuleCollider>();
326
+ child.transform.SetParent(root.transform, false);
327
+
328
+ root.SetActive(false);
329
+ return tester;
330
+ }
331
+
332
+ private GameObject CreateGameObjectPrefab()
333
+ {
334
+ GameObject root = Track(new GameObject("ReflexGameObjectPrefab"));
335
+ root.AddComponent<ReflexRelationalTester>();
336
+
337
+ GameObject child = new GameObject("ReflexGameObjectPrefabChild");
338
+ child.AddComponent<CapsuleCollider>();
339
+ child.transform.SetParent(root.transform, false);
340
+
341
+ root.SetActive(false);
342
+ return root;
343
+ }
344
+
345
+ private AttributeMetadataCache CreateCacheFor(Type componentType)
346
+ {
347
+ AttributeMetadataCache cache = Track(
348
+ ScriptableObject.CreateInstance<AttributeMetadataCache>()
349
+ );
350
+
351
+ AttributeMetadataCache.RelationalFieldMetadata[] fields =
352
+ {
353
+ new(
354
+ nameof(ReflexRelationalTester.parentBody),
355
+ AttributeMetadataCache.RelationalAttributeKind.Parent,
356
+ AttributeMetadataCache.FieldKind.Single,
357
+ typeof(Rigidbody).AssemblyQualifiedName,
358
+ false
359
+ ),
360
+ new(
361
+ nameof(ReflexRelationalTester.childCollider),
362
+ AttributeMetadataCache.RelationalAttributeKind.Child,
363
+ AttributeMetadataCache.FieldKind.Single,
364
+ typeof(CapsuleCollider).AssemblyQualifiedName,
365
+ false
366
+ ),
367
+ };
368
+
369
+ AttributeMetadataCache.RelationalTypeMetadata[] relationalTypes =
370
+ {
371
+ new(componentType.AssemblyQualifiedName, fields),
372
+ };
373
+
374
+ cache._relationalTypeMetadata = relationalTypes;
375
+ cache.ForceRebuildForTests();
376
+ return cache;
377
+ }
378
+
379
+ private sealed class RecordingAssigner : IRelationalComponentAssigner
380
+ {
381
+ private readonly List<Component> _assignedComponents = new();
382
+
383
+ public int CallCount { get; private set; }
384
+
385
+ public Component LastComponent { get; private set; }
386
+
387
+ public IReadOnlyList<Component> AssignedComponents => _assignedComponents;
388
+
389
+ public bool HasRelationalAssignments(Type componentType)
390
+ {
391
+ return true;
392
+ }
393
+
394
+ public void Assign(Component component)
395
+ {
396
+ LastComponent = component;
397
+ CallCount++;
398
+ if (component != null)
399
+ {
400
+ _assignedComponents.Add(component);
401
+ }
402
+ component?.AssignRelationalComponents();
403
+ }
404
+
405
+ public void Assign(IEnumerable<Component> components)
406
+ {
407
+ if (components == null)
408
+ {
409
+ return;
410
+ }
411
+
412
+ foreach (Component component in components)
413
+ {
414
+ Assign(component);
415
+ }
416
+ }
417
+
418
+ public void AssignHierarchy(GameObject root, bool includeInactiveChildren = true)
419
+ {
420
+ if (root == null)
421
+ {
422
+ return;
423
+ }
424
+
425
+ Component[] components = root.GetComponentsInChildren<Component>(
426
+ includeInactiveChildren
427
+ );
428
+ for (int i = 0; i < components.Length; i++)
429
+ {
430
+ Assign(components[i]);
431
+ }
432
+ }
433
+ }
434
+
435
+ private sealed class ReflexRelationalTester : MonoBehaviour
436
+ {
437
+ [ParentComponent(OnlyAncestors = true)]
438
+ public Rigidbody parentBody;
439
+
440
+ [ChildComponent(OnlyDescendants = true)]
441
+ public CapsuleCollider childCollider;
442
+ }
443
+ }
444
+ }
445
+ #endif
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 5f80cea9bb684c5785835732e55f45cd
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "WallstopStudios.UnityHelpers.Tests.Runtime.Reflex",
3
+ "rootNamespace": "WallstopStudios.UnityHelpers.Tests.Integrations.Reflex",
4
+ "references": [
5
+ "UnityEditor.TestRunner",
6
+ "UnityEngine.TestRunner",
7
+ "WallstopStudios.UnityHelpers",
8
+ "WallstopStudios.UnityHelpers.Integration.Reflex",
9
+ "WallstopStudios.UnityHelpers.Tests.Runtime",
10
+ "WallstopStudios.UnityHelpers.Tests.Editor",
11
+ "Reflex"
12
+ ],
13
+ "includePlatforms": [],
14
+ "excludePlatforms": [],
15
+ "allowUnsafeCode": false,
16
+ "overrideReferences": true,
17
+ "precompiledReferences": ["nunit.framework.dll"],
18
+ "autoReferenced": false,
19
+ "defineConstraints": ["UNITY_INCLUDE_TESTS", "REFLEX_PRESENT"],
20
+ "versionDefines": [
21
+ {
22
+ "name": "com.gustavopsantos.reflex",
23
+ "expression": "0.0.1",
24
+ "define": "REFLEX_PRESENT"
25
+ }
26
+ ],
27
+ "noEngineReferences": false
28
+ }
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: 21e3440d83c64c6fa3aab578d5464e90
3
+ AssemblyDefinitionImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 7a4391f438bd4bf4b7146f7f933e18c5
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
@@ -15,6 +15,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
15
15
 
16
16
  public sealed class RelationalComponentsVContainerTests : CommonTestBase
17
17
  {
18
+ [SetUp]
19
+ public void CommonSetup()
20
+ {
21
+ ReflexTestSupport.EnsureReflexSettings();
22
+ }
23
+
18
24
  [Test]
19
25
  public void ResolverExtensionsUseBoundAssigner()
20
26
  {
@@ -74,7 +80,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
74
80
  Scene scene = CreateTempScene("VContainerTestScene_Active");
75
81
  ContainerBuilder builder = new();
76
82
  builder.RegisterInstance(cache).AsSelf();
77
- RecordingAssigner assigner = new RecordingAssigner();
83
+ RecordingAssigner assigner = new();
78
84
  builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
79
85
  IObjectResolver resolver = builder.Build();
80
86
 
@@ -83,7 +89,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
83
89
  SceneManager.MoveGameObjectToScene(rootObj, scene);
84
90
  yield return null;
85
91
 
86
- RelationalComponentEntryPoint entryPoint = new RelationalComponentEntryPoint(
92
+ RelationalComponentEntryPoint entryPoint = new(
87
93
  resolver.Resolve<IRelationalComponentAssigner>(),
88
94
  cache,
89
95
  RelationalSceneAssignmentOptions.Default
@@ -105,16 +111,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
105
111
  public System.Collections.IEnumerator SceneLoadListenerAssignsAdditiveSceneSinglePass()
106
112
  {
107
113
  AttributeMetadataCache cache = CreateCacheFor(typeof(VContainerRelationalTester));
108
- RelationalComponentAssigner assigner = new RelationalComponentAssigner(cache);
109
- RelationalSceneAssignmentOptions options = new RelationalSceneAssignmentOptions(
114
+ RelationalComponentAssigner assigner = new(cache);
115
+ RelationalSceneAssignmentOptions options = new(
110
116
  includeInactive: true,
111
117
  useSinglePassScan: true
112
118
  );
113
- RelationalSceneLoadListener listener = new RelationalSceneLoadListener(
114
- assigner,
115
- cache,
116
- options
117
- );
119
+ RelationalSceneLoadListener listener = new(assigner, cache, options);
118
120
  listener.Initialize();
119
121
  TrackDisposable(listener);
120
122
 
@@ -146,16 +148,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
146
148
  public System.Collections.IEnumerator SceneLoadListenerAssignsAdditiveSceneMultiPass()
147
149
  {
148
150
  AttributeMetadataCache cache = CreateCacheFor(typeof(VContainerRelationalTester));
149
- RelationalComponentAssigner assigner = new RelationalComponentAssigner(cache);
150
- RelationalSceneAssignmentOptions options = new RelationalSceneAssignmentOptions(
151
+ RelationalComponentAssigner assigner = new(cache);
152
+ RelationalSceneAssignmentOptions options = new(
151
153
  includeInactive: true,
152
154
  useSinglePassScan: false
153
155
  );
154
- RelationalSceneLoadListener listener = new RelationalSceneLoadListener(
155
- assigner,
156
- cache,
157
- options
158
- );
156
+ RelationalSceneLoadListener listener = new(assigner, cache, options);
159
157
  listener.Initialize();
160
158
  TrackDisposable(listener);
161
159
 
@@ -315,7 +313,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
315
313
  Scene active = CreateTempScene("VContainerActiveScene_Sep");
316
314
  ContainerBuilder builder = new();
317
315
  builder.RegisterInstance(cache).AsSelf();
318
- RecordingAssigner assigner = new RecordingAssigner();
316
+ RecordingAssigner assigner = new();
319
317
  builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
320
318
  IObjectResolver resolver = builder.Build();
321
319
 
@@ -327,7 +325,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
327
325
  SceneManager.MoveGameObjectToScene(testerB.transform.root.gameObject, secondary);
328
326
  yield return null;
329
327
 
330
- RelationalComponentEntryPoint entryPoint = new RelationalComponentEntryPoint(
328
+ RelationalComponentEntryPoint entryPoint = new(
331
329
  resolver.Resolve<IRelationalComponentAssigner>(),
332
330
  cache,
333
331
  new RelationalSceneAssignmentOptions(includeInactive: true)
@@ -393,7 +391,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
393
391
  Scene scene = CreateTempScene("VContainerTestScene_Inactive");
394
392
  ContainerBuilder builder = new();
395
393
  builder.RegisterInstance(cache).AsSelf();
396
- RecordingAssigner assigner = new RecordingAssigner();
394
+ RecordingAssigner assigner = new();
397
395
  builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
398
396
  IObjectResolver resolver = builder.Build();
399
397
 
@@ -403,7 +401,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
403
401
  SceneManager.MoveGameObjectToScene(rootObj, scene);
404
402
  yield return null;
405
403
 
406
- RelationalComponentEntryPoint disabledEntryPoint = new RelationalComponentEntryPoint(
404
+ RelationalComponentEntryPoint disabledEntryPoint = new(
407
405
  resolver.Resolve<IRelationalComponentAssigner>(),
408
406
  cache,
409
407
  new RelationalSceneAssignmentOptions(includeInactive: false)
@@ -418,7 +416,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
418
416
  "Disabled option should skip inactive components"
419
417
  );
420
418
 
421
- RelationalComponentEntryPoint enabledEntryPoint = new RelationalComponentEntryPoint(
419
+ RelationalComponentEntryPoint enabledEntryPoint = new(
422
420
  resolver.Resolve<IRelationalComponentAssigner>(),
423
421
  cache,
424
422
  new RelationalSceneAssignmentOptions(includeInactive: true)
@@ -442,7 +440,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
442
440
  Scene scene = CreateTempScene("VContainerMultiPassScene");
443
441
  ContainerBuilder builder = new();
444
442
  builder.RegisterInstance(cache).AsSelf();
445
- RecordingAssigner assigner = new RecordingAssigner();
443
+ RecordingAssigner assigner = new();
446
444
  builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
447
445
  IObjectResolver resolver = builder.Build();
448
446
 
@@ -450,7 +448,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
450
448
  SceneManager.MoveGameObjectToScene(tester.transform.root.gameObject, scene);
451
449
  yield return null;
452
450
 
453
- RelationalComponentEntryPoint entryPoint = new RelationalComponentEntryPoint(
451
+ RelationalComponentEntryPoint entryPoint = new(
454
452
  resolver.Resolve<IRelationalComponentAssigner>(),
455
453
  cache,
456
454
  new RelationalSceneAssignmentOptions(
@@ -497,14 +495,14 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
497
495
 
498
496
  AttributeMetadataCache.RelationalFieldMetadata[] fields =
499
497
  {
500
- new AttributeMetadataCache.RelationalFieldMetadata(
498
+ new(
501
499
  nameof(VContainerRelationalTester.parentBody),
502
500
  AttributeMetadataCache.RelationalAttributeKind.Parent,
503
501
  AttributeMetadataCache.FieldKind.Single,
504
502
  typeof(Rigidbody).AssemblyQualifiedName,
505
503
  isInterface: false
506
504
  ),
507
- new AttributeMetadataCache.RelationalFieldMetadata(
505
+ new(
508
506
  nameof(VContainerRelationalTester.childCollider),
509
507
  AttributeMetadataCache.RelationalAttributeKind.Child,
510
508
  AttributeMetadataCache.FieldKind.Single,
@@ -515,10 +513,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
515
513
 
516
514
  AttributeMetadataCache.RelationalTypeMetadata[] relationalTypes =
517
515
  {
518
- new AttributeMetadataCache.RelationalTypeMetadata(
519
- componentType.AssemblyQualifiedName,
520
- fields
521
- ),
516
+ new(componentType.AssemblyQualifiedName, fields),
522
517
  };
523
518
 
524
519
  cache._relationalTypeMetadata = relationalTypes;