com.wallstop-studios.dxmessaging 2.0.0 → 2.1.1
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/.github/workflows/format-on-demand.yml +2 -2
- package/.github/workflows/json-format-check.yml +1 -1
- package/.github/workflows/markdown-json.yml +1 -1
- package/.github/workflows/markdownlint.yml +1 -1
- package/.github/workflows/npm-publish.yml +1 -1
- package/.github/workflows/prettier-autofix.yml +2 -2
- package/.github/workflows/yaml-format-lint.yml +1 -1
- package/Docs/Advanced.md +26 -1
- package/Docs/Comparisons.md +1229 -146
- package/Docs/Compatibility.md +27 -0
- package/Docs/DesignAndArchitecture.md +41 -34
- package/Docs/EmitShorthands.md +34 -0
- package/Docs/Helpers.md +91 -76
- package/Docs/Index.md +28 -25
- package/Docs/Install.md +29 -6
- package/Docs/Integrations/Reflex.md +292 -0
- package/{package-lock.json.meta → Docs/Integrations/Reflex.md.meta} +1 -1
- package/Docs/Integrations/VContainer.md +324 -0
- package/Docs/Integrations/VContainer.md.meta +7 -0
- package/Docs/Integrations/Zenject.md +333 -0
- package/Docs/Integrations/Zenject.md.meta +7 -0
- package/{Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.pdb.meta → Docs/Integrations.meta} +2 -1
- package/Docs/InterceptorsAndOrdering.md +371 -17
- package/Docs/ListeningPatterns.md +206 -0
- package/Docs/MessageBusProviders.md +496 -0
- package/Docs/MessageBusProviders.md.meta +7 -0
- package/Docs/MessageTypes.md +27 -0
- package/Docs/MigrationGuide.md +45 -0
- package/Docs/Patterns.md +286 -0
- package/Docs/Performance.md +9 -9
- package/Docs/QuickReference.md +31 -0
- package/Docs/QuickStart.md +1 -2
- package/Docs/RuntimeConfiguration.md +407 -0
- package/Docs/RuntimeConfiguration.md.meta +7 -0
- package/Docs/UnityIntegration.md +3 -1
- package/Docs/VisualGuide.md +206 -157
- package/Editor/CustomEditors/MessagingComponentEditor.cs +15 -6
- package/README.md +148 -26
- package/Runtime/AssemblyInfo.cs +4 -0
- package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs +1 -1
- package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs +1 -1
- package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs +1 -1
- package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs +1 -1
- package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs +1 -1
- package/Runtime/Core/Extensions/MessageBusExtensions.cs +253 -0
- package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -0
- package/Runtime/Core/Extensions/MessageExtensions.cs +137 -89
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs +23 -0
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -0
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs +14 -0
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -0
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +18 -0
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -0
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs +26 -0
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -0
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +383 -0
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -0
- package/Runtime/Core/MessageHandler.cs +198 -27
- package/Runtime/Core/MessageRegistrationToken.cs +67 -25
- package/Runtime/Core/Messages/IBroadcastMessage.cs +1 -1
- package/Runtime/Core/Messages/ITargetedMessage.cs +1 -1
- package/Runtime/Core/Messages/IUntargetedMessage.cs +1 -1
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs +31 -0
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -0
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs +38 -0
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -0
- package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs +11 -0
- package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -0
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -0
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -0
- package/Runtime/Unity/Integrations/Reflex.meta +8 -0
- package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs +11 -0
- package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +46 -0
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -0
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -0
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -0
- package/Runtime/Unity/Integrations/VContainer.meta +8 -0
- package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs +11 -0
- package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -0
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -0
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +55 -0
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -0
- package/Runtime/Unity/Integrations/Zenject.meta +8 -0
- package/Runtime/Unity/Integrations.meta +8 -0
- package/Runtime/Unity/MessageAwareComponent.cs +102 -0
- package/Runtime/Unity/MessageBusProviderHandle.cs +97 -0
- package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -0
- package/Runtime/Unity/MessagingComponent.cs +175 -9
- package/Runtime/Unity/MessagingComponentInstaller.cs +120 -0
- package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -0
- package/Runtime/Unity/ScriptableMessageBusProvider.cs +14 -0
- package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -0
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -0
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -0
- package/Samples~/DI/Prefabs.meta +8 -0
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -0
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -0
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -0
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -0
- package/Samples~/DI/Providers.meta +8 -0
- package/Samples~/DI/README.md +51 -0
- package/Samples~/DI/README.md.meta +7 -0
- package/Samples~/DI/Reflex/SampleInstaller.cs +75 -0
- package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -0
- package/Samples~/DI/Reflex.meta +8 -0
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs +79 -0
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -0
- package/Samples~/DI/VContainer.meta +8 -0
- package/Samples~/DI/Zenject/SampleInstaller.cs +65 -0
- package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -0
- package/Samples~/DI/Zenject.meta +8 -0
- package/Samples~/DI.meta +8 -0
- package/Samples~/Mini Combat/README.md +5 -7
- package/Samples~/Mini Combat/Walkthrough.md +18 -24
- package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs +12 -2
- package/Samples~/UI Buttons + Inspector/README.md.meta +7 -0
- package/Tests/Runtime/Benchmarks/BenchmarkSession.cs +444 -0
- package/Tests/Runtime/Benchmarks/BenchmarkSession.cs.meta +11 -0
- package/Tests/Runtime/Benchmarks/BenchmarkTestBase.cs +94 -0
- package/Tests/Runtime/Benchmarks/BenchmarkTestBase.cs.meta +11 -0
- package/Tests/Runtime/Benchmarks/ComparisonPerformanceTests.cs +395 -0
- package/Tests/Runtime/Benchmarks/ComparisonPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Benchmarks/PerformanceTests.cs +77 -429
- package/Tests/Runtime/Benchmarks/ProviderResolutionBenchmarks.cs +142 -0
- package/Tests/Runtime/Benchmarks/ProviderResolutionBenchmarks.cs.meta +12 -0
- package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef +50 -0
- package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef.meta +7 -0
- package/Tests/Runtime/Core/DefaultBusFallbackTests.cs +333 -0
- package/Tests/Runtime/Core/DefaultBusFallbackTests.cs.meta +11 -0
- package/Tests/Runtime/Core/Extensions/MessageBusExtensionsTests.cs +278 -0
- package/Tests/Runtime/Core/Extensions/MessageBusExtensionsTests.cs.meta +11 -0
- package/Tests/Runtime/Core/Extensions/MessageExtensionsProviderTests.cs +289 -0
- package/Tests/Runtime/Core/Extensions/MessageExtensionsProviderTests.cs.meta +11 -0
- package/Tests/Runtime/Core/Extensions.meta +8 -0
- package/Tests/Runtime/Core/IntegrationShimSmokeTests.cs +57 -0
- package/Tests/Runtime/Core/IntegrationShimSmokeTests.cs.meta +11 -0
- package/Tests/Runtime/Core/MessageHandlerGlobalBusTests.cs +219 -0
- package/Tests/Runtime/Core/MessageHandlerGlobalBusTests.cs.meta +11 -0
- package/Tests/Runtime/Core/MessageRegistrationBuilderTests.cs +204 -0
- package/Tests/Runtime/Core/MessageRegistrationBuilderTests.cs.meta +11 -0
- package/Tests/Runtime/Core/MessagingTestBase.cs +4 -4
- package/Tests/Runtime/Core/NominalTests.cs +2 -2
- package/Tests/Runtime/Core/TypedShorthandTests.cs +2 -2
- package/Tests/Runtime/Core/UntargetedEquivalenceTests.cs +1 -1
- package/Tests/Runtime/Core/UntargetedPrefreezeTests.cs +2 -4
- package/Tests/Runtime/Integrations/Reflex/ReflexIntegrationTests.cs +162 -0
- package/Tests/Runtime/Integrations/Reflex/ReflexIntegrationTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Reflex/Resources/ReflexSettings.asset +16 -0
- package/Tests/Runtime/Integrations/Reflex/Resources/ReflexSettings.asset.meta +8 -0
- package/Tests/Runtime/Integrations/Reflex/Resources.meta +8 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef +27 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/Reflex.meta +8 -0
- package/Tests/Runtime/Integrations/VContainer/VContainerIntegrationTests.cs +140 -0
- package/Tests/Runtime/Integrations/VContainer/VContainerIntegrationTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef +37 -0
- package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/VContainer.meta +8 -0
- package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef +37 -0
- package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs +140 -0
- package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Zenject.meta +8 -0
- package/Tests/Runtime/Integrations.meta +8 -0
- package/Tests/Runtime/Scripts/Components/EmptyMessageAwareComponent.cs +1 -1
- package/Tests/Runtime/Scripts/Components/GenericMessageAwareComponent.cs +1 -1
- package/Tests/Runtime/Scripts/Components/SimpleMessageAwareComponent.cs +1 -1
- package/Tests/Runtime/TestUtilities/UnityFixtureBase.cs +64 -0
- package/Tests/Runtime/TestUtilities/UnityFixtureBase.cs.meta +12 -0
- package/Tests/Runtime/TestUtilities.meta +9 -0
- package/Tests/Runtime/Unity/MessageBusProviderAssetTests.cs +57 -0
- package/Tests/Runtime/Unity/MessageBusProviderAssetTests.cs.meta +11 -0
- package/Tests/Runtime/Unity/MessageBusProviderHandleTests.cs +107 -0
- package/Tests/Runtime/Unity/MessageBusProviderHandleTests.cs.meta +12 -0
- package/Tests/Runtime/Unity/MessagingComponentProviderIntegrationTests.cs +210 -0
- package/Tests/Runtime/Unity/MessagingComponentProviderIntegrationTests.cs.meta +12 -0
- package/Tests/Runtime/Unity.meta +9 -0
- package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.asmdef +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
namespace DxMessaging.Tests.Runtime.Benchmarks
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Collections.Generic;
|
|
5
|
+
using System.Globalization;
|
|
6
|
+
using System.IO;
|
|
7
|
+
using System.Linq;
|
|
8
|
+
using System.Runtime.InteropServices;
|
|
9
|
+
using System.Text;
|
|
10
|
+
using System.Text.RegularExpressions;
|
|
11
|
+
using UnityEngine;
|
|
12
|
+
|
|
13
|
+
internal readonly struct BenchmarkEntry
|
|
14
|
+
{
|
|
15
|
+
internal BenchmarkEntry(string messageTech, long operationsPerSecond, bool allocating)
|
|
16
|
+
{
|
|
17
|
+
MessageTech = messageTech;
|
|
18
|
+
OperationsPerSecond = operationsPerSecond;
|
|
19
|
+
Allocating = allocating;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
internal string MessageTech { get; }
|
|
23
|
+
|
|
24
|
+
internal long OperationsPerSecond { get; }
|
|
25
|
+
|
|
26
|
+
internal bool Allocating { get; }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public sealed class BenchmarkSession : IDisposable
|
|
30
|
+
{
|
|
31
|
+
private readonly string _sectionName;
|
|
32
|
+
private readonly string _headingPrefix;
|
|
33
|
+
private readonly IReadOnlyList<Func<string>> _docPathResolvers;
|
|
34
|
+
private readonly List<BenchmarkEntry> _entries = new();
|
|
35
|
+
|
|
36
|
+
internal BenchmarkSession(
|
|
37
|
+
string sectionName,
|
|
38
|
+
string headingPrefix,
|
|
39
|
+
IReadOnlyList<Func<string>> docPathResolvers
|
|
40
|
+
)
|
|
41
|
+
{
|
|
42
|
+
_sectionName = sectionName;
|
|
43
|
+
_headingPrefix = headingPrefix;
|
|
44
|
+
_docPathResolvers = docPathResolvers;
|
|
45
|
+
|
|
46
|
+
Debug.Log("| Message Tech | Operations / Second | Allocations? |");
|
|
47
|
+
Debug.Log("| ------------ | ------------------- | ------------ | ");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
internal void Record(string messageTech, long operationsPerSecond, bool allocating)
|
|
51
|
+
{
|
|
52
|
+
string formattedOperations = operationsPerSecond.ToString(
|
|
53
|
+
"N0",
|
|
54
|
+
CultureInfo.InvariantCulture
|
|
55
|
+
);
|
|
56
|
+
Debug.Log($"| {messageTech} | {formattedOperations} | {(allocating ? "Yes" : "No")} |");
|
|
57
|
+
_entries.Add(new BenchmarkEntry(messageTech, operationsPerSecond, allocating));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public void Dispose()
|
|
61
|
+
{
|
|
62
|
+
try
|
|
63
|
+
{
|
|
64
|
+
if (_entries.Count == 0)
|
|
65
|
+
{
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (string.IsNullOrEmpty(_sectionName))
|
|
70
|
+
{
|
|
71
|
+
Debug.LogWarning(
|
|
72
|
+
"Skipping benchmark documentation update because the section name could not be determined."
|
|
73
|
+
);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
BenchmarkDocumentation.TryWriteBenchmarks(
|
|
78
|
+
_sectionName!,
|
|
79
|
+
_headingPrefix,
|
|
80
|
+
_entries,
|
|
81
|
+
_docPathResolvers
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
finally
|
|
85
|
+
{
|
|
86
|
+
_entries.Clear();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
internal static class BenchmarkDocumentation
|
|
92
|
+
{
|
|
93
|
+
private const string PerformanceHeader =
|
|
94
|
+
"# Performance Benchmarks\n\n"
|
|
95
|
+
+ "This page is auto-updated by the Unity PlayMode benchmark tests in `Tests/Runtime/Benchmarks/PerformanceTests.cs`.\n\n"
|
|
96
|
+
+ "How it works:\n\n"
|
|
97
|
+
+ "- Run PlayMode tests locally in your Unity project that references this package.\n"
|
|
98
|
+
+ "- The benchmark test writes an OS-specific section below with a markdown table.\n"
|
|
99
|
+
+ "- CI runs skip writing to avoid noisy diffs.\n\n"
|
|
100
|
+
+ "See also: `Docs/DesignAndArchitecture.md#performance-optimizations` for design details.\n";
|
|
101
|
+
|
|
102
|
+
internal static string GetOperatingSystemSection()
|
|
103
|
+
{
|
|
104
|
+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
105
|
+
{
|
|
106
|
+
return "Windows";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
|
110
|
+
{
|
|
111
|
+
return "macOS";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
115
|
+
{
|
|
116
|
+
return "Linux";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private static bool IsRunningInContinuousIntegration()
|
|
123
|
+
{
|
|
124
|
+
if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_ACTIONS")))
|
|
125
|
+
{
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CI")))
|
|
130
|
+
{
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("JENKINS_URL")))
|
|
135
|
+
{
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITLAB_CI")))
|
|
140
|
+
{
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
internal static void TryWriteBenchmarks(
|
|
148
|
+
string sectionName,
|
|
149
|
+
string headingPrefix,
|
|
150
|
+
IReadOnlyList<BenchmarkEntry> entries,
|
|
151
|
+
IReadOnlyList<Func<string>> docPathResolvers
|
|
152
|
+
)
|
|
153
|
+
{
|
|
154
|
+
if (entries.Count == 0)
|
|
155
|
+
{
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (IsRunningInContinuousIntegration())
|
|
160
|
+
{
|
|
161
|
+
Debug.Log(
|
|
162
|
+
$"Skipping benchmarks update for {sectionName} because the benchmarks are running in CI."
|
|
163
|
+
);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
string docPath = ResolveDocPath(docPathResolvers);
|
|
168
|
+
if (string.IsNullOrEmpty(docPath))
|
|
169
|
+
{
|
|
170
|
+
Debug.LogWarning(
|
|
171
|
+
$"Skipping benchmarks update for {sectionName} because no documentation target could be located."
|
|
172
|
+
);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try
|
|
177
|
+
{
|
|
178
|
+
string table = BuildTable(entries);
|
|
179
|
+
string originalContent = File.Exists(docPath)
|
|
180
|
+
? File.ReadAllText(docPath)
|
|
181
|
+
: string.Empty;
|
|
182
|
+
string updatedContent = ReplaceSection(
|
|
183
|
+
originalContent,
|
|
184
|
+
sectionName,
|
|
185
|
+
headingPrefix,
|
|
186
|
+
table
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (string.Equals(originalContent, updatedContent, StringComparison.Ordinal))
|
|
190
|
+
{
|
|
191
|
+
Debug.Log(
|
|
192
|
+
$"Benchmark section for {sectionName} is already up to date in {docPath}."
|
|
193
|
+
);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
File.WriteAllText(docPath, updatedContent, new UTF8Encoding(false));
|
|
198
|
+
Debug.Log($"Updated benchmarks for {sectionName} in {docPath}.");
|
|
199
|
+
}
|
|
200
|
+
catch (Exception exception)
|
|
201
|
+
{
|
|
202
|
+
Debug.LogWarning(
|
|
203
|
+
$"Failed to update benchmark documentation for {sectionName}: {exception}"
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
internal static string TryFindPerformanceDocPath()
|
|
209
|
+
{
|
|
210
|
+
return TryFindDocPath(Path.Combine("Docs", "Performance.md"), PerformanceHeader);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
internal static string TryFindComparisonsDocPath()
|
|
214
|
+
{
|
|
215
|
+
return TryFindDocPath(Path.Combine("Docs", "Comparisons.md"));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
internal static string TryFindReadmePath()
|
|
219
|
+
{
|
|
220
|
+
return TrySearchForFile("README.md");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private static string BuildTable(IReadOnlyList<BenchmarkEntry> entries)
|
|
224
|
+
{
|
|
225
|
+
StringBuilder builder = new();
|
|
226
|
+
builder.AppendLine("| Message Tech | Operations / Second | Allocations? |");
|
|
227
|
+
builder.AppendLine("| ------------ | ------------------- | ------------ |");
|
|
228
|
+
|
|
229
|
+
foreach (BenchmarkEntry entry in entries)
|
|
230
|
+
{
|
|
231
|
+
builder
|
|
232
|
+
.Append("| ")
|
|
233
|
+
.Append(entry.MessageTech)
|
|
234
|
+
.Append(" | ")
|
|
235
|
+
.Append(entry.OperationsPerSecond.ToString("N0", CultureInfo.InvariantCulture))
|
|
236
|
+
.Append(" | ")
|
|
237
|
+
.Append(entry.Allocating ? "Yes" : "No")
|
|
238
|
+
.AppendLine(" |");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return builder.ToString().TrimEnd('\r', '\n');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private static string ReplaceSection(
|
|
245
|
+
string content,
|
|
246
|
+
string sectionName,
|
|
247
|
+
string headingPrefix,
|
|
248
|
+
string tableContent
|
|
249
|
+
)
|
|
250
|
+
{
|
|
251
|
+
string replacement = $"{headingPrefix}{sectionName}\n\n{tableContent}\n";
|
|
252
|
+
int headingLevel = CountHeadingLevel(headingPrefix);
|
|
253
|
+
string stopPattern = headingLevel <= 1 ? "#" : $"#{{1,{headingLevel}}}";
|
|
254
|
+
string pattern =
|
|
255
|
+
$@"^{Regex.Escape(headingPrefix)}{Regex.Escape(sectionName)}[^\S\r\n]*(?:\r?\n|$)[\s\S]*?(?=^\s*{stopPattern}\s|\Z)";
|
|
256
|
+
Regex regex = new(pattern, RegexOptions.CultureInvariant | RegexOptions.Multiline);
|
|
257
|
+
string updated = regex.Replace(content, replacement, 1);
|
|
258
|
+
|
|
259
|
+
if (string.Equals(content, updated, StringComparison.Ordinal))
|
|
260
|
+
{
|
|
261
|
+
string prefix = content.EndsWith("\n", StringComparison.Ordinal)
|
|
262
|
+
? string.Empty
|
|
263
|
+
: "\n";
|
|
264
|
+
updated = $"{content}{prefix}{replacement}";
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!updated.EndsWith("\n", StringComparison.Ordinal))
|
|
268
|
+
{
|
|
269
|
+
updated += "\n";
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return updated;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private static int CountHeadingLevel(string headingPrefix)
|
|
276
|
+
{
|
|
277
|
+
int level = headingPrefix.Count(c => c == '#');
|
|
278
|
+
return level > 0 ? level : 1;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private static string ResolveDocPath(IReadOnlyList<Func<string>> resolvers)
|
|
282
|
+
{
|
|
283
|
+
foreach (Func<string> resolver in resolvers)
|
|
284
|
+
{
|
|
285
|
+
if (resolver == null)
|
|
286
|
+
{
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
try
|
|
291
|
+
{
|
|
292
|
+
string path = resolver();
|
|
293
|
+
if (!string.IsNullOrEmpty(path))
|
|
294
|
+
{
|
|
295
|
+
return path;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (Exception exception)
|
|
299
|
+
{
|
|
300
|
+
Debug.LogWarning($"Failed to resolve documentation path: {exception}");
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private static string TryFindDocPath(string relativePath, string seedContent = null)
|
|
308
|
+
{
|
|
309
|
+
string discovered = TrySearchForFile(relativePath);
|
|
310
|
+
if (!string.IsNullOrEmpty(discovered))
|
|
311
|
+
{
|
|
312
|
+
return discovered;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
string readmePath = TryFindReadmePath();
|
|
316
|
+
if (!string.IsNullOrEmpty(readmePath))
|
|
317
|
+
{
|
|
318
|
+
string candidate = TryEnsureFileAdjacent(readmePath, relativePath, seedContent);
|
|
319
|
+
if (!string.IsNullOrEmpty(candidate))
|
|
320
|
+
{
|
|
321
|
+
return candidate;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return TryEnsureFileAdjacent(
|
|
326
|
+
Directory.GetCurrentDirectory(),
|
|
327
|
+
relativePath,
|
|
328
|
+
seedContent
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private static string TrySearchForFile(string relativePath)
|
|
333
|
+
{
|
|
334
|
+
string current = Directory.GetCurrentDirectory();
|
|
335
|
+
while (!string.IsNullOrEmpty(current))
|
|
336
|
+
{
|
|
337
|
+
string candidate = Path.Combine(current, relativePath);
|
|
338
|
+
if (File.Exists(candidate))
|
|
339
|
+
{
|
|
340
|
+
return candidate;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
string packageCandidate = Path.Combine(
|
|
344
|
+
current,
|
|
345
|
+
"Packages",
|
|
346
|
+
"com.wallstop-studios.dxmessaging",
|
|
347
|
+
relativePath
|
|
348
|
+
);
|
|
349
|
+
if (File.Exists(packageCandidate))
|
|
350
|
+
{
|
|
351
|
+
return packageCandidate;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
DirectoryInfo parent = Directory.GetParent(current);
|
|
355
|
+
current = parent?.FullName;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
string assemblyLocation = typeof(BenchmarkDocumentation).Assembly.Location;
|
|
359
|
+
if (string.IsNullOrEmpty(assemblyLocation))
|
|
360
|
+
{
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
string assemblyDirectory = Path.GetDirectoryName(assemblyLocation);
|
|
365
|
+
if (string.IsNullOrEmpty(assemblyDirectory))
|
|
366
|
+
{
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
DirectoryInfo directory = new(assemblyDirectory);
|
|
371
|
+
while (directory != null)
|
|
372
|
+
{
|
|
373
|
+
string candidate = Path.Combine(directory.FullName, relativePath);
|
|
374
|
+
if (File.Exists(candidate))
|
|
375
|
+
{
|
|
376
|
+
return candidate;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
string packageCandidate = Path.Combine(
|
|
380
|
+
directory.FullName,
|
|
381
|
+
"Packages",
|
|
382
|
+
"com.wallstop-studios.dxmessaging",
|
|
383
|
+
relativePath
|
|
384
|
+
);
|
|
385
|
+
if (File.Exists(packageCandidate))
|
|
386
|
+
{
|
|
387
|
+
return packageCandidate;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
directory = directory.Parent;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private static string TryEnsureFileAdjacent(
|
|
397
|
+
string anchorPath,
|
|
398
|
+
string relativePath,
|
|
399
|
+
string seedContent
|
|
400
|
+
)
|
|
401
|
+
{
|
|
402
|
+
string baseDirectory = File.Exists(anchorPath)
|
|
403
|
+
? Path.GetDirectoryName(anchorPath) ?? string.Empty
|
|
404
|
+
: anchorPath;
|
|
405
|
+
if (string.IsNullOrEmpty(baseDirectory))
|
|
406
|
+
{
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
string candidate = Path.Combine(baseDirectory, relativePath);
|
|
411
|
+
string directory = Path.GetDirectoryName(candidate);
|
|
412
|
+
try
|
|
413
|
+
{
|
|
414
|
+
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
|
|
415
|
+
{
|
|
416
|
+
Directory.CreateDirectory(directory);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (File.Exists(candidate))
|
|
420
|
+
{
|
|
421
|
+
return candidate;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (!string.IsNullOrEmpty(seedContent))
|
|
425
|
+
{
|
|
426
|
+
File.WriteAllText(candidate, seedContent, new UTF8Encoding(false));
|
|
427
|
+
}
|
|
428
|
+
else
|
|
429
|
+
{
|
|
430
|
+
File.WriteAllText(candidate, string.Empty, new UTF8Encoding(false));
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return candidate;
|
|
434
|
+
}
|
|
435
|
+
catch (Exception exception)
|
|
436
|
+
{
|
|
437
|
+
Debug.LogWarning(
|
|
438
|
+
$"Failed to create documentation file at {candidate}: {exception}"
|
|
439
|
+
);
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
namespace DxMessaging.Tests.Runtime.Benchmarks
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Globalization;
|
|
5
|
+
using DxMessaging.Tests.Runtime.Core;
|
|
6
|
+
using Scripts.Components;
|
|
7
|
+
using UnityEngine;
|
|
8
|
+
using Object = UnityEngine.Object;
|
|
9
|
+
|
|
10
|
+
public abstract class BenchmarkTestBase : MessagingTestBase
|
|
11
|
+
{
|
|
12
|
+
protected const int NumInvocationsPerIteration = 10_000;
|
|
13
|
+
|
|
14
|
+
private BenchmarkSession _session;
|
|
15
|
+
|
|
16
|
+
protected BenchmarkSession CurrentSession => _session;
|
|
17
|
+
|
|
18
|
+
protected void RunWithSession(BenchmarkSession session, Action body)
|
|
19
|
+
{
|
|
20
|
+
if (session == null)
|
|
21
|
+
{
|
|
22
|
+
throw new ArgumentNullException(nameof(session));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (body == null)
|
|
26
|
+
{
|
|
27
|
+
throw new ArgumentNullException(nameof(body));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_session = session;
|
|
31
|
+
try
|
|
32
|
+
{
|
|
33
|
+
using (session)
|
|
34
|
+
{
|
|
35
|
+
body();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
finally
|
|
39
|
+
{
|
|
40
|
+
_session = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected void RecordBenchmark(string label, int count, TimeSpan duration, bool allocating)
|
|
45
|
+
{
|
|
46
|
+
long operationsPerSecond =
|
|
47
|
+
duration.TotalSeconds <= 0.0 ? 0 : (long)Math.Floor(count / duration.TotalSeconds);
|
|
48
|
+
|
|
49
|
+
if (_session != null)
|
|
50
|
+
{
|
|
51
|
+
_session.Record(label, operationsPerSecond, allocating);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
string formatted = operationsPerSecond.ToString("N0", CultureInfo.InvariantCulture);
|
|
56
|
+
Debug.Log($"| {label} | {formatted} | {(allocating ? "Yes" : "No")} |");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
protected GameObject CreateBenchmarkGameObject()
|
|
60
|
+
{
|
|
61
|
+
GameObject target = new(
|
|
62
|
+
"Benchmark",
|
|
63
|
+
typeof(EmptyMessageAwareComponent),
|
|
64
|
+
typeof(SpriteRenderer),
|
|
65
|
+
typeof(Rigidbody2D),
|
|
66
|
+
typeof(CircleCollider2D),
|
|
67
|
+
typeof(LineRenderer)
|
|
68
|
+
);
|
|
69
|
+
_spawned.Add(target);
|
|
70
|
+
return target;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected void RunWithComponent(Action<EmptyMessageAwareComponent> action)
|
|
74
|
+
{
|
|
75
|
+
if (action == null)
|
|
76
|
+
{
|
|
77
|
+
throw new ArgumentNullException(nameof(action));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
GameObject go = CreateBenchmarkGameObject();
|
|
81
|
+
try
|
|
82
|
+
{
|
|
83
|
+
EmptyMessageAwareComponent component =
|
|
84
|
+
go.GetComponent<EmptyMessageAwareComponent>();
|
|
85
|
+
action(component);
|
|
86
|
+
}
|
|
87
|
+
finally
|
|
88
|
+
{
|
|
89
|
+
_spawned.Remove(go);
|
|
90
|
+
Object.Destroy(go);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|