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.
- package/.editorconfig +1 -1
- package/.gitattributes +1 -1
- package/.githooks/pre-commit +31 -5
- package/.githooks/pre-push +50 -0
- package/.github/dependabot.yml +24 -2
- package/.github/scripts/check-markdown-links.ps1 +77 -0
- package/.github/scripts/check_markdown_links.py +89 -0
- package/.github/scripts/check_markdown_url_encoding.py +74 -0
- package/.github/scripts/validate_markdown_links.py +194 -0
- package/.github/workflows/csharpier-autofix.yml +152 -0
- package/.github/workflows/format-on-demand.yml +305 -0
- package/.github/workflows/lint-doc-links.yml +8 -5
- package/.github/workflows/markdown-json.yml +6 -2
- package/.github/workflows/prettier-autofix.yml +195 -0
- package/.github/workflows/update-dotnet-tools.yml +80 -0
- package/.github/workflows/yaml-format-lint.yml +41 -0
- package/.lychee.toml +4 -4
- package/.markdownlint.jsonc +21 -0
- package/.pre-commit-config.yaml +11 -3
- package/.yamllint.yaml +31 -0
- package/AGENTS.md +5 -1
- package/CHANGELOG.md +11 -0
- package/CONTRIBUTING.md +49 -0
- package/CONTRIBUTING.md.meta +7 -0
- package/EDITOR_TOOLS_GUIDE.md +4 -0
- package/Editor/AnimationEventEditor.cs +337 -160
- package/Editor/Core/Helper/AnimationEventHelpers.cs +178 -152
- package/Editor/CustomEditors/PersistentDirectoryGUI.cs +20 -11
- package/Editor/CustomEditors/TexturePlatformOverrideEntryDrawer.cs +11 -2
- package/Editor/FitTextureSizeWindow.cs +43 -19
- package/Editor/PersistentDirectorySettings.cs +64 -12
- package/Editor/PrefabChecker.cs +72 -5
- package/Editor/Sprites/AnimationCopier.cs +132 -56
- package/Editor/Sprites/AnimationCreator.cs +63 -22
- package/Editor/Sprites/AnimationViewerWindow.cs +42 -6
- package/Editor/Sprites/TexturePlatformNameHelper.cs +50 -39
- package/Editor/Sprites/TextureResizerWizard.cs +23 -1
- package/Editor/Sprites/TextureSettingsApplierWindow.cs +148 -85
- package/Editor/Tools/ImageBlurTool.cs +81 -10
- package/Editor/Utils/EditorUi.cs +1 -1
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +1 -1
- package/GETTING_STARTED.md +40 -56
- package/RANDOM_PERFORMANCE.md +12 -12
- package/README.md +395 -2407
- package/RELATIONAL_COMPONENTS.md +92 -83
- package/Runtime/AssemblyInfo.cs +2 -0
- package/Runtime/Core/Attributes/NotNullAttribute.cs +1 -3
- package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +50 -5
- package/Runtime/Core/DataStructure/CyclicBuffer.cs +0 -1
- package/Runtime/Core/Extension/RandomExtensions.cs +68 -0
- package/Runtime/Core/Extension/WallstopStudiosLogger.cs +16 -0
- package/Runtime/Core/Helper/Partials/ObjectHelpers.cs +4 -1
- package/Runtime/Core/Helper/ReflectionHelpers.cs +21 -10
- package/Runtime/Core/Helper/SpriteHelpers.cs +3 -1
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +45 -1
- package/Runtime/Core/Serialization/JsonConverters/GameObjectConverter.cs +13 -5
- package/Runtime/Core/Serialization/JsonConverters/ResolutionConverter.cs +1 -1
- package/Runtime/Core/Serialization/JsonConverters/TypeConverter.cs +1 -1
- package/Runtime/Core/Serialization/Serializer.cs +101 -0
- package/Runtime/Integrations/VContainer/AssemblyInfo.cs +9 -0
- package/Runtime/Integrations/VContainer/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Integrations/VContainer/ObjectResolverRelationalExtensions.cs +96 -0
- package/Runtime/Integrations/VContainer/RelationalComponentEntryPoint.cs +90 -10
- package/Runtime/Integrations/VContainer/RelationalComponentsBuilderExtensions.cs +13 -1
- package/Runtime/Integrations/VContainer/RelationalObjectPools.cs +114 -0
- package/Runtime/Integrations/VContainer/RelationalObjectPools.cs.meta +11 -0
- package/Runtime/Integrations/VContainer/RelationalSceneAssignmentOptions.cs +16 -4
- package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs +241 -0
- package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs.meta +11 -0
- package/Runtime/Integrations/Zenject/AssemblyInfo.cs +9 -0
- package/Runtime/Integrations/Zenject/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Integrations/Zenject/DiContainerRelationalExtensions.cs +69 -2
- package/Runtime/Integrations/Zenject/RelationalComponentSceneInitializer.cs +89 -12
- package/Runtime/Integrations/Zenject/RelationalComponentsInstaller.cs +23 -1
- package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs +44 -0
- package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs.meta +11 -0
- package/Runtime/Integrations/Zenject/RelationalSceneAssignmentOptions.cs +16 -10
- package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs +243 -0
- package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs.meta +11 -0
- package/Runtime/Tags/AttributeMetadataCache.cs +1 -4
- package/Runtime/Utils/Buffers.cs +4 -4
- package/Runtime/Utils/ScriptableObjectSingleton.cs +0 -1
- package/Runtime/Utils/SetTextureImportData.cs +3 -1
- package/Runtime/Utils/TextureScale.cs +10 -2
- package/Runtime/Visuals/UGUI/EnhancedImage.cs +6 -0
- package/Runtime/Visuals/UIToolkit/LayeredImage.cs +4 -1
- package/SERIALIZATION.md +15 -0
- package/SPATIAL_TREE_2D_PERFORMANCE.md +85 -82
- package/SPATIAL_TREE_3D_PERFORMANCE.md +94 -91
- package/Samples~/DI - VContainer/README.md +232 -51
- package/Samples~/DI - VContainer/Scripts/GameLifetimeScope.cs +22 -4
- package/Samples~/DI - VContainer/Scripts/RelationalConsumer.cs +5 -2
- package/Samples~/DI - VContainer/Scripts/Spawner.cs +113 -4
- package/Samples~/DI - Zenject/README.md +217 -53
- package/Samples~/DI - Zenject/Scripts/RelationalConsumer.cs +3 -0
- package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs +37 -0
- package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs.meta +12 -0
- package/Samples~/DI - Zenject/Scripts/SpawnerZenject.cs +74 -3
- package/Samples~/Random - PRNG/README.md +2 -1
- package/Samples~/Relational Components - Basic/README.md +3 -1
- package/Samples~/Serialization - JSON/README.md +2 -1
- package/Samples~/Spatial Structures - 2D and 3D/README.md +2 -1
- package/Samples~/UGUI - EnhancedImage/README.md +2 -1
- package/Samples~/UI Toolkit - MultiFile Selector (Editor)/README.md +2 -1
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/Tests/Editor/Attributes/AnimationEventHelpersTests.cs +16 -0
- package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +3 -3
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +6 -2
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +170 -0
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs.meta +11 -0
- package/Tests/Editor/Integrations/VContainer/WallstopStudios.UnityHelpers.Tests.Editor.VContainer.asmdef +2 -1
- package/Tests/Editor/Integrations/Zenject/WallstopStudios.UnityHelpers.Tests.Editor.Zenject.asmdef +3 -2
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +131 -0
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs.meta +11 -0
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +6 -2
- package/Tests/Editor/PersistentDirectorySettingsTests.cs +59 -0
- package/Tests/Editor/PersistentDirectorySettingsTests.cs.meta +11 -0
- package/Tests/Editor/PrefabCheckerReportTests.cs +32 -0
- package/Tests/Editor/PrefabCheckerReportTests.cs.meta +11 -0
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +64 -0
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs.meta +11 -0
- package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +1 -1
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +38 -0
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs.meta +11 -0
- package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +1 -1
- package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +12 -12
- package/Tests/Editor/Sprites/SpriteCropperTests.cs +9 -9
- package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +3 -3
- package/Tests/Editor/Sprites/TexturePlatformNameHelperTests.cs +18 -0
- package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +5 -5
- package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +3 -3
- package/Tests/Editor/Sprites/TextureSettingsApplierWizardAdditionalTests.cs +4 -4
- package/Tests/Editor/Sprites/TextureSettingsApplierWizardTests.cs +4 -4
- package/Tests/Editor/Tools/ImageBlurToolTests.cs +22 -110
- package/Tests/Editor/Utils/CommonTestBase.cs +43 -1
- package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +5 -5
- package/Tests/Editor/Windows/FitTextureSizeWindowTests.cs +66 -74
- package/Tests/Runtime/Attributes/RelationalComponentInitializerTests.cs +4 -15
- package/Tests/Runtime/DataStructures/SpatialTree3DBoundsConsistencyTests.cs +29 -29
- package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +259 -218
- package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +86 -0
- package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +255 -227
- package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +5 -0
- package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +3 -0
- package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs +30 -0
- package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs.meta +11 -0
- package/Tests/Runtime/Serialization/JsonConverterTests.cs +8 -12
- package/Tests/Runtime/Serialization/JsonSerializationTest.cs +16 -5
- package/Tests/Runtime/Serialization/SerializerAdditionalTests.cs +12 -0
- package/Tests/Runtime/Serialization/SerializerFileIoTests.cs +105 -0
- package/Tests/Runtime/Serialization/SerializerFileIoTests.cs.meta +11 -0
- package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs +247 -0
- package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs.meta +11 -0
- package/Tests/Runtime/TestUtils/CommonTestBase.cs +88 -0
- package/Tests/Runtime/Utils/CoroutineHandlerTests.cs +1 -1
- package/Tests/Runtime/Utils/LZMAComprehensiveTests.cs +1 -1
- package/Tests/Runtime/Utils/LZMATests.cs +1 -1
- package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +1 -1
- package/Tests/Runtime/Visuals/EnhancedImageTests.cs +25 -56
- package/Tests/Runtime/Visuals/VisualsTestHelpers.cs +1 -8
- package/package-lock.json.meta +7 -0
- package/package.json +8 -4
- package/scripts/check-eol.ps1 +4 -5
- package/scripts/lint-tests.ps1 +156 -0
- package/scripts/lint-tests.ps1.meta +7 -0
- package/scripts/normalize-eol.ps1 +6 -9
- package/.github/workflows/csharpier.yml +0 -135
|
@@ -11,7 +11,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
11
11
|
public void RoundtripVariousSizes()
|
|
12
12
|
{
|
|
13
13
|
Random random = new(12345);
|
|
14
|
-
int[] sizes =
|
|
14
|
+
int[] sizes = { 0, 1, 3, 5, 32, 64, 257, 1024, 4096 };
|
|
15
15
|
foreach (int length in sizes)
|
|
16
16
|
{
|
|
17
17
|
byte[] data = new byte[length];
|
|
@@ -24,7 +24,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
24
24
|
[Test]
|
|
25
25
|
public void DecompressingGarbageThrows()
|
|
26
26
|
{
|
|
27
|
-
byte[] garbage =
|
|
27
|
+
byte[] garbage = { 1, 2, 3, 4, 5 };
|
|
28
28
|
Assert.Throws<Exception>(() => LZMA.Decompress(garbage));
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
2
2
|
{
|
|
3
|
-
using System.Reflection;
|
|
4
3
|
using NUnit.Framework;
|
|
5
4
|
using UnityEngine;
|
|
6
5
|
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
@@ -17,10 +16,10 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
17
16
|
EnhancedImage image = CreateEnhancedImage(out Material baseMaterial);
|
|
18
17
|
image.color = new Color(0.2f, 0.4f, 0.6f, 0.8f);
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
image.InvokeStartForTests();
|
|
21
20
|
|
|
22
21
|
Material cached = image.material;
|
|
23
|
-
Assert.
|
|
22
|
+
Assert.IsTrue(cached != null);
|
|
24
23
|
Assert.AreNotSame(baseMaterial, cached);
|
|
25
24
|
Assert.IsTrue(cached.GetColor("_Color").Approximately(image.color));
|
|
26
25
|
}
|
|
@@ -29,13 +28,13 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
29
28
|
public void HdrColorAboveSdrOverridesMaterialColor()
|
|
30
29
|
{
|
|
31
30
|
EnhancedImage image = CreateEnhancedImage(out _);
|
|
32
|
-
|
|
31
|
+
image.InvokeStartForTests();
|
|
33
32
|
|
|
34
33
|
Color hdr = new(2f, 0.5f, 0.25f, 1f);
|
|
35
34
|
image.HdrColor = hdr;
|
|
36
35
|
|
|
37
36
|
Material cached = image.material;
|
|
38
|
-
Assert.
|
|
37
|
+
Assert.IsTrue(cached != null);
|
|
39
38
|
Assert.IsTrue(cached.GetColor("_Color").Approximately(hdr));
|
|
40
39
|
}
|
|
41
40
|
|
|
@@ -45,11 +44,9 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
45
44
|
EnhancedImage image = CreateEnhancedImage(out _);
|
|
46
45
|
Texture2D mask = Track(new Texture2D(4, 4, TextureFormat.RGBA32, false, false));
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
.GetField("_shapeMask", BindingFlags.Instance | BindingFlags.NonPublic)
|
|
50
|
-
?.SetValue(image, mask);
|
|
47
|
+
image._shapeMask = mask;
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
image.InvokeStartForTests();
|
|
53
50
|
|
|
54
51
|
Texture maskInMaterial = image.material.GetTexture("_ShapeMask");
|
|
55
52
|
Assert.That(maskInMaterial, Is.SameAs(mask));
|
|
@@ -59,21 +56,15 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
59
56
|
public void OnDestroyReleasesCachedMaterialInstance()
|
|
60
57
|
{
|
|
61
58
|
EnhancedImage image = CreateEnhancedImage(out _);
|
|
62
|
-
|
|
59
|
+
image.InvokeStartForTests();
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
BindingFlags.Instance | BindingFlags.NonPublic
|
|
67
|
-
);
|
|
68
|
-
Assert.That(cachedField, Is.Not.Null);
|
|
69
|
-
|
|
70
|
-
Material cachedBefore = (Material)cachedField.GetValue(image);
|
|
71
|
-
Assert.That(cachedBefore, Is.Not.Null);
|
|
61
|
+
Material cachedBefore = image.CachedMaterialInstanceForTests;
|
|
62
|
+
Assert.IsTrue(cachedBefore != null);
|
|
72
63
|
|
|
73
|
-
|
|
64
|
+
image.InvokeOnDestroyForTests();
|
|
74
65
|
|
|
75
|
-
Material cachedAfter =
|
|
76
|
-
Assert.
|
|
66
|
+
Material cachedAfter = image.CachedMaterialInstanceForTests;
|
|
67
|
+
Assert.IsTrue(cachedAfter == null);
|
|
77
68
|
Assert.That(cachedBefore == null, Is.True);
|
|
78
69
|
}
|
|
79
70
|
|
|
@@ -83,17 +74,11 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
83
74
|
EnhancedImage image = CreateEnhancedImage(out _);
|
|
84
75
|
image.material = null;
|
|
85
76
|
|
|
86
|
-
|
|
77
|
+
image.InvokeStartForTests();
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
);
|
|
92
|
-
Assert.That(field, Is.Not.Null);
|
|
93
|
-
Material cached = (Material)field.GetValue(image);
|
|
94
|
-
Assert.That(
|
|
95
|
-
cached,
|
|
96
|
-
Is.Null,
|
|
79
|
+
Material cached = image.CachedMaterialInstanceForTests;
|
|
80
|
+
Assert.IsTrue(
|
|
81
|
+
cached == null,
|
|
97
82
|
"Expected no instance to be created when material is null."
|
|
98
83
|
);
|
|
99
84
|
}
|
|
@@ -105,10 +90,10 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
105
90
|
image.color = new Color(0.1f, 0.2f, 0.3f, 0.9f);
|
|
106
91
|
image.HdrColor = new Color(0.4f, 0.4f, 0.4f, 0.4f);
|
|
107
92
|
|
|
108
|
-
|
|
93
|
+
image.InvokeStartForTests();
|
|
109
94
|
|
|
110
95
|
Material cached = image.material;
|
|
111
|
-
Assert.
|
|
96
|
+
Assert.IsTrue(cached != null);
|
|
112
97
|
Assert.AreNotSame(baseMaterial, cached);
|
|
113
98
|
Assert.IsTrue(
|
|
114
99
|
cached.GetColor("_Color").Approximately(image.color),
|
|
@@ -120,9 +105,9 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
120
105
|
public void MaterialInstanceIsReusedAcrossUpdates()
|
|
121
106
|
{
|
|
122
107
|
EnhancedImage image = CreateEnhancedImage(out _);
|
|
123
|
-
|
|
108
|
+
image.InvokeStartForTests();
|
|
124
109
|
Material first = image.material;
|
|
125
|
-
Assert.
|
|
110
|
+
Assert.IsTrue(first != null);
|
|
126
111
|
|
|
127
112
|
image.HdrColor = new Color(1.1f, 0.2f, 0.3f, 1f);
|
|
128
113
|
Material second = image.material;
|
|
@@ -137,11 +122,11 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
137
122
|
public void StartDoesNotDuplicateExistingInstance()
|
|
138
123
|
{
|
|
139
124
|
EnhancedImage image = CreateEnhancedImage(out _);
|
|
140
|
-
|
|
125
|
+
image.InvokeStartForTests();
|
|
141
126
|
Material first = image.material;
|
|
142
|
-
Assert.
|
|
127
|
+
Assert.IsTrue(first != null);
|
|
143
128
|
|
|
144
|
-
|
|
129
|
+
image.InvokeStartForTests();
|
|
145
130
|
Material second = image.material;
|
|
146
131
|
Assert.That(
|
|
147
132
|
second,
|
|
@@ -153,11 +138,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
153
138
|
private EnhancedImage CreateEnhancedImage(out Material baseMaterial)
|
|
154
139
|
{
|
|
155
140
|
Shader shader = Shader.Find("UI/Default");
|
|
156
|
-
Assert.
|
|
157
|
-
shader,
|
|
158
|
-
Is.Not.Null,
|
|
159
|
-
"Expected UI/Default shader to be available for tests."
|
|
160
|
-
);
|
|
141
|
+
Assert.IsTrue(shader != null, "Expected UI/Default shader to be available for tests.");
|
|
161
142
|
|
|
162
143
|
baseMaterial = Track(new Material(shader));
|
|
163
144
|
|
|
@@ -167,18 +148,6 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
167
148
|
return image;
|
|
168
149
|
}
|
|
169
150
|
|
|
170
|
-
|
|
171
|
-
{
|
|
172
|
-
MethodInfo method = typeof(EnhancedImage).GetMethod(
|
|
173
|
-
methodName,
|
|
174
|
-
BindingFlags.Instance | BindingFlags.NonPublic
|
|
175
|
-
);
|
|
176
|
-
Assert.That(
|
|
177
|
-
method,
|
|
178
|
-
Is.Not.Null,
|
|
179
|
-
$"Expected method {methodName} to exist on EnhancedImage."
|
|
180
|
-
);
|
|
181
|
-
method.Invoke(image, null);
|
|
182
|
-
}
|
|
151
|
+
// No reflection lifecycle helpers needed; use internal wrappers on EnhancedImage
|
|
183
152
|
}
|
|
184
153
|
}
|
|
@@ -67,14 +67,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Visuals
|
|
|
67
67
|
|
|
68
68
|
public static Texture2D[] GetComputedTextures(LayeredImage image, List<Object> tracked)
|
|
69
69
|
{
|
|
70
|
-
Texture2D[] computed =
|
|
71
|
-
typeof(LayeredImage)
|
|
72
|
-
.GetField(
|
|
73
|
-
"_computed",
|
|
74
|
-
System.Reflection.BindingFlags.NonPublic
|
|
75
|
-
| System.Reflection.BindingFlags.Instance
|
|
76
|
-
)
|
|
77
|
-
.GetValue(image);
|
|
70
|
+
Texture2D[] computed = image?.ComputedTexturesForTests;
|
|
78
71
|
|
|
79
72
|
foreach (Texture2D texture in computed)
|
|
80
73
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "com.wallstop-studios.unity-helpers",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"displayName": "Unity Helpers",
|
|
5
5
|
"description": "Various Unity Helper Library",
|
|
6
6
|
"dependencies": {},
|
|
@@ -84,12 +84,16 @@
|
|
|
84
84
|
"format:md:check": "prettier --check \"**/*.{md,markdown}\"",
|
|
85
85
|
"format:json": "prettier --write \"**/*.{json,asmdef,asmref}\"",
|
|
86
86
|
"format:json:check": "prettier --check \"**/*.{json,asmdef,asmref}\"",
|
|
87
|
+
"format:yaml": "prettier --write \"**/*.{yml,yaml}\"",
|
|
88
|
+
"format:yaml:check": "prettier --check \"**/*.{yml,yaml}\"",
|
|
87
89
|
"lint:markdown": "markdownlint \"**/*.md\" \"**/*.markdown\" --config .markdownlint.json --ignore-path .markdownlintignore",
|
|
88
|
-
"validate:content": "npm run lint:docs && npm run lint:markdown && npm run format:md:check && npm run format:json:check",
|
|
90
|
+
"validate:content": "npm run lint:docs && npm run lint:markdown && npm run format:md:check && npm run format:json:check && npm run format:yaml:check",
|
|
91
|
+
"validate:prepush": "npm run validate:content && npm run eol:check && npm run lint:tests",
|
|
92
|
+
"lint:tests": "pwsh -NoProfile -File scripts/lint-tests.ps1 -VerboseOutput",
|
|
89
93
|
"prepublishOnly": "npm run validate:content"
|
|
90
94
|
},
|
|
91
95
|
"devDependencies": {
|
|
92
|
-
"markdownlint-cli": "^0.
|
|
93
|
-
"prettier": "
|
|
96
|
+
"markdownlint-cli": "^0.45.0",
|
|
97
|
+
"prettier": "3.6.2"
|
|
94
98
|
}
|
|
95
99
|
}
|
package/scripts/check-eol.ps1
CHANGED
|
@@ -24,10 +24,10 @@ $bomIssues = New-Object System.Collections.Generic.List[string]
|
|
|
24
24
|
|
|
25
25
|
foreach ($path in Get-TrackedFiles) {
|
|
26
26
|
try { $bytes = [System.IO.File]::ReadAllBytes($path) } catch { continue }
|
|
27
|
-
# BOM
|
|
27
|
+
# BOM (flag any file that contains a UTF-8 BOM — we require NO BOM)
|
|
28
28
|
$hasBom = $false
|
|
29
29
|
if ($bytes.Length -ge 3) { $hasBom = ($bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) }
|
|
30
|
-
if (
|
|
30
|
+
if ($hasBom) { $bomIssues.Add($path) | Out-Null }
|
|
31
31
|
# CRLF check
|
|
32
32
|
$hasLfOnly = $false
|
|
33
33
|
for ($i = 0; $i -lt $bytes.Length; $i++) {
|
|
@@ -41,12 +41,11 @@ if ($VerboseOutput) {
|
|
|
41
41
|
Write-Host "LF-only or mixed EOL files:"; $lfIssues | Sort-Object -Unique | ForEach-Object { " - $_" }
|
|
42
42
|
}
|
|
43
43
|
if ($bomIssues.Count -gt 0) {
|
|
44
|
-
Write-Host "
|
|
44
|
+
Write-Host "Contains UTF-8 BOM (disallowed):"; $bomIssues | Sort-Object -Unique | ForEach-Object { " - $_" }
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
Write-Host "LF issues: $($lfIssues | Sort-Object -Unique | Measure-Object | % Count)"
|
|
49
|
-
Write-Host "
|
|
49
|
+
Write-Host "Files with BOM: $($bomIssues | Sort-Object -Unique | Measure-Object | % Count)"
|
|
50
50
|
|
|
51
51
|
if ($lfIssues.Count -gt 0 -or $bomIssues.Count -gt 0) { exit 3 }
|
|
52
|
-
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Param(
|
|
2
|
+
[switch]$VerboseOutput
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
Set-StrictMode -Version Latest
|
|
6
|
+
$ErrorActionPreference = 'Stop'
|
|
7
|
+
|
|
8
|
+
function Write-Info($msg) {
|
|
9
|
+
if ($VerboseOutput) { Write-Host "[lint-tests] $msg" -ForegroundColor Cyan }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# Heuristics and allowlists
|
|
13
|
+
$testRoots = @('Tests')
|
|
14
|
+
$allowedHelperFiles = @(
|
|
15
|
+
'Tests/Runtime/Visuals/VisualsTestHelpers.cs'
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
$destroyPattern = [regex]'(?<!UNH-SUPPRESS).*\b(?:UnityEngine\.)?Object\.(?:DestroyImmediate|Destroy)\s*\((?<arg>[^)]*)\)'
|
|
19
|
+
$createAssignObjectPattern = [regex]'(?<var>\b\w+)\s*=\s*new\s+(?<type>GameObject|Texture2D|Material|Mesh|Camera)\s*\('
|
|
20
|
+
$createInlineTrackPattern = [regex]'\bTrack\s*\(\s*new\s+(?:GameObject|Texture2D|Material|Mesh|Camera)\s*\('
|
|
21
|
+
$createSoAssignPattern = [regex]'(?<var>\b\w+)\s*=\s*ScriptableObject\.CreateInstance\s*<'
|
|
22
|
+
|
|
23
|
+
# Returns true if line contains an allowlisted helper file path
|
|
24
|
+
function Is-AllowlistedFile([string]$relPath) {
|
|
25
|
+
foreach ($a in $allowedHelperFiles) {
|
|
26
|
+
if ($relPath -replace '\\','/' -ieq $a) { return $true }
|
|
27
|
+
}
|
|
28
|
+
return $false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Get-RelativePath([string]$path) {
|
|
32
|
+
$root = (Get-Location).Path
|
|
33
|
+
return ($path.Substring($root.Length).TrimStart([System.IO.Path]::DirectorySeparatorChar))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
$violations = @()
|
|
37
|
+
|
|
38
|
+
foreach ($root in $testRoots) {
|
|
39
|
+
if (-not (Test-Path $root)) { continue }
|
|
40
|
+
Get-ChildItem -Recurse -Include *.cs -Path $root | ForEach-Object {
|
|
41
|
+
$file = $_.FullName
|
|
42
|
+
$rel = Get-RelativePath $file
|
|
43
|
+
$content = Get-Content $file
|
|
44
|
+
$text = $content -join "`n"
|
|
45
|
+
|
|
46
|
+
if (Is-AllowlistedFile $rel) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Skip meta or non-source
|
|
51
|
+
if ($file -like '*.meta') { return }
|
|
52
|
+
|
|
53
|
+
# Check destroy calls; allow if argument var was tracked earlier in file
|
|
54
|
+
$lineIndex = 0
|
|
55
|
+
foreach ($line in $content) {
|
|
56
|
+
$lineIndex++
|
|
57
|
+
if ($destroyPattern.IsMatch($line)) {
|
|
58
|
+
$m = $destroyPattern.Match($line)
|
|
59
|
+
$arg = ($m.Groups['arg'].Value).Trim()
|
|
60
|
+
# Extract variable token before any commas or closing paren
|
|
61
|
+
$varName = $arg -replace ',.*','' -replace '\)',''
|
|
62
|
+
$allowed = $false
|
|
63
|
+
if (-not [string]::IsNullOrWhiteSpace($varName)) {
|
|
64
|
+
# Search up to 100 lines above for Track(varName)
|
|
65
|
+
$searchStart = [Math]::Max(0, $lineIndex - 100)
|
|
66
|
+
for ($i = $searchStart; $i -lt $lineIndex; $i++) {
|
|
67
|
+
if ($content[$i] -match "Track\s*\(\s*$varName\b") { $allowed = $true; break }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (-not $allowed) {
|
|
71
|
+
$violations += (@{
|
|
72
|
+
Path=$rel; Line=$lineIndex; Message="UNH001: Avoid direct destroy in tests; track object and let teardown clean up (or add // UNH-SUPPRESS)"
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Check untracked new allocations via assignment (var = new Type(...))
|
|
79
|
+
$assignMatches = $createAssignObjectPattern.Matches($text)
|
|
80
|
+
foreach ($am in $assignMatches) {
|
|
81
|
+
$var = $am.Groups['var'].Value
|
|
82
|
+
if ([string]::IsNullOrWhiteSpace($var)) { continue }
|
|
83
|
+
# Find the index of this match in terms of line
|
|
84
|
+
$prefix = $text.Substring(0, $am.Index)
|
|
85
|
+
$lineNo = ($prefix -split "`n").Length
|
|
86
|
+
# Look ahead 10 lines for Track(var)
|
|
87
|
+
$endLine = [Math]::Min($content.Count, $lineNo + 10)
|
|
88
|
+
$found = $false
|
|
89
|
+
for ($j = $lineNo; $j -le $endLine; $j++) {
|
|
90
|
+
if ($content[$j-1] -match "Track\s*\(\s*$var\b") { $found = $true; break }
|
|
91
|
+
}
|
|
92
|
+
if (-not $found) {
|
|
93
|
+
$violations += (@{
|
|
94
|
+
Path=$rel; Line=$lineNo; Message="UNH002: Unity object allocation should be tracked: add Track($var)"
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Check inline Track(new ...) OK; but find bare inline new ... in args without Track
|
|
100
|
+
if ($text -match '\bnew\s+(GameObject|Texture2D|Material|Mesh|Camera)\s*\(') {
|
|
101
|
+
# If Track(new ...) not present at all, flag a generic warning at file level
|
|
102
|
+
if (-not $createInlineTrackPattern.IsMatch($text)) {
|
|
103
|
+
# locate first occurrence for line number
|
|
104
|
+
$m = [regex]::Match($text, '\bnew\s+(GameObject|Texture2D|Material|Mesh|Camera)\s*\(')
|
|
105
|
+
$lineNo = (($text.Substring(0, $m.Index)) -split "`n").Length
|
|
106
|
+
$violations += (@{
|
|
107
|
+
Path=$rel; Line=$lineNo; Message="UNH002: Inline Unity object creation should be passed to Track(new …)"
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Check ScriptableObject.CreateInstance<T>() assigned, ensure tracked
|
|
113
|
+
$soMatches = $createSoAssignPattern.Matches($text)
|
|
114
|
+
foreach ($sm in $soMatches) {
|
|
115
|
+
$var = $sm.Groups['var'].Value
|
|
116
|
+
if ([string]::IsNullOrWhiteSpace($var)) { continue }
|
|
117
|
+
$prefix = $text.Substring(0, $sm.Index)
|
|
118
|
+
$lineNo = ($prefix -split "`n").Length
|
|
119
|
+
$found = $false
|
|
120
|
+
$endLine = [Math]::Min($content.Count, $lineNo + 10)
|
|
121
|
+
for ($j = $lineNo; $j -le $endLine; $j++) {
|
|
122
|
+
if ($content[$j-1] -match "Track\s*\(\s*$var\b") { $found = $true; break }
|
|
123
|
+
}
|
|
124
|
+
if (-not $found) {
|
|
125
|
+
$violations += (@{
|
|
126
|
+
Path=$rel; Line=$lineNo; Message="UNH002: ScriptableObject instance should be tracked: add Track($var)"
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# Enforce CommonTestBase inheritance only if file creates Unity objects and is under Runtime/ or Editor/
|
|
132
|
+
$createsUnity = ($assignMatches.Count -gt 0) -or ($text -match '\bnew\s+(GameObject|Texture2D|Material|Mesh|Camera)\s*\(') -or ($soMatches.Count -gt 0)
|
|
133
|
+
if ($createsUnity) {
|
|
134
|
+
$usesBase = ($text -match ':\s*CommonTestBase')
|
|
135
|
+
if (-not $usesBase) {
|
|
136
|
+
# Only enforce for test classes; skip helper-only files
|
|
137
|
+
if ($text -match '\bnamespace\s+WallstopStudios') {
|
|
138
|
+
$violations += (@{
|
|
139
|
+
Path=$rel; Line=1; Message="UNH003: Test classes creating Unity objects should inherit CommonTestBase (Editor or Runtime variant)"
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if ($violations.Count -gt 0) {
|
|
148
|
+
Write-Host "Test lifecycle lint failed:" -ForegroundColor Red
|
|
149
|
+
foreach ($v in $violations) {
|
|
150
|
+
Write-Host ("{0}:{1}: {2}" -f $v.Path, $v.Line, $v.Message) -ForegroundColor Yellow
|
|
151
|
+
}
|
|
152
|
+
exit 1
|
|
153
|
+
} else {
|
|
154
|
+
Write-Info "No issues found in test code."
|
|
155
|
+
}
|
|
156
|
+
|
|
@@ -28,7 +28,7 @@ function To-CrLf([string]$text) {
|
|
|
28
28
|
|
|
29
29
|
$changed = 0
|
|
30
30
|
$eolFixed = 0
|
|
31
|
-
$
|
|
31
|
+
$bomRemoved = 0
|
|
32
32
|
$modified = New-Object System.Collections.Generic.List[string]
|
|
33
33
|
|
|
34
34
|
$tracked = Get-TrackedFiles
|
|
@@ -50,15 +50,13 @@ foreach ($path in $tracked) {
|
|
|
50
50
|
|
|
51
51
|
$fileChanged = $false
|
|
52
52
|
if ($normalized -ne $text) { $fileChanged = $true; $eolFixed++ }
|
|
53
|
-
|
|
53
|
+
# Remove BOM if present (we enforce UTF-8 without BOM)
|
|
54
|
+
if ($hasBom) { $fileChanged = $true; $bomRemoved++ }
|
|
54
55
|
|
|
55
56
|
if ($fileChanged) {
|
|
56
57
|
if (-not $DryRun) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
$out.AddRange([byte[]](0xEF,0xBB,0xBF))
|
|
60
|
-
$out.AddRange([System.Text.Encoding]::UTF8.GetBytes($normalized))
|
|
61
|
-
[System.IO.File]::WriteAllBytes($path, $out.ToArray())
|
|
58
|
+
# Write UTF-8 without BOM
|
|
59
|
+
[System.IO.File]::WriteAllBytes($path, [System.Text.Encoding]::UTF8.GetBytes($normalized))
|
|
62
60
|
}
|
|
63
61
|
$changed++
|
|
64
62
|
$modified.Add($path) | Out-Null
|
|
@@ -66,6 +64,5 @@ foreach ($path in $tracked) {
|
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
|
|
69
|
-
Write-Host "Files fixed: $changed (EOL:$eolFixed,
|
|
67
|
+
Write-Host "Files fixed: $changed (EOL:$eolFixed, BOMRemoved:$bomRemoved)"
|
|
70
68
|
if ($DryRun -and $changed -gt 0) { exit 2 }
|
|
71
|
-
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
name: CSharpier
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
|
-
pull_request:
|
|
8
|
-
|
|
9
|
-
permissions:
|
|
10
|
-
contents: read
|
|
11
|
-
pull-requests: write
|
|
12
|
-
|
|
13
|
-
jobs:
|
|
14
|
-
format-check:
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
steps:
|
|
17
|
-
- name: Checkout
|
|
18
|
-
uses: actions/checkout@v4
|
|
19
|
-
with:
|
|
20
|
-
fetch-depth: 0
|
|
21
|
-
|
|
22
|
-
- name: Setup .NET SDK
|
|
23
|
-
uses: actions/setup-dotnet@v4
|
|
24
|
-
with:
|
|
25
|
-
dotnet-version: '8.0.x'
|
|
26
|
-
|
|
27
|
-
- name: Restore dotnet tools
|
|
28
|
-
run: dotnet tool restore
|
|
29
|
-
|
|
30
|
-
- name: CSharpier check
|
|
31
|
-
id: csharpier_check
|
|
32
|
-
run: |
|
|
33
|
-
dotnet tool run csharpier check .
|
|
34
|
-
continue-on-error: true
|
|
35
|
-
|
|
36
|
-
- name: Generate CSharpier diff
|
|
37
|
-
if: ${{ steps.csharpier_check.outcome == 'failure' }}
|
|
38
|
-
run: |
|
|
39
|
-
echo "Formatting issues detected. Generating diff..."
|
|
40
|
-
dotnet tool run csharpier format .
|
|
41
|
-
git config user.email "actions@github.com"
|
|
42
|
-
git config user.name "github-actions[bot]"
|
|
43
|
-
git diff > csharpier.patch || true
|
|
44
|
-
# Provide a concise summary, too
|
|
45
|
-
git status --porcelain > csharpier_changed_files.txt
|
|
46
|
-
|
|
47
|
-
- name: Skip PR comment on fork
|
|
48
|
-
if: ${{ github.event_name == 'pull_request' && steps.csharpier_check.outcome == 'failure' && github.event.pull_request.head.repo.fork }}
|
|
49
|
-
run: |
|
|
50
|
-
echo "Forked PR detected; skipping sticky PR comment due to restricted permissions. Full diff uploaded as artifact."
|
|
51
|
-
{
|
|
52
|
-
echo "## CSharpier Formatting Issues Detected"
|
|
53
|
-
echo ""
|
|
54
|
-
echo "- PR is from a fork; comment skipped due to token restrictions."
|
|
55
|
-
echo "- Full diff uploaded as artifact 'csharpier-diff'."
|
|
56
|
-
} >> $GITHUB_STEP_SUMMARY
|
|
57
|
-
|
|
58
|
-
- name: Read diff for comment
|
|
59
|
-
if: ${{ github.event_name == 'pull_request' && steps.csharpier_check.outcome == 'failure' && !github.event.pull_request.head.repo.fork }}
|
|
60
|
-
id: read_diff
|
|
61
|
-
shell: bash
|
|
62
|
-
run: |
|
|
63
|
-
# Limit diff size in comment to ~60KB to avoid API limits
|
|
64
|
-
max=60000
|
|
65
|
-
content=$(cat csharpier.patch)
|
|
66
|
-
if [ ${#content} -gt $max ]; then
|
|
67
|
-
echo "diff<<EOF" >> $GITHUB_OUTPUT
|
|
68
|
-
echo "(Diff too large for inline comment. See attached artifact 'csharpier-diff'.)" >> $GITHUB_OUTPUT
|
|
69
|
-
echo EOF >> $GITHUB_OUTPUT
|
|
70
|
-
else
|
|
71
|
-
echo "diff<<EOF" >> $GITHUB_OUTPUT
|
|
72
|
-
printf '%s\n' "$content" >> $GITHUB_OUTPUT
|
|
73
|
-
echo EOF >> $GITHUB_OUTPUT
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
- name: Post CSharpier suggestion comment
|
|
77
|
-
if: ${{ github.event_name == 'pull_request' && steps.csharpier_check.outcome == 'failure' && !github.event.pull_request.head.repo.fork }}
|
|
78
|
-
uses: marocchino/sticky-pull-request-comment@v2
|
|
79
|
-
with:
|
|
80
|
-
header: csharpier-formatting
|
|
81
|
-
message: |
|
|
82
|
-
CSharpier found formatting changes. This job is failing until formatting is applied.
|
|
83
|
-
|
|
84
|
-
To apply locally:
|
|
85
|
-
1) dotnet tool restore
|
|
86
|
-
2) dotnet tool run csharpier format .
|
|
87
|
-
|
|
88
|
-
Diff of proposed changes:
|
|
89
|
-
|
|
90
|
-
```diff
|
|
91
|
-
${{ steps.read_diff.outputs.diff }}
|
|
92
|
-
```
|
|
93
|
-
env:
|
|
94
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
95
|
-
|
|
96
|
-
- name: Attach full patch as artifact
|
|
97
|
-
if: ${{ steps.csharpier_check.outcome == 'failure' }}
|
|
98
|
-
uses: actions/upload-artifact@v4
|
|
99
|
-
with:
|
|
100
|
-
name: csharpier-diff
|
|
101
|
-
path: |
|
|
102
|
-
csharpier.patch
|
|
103
|
-
csharpier_changed_files.txt
|
|
104
|
-
|
|
105
|
-
- name: Job summary (formatting required)
|
|
106
|
-
if: ${{ steps.csharpier_check.outcome == 'failure' }}
|
|
107
|
-
run: |
|
|
108
|
-
{
|
|
109
|
-
echo "## CSharpier Formatting Required"
|
|
110
|
-
echo ""
|
|
111
|
-
echo "- The formatter detected changes that need to be applied."
|
|
112
|
-
echo "- Apply locally: dotnet tool restore && dotnet tool run csharpier format ."
|
|
113
|
-
echo "- Changed files and full patch are uploaded as 'csharpier-diff' artifact."
|
|
114
|
-
} >> $GITHUB_STEP_SUMMARY
|
|
115
|
-
if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.fork }}" = "true" ]; then
|
|
116
|
-
echo "- PR comment was skipped because this is a fork." >> $GITHUB_STEP_SUMMARY
|
|
117
|
-
fi
|
|
118
|
-
|
|
119
|
-
- name: Fail if formatting required
|
|
120
|
-
if: ${{ steps.csharpier_check.outcome == 'failure' }}
|
|
121
|
-
run: |
|
|
122
|
-
echo "CSharpier formatting required. Failing the job." 1>&2
|
|
123
|
-
exit 1
|
|
124
|
-
|
|
125
|
-
- name: Success summary
|
|
126
|
-
if: ${{ steps.csharpier_check.outcome == 'success' }}
|
|
127
|
-
run: |
|
|
128
|
-
echo "CSharpier check passed."
|
|
129
|
-
{
|
|
130
|
-
echo "## CSharpier Check Passed"
|
|
131
|
-
echo ""
|
|
132
|
-
echo "- No formatting changes required."
|
|
133
|
-
} >> $GITHUB_STEP_SUMMARY
|
|
134
|
-
|
|
135
|
-
|