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
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 34b0bd29cce66a441bc576049f1dd8c8
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -11,8 +11,9 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
11
11
  using WallstopStudios.UnityHelpers.Core.Attributes;
12
12
  using WallstopStudios.UnityHelpers.Integrations.Zenject;
13
13
  using WallstopStudios.UnityHelpers.Tags;
14
+ using WallstopStudios.UnityHelpers.Tests.TestUtils;
14
15
 
15
- public sealed class RelationalComponentsZenjectTests
16
+ public sealed class RelationalComponentsZenjectTests : CommonTestBase
16
17
  {
17
18
  private DiContainer Container;
18
19
 
@@ -22,21 +23,6 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
22
23
  Container = new DiContainer();
23
24
  }
24
25
 
25
- private readonly List<GameObject> _spawned = new();
26
-
27
- [TearDown]
28
- public void Cleanup()
29
- {
30
- for (int i = 0; i < _spawned.Count; i++)
31
- {
32
- if (_spawned[i] != null)
33
- {
34
- UnityEngine.Object.DestroyImmediate(_spawned[i]);
35
- }
36
- }
37
- _spawned.Clear();
38
- }
39
-
40
26
  [Test]
41
27
  public void ContainerExtensionsUseBoundAssigner()
42
28
  {
@@ -57,14 +43,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
57
43
  Is.SameAs(tester),
58
44
  "Assigner should receive the same component instance"
59
45
  );
60
- Assert.That(
61
- tester.parentBody,
62
- Is.Not.Null,
46
+ Assert.IsTrue(
47
+ tester.parentBody != null,
63
48
  "ParentComponent assignment should set parentBody"
64
49
  );
65
- Assert.That(
66
- tester.childCollider,
67
- Is.Not.Null,
50
+ Assert.IsTrue(
51
+ tester.childCollider != null,
68
52
  "ChildComponent assignment should set childCollider"
69
53
  );
70
54
  }
@@ -76,14 +60,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
76
60
 
77
61
  Container.AssignRelationalComponents(tester);
78
62
 
79
- Assert.That(
80
- tester.parentBody,
81
- Is.Not.Null,
63
+ Assert.IsTrue(
64
+ tester.parentBody != null,
82
65
  "Fallback should assign parentBody without a bound assigner"
83
66
  );
84
- Assert.That(
85
- tester.childCollider,
86
- Is.Not.Null,
67
+ Assert.IsTrue(
68
+ tester.childCollider != null,
87
69
  "Fallback should assign childCollider without a bound assigner"
88
70
  );
89
71
  }
@@ -92,126 +74,202 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
92
74
  public System.Collections.IEnumerator SceneInitializerAssignsActiveSceneComponents()
93
75
  {
94
76
  AttributeMetadataCache cache = CreateCacheFor(typeof(ZenjectRelationalTester));
95
- try
96
- {
97
- Container.BindInstance(cache);
98
- RecordingAssigner assigner = new();
99
- Container.Bind<IRelationalComponentAssigner>().FromInstance(assigner);
100
- Container.BindInstance(RelationalSceneAssignmentOptions.Default);
101
- Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
102
-
103
- Scene scene = SceneManager.CreateScene("ZenjectTestScene_Active");
104
- SceneManager.SetActiveScene(scene);
105
- ZenjectRelationalTester tester = CreateHierarchy();
106
- GameObject root = tester.transform.root.gameObject;
107
- SceneManager.MoveGameObjectToScene(root, scene);
108
- yield return null;
109
-
110
- IInitializable initializer = Container.Resolve<IInitializable>();
111
- initializer.Initialize();
112
- yield return null;
113
-
114
- Assert.That(
115
- assigner.CallCount,
116
- Is.EqualTo(1),
117
- "Initializer should invoke assigner exactly once for the tester component"
118
- );
119
- Assert.That(
120
- assigner.LastComponent,
121
- Is.SameAs(tester),
122
- "Initializer should target the created tester instance"
123
- );
124
- }
125
- finally
126
- {
127
- if (cache != null)
128
- {
129
- UnityEngine.Object.DestroyImmediate(cache);
130
- }
131
- }
77
+ Container.BindInstance(cache);
78
+ RecordingAssigner assigner = new();
79
+ Container.Bind<IRelationalComponentAssigner>().FromInstance(assigner);
80
+ Container.BindInstance(RelationalSceneAssignmentOptions.Default);
81
+ Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
82
+
83
+ Scene scene = CreateTempScene("ZenjectTestScene_Active");
84
+ ZenjectRelationalTester tester = CreateHierarchy();
85
+ GameObject root = tester.transform.root.gameObject;
86
+ SceneManager.MoveGameObjectToScene(root, scene);
87
+ yield return null;
88
+
89
+ IInitializable initializer = Container.Resolve<IInitializable>();
90
+ initializer.Initialize();
91
+ yield return null;
92
+
93
+ Assert.That(
94
+ assigner.CallCount,
95
+ Is.EqualTo(1),
96
+ "Initializer should invoke assigner exactly once for the tester component"
97
+ );
98
+ Assert.That(
99
+ assigner.LastComponent,
100
+ Is.SameAs(tester),
101
+ "Initializer should target the created tester instance"
102
+ );
132
103
  }
133
104
 
134
105
  [UnityTest]
135
106
  public System.Collections.IEnumerator SceneInitializerSkipsInactiveWhenOptionDisabled()
136
107
  {
137
108
  AttributeMetadataCache cache = CreateCacheFor(typeof(ZenjectRelationalTester));
138
- try
139
- {
140
- Container.BindInstance(cache);
141
- RecordingAssigner assigner = new();
142
- Container.Bind<IRelationalComponentAssigner>().FromInstance(assigner);
143
- Container.BindInstance(new RelationalSceneAssignmentOptions(false));
144
- Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
145
-
146
- Scene scene = SceneManager.CreateScene("ZenjectTestScene_InactiveFalse");
147
- SceneManager.SetActiveScene(scene);
148
- ZenjectRelationalTester tester = CreateHierarchy();
149
- tester.gameObject.SetActive(false);
150
- GameObject root = tester.transform.root.gameObject;
151
- SceneManager.MoveGameObjectToScene(root, scene);
152
- yield return null;
153
-
154
- IInitializable initializer = Container.Resolve<IInitializable>();
155
- initializer.Initialize();
156
- yield return null;
157
-
158
- Assert.That(
159
- assigner.CallCount,
160
- Is.EqualTo(0),
161
- "Initializer should skip inactive tester when IncludeInactive is false"
162
- );
163
- }
164
- finally
165
- {
166
- if (cache != null)
167
- {
168
- UnityEngine.Object.DestroyImmediate(cache);
169
- }
170
- }
109
+ Container.BindInstance(cache);
110
+ RecordingAssigner assigner = new();
111
+ Container.Bind<IRelationalComponentAssigner>().FromInstance(assigner);
112
+ Container.BindInstance(new RelationalSceneAssignmentOptions(false));
113
+ Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
114
+
115
+ Scene scene = CreateTempScene("ZenjectTestScene_InactiveFalse");
116
+ ZenjectRelationalTester tester = CreateHierarchy();
117
+ tester.gameObject.SetActive(false);
118
+ GameObject root = tester.transform.root.gameObject;
119
+ SceneManager.MoveGameObjectToScene(root, scene);
120
+ yield return null;
121
+
122
+ IInitializable initializer = Container.Resolve<IInitializable>();
123
+ initializer.Initialize();
124
+ yield return null;
125
+
126
+ Assert.That(
127
+ assigner.CallCount,
128
+ Is.EqualTo(0),
129
+ "Initializer should skip inactive tester when IncludeInactive is false"
130
+ );
171
131
  }
172
132
 
173
133
  [UnityTest]
174
134
  public System.Collections.IEnumerator SceneInitializerIncludesInactiveWhenOptionEnabled()
175
135
  {
176
136
  AttributeMetadataCache cache = CreateCacheFor(typeof(ZenjectRelationalTester));
177
- try
178
- {
179
- Container.BindInstance(cache);
180
- RecordingAssigner assigner = new();
181
- Container.Bind<IRelationalComponentAssigner>().FromInstance(assigner);
182
- Container.BindInstance(new RelationalSceneAssignmentOptions(true));
183
- Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
184
-
185
- Scene scene = SceneManager.CreateScene("ZenjectTestScene_InactiveTrue");
186
- SceneManager.SetActiveScene(scene);
187
- ZenjectRelationalTester tester = CreateHierarchy();
188
- tester.gameObject.SetActive(false);
189
- GameObject root = tester.transform.root.gameObject;
190
- SceneManager.MoveGameObjectToScene(root, scene);
191
- yield return null;
192
-
193
- IInitializable initializer = Container.Resolve<IInitializable>();
194
- initializer.Initialize();
195
- yield return null;
196
-
197
- Assert.That(
198
- assigner.CallCount,
199
- Is.EqualTo(1),
200
- "Initializer should include inactive tester when IncludeInactive is true"
201
- );
202
- Assert.That(
203
- assigner.LastComponent,
204
- Is.SameAs(tester),
205
- "Initializer should target the inactive tester component"
206
- );
207
- }
208
- finally
209
- {
210
- if (cache != null)
211
- {
212
- UnityEngine.Object.DestroyImmediate(cache);
213
- }
214
- }
137
+ Container.BindInstance(cache);
138
+ RecordingAssigner assigner = new();
139
+ Container.Bind<IRelationalComponentAssigner>().FromInstance(assigner);
140
+ Container.BindInstance(new RelationalSceneAssignmentOptions(true));
141
+ Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
142
+
143
+ Scene scene = CreateTempScene("ZenjectTestScene_InactiveTrue");
144
+ ZenjectRelationalTester tester = CreateHierarchy();
145
+ tester.gameObject.SetActive(false);
146
+ GameObject root = tester.transform.root.gameObject;
147
+ SceneManager.MoveGameObjectToScene(root, scene);
148
+ yield return null;
149
+
150
+ IInitializable initializer = Container.Resolve<IInitializable>();
151
+ initializer.Initialize();
152
+ yield return null;
153
+
154
+ Assert.That(
155
+ assigner.CallCount,
156
+ Is.EqualTo(1),
157
+ "Initializer should include inactive tester when IncludeInactive is true"
158
+ );
159
+ Assert.That(
160
+ assigner.LastComponent,
161
+ Is.SameAs(tester),
162
+ "Initializer should target the inactive tester component"
163
+ );
164
+ }
165
+
166
+ [UnityTest]
167
+ public System.Collections.IEnumerator SceneInitializerUsesMultiPassWhenConfigured()
168
+ {
169
+ AttributeMetadataCache cache = CreateCacheFor(typeof(ZenjectRelationalTester));
170
+ Container.BindInstance(cache);
171
+ RecordingAssigner assigner = new();
172
+ Container.Bind<IRelationalComponentAssigner>().FromInstance(assigner);
173
+ Container.BindInstance(
174
+ new RelationalSceneAssignmentOptions(true, useSinglePassScan: false)
175
+ );
176
+ Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
177
+
178
+ Scene scene = CreateTempScene("ZenjectMultiPassScene");
179
+ ZenjectRelationalTester tester = CreateHierarchy();
180
+ SceneManager.MoveGameObjectToScene(tester.transform.root.gameObject, scene);
181
+ yield return null;
182
+
183
+ IInitializable initializer = Container.Resolve<IInitializable>();
184
+ initializer.Initialize();
185
+ yield return null;
186
+
187
+ Assert.That(
188
+ assigner.CallCount,
189
+ Is.EqualTo(1),
190
+ "Multi-pass configuration should assign each relational component once"
191
+ );
192
+ Assert.That(
193
+ assigner.LastComponent,
194
+ Is.SameAs(tester),
195
+ "Multi-pass configuration should target the tester in the active scene"
196
+ );
197
+ }
198
+
199
+ [UnityTest]
200
+ public System.Collections.IEnumerator SceneLoadListenerAssignsAdditiveSceneSinglePass()
201
+ {
202
+ AttributeMetadataCache cache = CreateCacheFor(typeof(ZenjectRelationalTester));
203
+ RelationalComponentAssigner assigner = new RelationalComponentAssigner(cache);
204
+ RelationalSceneAssignmentOptions options = new RelationalSceneAssignmentOptions(
205
+ includeInactive: true,
206
+ useSinglePassScan: true
207
+ );
208
+ RelationalSceneLoadListener listener = new RelationalSceneLoadListener(
209
+ assigner,
210
+ cache,
211
+ options
212
+ );
213
+ listener.Initialize();
214
+ TrackDisposable(listener);
215
+
216
+ Scene additive = CreateTempScene("Zenject_Additive_Runtime_Single", setActive: false);
217
+
218
+ ZenjectRelationalTester tester = CreateHierarchy();
219
+ GameObject root = tester.transform.root.gameObject;
220
+ SceneManager.MoveGameObjectToScene(root, additive);
221
+
222
+ yield return null;
223
+
224
+ listener.OnSceneLoaded(additive, LoadSceneMode.Additive);
225
+ yield return null;
226
+
227
+ Assert.IsTrue(
228
+ tester.parentBody != null,
229
+ "Scene load listener should assign parentBody in single-pass mode"
230
+ );
231
+ Assert.IsTrue(
232
+ tester.childCollider != null,
233
+ "Scene load listener should assign childCollider in single-pass mode"
234
+ );
235
+ }
236
+
237
+ [UnityTest]
238
+ public System.Collections.IEnumerator SceneLoadListenerAssignsAdditiveSceneMultiPass()
239
+ {
240
+ AttributeMetadataCache cache = CreateCacheFor(typeof(ZenjectRelationalTester));
241
+ RelationalComponentAssigner assigner = new RelationalComponentAssigner(cache);
242
+ RelationalSceneAssignmentOptions options = new RelationalSceneAssignmentOptions(
243
+ includeInactive: true,
244
+ useSinglePassScan: false
245
+ );
246
+ RelationalSceneLoadListener listener = new RelationalSceneLoadListener(
247
+ assigner,
248
+ cache,
249
+ options
250
+ );
251
+ listener.Initialize();
252
+ TrackDisposable(listener);
253
+
254
+ Scene additive = CreateTempScene("Zenject_Additive_Runtime_Multi", setActive: false);
255
+
256
+ ZenjectRelationalTester tester = CreateHierarchy();
257
+ GameObject root = tester.transform.root.gameObject;
258
+ SceneManager.MoveGameObjectToScene(root, additive);
259
+
260
+ yield return null;
261
+
262
+ listener.OnSceneLoaded(additive, LoadSceneMode.Additive);
263
+ yield return null;
264
+
265
+ Assert.IsTrue(
266
+ tester.parentBody != null,
267
+ "Scene load listener should assign parentBody in multi-pass mode"
268
+ );
269
+ Assert.IsTrue(
270
+ tester.childCollider != null,
271
+ "Scene load listener should assign childCollider in multi-pass mode"
272
+ );
215
273
  }
216
274
 
217
275
  [Test]
@@ -219,14 +277,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
219
277
  {
220
278
  ZenjectRelationalTester tester = CreateHierarchy();
221
279
  Container.AssignRelationalHierarchy(tester.gameObject, includeInactiveChildren: false);
222
- Assert.That(
223
- tester.parentBody,
224
- Is.Not.Null,
280
+ Assert.IsTrue(
281
+ tester.parentBody != null,
225
282
  "AssignRelationalHierarchy should assign parentBody"
226
283
  );
227
- Assert.That(
228
- tester.childCollider,
229
- Is.Not.Null,
284
+ Assert.IsTrue(
285
+ tester.childCollider != null,
230
286
  "AssignRelationalHierarchy should assign childCollider"
231
287
  );
232
288
  }
@@ -235,46 +291,35 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
235
291
  public System.Collections.IEnumerator SceneInitializerIgnoresNonActiveScenes()
236
292
  {
237
293
  AttributeMetadataCache cache = CreateCacheFor(typeof(ZenjectRelationalTester));
238
- try
239
- {
240
- RecordingAssigner assigner = new();
241
- Container.BindInstance(cache);
242
- Container.Bind<IRelationalComponentAssigner>().FromInstance(assigner);
243
- Container.BindInstance(new RelationalSceneAssignmentOptions(true));
244
- Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
245
-
246
- Scene active = SceneManager.CreateScene("ZenjectActiveScene_Sep");
247
- SceneManager.SetActiveScene(active);
248
- ZenjectRelationalTester testerA = CreateHierarchy();
249
- SceneManager.MoveGameObjectToScene(testerA.transform.root.gameObject, active);
250
-
251
- Scene secondary = SceneManager.CreateScene("ZenjectSecondaryScene_Sep");
252
- ZenjectRelationalTester testerB = CreateHierarchy();
253
- SceneManager.MoveGameObjectToScene(testerB.transform.root.gameObject, secondary);
254
- yield return null;
255
-
256
- IInitializable initializer = Container.Resolve<IInitializable>();
257
- initializer.Initialize();
258
- yield return null;
259
-
260
- Assert.That(
261
- assigner.CallCount,
262
- Is.EqualTo(1),
263
- "Initializer should only process components from the active scene"
264
- );
265
- Assert.That(
266
- assigner.LastComponent,
267
- Is.SameAs(testerA),
268
- "Active scene tester should be assigned"
269
- );
270
- }
271
- finally
272
- {
273
- if (cache != null)
274
- {
275
- UnityEngine.Object.DestroyImmediate(cache);
276
- }
277
- }
294
+ RecordingAssigner assigner = new();
295
+ Container.BindInstance(cache);
296
+ Container.Bind<IRelationalComponentAssigner>().FromInstance(assigner);
297
+ Container.BindInstance(new RelationalSceneAssignmentOptions(true));
298
+ Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
299
+
300
+ Scene active = CreateTempScene("ZenjectActiveScene_Sep");
301
+ ZenjectRelationalTester testerA = CreateHierarchy();
302
+ SceneManager.MoveGameObjectToScene(testerA.transform.root.gameObject, active);
303
+
304
+ Scene secondary = CreateTempScene("ZenjectSecondaryScene_Sep", setActive: false);
305
+ ZenjectRelationalTester testerB = CreateHierarchy();
306
+ SceneManager.MoveGameObjectToScene(testerB.transform.root.gameObject, secondary);
307
+ yield return null;
308
+
309
+ IInitializable initializer = Container.Resolve<IInitializable>();
310
+ initializer.Initialize();
311
+ yield return null;
312
+
313
+ Assert.That(
314
+ assigner.CallCount,
315
+ Is.EqualTo(1),
316
+ "Initializer should only process components from the active scene"
317
+ );
318
+ Assert.That(
319
+ assigner.LastComponent,
320
+ Is.SameAs(testerA),
321
+ "Active scene tester should be assigned"
322
+ );
278
323
  }
279
324
 
280
325
  [Test]
@@ -298,24 +343,20 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
298
343
  rootTester.gameObject,
299
344
  includeInactiveChildren: false
300
345
  );
301
- Assert.That(
302
- rootTester.parentBody,
303
- Is.Not.Null,
346
+ Assert.IsTrue(
347
+ rootTester.parentBody != null,
304
348
  "Root tester should be assigned even when includeInactiveChildren is false"
305
349
  );
306
- Assert.That(
307
- rootTester.childCollider,
308
- Is.Not.Null,
350
+ Assert.IsTrue(
351
+ rootTester.childCollider != null,
309
352
  "Root tester should be assigned even when includeInactiveChildren is false"
310
353
  );
311
- Assert.That(
312
- subTester.parentBody,
313
- Is.Null,
354
+ Assert.IsTrue(
355
+ subTester.parentBody == null,
314
356
  "Inactive sub tester should be skipped when includeInactiveChildren is false"
315
357
  );
316
- Assert.That(
317
- subTester.childCollider,
318
- Is.Null,
358
+ Assert.IsTrue(
359
+ subTester.childCollider == null,
319
360
  "Inactive sub tester should be skipped when includeInactiveChildren is false"
320
361
  );
321
362
 
@@ -324,14 +365,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
324
365
  rootTester.gameObject,
325
366
  includeInactiveChildren: true
326
367
  );
327
- Assert.That(
328
- subTester.parentBody,
329
- Is.Not.Null,
368
+ Assert.IsTrue(
369
+ subTester.parentBody != null,
330
370
  "Inactive sub tester should be assigned when includeInactiveChildren is true"
331
371
  );
332
- Assert.That(
333
- subTester.childCollider,
334
- Is.Not.Null,
372
+ Assert.IsTrue(
373
+ subTester.childCollider != null,
335
374
  "Inactive sub tester should be assigned when includeInactiveChildren is true"
336
375
  );
337
376
  }
@@ -377,14 +416,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
377
416
  Is.SameAs(instance),
378
417
  "Instantiate should target the created tester instance"
379
418
  );
380
- Assert.That(
381
- instance.parentBody,
382
- Is.Not.Null,
419
+ Assert.IsTrue(
420
+ instance.parentBody != null,
383
421
  "ParentComponent should be assigned from override parent"
384
422
  );
385
- Assert.That(
386
- instance.childCollider,
387
- Is.Not.Null,
423
+ Assert.IsTrue(
424
+ instance.childCollider != null,
388
425
  "ChildComponent should be assigned from prefab child collider"
389
426
  );
390
427
  }
@@ -403,14 +440,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
403
440
  );
404
441
  Track(instance.gameObject);
405
442
 
406
- Assert.That(
407
- instance.parentBody,
408
- Is.Not.Null,
443
+ Assert.IsTrue(
444
+ instance.parentBody != null,
409
445
  "ParentComponent should be assigned from override parent without a bound assigner"
410
446
  );
411
- Assert.That(
412
- instance.childCollider,
413
- Is.Not.Null,
447
+ Assert.IsTrue(
448
+ instance.childCollider != null,
414
449
  "ChildComponent should be assigned without a bound assigner"
415
450
  );
416
451
  }
@@ -429,14 +464,12 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
429
464
  );
430
465
  Track(instance.gameObject);
431
466
 
432
- Assert.That(
433
- instance.parentBody,
434
- Is.Not.Null,
467
+ Assert.IsTrue(
468
+ instance.parentBody != null,
435
469
  "ParentComponent should be assigned from override parent"
436
470
  );
437
- Assert.That(
438
- instance.childCollider,
439
- Is.Not.Null,
471
+ Assert.IsTrue(
472
+ instance.childCollider != null,
440
473
  "ChildComponent should be assigned from prefab child collider"
441
474
  );
442
475
  }
@@ -485,16 +518,11 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
485
518
  return tester;
486
519
  }
487
520
 
488
- private GameObject Track(GameObject gameObject)
521
+ private AttributeMetadataCache CreateCacheFor(Type componentType)
489
522
  {
490
- _spawned.Add(gameObject);
491
- return gameObject;
492
- }
493
-
494
- private static AttributeMetadataCache CreateCacheFor(Type componentType)
495
- {
496
- AttributeMetadataCache cache =
497
- ScriptableObject.CreateInstance<AttributeMetadataCache>();
523
+ AttributeMetadataCache cache = Track(
524
+ ScriptableObject.CreateInstance<AttributeMetadataCache>()
525
+ );
498
526
 
499
527
  AttributeMetadataCache.RelationalFieldMetadata[] fields =
500
528
  {
@@ -428,6 +428,11 @@ namespace WallstopStudios.UnityHelpers.Tests.Performance
428
428
  return lines;
429
429
  }
430
430
 
431
+ // Add an intermediate heading so tab headers (####) increment correctly from h3.
432
+ lines.Add("### Datasets");
433
+
434
+ lines.Add(string.Empty);
435
+
431
436
  lines.Add("<!-- tabs:start -->");
432
437
 
433
438
  lines.Add(string.Empty);
@@ -389,6 +389,9 @@ namespace WallstopStudios.UnityHelpers.Tests.Performance
389
389
  return lines;
390
390
  }
391
391
 
392
+ // Add an intermediate heading so tab headers (####) increment correctly from h3.
393
+ lines.Add("### Datasets");
394
+ lines.Add(string.Empty);
392
395
  lines.Add("<!-- tabs:start -->");
393
396
  lines.Add(string.Empty);
394
397
  foreach ((DatasetSpec dataset, List<string> datasetLines) in datasetOutputs)