com.wallstop-studios.unity-helpers 2.0.0-rc81.9 → 2.0.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.
Files changed (168) hide show
  1. package/.editorconfig +1 -1
  2. package/.gitattributes +1 -1
  3. package/.githooks/pre-commit +31 -5
  4. package/.githooks/pre-push +50 -0
  5. package/.github/dependabot.yml +24 -2
  6. package/.github/scripts/check-markdown-links.ps1 +77 -0
  7. package/.github/scripts/check_markdown_links.py +89 -0
  8. package/.github/scripts/check_markdown_url_encoding.py +74 -0
  9. package/.github/scripts/validate_markdown_links.py +194 -0
  10. package/.github/workflows/csharpier-autofix.yml +152 -0
  11. package/.github/workflows/format-on-demand.yml +305 -0
  12. package/.github/workflows/lint-doc-links.yml +8 -5
  13. package/.github/workflows/markdown-json.yml +6 -2
  14. package/.github/workflows/prettier-autofix.yml +195 -0
  15. package/.github/workflows/update-dotnet-tools.yml +80 -0
  16. package/.github/workflows/yaml-format-lint.yml +41 -0
  17. package/.lychee.toml +4 -4
  18. package/.markdownlint.jsonc +21 -0
  19. package/.pre-commit-config.yaml +11 -3
  20. package/.yamllint.yaml +31 -0
  21. package/AGENTS.md +5 -1
  22. package/CHANGELOG.md +11 -0
  23. package/CONTRIBUTING.md +49 -0
  24. package/CONTRIBUTING.md.meta +7 -0
  25. package/EDITOR_TOOLS_GUIDE.md +4 -0
  26. package/Editor/AnimationEventEditor.cs +337 -160
  27. package/Editor/Core/Helper/AnimationEventHelpers.cs +178 -152
  28. package/Editor/CustomEditors/PersistentDirectoryGUI.cs +20 -11
  29. package/Editor/CustomEditors/TexturePlatformOverrideEntryDrawer.cs +11 -2
  30. package/Editor/FitTextureSizeWindow.cs +43 -19
  31. package/Editor/PersistentDirectorySettings.cs +64 -12
  32. package/Editor/PrefabChecker.cs +72 -5
  33. package/Editor/Sprites/AnimationCopier.cs +132 -56
  34. package/Editor/Sprites/AnimationCreator.cs +63 -22
  35. package/Editor/Sprites/AnimationViewerWindow.cs +42 -6
  36. package/Editor/Sprites/TexturePlatformNameHelper.cs +50 -39
  37. package/Editor/Sprites/TextureResizerWizard.cs +23 -1
  38. package/Editor/Sprites/TextureSettingsApplierWindow.cs +148 -85
  39. package/Editor/Tools/ImageBlurTool.cs +81 -10
  40. package/Editor/Utils/EditorUi.cs +1 -1
  41. package/Editor/Utils/ScriptableObjectSingletonCreator.cs +1 -1
  42. package/GETTING_STARTED.md +40 -56
  43. package/RANDOM_PERFORMANCE.md +12 -12
  44. package/README.md +395 -2407
  45. package/RELATIONAL_COMPONENTS.md +92 -83
  46. package/Runtime/AssemblyInfo.cs +2 -0
  47. package/Runtime/Core/Attributes/NotNullAttribute.cs +1 -3
  48. package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +50 -5
  49. package/Runtime/Core/DataStructure/CyclicBuffer.cs +0 -1
  50. package/Runtime/Core/Extension/RandomExtensions.cs +68 -0
  51. package/Runtime/Core/Extension/WallstopStudiosLogger.cs +16 -0
  52. package/Runtime/Core/Helper/Partials/ObjectHelpers.cs +4 -1
  53. package/Runtime/Core/Helper/ReflectionHelpers.cs +21 -10
  54. package/Runtime/Core/Helper/SpriteHelpers.cs +3 -1
  55. package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +45 -1
  56. package/Runtime/Core/Serialization/JsonConverters/GameObjectConverter.cs +13 -5
  57. package/Runtime/Core/Serialization/JsonConverters/ResolutionConverter.cs +1 -1
  58. package/Runtime/Core/Serialization/JsonConverters/TypeConverter.cs +1 -1
  59. package/Runtime/Core/Serialization/Serializer.cs +101 -0
  60. package/Runtime/Integrations/VContainer/AssemblyInfo.cs +9 -0
  61. package/Runtime/Integrations/VContainer/AssemblyInfo.cs.meta +3 -0
  62. package/Runtime/Integrations/VContainer/ObjectResolverRelationalExtensions.cs +96 -0
  63. package/Runtime/Integrations/VContainer/RelationalComponentEntryPoint.cs +90 -10
  64. package/Runtime/Integrations/VContainer/RelationalComponentsBuilderExtensions.cs +13 -1
  65. package/Runtime/Integrations/VContainer/RelationalObjectPools.cs +114 -0
  66. package/Runtime/Integrations/VContainer/RelationalObjectPools.cs.meta +11 -0
  67. package/Runtime/Integrations/VContainer/RelationalSceneAssignmentOptions.cs +16 -4
  68. package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs +241 -0
  69. package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs.meta +11 -0
  70. package/Runtime/Integrations/Zenject/AssemblyInfo.cs +9 -0
  71. package/Runtime/Integrations/Zenject/AssemblyInfo.cs.meta +3 -0
  72. package/Runtime/Integrations/Zenject/DiContainerRelationalExtensions.cs +69 -2
  73. package/Runtime/Integrations/Zenject/RelationalComponentSceneInitializer.cs +89 -12
  74. package/Runtime/Integrations/Zenject/RelationalComponentsInstaller.cs +23 -1
  75. package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs +44 -0
  76. package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs.meta +11 -0
  77. package/Runtime/Integrations/Zenject/RelationalSceneAssignmentOptions.cs +16 -10
  78. package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs +243 -0
  79. package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs.meta +11 -0
  80. package/Runtime/Tags/AttributeMetadataCache.cs +1 -4
  81. package/Runtime/Utils/Buffers.cs +4 -4
  82. package/Runtime/Utils/ScriptableObjectSingleton.cs +0 -1
  83. package/Runtime/Utils/SetTextureImportData.cs +3 -1
  84. package/Runtime/Utils/TextureScale.cs +10 -2
  85. package/Runtime/Visuals/UGUI/EnhancedImage.cs +6 -0
  86. package/Runtime/Visuals/UIToolkit/LayeredImage.cs +4 -1
  87. package/SERIALIZATION.md +15 -0
  88. package/SPATIAL_TREE_2D_PERFORMANCE.md +85 -82
  89. package/SPATIAL_TREE_3D_PERFORMANCE.md +94 -91
  90. package/Samples~/DI - VContainer/README.md +232 -51
  91. package/Samples~/DI - VContainer/Scripts/GameLifetimeScope.cs +22 -4
  92. package/Samples~/DI - VContainer/Scripts/RelationalConsumer.cs +5 -2
  93. package/Samples~/DI - VContainer/Scripts/Spawner.cs +113 -4
  94. package/Samples~/DI - Zenject/README.md +217 -53
  95. package/Samples~/DI - Zenject/Scripts/RelationalConsumer.cs +3 -0
  96. package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs +37 -0
  97. package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs.meta +12 -0
  98. package/Samples~/DI - Zenject/Scripts/SpawnerZenject.cs +74 -3
  99. package/Samples~/Random - PRNG/README.md +2 -1
  100. package/Samples~/Relational Components - Basic/README.md +3 -1
  101. package/Samples~/Serialization - JSON/README.md +2 -1
  102. package/Samples~/Spatial Structures - 2D and 3D/README.md +2 -1
  103. package/Samples~/UGUI - EnhancedImage/README.md +2 -1
  104. package/Samples~/UI Toolkit - MultiFile Selector (Editor)/README.md +2 -1
  105. package/THIRD_PARTY_NOTICES.md +1 -1
  106. package/Tests/Editor/Attributes/AnimationEventHelpersTests.cs +16 -0
  107. package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +3 -3
  108. package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +6 -2
  109. package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +170 -0
  110. package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs.meta +11 -0
  111. package/Tests/Editor/Integrations/VContainer/WallstopStudios.UnityHelpers.Tests.Editor.VContainer.asmdef +2 -1
  112. package/Tests/Editor/Integrations/Zenject/WallstopStudios.UnityHelpers.Tests.Editor.Zenject.asmdef +3 -2
  113. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +131 -0
  114. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs.meta +11 -0
  115. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +6 -2
  116. package/Tests/Editor/PersistentDirectorySettingsTests.cs +59 -0
  117. package/Tests/Editor/PersistentDirectorySettingsTests.cs.meta +11 -0
  118. package/Tests/Editor/PrefabCheckerReportTests.cs +32 -0
  119. package/Tests/Editor/PrefabCheckerReportTests.cs.meta +11 -0
  120. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +64 -0
  121. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs.meta +11 -0
  122. package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +1 -1
  123. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +38 -0
  124. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs.meta +11 -0
  125. package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +1 -1
  126. package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +12 -12
  127. package/Tests/Editor/Sprites/SpriteCropperTests.cs +9 -9
  128. package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +3 -3
  129. package/Tests/Editor/Sprites/TexturePlatformNameHelperTests.cs +18 -0
  130. package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +5 -5
  131. package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +3 -3
  132. package/Tests/Editor/Sprites/TextureSettingsApplierWizardAdditionalTests.cs +4 -4
  133. package/Tests/Editor/Sprites/TextureSettingsApplierWizardTests.cs +4 -4
  134. package/Tests/Editor/Tools/ImageBlurToolTests.cs +22 -110
  135. package/Tests/Editor/Utils/CommonTestBase.cs +43 -1
  136. package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +5 -5
  137. package/Tests/Editor/Windows/FitTextureSizeWindowTests.cs +66 -74
  138. package/Tests/Runtime/Attributes/RelationalComponentInitializerTests.cs +4 -15
  139. package/Tests/Runtime/DataStructures/SpatialTree3DBoundsConsistencyTests.cs +29 -29
  140. package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +259 -218
  141. package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +86 -0
  142. package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs.meta +11 -0
  143. package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +255 -227
  144. package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +5 -0
  145. package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +3 -0
  146. package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs +30 -0
  147. package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs.meta +11 -0
  148. package/Tests/Runtime/Serialization/JsonConverterTests.cs +8 -12
  149. package/Tests/Runtime/Serialization/JsonSerializationTest.cs +16 -5
  150. package/Tests/Runtime/Serialization/SerializerAdditionalTests.cs +12 -0
  151. package/Tests/Runtime/Serialization/SerializerFileIoTests.cs +105 -0
  152. package/Tests/Runtime/Serialization/SerializerFileIoTests.cs.meta +11 -0
  153. package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs +247 -0
  154. package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs.meta +11 -0
  155. package/Tests/Runtime/TestUtils/CommonTestBase.cs +88 -0
  156. package/Tests/Runtime/Utils/CoroutineHandlerTests.cs +1 -1
  157. package/Tests/Runtime/Utils/LZMAComprehensiveTests.cs +1 -1
  158. package/Tests/Runtime/Utils/LZMATests.cs +1 -1
  159. package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +1 -1
  160. package/Tests/Runtime/Visuals/EnhancedImageTests.cs +25 -56
  161. package/Tests/Runtime/Visuals/VisualsTestHelpers.cs +1 -8
  162. package/package-lock.json.meta +7 -0
  163. package/package.json +8 -4
  164. package/scripts/check-eol.ps1 +4 -5
  165. package/scripts/lint-tests.ps1 +156 -0
  166. package/scripts/lint-tests.ps1.meta +7 -0
  167. package/scripts/normalize-eol.ps1 +6 -9
  168. package/.github/workflows/csharpier.yml +0 -135
@@ -11,24 +11,10 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
11
11
  using WallstopStudios.UnityHelpers.Core.Attributes;
12
12
  using WallstopStudios.UnityHelpers.Integrations.VContainer;
13
13
  using WallstopStudios.UnityHelpers.Tags;
14
+ using WallstopStudios.UnityHelpers.Tests.TestUtils;
14
15
 
15
- public sealed class RelationalComponentsVContainerTests
16
+ public sealed class RelationalComponentsVContainerTests : CommonTestBase
16
17
  {
17
- private readonly List<GameObject> _spawned = new();
18
-
19
- [TearDown]
20
- public void Cleanup()
21
- {
22
- for (int i = 0; i < _spawned.Count; i++)
23
- {
24
- if (_spawned[i] != null)
25
- {
26
- UnityEngine.Object.DestroyImmediate(_spawned[i]);
27
- }
28
- }
29
- _spawned.Clear();
30
- }
31
-
32
18
  [Test]
33
19
  public void ResolverExtensionsUseBoundAssigner()
34
20
  {
@@ -51,14 +37,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
51
37
  Is.SameAs(tester),
52
38
  "Assigner should receive the same component instance"
53
39
  );
54
- Assert.That(
55
- tester.parentBody,
56
- Is.Not.Null,
40
+ Assert.IsTrue(
41
+ tester.parentBody != null,
57
42
  "ParentComponent assignment should set parentBody"
58
43
  );
59
- Assert.That(
60
- tester.childCollider,
61
- Is.Not.Null,
44
+ Assert.IsTrue(
45
+ tester.childCollider != null,
62
46
  "ChildComponent assignment should set childCollider"
63
47
  );
64
48
  }
@@ -73,14 +57,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
73
57
 
74
58
  resolver.AssignRelationalComponents(tester);
75
59
 
76
- Assert.That(
77
- tester.parentBody,
78
- Is.Not.Null,
60
+ Assert.IsTrue(
61
+ tester.parentBody != null,
79
62
  "Fallback should assign parentBody without a bound assigner"
80
63
  );
81
- Assert.That(
82
- tester.childCollider,
83
- Is.Not.Null,
64
+ Assert.IsTrue(
65
+ tester.childCollider != null,
84
66
  "Fallback should assign childCollider without a bound assigner"
85
67
  );
86
68
  }
@@ -89,47 +71,113 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
89
71
  public System.Collections.IEnumerator EntryPointAssignsActiveSceneComponents()
90
72
  {
91
73
  AttributeMetadataCache cache = CreateCacheFor(typeof(VContainerRelationalTester));
92
- try
93
- {
94
- Scene scene = SceneManager.CreateScene("VContainerTestScene_Active");
95
- SceneManager.SetActiveScene(scene);
96
- ContainerBuilder builder = new();
97
- builder.RegisterInstance(cache).AsSelf();
98
- RecordingAssigner assigner = new RecordingAssigner();
99
- builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
100
- IObjectResolver resolver = builder.Build();
101
-
102
- VContainerRelationalTester tester = CreateHierarchy();
103
- GameObject rootObj = tester.transform.root.gameObject;
104
- SceneManager.MoveGameObjectToScene(rootObj, scene);
105
- yield return null;
106
-
107
- RelationalComponentEntryPoint entryPoint = new RelationalComponentEntryPoint(
108
- resolver.Resolve<IRelationalComponentAssigner>(),
109
- cache,
110
- RelationalSceneAssignmentOptions.Default
111
- );
112
- entryPoint.Initialize();
113
- yield return null;
74
+ Scene scene = CreateTempScene("VContainerTestScene_Active");
75
+ ContainerBuilder builder = new();
76
+ builder.RegisterInstance(cache).AsSelf();
77
+ RecordingAssigner assigner = new RecordingAssigner();
78
+ builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
79
+ IObjectResolver resolver = builder.Build();
114
80
 
115
- Assert.That(
116
- tester.parentBody,
117
- Is.Not.Null,
118
- "Entry point should assign parentBody in the active scene"
119
- );
120
- Assert.That(
121
- tester.childCollider,
122
- Is.Not.Null,
123
- "Entry point should assign childCollider in the active scene"
124
- );
125
- }
126
- finally
127
- {
128
- if (cache != null)
129
- {
130
- UnityEngine.Object.DestroyImmediate(cache);
131
- }
132
- }
81
+ VContainerRelationalTester tester = CreateHierarchy();
82
+ GameObject rootObj = tester.transform.root.gameObject;
83
+ SceneManager.MoveGameObjectToScene(rootObj, scene);
84
+ yield return null;
85
+
86
+ RelationalComponentEntryPoint entryPoint = new RelationalComponentEntryPoint(
87
+ resolver.Resolve<IRelationalComponentAssigner>(),
88
+ cache,
89
+ RelationalSceneAssignmentOptions.Default
90
+ );
91
+ entryPoint.Initialize();
92
+ yield return null;
93
+
94
+ Assert.IsTrue(
95
+ tester.parentBody != null,
96
+ "Entry point should assign parentBody in the active scene"
97
+ );
98
+ Assert.IsTrue(
99
+ tester.childCollider != null,
100
+ "Entry point should assign childCollider in the active scene"
101
+ );
102
+ }
103
+
104
+ [UnityTest]
105
+ public System.Collections.IEnumerator SceneLoadListenerAssignsAdditiveSceneSinglePass()
106
+ {
107
+ AttributeMetadataCache cache = CreateCacheFor(typeof(VContainerRelationalTester));
108
+ RelationalComponentAssigner assigner = new RelationalComponentAssigner(cache);
109
+ RelationalSceneAssignmentOptions options = new RelationalSceneAssignmentOptions(
110
+ includeInactive: true,
111
+ useSinglePassScan: true
112
+ );
113
+ RelationalSceneLoadListener listener = new RelationalSceneLoadListener(
114
+ assigner,
115
+ cache,
116
+ options
117
+ );
118
+ listener.Initialize();
119
+ TrackDisposable(listener);
120
+
121
+ Scene additive = CreateTempScene(
122
+ "VContainer_Additive_Runtime_Single",
123
+ setActive: false
124
+ );
125
+
126
+ VContainerRelationalTester tester = CreateHierarchy();
127
+ GameObject root = tester.transform.root.gameObject;
128
+ SceneManager.MoveGameObjectToScene(root, additive);
129
+
130
+ yield return null;
131
+
132
+ listener.OnSceneLoaded(additive, LoadSceneMode.Additive);
133
+ yield return null;
134
+
135
+ Assert.IsTrue(
136
+ tester.parentBody != null,
137
+ "Scene load listener should assign parentBody in single-pass mode"
138
+ );
139
+ Assert.IsTrue(
140
+ tester.childCollider != null,
141
+ "Scene load listener should assign childCollider in single-pass mode"
142
+ );
143
+ }
144
+
145
+ [UnityTest]
146
+ public System.Collections.IEnumerator SceneLoadListenerAssignsAdditiveSceneMultiPass()
147
+ {
148
+ AttributeMetadataCache cache = CreateCacheFor(typeof(VContainerRelationalTester));
149
+ RelationalComponentAssigner assigner = new RelationalComponentAssigner(cache);
150
+ RelationalSceneAssignmentOptions options = new RelationalSceneAssignmentOptions(
151
+ includeInactive: true,
152
+ useSinglePassScan: false
153
+ );
154
+ RelationalSceneLoadListener listener = new RelationalSceneLoadListener(
155
+ assigner,
156
+ cache,
157
+ options
158
+ );
159
+ listener.Initialize();
160
+ TrackDisposable(listener);
161
+
162
+ Scene additive = CreateTempScene("VContainer_Additive_Runtime_Multi", setActive: false);
163
+
164
+ VContainerRelationalTester tester = CreateHierarchy();
165
+ GameObject root = tester.transform.root.gameObject;
166
+ SceneManager.MoveGameObjectToScene(root, additive);
167
+
168
+ yield return null;
169
+
170
+ listener.OnSceneLoaded(additive, LoadSceneMode.Additive);
171
+ yield return null;
172
+
173
+ Assert.IsTrue(
174
+ tester.parentBody != null,
175
+ "Scene load listener should assign parentBody in multi-pass mode"
176
+ );
177
+ Assert.IsTrue(
178
+ tester.childCollider != null,
179
+ "Scene load listener should assign childCollider in multi-pass mode"
180
+ );
133
181
  }
134
182
 
135
183
  [Test]
@@ -147,14 +195,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
147
195
  Is.SameAs(tester),
148
196
  "BuildUpWithRelations should return the same component instance"
149
197
  );
150
- Assert.That(
151
- tester.parentBody,
152
- Is.Not.Null,
198
+ Assert.IsTrue(
199
+ tester.parentBody != null,
153
200
  "BuildUpWithRelations should assign parentBody"
154
201
  );
155
- Assert.That(
156
- tester.childCollider,
157
- Is.Not.Null,
202
+ Assert.IsTrue(
203
+ tester.childCollider != null,
158
204
  "BuildUpWithRelations should assign childCollider"
159
205
  );
160
206
  }
@@ -182,14 +228,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
182
228
  IObjectResolver resolver = builder.Build();
183
229
  VContainerRelationalTester tester = CreateHierarchy();
184
230
  resolver.AssignRelationalHierarchy(tester.gameObject, includeInactiveChildren: false);
185
- Assert.That(
186
- tester.parentBody,
187
- Is.Not.Null,
231
+ Assert.IsTrue(
232
+ tester.parentBody != null,
188
233
  "AssignRelationalHierarchy should assign parentBody"
189
234
  );
190
- Assert.That(
191
- tester.childCollider,
192
- Is.Not.Null,
235
+ Assert.IsTrue(
236
+ tester.childCollider != null,
193
237
  "AssignRelationalHierarchy should assign childCollider"
194
238
  );
195
239
  }
@@ -232,24 +276,20 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
232
276
  rootTester.gameObject,
233
277
  includeInactiveChildren: false
234
278
  );
235
- Assert.That(
236
- rootTester.parentBody,
237
- Is.Not.Null,
279
+ Assert.IsTrue(
280
+ rootTester.parentBody != null,
238
281
  "Root tester should be assigned even when includeInactiveChildren is false"
239
282
  );
240
- Assert.That(
241
- rootTester.childCollider,
242
- Is.Not.Null,
283
+ Assert.IsTrue(
284
+ rootTester.childCollider != null,
243
285
  "Root tester should be assigned even when includeInactiveChildren is false"
244
286
  );
245
- Assert.That(
246
- subTester.parentBody,
247
- Is.Null,
287
+ Assert.IsTrue(
288
+ subTester.parentBody == null,
248
289
  "Inactive sub tester should be skipped when includeInactiveChildren is false"
249
290
  );
250
- Assert.That(
251
- subTester.childCollider,
252
- Is.Null,
291
+ Assert.IsTrue(
292
+ subTester.childCollider == null,
253
293
  "Inactive sub tester should be skipped when includeInactiveChildren is false"
254
294
  );
255
295
 
@@ -258,14 +298,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
258
298
  rootTester.gameObject,
259
299
  includeInactiveChildren: true
260
300
  );
261
- Assert.That(
262
- subTester.parentBody,
263
- Is.Not.Null,
301
+ Assert.IsTrue(
302
+ subTester.parentBody != null,
264
303
  "Inactive sub tester should be assigned when includeInactiveChildren is true"
265
304
  );
266
- Assert.That(
267
- subTester.childCollider,
268
- Is.Not.Null,
305
+ Assert.IsTrue(
306
+ subTester.childCollider != null,
269
307
  "Inactive sub tester should be assigned when includeInactiveChildren is true"
270
308
  );
271
309
  }
@@ -274,51 +312,39 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
274
312
  public System.Collections.IEnumerator EntryPointIgnoresNonActiveScenes()
275
313
  {
276
314
  AttributeMetadataCache cache = CreateCacheFor(typeof(VContainerRelationalTester));
277
- try
278
- {
279
- Scene active = SceneManager.CreateScene("VContainerActiveScene_Sep");
280
- SceneManager.SetActiveScene(active);
281
-
282
- ContainerBuilder builder = new();
283
- builder.RegisterInstance(cache).AsSelf();
284
- RecordingAssigner assigner = new RecordingAssigner();
285
- builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
286
- IObjectResolver resolver = builder.Build();
287
-
288
- VContainerRelationalTester testerA = CreateHierarchy();
289
- SceneManager.MoveGameObjectToScene(testerA.transform.root.gameObject, active);
290
-
291
- Scene secondary = SceneManager.CreateScene("VContainerSecondaryScene_Sep");
292
- VContainerRelationalTester testerB = CreateHierarchy();
293
- SceneManager.MoveGameObjectToScene(testerB.transform.root.gameObject, secondary);
294
- yield return null;
295
-
296
- RelationalComponentEntryPoint entryPoint = new RelationalComponentEntryPoint(
297
- resolver.Resolve<IRelationalComponentAssigner>(),
298
- cache,
299
- new RelationalSceneAssignmentOptions(includeInactive: true)
300
- );
301
- entryPoint.Initialize();
302
- yield return null;
315
+ Scene active = CreateTempScene("VContainerActiveScene_Sep");
316
+ ContainerBuilder builder = new();
317
+ builder.RegisterInstance(cache).AsSelf();
318
+ RecordingAssigner assigner = new RecordingAssigner();
319
+ builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
320
+ IObjectResolver resolver = builder.Build();
303
321
 
304
- Assert.That(
305
- assigner.CallCount,
306
- Is.EqualTo(1),
307
- "Entry point should only process components from the active scene"
308
- );
309
- Assert.That(
310
- assigner.LastComponent,
311
- Is.SameAs(testerA),
312
- "Active scene tester should be assigned"
313
- );
314
- }
315
- finally
316
- {
317
- if (cache != null)
318
- {
319
- UnityEngine.Object.DestroyImmediate(cache);
320
- }
321
- }
322
+ VContainerRelationalTester testerA = CreateHierarchy();
323
+ SceneManager.MoveGameObjectToScene(testerA.transform.root.gameObject, active);
324
+
325
+ Scene secondary = CreateTempScene("VContainerSecondaryScene_Sep", setActive: false);
326
+ VContainerRelationalTester testerB = CreateHierarchy();
327
+ SceneManager.MoveGameObjectToScene(testerB.transform.root.gameObject, secondary);
328
+ yield return null;
329
+
330
+ RelationalComponentEntryPoint entryPoint = new RelationalComponentEntryPoint(
331
+ resolver.Resolve<IRelationalComponentAssigner>(),
332
+ cache,
333
+ new RelationalSceneAssignmentOptions(includeInactive: true)
334
+ );
335
+ entryPoint.Initialize();
336
+ yield return null;
337
+
338
+ Assert.That(
339
+ assigner.CallCount,
340
+ Is.EqualTo(1),
341
+ "Entry point should only process components from the active scene"
342
+ );
343
+ Assert.That(
344
+ assigner.LastComponent,
345
+ Is.SameAs(testerA),
346
+ "Active scene tester should be assigned"
347
+ );
322
348
  }
323
349
 
324
350
  [Test]
@@ -339,25 +365,23 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
339
365
  child.transform.SetParent(middle.transform);
340
366
 
341
367
  // Expect an error about missing child component due to IncludeInactive=false on attribute
342
- UnityEngine.TestTools.LogAssert.Expect(
343
- UnityEngine.LogType.Error,
368
+ LogAssert.Expect(
369
+ LogType.Error,
344
370
  new System.Text.RegularExpressions.Regex(
345
371
  ".*Unable to find child component of type UnityEngine\\.CapsuleCollider for field 'childCollider'.*"
346
372
  )
347
373
  );
348
374
  resolver.AssignRelationalComponents(tester);
349
- Assert.That(tester.parentBody, Is.Not.Null, "Parent assignment should succeed");
350
- Assert.That(
351
- tester.childCollider,
352
- Is.Null,
375
+ Assert.IsTrue(tester.parentBody != null, "Parent assignment should succeed");
376
+ Assert.IsTrue(
377
+ tester.childCollider == null,
353
378
  "Child assignment should ignore inactive child when IncludeInactive is false"
354
379
  );
355
380
 
356
381
  child.SetActive(true);
357
382
  resolver.AssignRelationalComponents(tester);
358
- Assert.That(
359
- tester.childCollider,
360
- Is.Not.Null,
383
+ Assert.IsTrue(
384
+ tester.childCollider != null,
361
385
  "Child assignment should include active child when IncludeInactive is false"
362
386
  );
363
387
  }
@@ -366,65 +390,87 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
366
390
  public System.Collections.IEnumerator EntryPointRespectsIncludeInactiveOption()
367
391
  {
368
392
  AttributeMetadataCache cache = CreateCacheFor(typeof(VContainerRelationalTester));
369
- try
370
- {
371
- Scene scene = SceneManager.CreateScene("VContainerTestScene_Inactive");
372
- SceneManager.SetActiveScene(scene);
373
- ContainerBuilder builder = new();
374
- builder.RegisterInstance(cache).AsSelf();
375
- RecordingAssigner assigner = new RecordingAssigner();
376
- builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
377
- IObjectResolver resolver = builder.Build();
378
-
379
- VContainerRelationalTester tester = CreateHierarchy();
380
- tester.gameObject.SetActive(false);
381
- GameObject rootObj = tester.transform.root.gameObject;
382
- SceneManager.MoveGameObjectToScene(rootObj, scene);
383
- yield return null;
384
-
385
- RelationalComponentEntryPoint disabledEntryPoint =
386
- new RelationalComponentEntryPoint(
387
- resolver.Resolve<IRelationalComponentAssigner>(),
388
- cache,
389
- new RelationalSceneAssignmentOptions(includeInactive: false)
390
- );
391
- disabledEntryPoint.Initialize();
392
- Assert.That(
393
- tester.parentBody,
394
- Is.Null,
395
- "Disabled option should skip inactive components"
396
- );
397
- Assert.That(
398
- tester.childCollider,
399
- Is.Null,
400
- "Disabled option should skip inactive components"
401
- );
393
+ Scene scene = CreateTempScene("VContainerTestScene_Inactive");
394
+ ContainerBuilder builder = new();
395
+ builder.RegisterInstance(cache).AsSelf();
396
+ RecordingAssigner assigner = new RecordingAssigner();
397
+ builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
398
+ IObjectResolver resolver = builder.Build();
402
399
 
403
- RelationalComponentEntryPoint enabledEntryPoint = new RelationalComponentEntryPoint(
404
- resolver.Resolve<IRelationalComponentAssigner>(),
405
- cache,
406
- new RelationalSceneAssignmentOptions(includeInactive: true)
407
- );
408
- enabledEntryPoint.Initialize();
409
- yield return null;
410
- Assert.That(
411
- tester.parentBody,
412
- Is.Not.Null,
413
- "Enabled option should include inactive components"
414
- );
415
- Assert.That(
416
- tester.childCollider,
417
- Is.Not.Null,
418
- "Enabled option should include inactive components"
419
- );
420
- }
421
- finally
422
- {
423
- if (cache != null)
424
- {
425
- UnityEngine.Object.DestroyImmediate(cache);
426
- }
427
- }
400
+ VContainerRelationalTester tester = CreateHierarchy();
401
+ tester.gameObject.SetActive(false);
402
+ GameObject rootObj = tester.transform.root.gameObject;
403
+ SceneManager.MoveGameObjectToScene(rootObj, scene);
404
+ yield return null;
405
+
406
+ RelationalComponentEntryPoint disabledEntryPoint = new RelationalComponentEntryPoint(
407
+ resolver.Resolve<IRelationalComponentAssigner>(),
408
+ cache,
409
+ new RelationalSceneAssignmentOptions(includeInactive: false)
410
+ );
411
+ disabledEntryPoint.Initialize();
412
+ Assert.IsTrue(
413
+ tester.parentBody == null,
414
+ "Disabled option should skip inactive components"
415
+ );
416
+ Assert.IsTrue(
417
+ tester.childCollider == null,
418
+ "Disabled option should skip inactive components"
419
+ );
420
+
421
+ RelationalComponentEntryPoint enabledEntryPoint = new RelationalComponentEntryPoint(
422
+ resolver.Resolve<IRelationalComponentAssigner>(),
423
+ cache,
424
+ new RelationalSceneAssignmentOptions(includeInactive: true)
425
+ );
426
+ enabledEntryPoint.Initialize();
427
+ yield return null;
428
+ Assert.IsTrue(
429
+ tester.parentBody != null,
430
+ "Enabled option should include inactive components"
431
+ );
432
+ Assert.IsTrue(
433
+ tester.childCollider != null,
434
+ "Enabled option should include inactive components"
435
+ );
436
+ }
437
+
438
+ [UnityTest]
439
+ public System.Collections.IEnumerator EntryPointUsesMultiPassWhenConfigured()
440
+ {
441
+ AttributeMetadataCache cache = CreateCacheFor(typeof(VContainerRelationalTester));
442
+ Scene scene = CreateTempScene("VContainerMultiPassScene");
443
+ ContainerBuilder builder = new();
444
+ builder.RegisterInstance(cache).AsSelf();
445
+ RecordingAssigner assigner = new RecordingAssigner();
446
+ builder.RegisterInstance(assigner).As<IRelationalComponentAssigner>();
447
+ IObjectResolver resolver = builder.Build();
448
+
449
+ VContainerRelationalTester tester = CreateHierarchy();
450
+ SceneManager.MoveGameObjectToScene(tester.transform.root.gameObject, scene);
451
+ yield return null;
452
+
453
+ RelationalComponentEntryPoint entryPoint = new RelationalComponentEntryPoint(
454
+ resolver.Resolve<IRelationalComponentAssigner>(),
455
+ cache,
456
+ new RelationalSceneAssignmentOptions(
457
+ includeInactive: true,
458
+ useSinglePassScan: false
459
+ )
460
+ );
461
+ entryPoint.Initialize();
462
+ yield return null;
463
+
464
+ Assert.That(
465
+ assigner.CallCount,
466
+ Is.EqualTo(1),
467
+ "Multi-pass configuration should still assign each relational component once"
468
+ );
469
+ Assert.That(
470
+ assigner.LastComponent,
471
+ Is.SameAs(tester),
472
+ "Multi-pass configuration should target the tracked tester"
473
+ );
428
474
  }
429
475
 
430
476
  private VContainerRelationalTester CreateHierarchy()
@@ -443,16 +489,11 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
443
489
  return tester;
444
490
  }
445
491
 
446
- private GameObject Track(GameObject gameObject)
492
+ private AttributeMetadataCache CreateCacheFor(Type componentType)
447
493
  {
448
- _spawned.Add(gameObject);
449
- return gameObject;
450
- }
451
-
452
- private static AttributeMetadataCache CreateCacheFor(Type componentType)
453
- {
454
- AttributeMetadataCache cache =
455
- ScriptableObject.CreateInstance<AttributeMetadataCache>();
494
+ AttributeMetadataCache cache = Track(
495
+ ScriptableObject.CreateInstance<AttributeMetadataCache>()
496
+ );
456
497
 
457
498
  AttributeMetadataCache.RelationalFieldMetadata[] fields =
458
499
  {
@@ -0,0 +1,86 @@
1
+ #if VCONTAINER_PRESENT
2
+ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
3
+ {
4
+ using System;
5
+ using global::VContainer;
6
+ using NUnit.Framework;
7
+ using UnityEngine;
8
+ using UnityEngine.Pool;
9
+ using WallstopStudios.UnityHelpers.Core.Attributes;
10
+ using WallstopStudios.UnityHelpers.Integrations.VContainer;
11
+ using WallstopStudios.UnityHelpers.Tests.TestUtils;
12
+
13
+ public sealed class RelationalObjectPoolsVContainerTests : CommonTestBase
14
+ {
15
+ [Test]
16
+ public void ComponentPoolGetWithRelationsInjectsAndAssigns()
17
+ {
18
+ ContainerBuilder builder = new ContainerBuilder();
19
+ IObjectResolver resolver = builder.Build();
20
+
21
+ ObjectPool<TestComponent> pool = RelationalObjectPools.CreatePoolWithRelations(
22
+ createFunc: () =>
23
+ {
24
+ GameObject go = Track(new GameObject("PooledRoot"));
25
+ go.AddComponent<Rigidbody>();
26
+ GameObject middle = Track(new GameObject("PooledMiddle"));
27
+ middle.transform.SetParent(go.transform);
28
+ TestComponent tester = middle.AddComponent<TestComponent>();
29
+ GameObject child = Track(new GameObject("PooledChild"));
30
+ child.transform.SetParent(middle.transform);
31
+ child.AddComponent<CapsuleCollider>();
32
+ return tester;
33
+ }
34
+ );
35
+
36
+ TestComponent comp = pool.GetWithRelations(resolver);
37
+
38
+ Assert.IsTrue(comp != null);
39
+ Assert.IsTrue(comp.parentBody != null);
40
+ Assert.IsTrue(comp.childCollider != null);
41
+
42
+ pool.Release(comp);
43
+ pool.Clear();
44
+ }
45
+
46
+ [Test]
47
+ public void GameObjectPoolGetWithRelationsInjectsAndAssigns()
48
+ {
49
+ ContainerBuilder builder = new ContainerBuilder();
50
+ IObjectResolver resolver = builder.Build();
51
+
52
+ GameObject prefab = Track(new GameObject("PrefabRoot"));
53
+ prefab.AddComponent<Rigidbody>();
54
+ GameObject mid = Track(new GameObject("PrefabMiddle"));
55
+ mid.transform.SetParent(prefab.transform);
56
+ TestComponent prefabComp = mid.AddComponent<TestComponent>();
57
+ GameObject child = Track(new GameObject("PrefabChild"));
58
+ child.transform.SetParent(mid.transform);
59
+ child.AddComponent<CapsuleCollider>();
60
+
61
+ ObjectPool<GameObject> pool = RelationalObjectPools.CreateGameObjectPoolWithRelations(
62
+ prefab
63
+ );
64
+
65
+ GameObject instance = Track(pool.GetWithRelations(resolver));
66
+ TestComponent comp = instance.GetComponentInChildren<TestComponent>(true);
67
+
68
+ Assert.IsTrue(comp != null);
69
+ Assert.IsTrue(comp.parentBody != null);
70
+ Assert.IsTrue(comp.childCollider != null);
71
+
72
+ pool.Release(instance);
73
+ pool.Clear();
74
+ }
75
+
76
+ private sealed class TestComponent : MonoBehaviour
77
+ {
78
+ [ParentComponent(OnlyAncestors = true)]
79
+ public Rigidbody parentBody;
80
+
81
+ [ChildComponent(OnlyDescendants = true)]
82
+ public CapsuleCollider childCollider;
83
+ }
84
+ }
85
+ }
86
+ #endif