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
@@ -3,10 +3,11 @@ UGUI – EnhancedImage
3
3
  Shows programmatic setup and basic usage of `EnhancedImage` with HDR tinting.
4
4
 
5
5
  How to use
6
+
6
7
  - Add `EnhancedImageDemo` to an empty scene and press Play.
7
8
  - Optionally assign a `materialTemplate` in the inspector to use a shader that exposes `_Color` and optional `_ShapeMask`.
8
9
 
9
10
  What it shows
11
+
10
12
  - Creating a `Canvas` and an `EnhancedImage` at runtime.
11
13
  - Setting an HDR tint via `HdrColor` (values > 1).
12
-
@@ -3,10 +3,11 @@ UI Toolkit – MultiFile Selector (Editor)
3
3
  A minimal EditorWindow showcasing `MultiFileSelectorElement` for fast in-Editor multi-file selection with breadcrumbs, filtering and virtualization.
4
4
 
5
5
  How to use
6
+
6
7
  - Import the sample, then open: `Window > Unity Helpers > MultiFile Selector Sample`.
7
8
  - Pick extensions and select multiple files; results are logged to the Console.
8
9
 
9
10
  What it shows
11
+
10
12
  - Creating and adding `MultiFileSelectorElement` to an EditorWindow.
11
13
  - Hooks for `OnFilesSelected` and `OnCancelled`.
12
-
@@ -27,7 +27,7 @@ Full License Texts
27
27
 
28
28
  Apache License
29
29
  Version 2.0, January 2004
30
- [http://www.apache.org/licenses/](http://www.apache.org/licenses/)
30
+ [https://www.apache.org/licenses/](https://www.apache.org/licenses/)
31
31
 
32
32
  TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
33
33
 
@@ -93,6 +93,22 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Attributes
93
93
 
94
94
  CollectionAssert.AreEquivalent(expected, methods.Select(method => method.Name));
95
95
  }
96
+
97
+ [Test]
98
+ public void GetPossibleAnimatorEventsAreSortedByName()
99
+ {
100
+ List<MethodInfo> methods = AnimationEventHelpers.GetPossibleAnimatorEventsForType(
101
+ typeof(AnimationEventSignatureHost)
102
+ );
103
+ // Ensure ascending ordinal sort by method name
104
+ for (int i = 1; i < methods.Count; i++)
105
+ {
106
+ Assert.LessOrEqual(
107
+ string.Compare(methods[i - 1].Name, methods[i].Name, StringComparison.Ordinal),
108
+ 0
109
+ );
110
+ }
111
+ }
96
112
  }
97
113
 
98
114
  internal enum AnimationEventSignal
@@ -98,13 +98,13 @@ namespace WallstopStudios.UnityHelpers.Tests.Core.Attributes
98
98
  GameObject go2 = NewGameObject("NonRelational");
99
99
  NonRelational non = go2.AddComponent<NonRelational>();
100
100
 
101
- Assert.IsNull(consumer.SR, "Precondition: relational field should start null");
101
+ Assert.IsTrue(consumer.SR == null, "Precondition: relational field should start null");
102
102
 
103
103
  List<Component> items = new List<Component> { consumer, null, non.transform };
104
104
  assigner.Assign(items);
105
105
 
106
- Assert.IsNotNull(
107
- consumer.SR,
106
+ Assert.IsTrue(
107
+ consumer.SR != null,
108
108
  "Relational field should be assigned by Assign(IEnumerable<Component>)"
109
109
  );
110
110
 
@@ -55,6 +55,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
55
55
  new[] { relationalMetadata }
56
56
  );
57
57
  cache.ForceRebuildForTests();
58
+ yield return null;
58
59
  #endif
59
60
 
60
61
  RelationalComponentAssigner assigner = new RelationalComponentAssigner(cache);
@@ -69,8 +70,11 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
69
70
  // Allow assignment to complete
70
71
  yield return null;
71
72
 
72
- Assert.NotNull(consumer);
73
- Assert.NotNull(consumer.SR, "Relational field should be assigned by entry point");
73
+ Assert.IsTrue(consumer != null);
74
+ Assert.IsTrue(
75
+ consumer.SR != null,
76
+ "Relational field should be assigned by entry point"
77
+ );
74
78
  }
75
79
  }
76
80
  }
@@ -0,0 +1,170 @@
1
+ #if VCONTAINER_PRESENT
2
+ namespace WallstopStudios.UnityHelpers.Tests.Integrations.VContainer
3
+ {
4
+ using System.Collections;
5
+ using global::VContainer;
6
+ using NUnit.Framework;
7
+ using UnityEngine;
8
+ using UnityEngine.SceneManagement;
9
+ using UnityEngine.TestTools;
10
+ using WallstopStudios.UnityHelpers.Core.Attributes;
11
+ using WallstopStudios.UnityHelpers.Integrations.VContainer;
12
+ using WallstopStudios.UnityHelpers.Tags;
13
+ using WallstopStudios.UnityHelpers.Tests.Editor.Utils;
14
+
15
+ public sealed class VContainerRelationalHelpersTests : CommonTestBase
16
+ {
17
+ private sealed class TestComponent : MonoBehaviour
18
+ {
19
+ [ParentComponent(OnlyAncestors = true)]
20
+ public Rigidbody parentBody;
21
+
22
+ [ChildComponent(OnlyDescendants = true)]
23
+ public CapsuleCollider childCollider;
24
+ }
25
+
26
+ [Test]
27
+ public void InjectWithRelationsAssignsFields()
28
+ {
29
+ ContainerBuilder builder = new ContainerBuilder();
30
+ IObjectResolver resolver = builder.Build();
31
+
32
+ // Build hierarchy: Parent(Rigidbody) -> Middle(TestComponent) -> Child(CapsuleCollider)
33
+ GameObject parent = NewGameObject("Parent");
34
+ parent.AddComponent<Rigidbody>();
35
+ GameObject middle = NewGameObject("Middle");
36
+ middle.transform.SetParent(parent.transform);
37
+ TestComponent comp = middle.AddComponent<TestComponent>();
38
+ GameObject child = NewGameObject("Child");
39
+ child.transform.SetParent(middle.transform);
40
+ child.AddComponent<CapsuleCollider>();
41
+
42
+ TestComponent result = resolver.InjectWithRelations(comp);
43
+
44
+ Assert.That(result, Is.SameAs(comp));
45
+ Assert.IsTrue(comp.parentBody != null);
46
+ Assert.IsTrue(comp.childCollider != null);
47
+ }
48
+
49
+ [Test]
50
+ public void InstantiateComponentWithRelationsAssignsFields()
51
+ {
52
+ ContainerBuilder builder = new ContainerBuilder();
53
+ IObjectResolver resolver = builder.Build();
54
+
55
+ // Build prefab hierarchy: Root(Rigidbody) -> Middle(TestComponent) -> Child(CapsuleCollider)
56
+ GameObject prefabRoot = NewGameObject("PrefabRoot");
57
+ prefabRoot.AddComponent<Rigidbody>();
58
+ GameObject prefabMiddle = NewGameObject("PrefabMiddle");
59
+ prefabMiddle.transform.SetParent(prefabRoot.transform);
60
+ TestComponent prefabComp = prefabMiddle.AddComponent<TestComponent>();
61
+ GameObject prefabChild = NewGameObject("PrefabChild");
62
+ prefabChild.transform.SetParent(prefabMiddle.transform);
63
+ prefabChild.AddComponent<CapsuleCollider>();
64
+
65
+ // Instantiate the full hierarchy and hydrate via GO helper
66
+ GameObject instanceRoot = resolver.InstantiateGameObjectWithRelations(prefabRoot);
67
+ TestComponent instance = instanceRoot.GetComponentInChildren<TestComponent>(true);
68
+
69
+ Assert.IsTrue(instance != null);
70
+ Assert.IsTrue(instance.parentBody != null);
71
+ Assert.IsTrue(instance.childCollider != null);
72
+ }
73
+
74
+ [Test]
75
+ public void InjectGameObjectWithRelationsAssignsHierarchy()
76
+ {
77
+ ContainerBuilder builder = new ContainerBuilder();
78
+ IObjectResolver resolver = builder.Build();
79
+
80
+ GameObject root = NewGameObject("Root");
81
+ root.AddComponent<Rigidbody>();
82
+ GameObject middle = NewGameObject("Middle");
83
+ middle.transform.SetParent(root.transform);
84
+ TestComponent comp = middle.AddComponent<TestComponent>();
85
+ GameObject child = NewGameObject("Child");
86
+ child.transform.SetParent(middle.transform);
87
+ child.AddComponent<CapsuleCollider>();
88
+
89
+ resolver.InjectGameObjectWithRelations(root);
90
+
91
+ Assert.IsTrue(comp.parentBody != null);
92
+ Assert.IsTrue(comp.childCollider != null);
93
+ }
94
+
95
+ [Test]
96
+ public void InstantiateGameObjectWithRelationsAssignsHierarchy()
97
+ {
98
+ ContainerBuilder builder = new ContainerBuilder();
99
+ IObjectResolver resolver = builder.Build();
100
+
101
+ GameObject prefab = NewGameObject("PrefabRoot");
102
+ prefab.AddComponent<Rigidbody>();
103
+ GameObject mid = NewGameObject("PrefabMiddle");
104
+ mid.transform.SetParent(prefab.transform);
105
+ TestComponent prefabComp = mid.AddComponent<TestComponent>();
106
+ GameObject child = NewGameObject("PrefabChild");
107
+ child.transform.SetParent(mid.transform);
108
+ child.AddComponent<CapsuleCollider>();
109
+
110
+ GameObject instance = resolver.InstantiateGameObjectWithRelations(prefab);
111
+ TestComponent comp = instance.GetComponentInChildren<TestComponent>(true);
112
+
113
+ Assert.IsTrue(comp != null);
114
+ Assert.IsTrue(comp.parentBody != null);
115
+ Assert.IsTrue(comp.childCollider != null);
116
+ }
117
+
118
+ [Test]
119
+ public void AssignerRecognizesBaseTypeMetadataForDerivedComponents()
120
+ {
121
+ AttributeMetadataCache cache = CreateScriptableObject<AttributeMetadataCache>();
122
+
123
+ AttributeMetadataCache.RelationalFieldMetadata[] fields =
124
+ {
125
+ new AttributeMetadataCache.RelationalFieldMetadata(
126
+ nameof(BaseWithSibling._spriteRenderer),
127
+ AttributeMetadataCache.RelationalAttributeKind.Sibling,
128
+ AttributeMetadataCache.FieldKind.Single,
129
+ typeof(SpriteRenderer).AssemblyQualifiedName,
130
+ false
131
+ ),
132
+ };
133
+ AttributeMetadataCache.RelationalTypeMetadata[] relational =
134
+ {
135
+ new AttributeMetadataCache.RelationalTypeMetadata(
136
+ typeof(BaseWithSibling).AssemblyQualifiedName,
137
+ fields
138
+ ),
139
+ };
140
+ cache._relationalTypeMetadata = relational;
141
+ cache.ForceRebuildForTests();
142
+
143
+ ContainerBuilder builder = new ContainerBuilder();
144
+ builder.RegisterInstance(cache).AsSelf();
145
+ builder
146
+ .RegisterInstance(new RelationalComponentAssigner(cache))
147
+ .As<IRelationalComponentAssigner>();
148
+ IObjectResolver resolver = builder.Build();
149
+
150
+ GameObject go = NewGameObject("Root");
151
+ go.AddComponent<SpriteRenderer>();
152
+ DerivedWithSibling comp = go.AddComponent<DerivedWithSibling>();
153
+
154
+ resolver.AssignRelationalComponents(comp);
155
+
156
+ Assert.IsTrue(comp.SR != null);
157
+ }
158
+
159
+ private class BaseWithSibling : MonoBehaviour
160
+ {
161
+ [SiblingComponent]
162
+ protected internal SpriteRenderer _spriteRenderer;
163
+
164
+ public SpriteRenderer SR => _spriteRenderer;
165
+ }
166
+
167
+ private sealed class DerivedWithSibling : BaseWithSibling { }
168
+ }
169
+ }
170
+ #endif
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 96ae7f33720de924cb793c45e98b66e5
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -6,7 +6,8 @@
6
6
  "UnityEngine.TestRunner",
7
7
  "WallstopStudios.UnityHelpers",
8
8
  "WallstopStudios.UnityHelpers.Integration.VContainer",
9
- "WallstopStudios.UnityHelpers.Tests.Editor"
9
+ "WallstopStudios.UnityHelpers.Tests.Editor",
10
+ "VContainer"
10
11
  ],
11
12
  "includePlatforms": ["Editor"],
12
13
  "excludePlatforms": [],
@@ -6,13 +6,14 @@
6
6
  "UnityEngine.TestRunner",
7
7
  "WallstopStudios.UnityHelpers",
8
8
  "WallstopStudios.UnityHelpers.Integration.Zenject",
9
- "WallstopStudios.UnityHelpers.Tests.Editor"
9
+ "WallstopStudios.UnityHelpers.Tests.Editor",
10
+ "Zenject"
10
11
  ],
11
12
  "includePlatforms": ["Editor"],
12
13
  "excludePlatforms": [],
13
14
  "allowUnsafeCode": false,
14
15
  "overrideReferences": true,
15
- "precompiledReferences": ["nunit.framework.dll"],
16
+ "precompiledReferences": ["nunit.framework.dll", "Zenject-usage.dll"],
16
17
  "autoReferenced": false,
17
18
  "defineConstraints": ["UNITY_INCLUDE_TESTS", "ZENJECT_PRESENT"],
18
19
  "versionDefines": [
@@ -0,0 +1,131 @@
1
+ #if ZENJECT_PRESENT
2
+ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
3
+ {
4
+ using System.Collections;
5
+ using global::Zenject;
6
+ using NUnit.Framework;
7
+ using UnityEngine;
8
+ using UnityEngine.SceneManagement;
9
+ using UnityEngine.TestTools;
10
+ using WallstopStudios.UnityHelpers.Core.Attributes;
11
+ using WallstopStudios.UnityHelpers.Integrations.Zenject;
12
+ using WallstopStudios.UnityHelpers.Tags;
13
+ using WallstopStudios.UnityHelpers.Tests.Editor.Utils;
14
+
15
+ public sealed class ZenjectRelationalHelpersTests : CommonTestBase
16
+ {
17
+ private sealed class TestComponent : MonoBehaviour
18
+ {
19
+ [ParentComponent(OnlyAncestors = true)]
20
+ public Rigidbody parentBody;
21
+
22
+ [ChildComponent(OnlyDescendants = true)]
23
+ public CapsuleCollider childCollider;
24
+ }
25
+
26
+ [Test]
27
+ public void InjectWithRelationsAssignsFields()
28
+ {
29
+ DiContainer container = new DiContainer();
30
+
31
+ // Build hierarchy: Parent(Rigidbody) -> Middle(TestComponent) -> Child(CapsuleCollider)
32
+ GameObject parent = NewGameObject("Root");
33
+ parent.AddComponent<Rigidbody>();
34
+ GameObject middle = NewGameObject("Middle");
35
+ middle.transform.SetParent(parent.transform);
36
+ TestComponent comp = middle.AddComponent<TestComponent>();
37
+ GameObject child = NewGameObject("Child");
38
+ child.transform.SetParent(middle.transform);
39
+ child.AddComponent<CapsuleCollider>();
40
+
41
+ TestComponent result = container.InjectWithRelations(comp);
42
+
43
+ Assert.That(result, Is.SameAs(comp));
44
+ Assert.IsTrue(comp.parentBody != null);
45
+ }
46
+
47
+ [Test]
48
+ public void InjectGameObjectWithRelationsAssignsHierarchy()
49
+ {
50
+ DiContainer container = new DiContainer();
51
+
52
+ GameObject root = NewGameObject("Root");
53
+ root.AddComponent<Rigidbody>();
54
+ GameObject middle = NewGameObject("Middle");
55
+ middle.transform.SetParent(root.transform);
56
+ TestComponent comp = middle.AddComponent<TestComponent>();
57
+ GameObject child = NewGameObject("Child");
58
+ child.transform.SetParent(middle.transform);
59
+ child.AddComponent<CapsuleCollider>();
60
+
61
+ container.InjectGameObjectWithRelations(root);
62
+
63
+ Assert.IsTrue(comp.parentBody != null);
64
+ Assert.IsTrue(comp.childCollider != null);
65
+ }
66
+
67
+ [Test]
68
+ public void InstantiateGameObjectWithRelationsAssignsHierarchy()
69
+ {
70
+ DiContainer container = new DiContainer();
71
+
72
+ GameObject prefab = NewGameObject("PrefabRoot");
73
+ prefab.AddComponent<Rigidbody>();
74
+ GameObject mid = NewGameObject("PrefabMiddle");
75
+ mid.transform.SetParent(prefab.transform);
76
+ TestComponent prefabComp = mid.AddComponent<TestComponent>();
77
+ GameObject child = NewGameObject("PrefabChild");
78
+ child.transform.SetParent(mid.transform);
79
+ child.AddComponent<CapsuleCollider>();
80
+
81
+ GameObject instance = container.InstantiateGameObjectWithRelations(prefab);
82
+ TestComponent comp = instance.GetComponentInChildren<TestComponent>(true);
83
+
84
+ Assert.IsTrue(comp != null);
85
+ Assert.IsTrue(comp.parentBody != null);
86
+ Assert.IsTrue(comp.childCollider != null);
87
+ }
88
+
89
+ [Test]
90
+ public void RelationalMemoryPoolAssignsOnSpawn()
91
+ {
92
+ // Create a pool and inject a container into the private field using reflection
93
+ RelationalMemoryPool<TestComponent> pool = new RelationalMemoryPool<TestComponent>();
94
+ DiContainer container = new DiContainer();
95
+
96
+ pool._container = container;
97
+
98
+ GameObject go = NewGameObject("PooledRoot");
99
+ go.AddComponent<Rigidbody>();
100
+ GameObject middle = NewGameObject("PooledMiddle");
101
+ middle.transform.SetParent(go.transform);
102
+ TestComponent comp = middle.AddComponent<TestComponent>();
103
+ GameObject child = NewGameObject("PooledChild");
104
+ child.transform.SetParent(middle.transform);
105
+ child.AddComponent<CapsuleCollider>();
106
+
107
+ // Call OnSpawned indirectly via protected method using a small helper
108
+ PoolHarness harness = new PoolHarness(pool);
109
+ harness.InvokeOnSpawned(comp);
110
+
111
+ Assert.IsTrue(comp.parentBody != null);
112
+ Assert.IsTrue(comp.childCollider != null);
113
+ }
114
+
115
+ private sealed class PoolHarness
116
+ {
117
+ private readonly RelationalMemoryPool<TestComponent> _pool;
118
+
119
+ public PoolHarness(RelationalMemoryPool<TestComponent> pool)
120
+ {
121
+ _pool = pool;
122
+ }
123
+
124
+ public void InvokeOnSpawned(TestComponent item)
125
+ {
126
+ _pool.InternalOnSpawned(item);
127
+ }
128
+ }
129
+ }
130
+ }
131
+ #endif
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 71b61345c8024944388a1bff583a268a
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -54,6 +54,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
54
54
  new[] { relationalMetadata }
55
55
  );
56
56
  cache.ForceRebuildForTests();
57
+ yield return null;
57
58
  #endif
58
59
 
59
60
  RelationalComponentAssigner assigner = new RelationalComponentAssigner(cache);
@@ -68,8 +69,11 @@ namespace WallstopStudios.UnityHelpers.Tests.Integrations.Zenject
68
69
 
69
70
  yield return null;
70
71
 
71
- Assert.NotNull(consumer);
72
- Assert.NotNull(consumer.SR, "Relational field should be assigned by initializer");
72
+ Assert.IsTrue(consumer != null);
73
+ Assert.IsTrue(
74
+ consumer.SR != null,
75
+ "Relational field should be assigned by initializer"
76
+ );
73
77
  }
74
78
  }
75
79
  }
@@ -0,0 +1,59 @@
1
+ namespace WallstopStudios.UnityHelpers.Tests.Editor
2
+ {
3
+ #if UNITY_EDITOR
4
+ using NUnit.Framework;
5
+ using UnityEditor;
6
+ using UnityEngine;
7
+ using WallstopStudios.UnityHelpers.Editor;
8
+
9
+ public sealed class PersistentDirectorySettingsTests
10
+ {
11
+ [Test]
12
+ public void GetPathsSortsByCountThenLastUsed()
13
+ {
14
+ PersistentDirectorySettings settings =
15
+ ScriptableObject.CreateInstance<PersistentDirectorySettings>();
16
+
17
+ string tool = "TestTool";
18
+ string ctx = "Context";
19
+
20
+ // B: 1 time, A: 3 times, C: 2 times
21
+ settings.RecordPath(tool, ctx, "Assets/A");
22
+ settings.RecordPath(tool, ctx, "Assets/A");
23
+ settings.RecordPath(tool, ctx, "Assets/A");
24
+
25
+ settings.RecordPath(tool, ctx, "Assets/C");
26
+ settings.RecordPath(tool, ctx, "Assets/C");
27
+
28
+ settings.RecordPath(tool, ctx, "Assets/B");
29
+
30
+ DirectoryUsageData[] paths = settings.GetPaths(tool, ctx);
31
+ Assert.IsNotNull(paths);
32
+ Assert.GreaterOrEqual(paths.Length, 3);
33
+ Assert.AreEqual("Assets/A", paths[0].path);
34
+ Assert.AreEqual("Assets/C", paths[1].path);
35
+ Assert.AreEqual("Assets/B", paths[2].path);
36
+ }
37
+
38
+ [Test]
39
+ public void GetPathsTopOnlyRespectsLimit()
40
+ {
41
+ PersistentDirectorySettings settings =
42
+ ScriptableObject.CreateInstance<PersistentDirectorySettings>();
43
+
44
+ string tool = "TopOnlyTool";
45
+ string ctx = "Context";
46
+
47
+ settings.RecordPath(tool, ctx, "Assets/One");
48
+ settings.RecordPath(tool, ctx, "Assets/One");
49
+ settings.RecordPath(tool, ctx, "Assets/Two");
50
+ settings.RecordPath(tool, ctx, "Assets/Three");
51
+
52
+ DirectoryUsageData[] top2 = settings.GetPaths(tool, ctx, topOnly: true, topN: 2);
53
+ Assert.IsNotNull(top2);
54
+ Assert.AreEqual(2, top2.Length);
55
+ Assert.AreEqual("Assets/One", top2[0].path);
56
+ }
57
+ }
58
+ #endif
59
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 809681431b5fa8f43849f0ef82f9b097
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,32 @@
1
+ namespace WallstopStudios.UnityHelpers.Tests.Editor
2
+ {
3
+ #if UNITY_EDITOR
4
+ using System;
5
+ using System.Collections.Generic;
6
+ using NUnit.Framework;
7
+ using WallstopStudios.UnityHelpers.Editor;
8
+
9
+ public sealed class PrefabCheckerReportTests
10
+ {
11
+ [Test]
12
+ public void ScanReportConstructorCopiesFolders()
13
+ {
14
+ var report = new PrefabChecker.ScanReport(new[] { "A", "B" });
15
+ string[] folders = report.folders;
16
+ CollectionAssert.AreEqual(new[] { "A", "B" }, folders);
17
+ }
18
+
19
+ [Test]
20
+ public void ScanReportAddCopiesMessages()
21
+ {
22
+ var report = new PrefabChecker.ScanReport(Array.Empty<string>());
23
+ report.Add("path.prefab", new List<string> { "m1", "m2" });
24
+ Assert.AreEqual(1, report.items.Count);
25
+ var first = report.items[0];
26
+ Assert.AreEqual("path.prefab", first.path);
27
+ string[] messages = first.messages;
28
+ CollectionAssert.AreEqual(new[] { "m1", "m2" }, messages);
29
+ }
30
+ }
31
+ #endif
32
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 5b1c8d8370daa1143a66a09863f869e9
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,64 @@
1
+ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
2
+ {
3
+ #if UNITY_EDITOR
4
+ using System;
5
+ using System.Collections.Generic;
6
+ using System.Linq;
7
+ using NUnit.Framework;
8
+ using UnityEngine;
9
+ using WallstopStudios.UnityHelpers.Editor.Sprites;
10
+
11
+ public sealed class AnimationCopierFilterTests
12
+ {
13
+ private static AnimationCopierWindow.AnimationFileInfo NewFileInfo(string name)
14
+ {
15
+ return new AnimationCopierWindow.AnimationFileInfo { FileName = name };
16
+ }
17
+
18
+ [Test]
19
+ public void SortsAscendingAndDescending()
20
+ {
21
+ AnimationCopierWindow wnd = ScriptableObject.CreateInstance<AnimationCopierWindow>();
22
+ var a = NewFileInfo("zeta.anim");
23
+ var b = NewFileInfo("alpha.anim");
24
+ var c = NewFileInfo("beta.anim");
25
+ var items = new List<AnimationCopierWindow.AnimationFileInfo> { a, b, c };
26
+
27
+ wnd._filterText = string.Empty;
28
+ wnd._filterUseRegex = false;
29
+ wnd._sortAscending = true;
30
+ var asc = wnd.ApplyFilterAndSort(items).ToList();
31
+ string[] ascNames = asc.Select(o => o.FileName).ToArray();
32
+ CollectionAssert.AreEqual(new[] { "alpha.anim", "beta.anim", "zeta.anim" }, ascNames);
33
+
34
+ wnd._sortAscending = false;
35
+ var desc = wnd.ApplyFilterAndSort(items).ToList();
36
+ string[] descNames = desc.Select(o => o.FileName).ToArray();
37
+ CollectionAssert.AreEqual(new[] { "zeta.anim", "beta.anim", "alpha.anim" }, descNames);
38
+ }
39
+
40
+ [Test]
41
+ public void FiltersBySubstringAndRegex()
42
+ {
43
+ AnimationCopierWindow wnd = ScriptableObject.CreateInstance<AnimationCopierWindow>();
44
+ var a = NewFileInfo("walk.anim");
45
+ var b = NewFileInfo("attack.anim");
46
+ var c = NewFileInfo("idle.anim");
47
+ var items = new List<AnimationCopierWindow.AnimationFileInfo> { a, b, c };
48
+
49
+ wnd._filterText = "ta";
50
+ wnd._filterUseRegex = false;
51
+ wnd._sortAscending = true;
52
+ var sub = wnd.ApplyFilterAndSort(items).ToList();
53
+ string[] subNames = sub.Select(o => o.FileName).ToArray();
54
+ CollectionAssert.AreEquivalent(new[] { "attack.anim" }, subNames);
55
+
56
+ wnd._filterText = "^i";
57
+ wnd._filterUseRegex = true;
58
+ var rx = wnd.ApplyFilterAndSort(items).ToList();
59
+ string[] rxNames = rx.Select(o => o.FileName).ToArray();
60
+ CollectionAssert.AreEquivalent(new[] { "idle.anim" }, rxNames);
61
+ }
62
+ }
63
+ #endif
64
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 3e868183fe25f654f85a8f3e44a0862b
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant: