com.wallstop-studios.unity-helpers 2.1.0 → 2.1.2

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 (106) hide show
  1. package/AGENTS.md +1 -0
  2. package/Docs/ILIST_SORTING_PERFORMANCE.md +92 -0
  3. package/{package-lock.json.meta → Docs/ILIST_SORTING_PERFORMANCE.md.meta} +1 -1
  4. package/Docs/INDEX.md +11 -1
  5. package/Docs/Images/random_generators.svg +7 -7
  6. package/Docs/RANDOM_PERFORMANCE.md +17 -14
  7. package/Docs/REFLECTION_HELPERS.md +84 -1
  8. package/Docs/REFLECTION_PERFORMANCE.md +169 -0
  9. package/Docs/REFLECTION_PERFORMANCE.md.meta +7 -0
  10. package/Docs/RELATIONAL_COMPONENTS.md +6 -0
  11. package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md +63 -0
  12. package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md.meta +7 -0
  13. package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
  14. package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
  15. package/Editor/Core/Helper/AnimationEventHelpers.cs +1 -1
  16. package/Editor/Sprites/AnimationCopier.cs +1 -1
  17. package/Editor/Sprites/AnimationViewerWindow.cs +4 -4
  18. package/Editor/Sprites/SpriteSettingsApplierAPI.cs +2 -1
  19. package/Editor/Sprites/TextureResizerWizard.cs +4 -3
  20. package/Editor/Utils/ScriptableObjectSingletonCreator.cs +3 -3
  21. package/README.md +33 -18
  22. package/Runtime/Core/Attributes/BaseRelationalComponentAttribute.cs +147 -20
  23. package/Runtime/Core/Attributes/ChildComponentAttribute.cs +630 -117
  24. package/Runtime/Core/Attributes/NotNullAttribute.cs +5 -2
  25. package/Runtime/Core/Attributes/ParentComponentAttribute.cs +477 -103
  26. package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +26 -3
  27. package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +19 -3
  28. package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +265 -92
  29. package/Runtime/Core/CodeGen.meta +8 -0
  30. package/Runtime/Core/DataStructure/ImmutableBitSet.cs +5 -20
  31. package/Runtime/Core/Extension/IListExtensions.cs +720 -12
  32. package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +11 -7
  33. package/Runtime/Core/Helper/Objects.cs +1 -1
  34. package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs +5142 -0
  35. package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs.meta +11 -0
  36. package/Runtime/Core/Helper/ReflectionHelpers.cs +1812 -1518
  37. package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +2 -3
  38. package/Runtime/Core/Math/Line2D.cs +2 -4
  39. package/Runtime/Core/Math/Line3D.cs +2 -4
  40. package/Runtime/Core/Random/AbstractRandom.cs +52 -5
  41. package/Runtime/Core/Random/DotNetRandom.cs +3 -3
  42. package/Runtime/Core/Random/FlurryBurstRandom.cs +279 -0
  43. package/Runtime/Core/Random/FlurryBurstRandom.cs.meta +3 -0
  44. package/Runtime/Core/Random/IllusionFlow.cs +3 -3
  45. package/Runtime/Core/Random/LinearCongruentialGenerator.cs +3 -3
  46. package/Runtime/Core/Random/PcgRandom.cs +6 -6
  47. package/Runtime/Core/Random/PhotonSpinRandom.cs +387 -0
  48. package/Runtime/Core/Random/PhotonSpinRandom.cs.meta +3 -0
  49. package/Runtime/Core/Random/RomuDuo.cs +3 -3
  50. package/Runtime/Core/Random/SplitMix64.cs +3 -3
  51. package/Runtime/Core/Random/SquirrelRandom.cs +6 -4
  52. package/Runtime/Core/Random/StormDropRandom.cs +271 -0
  53. package/Runtime/Core/Random/StormDropRandom.cs.meta +3 -0
  54. package/Runtime/Core/Random/UnityRandom.cs +3 -3
  55. package/Runtime/Core/Random/WyRandom.cs +6 -4
  56. package/Runtime/Core/Random/XorShiftRandom.cs +3 -3
  57. package/Runtime/Core/Random/XoroShiroRandom.cs +3 -3
  58. package/Runtime/Tags/AttributeMetadataCache.cs +316 -9
  59. package/Runtime/Tags/CosmeticEffectData.cs +1 -1
  60. package/Runtime/Visuals/UIToolkit/MultiFileSelectorElement.cs +3 -3
  61. package/Tests/Editor/Helper/HelpersTests.cs +2 -2
  62. package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs +87 -0
  63. package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs.meta +11 -0
  64. package/Tests/Editor/Helper/SpriteHelpersTests.cs +1 -1
  65. package/Tests/Editor/PrefabCheckerReportTests.cs +3 -3
  66. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +18 -12
  67. package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +8 -7
  68. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -1
  69. package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +6 -5
  70. package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +2 -1
  71. package/Tests/Editor/Sprites/SpriteCropperTests.cs +7 -6
  72. package/Tests/Editor/Sprites/SpritePivotAdjusterAdditionalTests.cs +2 -1
  73. package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +4 -3
  74. package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +10 -9
  75. package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +2 -1
  76. package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs +192 -0
  77. package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
  78. package/Tests/Editor/Tags.meta +8 -0
  79. package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
  80. package/Tests/Runtime/Helper/ObjectsTests.cs +4 -4
  81. package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs +2923 -0
  82. package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs.meta +11 -0
  83. package/Tests/Runtime/Helper/ReflectionHelperTests.cs +660 -0
  84. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +2 -2
  85. package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs +346 -0
  86. package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs.meta +11 -0
  87. package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -0
  88. package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs +1238 -0
  89. package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs.meta +11 -0
  90. package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs +832 -0
  91. package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs.meta +11 -0
  92. package/Tests/Runtime/Random/FlurryBurstRandomTests.cs +12 -0
  93. package/Tests/Runtime/Random/FlurryBurstRandomTests.cs.meta +3 -0
  94. package/Tests/Runtime/Random/PhotonSpinRandomTests.cs +12 -0
  95. package/Tests/Runtime/Random/PhotonSpinRandomTests.cs.meta +3 -0
  96. package/Tests/Runtime/Random/RandomProtoSerializationTests.cs +14 -0
  97. package/Tests/Runtime/Random/RandomTestBase.cs +39 -4
  98. package/Tests/Runtime/Random/StormDropRandomTests.cs +12 -0
  99. package/Tests/Runtime/Random/StormDropRandomTests.cs.meta +3 -0
  100. package/Tests/Runtime/Serialization/ProtoInterfaceResolutionEdgeTests.cs +2 -2
  101. package/Tests/Runtime/Serialization/ProtoRootRegistrationTests.cs +1 -1
  102. package/Tests/Runtime/Serialization/ProtoSerializeBehaviorTests.cs +1 -1
  103. package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +2 -2
  104. package/package.json +1 -1
  105. package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +0 -60
  106. package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +0 -3
@@ -6,6 +6,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
6
6
  using UnityEditor;
7
7
  using UnityEngine;
8
8
  using UnityEngine.U2D;
9
+ using WallstopStudios.UnityHelpers.Core.Helper;
9
10
  using WallstopStudios.UnityHelpers.Editor.Sprites;
10
11
  using WallstopStudios.UnityHelpers.Tests.Editor.Utils;
11
12
 
@@ -31,7 +32,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
31
32
  public void GeneratesSpriteAtlasAssetFromConfig()
32
33
  {
33
34
  // Create a source sprite
34
- string spritePath = Path.Combine(Root, "icon.png").Replace('\\', '/');
35
+ string spritePath = Path.Combine(Root, "icon.png").SanitizePath();
35
36
  CreatePng(spritePath, 8, 8, Color.red);
36
37
  AssetDatabase.Refresh();
37
38
 
@@ -41,7 +42,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
41
42
  config.spritesToPack.Add(AssetDatabase.LoadAssetAtPath<Sprite>(spritePath));
42
43
  config.outputSpriteAtlasDirectory = Root;
43
44
  config.outputSpriteAtlasName = "TestAtlas";
44
- string configPath = Path.Combine(Root, "TestAtlasConfig.asset").Replace('\\', '/');
45
+ string configPath = Path.Combine(Root, "TestAtlasConfig.asset").SanitizePath();
45
46
  AssetDatabase.CreateAsset(config, configPath);
46
47
  AssetDatabase.SaveAssets();
47
48
  AssetDatabase.Refresh();
@@ -55,7 +56,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
55
56
 
56
57
  AssetDatabase.Refresh();
57
58
 
58
- string atlasPath = Path.Combine(Root, "TestAtlas.spriteatlas").Replace('\\', '/');
59
+ string atlasPath = Path.Combine(Root, "TestAtlas.spriteatlas").SanitizePath();
59
60
  SpriteAtlas atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasPath);
60
61
  Assert.IsTrue(atlas != null, ".spriteatlas should be generated");
61
62
  }
@@ -77,7 +78,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
77
78
 
78
79
  private static void CreatePng(string relPath, int w, int h, Color c)
79
80
  {
80
- string dir = Path.GetDirectoryName(relPath).Replace('\\', '/');
81
+ string dir = Path.GetDirectoryName(relPath)?.SanitizePath();
81
82
  EnsureFolder(dir);
82
83
  Texture2D t = new(w, h, TextureFormat.RGBA32, false);
83
84
  Color[] pix = new Color[w * h];
@@ -101,7 +102,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
101
102
  ),
102
103
  rel
103
104
  )
104
- .Replace('\\', '/');
105
+ .SanitizePath();
105
106
  }
106
107
  }
107
108
  #endif
@@ -5,6 +5,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
5
5
  using NUnit.Framework;
6
6
  using UnityEditor;
7
7
  using UnityEngine;
8
+ using WallstopStudios.UnityHelpers.Core.Helper;
8
9
  using WallstopStudios.UnityHelpers.Editor.Sprites;
9
10
  using WallstopStudios.UnityHelpers.Tests.Editor.Utils;
10
11
  using Object = UnityEngine.Object;
@@ -30,7 +31,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
30
31
  [Test]
31
32
  public void AppliesPaddingAndAdjustsPivotCorrectly()
32
33
  {
33
- string src = (Root + "/pad_src.png").Replace('\\', '/');
34
+ string src = (Root + "/pad_src.png").SanitizePath();
34
35
  // 20x20, opaque 10x10 at (5,5)
35
36
  CreatePngWithOpaqueRect(src, 20, 20, 5, 5, 10, 10, Color.white);
36
37
  AssetDatabase.Refresh();
@@ -5,6 +5,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
5
5
  using NUnit.Framework;
6
6
  using UnityEditor;
7
7
  using UnityEngine;
8
+ using WallstopStudios.UnityHelpers.Core.Helper;
8
9
  using WallstopStudios.UnityHelpers.Editor.Sprites;
9
10
  using WallstopStudios.UnityHelpers.Tests.Editor.Utils;
10
11
 
@@ -29,7 +30,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
29
30
  [Test]
30
31
  public void CropsTransparentMarginsAndPreservesPivot()
31
32
  {
32
- string src = Path.Combine(Root, "src.png").Replace('\\', '/');
33
+ string src = Path.Combine(Root, "src.png").SanitizePath();
33
34
  // 16x16 with an opaque 10x10 square starting at (3,3)
34
35
  CreatePngWithOpaqueRect(src, 16, 16, 3, 3, 10, 10, Color.white);
35
36
  AssetDatabase.Refresh();
@@ -72,8 +73,8 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
72
73
  [Test]
73
74
  public void WritesToOutputDirectoryWhenNotOverwriting()
74
75
  {
75
- string src = Path.Combine(Root, "src2.png").Replace('\\', '/');
76
- string outDir = Path.Combine(Root, "Out").Replace('\\', '/');
76
+ string src = Path.Combine(Root, "src2.png").SanitizePath();
77
+ string outDir = Path.Combine(Root, "Out").SanitizePath();
77
78
  EnsureFolder(outDir);
78
79
  CreatePngWithOpaqueRect(src, 8, 8, 2, 2, 4, 4, Color.green);
79
80
  AssetDatabase.Refresh();
@@ -96,7 +97,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
96
97
 
97
98
  AssetDatabase.Refresh();
98
99
 
99
- string dst = Path.Combine(outDir, "Cropped_src2.png").Replace('\\', '/');
100
+ string dst = Path.Combine(outDir, "Cropped_src2.png").SanitizePath();
100
101
  Assert.That(File.Exists(RelToFull(dst)), Is.True, "Cropped output should exist");
101
102
 
102
103
  Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(dst);
@@ -116,7 +117,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
116
117
  Color color
117
118
  )
118
119
  {
119
- string dir = Path.GetDirectoryName(relPath).Replace('\\', '/');
120
+ string dir = Path.GetDirectoryName(relPath)?.SanitizePath();
120
121
  EnsureFolder(dir);
121
122
  Texture2D t = new(w, h, TextureFormat.RGBA32, false) { alphaIsTransparency = true };
122
123
  Color[] pix = new Color[w * h];
@@ -155,7 +156,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
155
156
  ),
156
157
  rel
157
158
  )
158
- .Replace('\\', '/');
159
+ .SanitizePath();
159
160
  }
160
161
  }
161
162
  #endif
@@ -5,6 +5,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
5
5
  using NUnit.Framework;
6
6
  using UnityEditor;
7
7
  using UnityEngine;
8
+ using WallstopStudios.UnityHelpers.Core.Helper;
8
9
  using WallstopStudios.UnityHelpers.Editor.Sprites;
9
10
  using WallstopStudios.UnityHelpers.Tests.Editor.Utils;
10
11
  using Object = UnityEngine.Object;
@@ -32,7 +33,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
32
33
  [Test]
33
34
  public void RespectsAlphaCutoffWhenComputingPivot()
34
35
  {
35
- string src = (Root + "/alpha_bias.png").Replace('\\', '/');
36
+ string src = (Root + "/alpha_bias.png").SanitizePath();
36
37
  // 20x20 with a faint (alpha=0.2) 4x4 block at bottom-left and a solid 4x4 at top-right
37
38
  CreateDualAlphaPattern(src, 20, 20);
38
39
  AssetDatabase.Refresh();
@@ -5,6 +5,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
5
5
  using NUnit.Framework;
6
6
  using UnityEditor;
7
7
  using UnityEngine;
8
+ using WallstopStudios.UnityHelpers.Core.Helper;
8
9
  using WallstopStudios.UnityHelpers.Editor.Sprites;
9
10
  using WallstopStudios.UnityHelpers.Tests.Editor.Utils;
10
11
 
@@ -29,7 +30,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
29
30
  [Test]
30
31
  public void AdjustsPivotToAlphaWeightedCenter()
31
32
  {
32
- string path = Path.Combine(Root, "pivot.png").Replace('\\', '/');
33
+ string path = Path.Combine(Root, "pivot.png").SanitizePath();
33
34
  // 10x10 image, opaque L-shape to bias center toward bottom-left
34
35
  CreateAsymmetricAlpha(path, 10, 10);
35
36
  AssetDatabase.Refresh();
@@ -66,7 +67,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
66
67
 
67
68
  private static void CreateAsymmetricAlpha(string relPath, int w, int h)
68
69
  {
69
- string dir = Path.GetDirectoryName(relPath).Replace('\\', '/');
70
+ string dir = Path.GetDirectoryName(relPath).SanitizePath();
70
71
  EnsureFolder(dir);
71
72
  Texture2D t = new(w, h, TextureFormat.RGBA32, false) { alphaIsTransparency = true };
72
73
  Color[] pix = new Color[w * h];
@@ -106,7 +107,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
106
107
  ),
107
108
  rel
108
109
  )
109
- .Replace('\\', '/');
110
+ .SanitizePath();
110
111
  }
111
112
  }
112
113
  #endif
@@ -5,6 +5,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
5
5
  using NUnit.Framework;
6
6
  using UnityEditor;
7
7
  using UnityEngine;
8
+ using WallstopStudios.UnityHelpers.Core.Helper;
8
9
  using WallstopStudios.UnityHelpers.Editor.Sprites;
9
10
  using WallstopStudios.UnityHelpers.Tests.Editor.Utils;
10
11
 
@@ -31,7 +32,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
31
32
  [Test]
32
33
  public void ResizesTextureAccordingToMultipliers()
33
34
  {
34
- string path = Path.Combine(Root, "tex.png").Replace('\\', '/');
35
+ string path = Path.Combine(Root, "tex.png").SanitizePath();
35
36
  CreatePng(path, 16, 10, Color.green);
36
37
  AssetDatabase.Refresh();
37
38
 
@@ -59,7 +60,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
59
60
  [Test]
60
61
  public void DoesNothingWhenNumResizesIsZero()
61
62
  {
62
- string path = Path.Combine(Root, "nochange.png").Replace('\\', '/');
63
+ string path = Path.Combine(Root, "nochange.png").SanitizePath();
63
64
  CreatePng(path, 12, 7, Color.blue);
64
65
  AssetDatabase.Refresh();
65
66
  int w0 = AssetDatabase.LoadAssetAtPath<Texture2D>(path).width;
@@ -80,7 +81,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
80
81
  [Test]
81
82
  public void RespectsDryRunAndDoesNotModifyFile()
82
83
  {
83
- string path = Path.Combine(Root, "dry.png").Replace('\\', '/');
84
+ string path = Path.Combine(Root, "dry.png").SanitizePath();
84
85
  CreatePng(path, 10, 6, Color.white);
85
86
  AssetDatabase.Refresh();
86
87
 
@@ -108,7 +109,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
108
109
  [Test]
109
110
  public void WritesToOutputFolderLeavingOriginalUnchanged()
110
111
  {
111
- string path = Path.Combine(Root, "out.png").Replace('\\', '/');
112
+ string path = Path.Combine(Root, "out.png").SanitizePath();
112
113
  CreatePng(path, 8, 4, Color.black);
113
114
  AssetDatabase.Refresh();
114
115
 
@@ -134,7 +135,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
134
135
  Assert.That(orig.width, Is.EqualTo(8));
135
136
  Assert.That(orig.height, Is.EqualTo(4));
136
137
 
137
- string outPath = Path.Combine(OutRoot, "out.png").Replace('\\', '/');
138
+ string outPath = Path.Combine(OutRoot, "out.png").SanitizePath();
138
139
  Texture2D outTex = AssetDatabase.LoadAssetAtPath<Texture2D>(outPath);
139
140
  Assert.IsTrue(outTex != null, "Expected resized texture in output folder");
140
141
  Assert.That(outTex.width, Is.EqualTo(16));
@@ -144,7 +145,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
144
145
  [Test]
145
146
  public void MultiplePassesAccumulateSize()
146
147
  {
147
- string path = Path.Combine(Root, "multi.png").Replace('\\', '/');
148
+ string path = Path.Combine(Root, "multi.png").SanitizePath();
148
149
  CreatePng(path, 16, 10, Color.gray);
149
150
  AssetDatabase.Refresh();
150
151
 
@@ -171,7 +172,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
171
172
  [Test]
172
173
  public void RestoresImporterReadabilityAfterRun()
173
174
  {
174
- string path = Path.Combine(Root, "restore.png").Replace('\\', '/');
175
+ string path = Path.Combine(Root, "restore.png").SanitizePath();
175
176
  CreatePng(path, 8, 8, Color.red);
176
177
  AssetDatabase.Refresh();
177
178
 
@@ -217,7 +218,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
217
218
 
218
219
  private static void CreatePng(string relPath, int w, int h, Color c)
219
220
  {
220
- string dir = Path.GetDirectoryName(relPath).Replace('\\', '/');
221
+ string dir = Path.GetDirectoryName(relPath).SanitizePath();
221
222
  EnsureFolder(dir);
222
223
  Texture2D t = new(w, h, TextureFormat.RGBA32, false);
223
224
  Color[] pix = new Color[w * h];
@@ -241,7 +242,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
241
242
  ),
242
243
  rel
243
244
  )
244
- .Replace('\\', '/');
245
+ .SanitizePath();
245
246
  }
246
247
  }
247
248
  #endif
@@ -5,6 +5,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
5
5
  using NUnit.Framework;
6
6
  using UnityEditor;
7
7
  using UnityEngine;
8
+ using WallstopStudios.UnityHelpers.Core.Helper;
8
9
  using WallstopStudios.UnityHelpers.Editor.Sprites;
9
10
  using WallstopStudios.UnityHelpers.Tests.Editor.Utils;
10
11
 
@@ -32,7 +33,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Editor.Sprites
32
33
  [Test]
33
34
  public void AppliesDefaultPlatformSettingsViaAPI()
34
35
  {
35
- string texPath = (Root + "/api_tex.png").Replace('\\', '/');
36
+ string texPath = (Root + "/api_tex.png").SanitizePath();
36
37
  CreatePng(texPath, 16, 16, Color.white);
37
38
  AssetDatabase.Refresh();
38
39
 
@@ -0,0 +1,192 @@
1
+ namespace WallstopStudios.UnityHelpers.Tests.Editor.Tags
2
+ {
3
+ using System;
4
+ using System.Reflection;
5
+ using NUnit.Framework;
6
+ using UnityEngine;
7
+ using UnityHelpers.Tags;
8
+
9
+ [TestFixture]
10
+ public sealed class AttributeMetadataCacheTests
11
+ {
12
+ [Test]
13
+ public void SetMetadataSortsSerializedContent()
14
+ {
15
+ AttributeMetadataCache cache =
16
+ ScriptableObject.CreateInstance<AttributeMetadataCache>();
17
+
18
+ try
19
+ {
20
+ string[] attributeNames = new string[] { "Gamma", "Alpha", "Beta" };
21
+
22
+ string alphaAttributesTypeName =
23
+ typeof(AlphaAttributesComponent).AssemblyQualifiedName ?? string.Empty;
24
+ string bravoAttributesTypeName =
25
+ typeof(BravoAttributesComponent).AssemblyQualifiedName ?? string.Empty;
26
+
27
+ AttributeMetadataCache.TypeFieldMetadata alphaTypeMetadata = new(
28
+ alphaAttributesTypeName,
29
+ new string[] { "gammaField", "betaField" }
30
+ );
31
+
32
+ AttributeMetadataCache.TypeFieldMetadata bravoTypeMetadata = new(
33
+ bravoAttributesTypeName,
34
+ new string[] { "zetaField", "alphaField" }
35
+ );
36
+
37
+ string alphaRelationalTypeName =
38
+ typeof(AlphaRelationalComponent).AssemblyQualifiedName ?? string.Empty;
39
+ string bravoRelationalTypeName =
40
+ typeof(BravoRelationalComponent).AssemblyQualifiedName ?? string.Empty;
41
+
42
+ AttributeMetadataCache.RelationalFieldMetadata[] alphaRelationalFields =
43
+ new AttributeMetadataCache.RelationalFieldMetadata[]
44
+ {
45
+ new(
46
+ "betaRelation",
47
+ AttributeMetadataCache.RelationalAttributeKind.Parent,
48
+ AttributeMetadataCache.FieldKind.HashSet,
49
+ typeof(Light).AssemblyQualifiedName ?? string.Empty,
50
+ true
51
+ ),
52
+ new(
53
+ "alphaRelation",
54
+ AttributeMetadataCache.RelationalAttributeKind.Child,
55
+ AttributeMetadataCache.FieldKind.Single,
56
+ typeof(Transform).AssemblyQualifiedName ?? string.Empty,
57
+ false
58
+ ),
59
+ };
60
+
61
+ AttributeMetadataCache.RelationalFieldMetadata[] bravoRelationalFields =
62
+ new AttributeMetadataCache.RelationalFieldMetadata[]
63
+ {
64
+ new(
65
+ "zetaRelation",
66
+ AttributeMetadataCache.RelationalAttributeKind.Sibling,
67
+ AttributeMetadataCache.FieldKind.List,
68
+ typeof(Camera).AssemblyQualifiedName ?? string.Empty,
69
+ true
70
+ ),
71
+ new(
72
+ "alphaRelation",
73
+ AttributeMetadataCache.RelationalAttributeKind.Child,
74
+ AttributeMetadataCache.FieldKind.Single,
75
+ typeof(Transform).AssemblyQualifiedName ?? string.Empty,
76
+ false
77
+ ),
78
+ };
79
+
80
+ AttributeMetadataCache.RelationalTypeMetadata[] relationalMetadata =
81
+ new AttributeMetadataCache.RelationalTypeMetadata[]
82
+ {
83
+ new(
84
+ bravoRelationalTypeName,
85
+ new AttributeMetadataCache.RelationalFieldMetadata[]
86
+ {
87
+ bravoRelationalFields[0],
88
+ null,
89
+ bravoRelationalFields[1],
90
+ }
91
+ ),
92
+ null,
93
+ new(alphaRelationalTypeName, alphaRelationalFields),
94
+ };
95
+
96
+ AttributeMetadataCache.TypeFieldMetadata[] typeMetadata =
97
+ new AttributeMetadataCache.TypeFieldMetadata[]
98
+ {
99
+ bravoTypeMetadata,
100
+ null,
101
+ alphaTypeMetadata,
102
+ };
103
+
104
+ cache.SetMetadata(attributeNames, typeMetadata, relationalMetadata);
105
+
106
+ string[] storedAttributeNames = GetPrivateField<string[]>(
107
+ cache,
108
+ "_allAttributeNames"
109
+ );
110
+ Assert.That(
111
+ storedAttributeNames,
112
+ Is.EqualTo(new string[] { "Alpha", "Beta", "Gamma" })
113
+ );
114
+
115
+ AttributeMetadataCache.TypeFieldMetadata[] storedTypeMetadata =
116
+ GetPrivateField<AttributeMetadataCache.TypeFieldMetadata[]>(
117
+ cache,
118
+ "_typeMetadata"
119
+ );
120
+ Assert.That(storedTypeMetadata.Length, Is.EqualTo(2));
121
+ Assert.That(storedTypeMetadata[0].typeName, Is.EqualTo(alphaAttributesTypeName));
122
+ Assert.That(
123
+ storedTypeMetadata[0].fieldNames,
124
+ Is.EqualTo(new string[] { "betaField", "gammaField" })
125
+ );
126
+ Assert.That(storedTypeMetadata[1].typeName, Is.EqualTo(bravoAttributesTypeName));
127
+ Assert.That(
128
+ storedTypeMetadata[1].fieldNames,
129
+ Is.EqualTo(new string[] { "alphaField", "zetaField" })
130
+ );
131
+
132
+ AttributeMetadataCache.RelationalTypeMetadata[] storedRelationalMetadata =
133
+ GetPrivateField<AttributeMetadataCache.RelationalTypeMetadata[]>(
134
+ cache,
135
+ "_relationalTypeMetadata"
136
+ );
137
+ Assert.That(storedRelationalMetadata.Length, Is.EqualTo(2));
138
+ Assert.That(
139
+ storedRelationalMetadata[0].typeName,
140
+ Is.EqualTo(alphaRelationalTypeName)
141
+ );
142
+ Assert.That(storedRelationalMetadata[0].fields.Length, Is.EqualTo(2));
143
+ Assert.That(
144
+ storedRelationalMetadata[0].fields[0].fieldName,
145
+ Is.EqualTo("alphaRelation")
146
+ );
147
+ Assert.That(
148
+ storedRelationalMetadata[0].fields[1].fieldName,
149
+ Is.EqualTo("betaRelation")
150
+ );
151
+ Assert.That(
152
+ storedRelationalMetadata[1].typeName,
153
+ Is.EqualTo(bravoRelationalTypeName)
154
+ );
155
+ Assert.That(storedRelationalMetadata[1].fields.Length, Is.EqualTo(2));
156
+ Assert.That(
157
+ storedRelationalMetadata[1].fields[0].fieldName,
158
+ Is.EqualTo("alphaRelation")
159
+ );
160
+ Assert.That(
161
+ storedRelationalMetadata[1].fields[1].fieldName,
162
+ Is.EqualTo("zetaRelation")
163
+ );
164
+ }
165
+ finally
166
+ {
167
+ UnityEngine.Object.DestroyImmediate(cache);
168
+ }
169
+ }
170
+
171
+ private static T GetPrivateField<T>(AttributeMetadataCache cache, string fieldName)
172
+ {
173
+ BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
174
+ FieldInfo field = typeof(AttributeMetadataCache).GetField(fieldName, bindingFlags);
175
+ if (field == null)
176
+ {
177
+ throw new InvalidOperationException($"Field '{fieldName}' was not found.");
178
+ }
179
+
180
+ object value = field.GetValue(cache);
181
+ return value is T castValue ? castValue : default;
182
+ }
183
+
184
+ private sealed class AlphaAttributesComponent : AttributesComponent { }
185
+
186
+ private sealed class BravoAttributesComponent : AttributesComponent { }
187
+
188
+ private sealed class AlphaRelationalComponent : MonoBehaviour { }
189
+
190
+ private sealed class BravoRelationalComponent : MonoBehaviour { }
191
+ }
192
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: e6e89a6fc9964f9f92a7bb5cbb4f793d
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,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 43bfcdf80a014948b5377ffa29afa8df
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant: