com.wallstop-studios.dxmessaging 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/.github/workflows/format-on-demand.yml +2 -2
  2. package/.github/workflows/json-format-check.yml +1 -1
  3. package/.github/workflows/markdown-json.yml +1 -1
  4. package/.github/workflows/markdownlint.yml +1 -1
  5. package/.github/workflows/npm-publish.yml +1 -1
  6. package/.github/workflows/prettier-autofix.yml +2 -2
  7. package/.github/workflows/yaml-format-lint.yml +1 -1
  8. package/Docs/Advanced.md +26 -1
  9. package/Docs/Comparisons.md +1229 -146
  10. package/Docs/Compatibility.md +27 -0
  11. package/Docs/DesignAndArchitecture.md +41 -34
  12. package/Docs/EmitShorthands.md +34 -0
  13. package/Docs/Helpers.md +1 -1
  14. package/Docs/Index.md +28 -25
  15. package/Docs/Install.md +29 -6
  16. package/Docs/Integrations/Reflex.md +292 -0
  17. package/Docs/Integrations/Reflex.md.meta +7 -0
  18. package/Docs/Integrations/VContainer.md +324 -0
  19. package/Docs/Integrations/VContainer.md.meta +7 -0
  20. package/Docs/Integrations/Zenject.md +333 -0
  21. package/Docs/Integrations/Zenject.md.meta +7 -0
  22. package/{Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.pdb.meta → Docs/Integrations.meta} +2 -1
  23. package/Docs/InterceptorsAndOrdering.md +371 -17
  24. package/Docs/ListeningPatterns.md +206 -0
  25. package/Docs/MessageBusProviders.md +496 -0
  26. package/Docs/MessageBusProviders.md.meta +7 -0
  27. package/Docs/MessageTypes.md +27 -0
  28. package/Docs/MigrationGuide.md +45 -0
  29. package/Docs/Patterns.md +286 -0
  30. package/Docs/Performance.md +9 -9
  31. package/Docs/QuickReference.md +31 -0
  32. package/Docs/RuntimeConfiguration.md +407 -0
  33. package/Docs/RuntimeConfiguration.md.meta +7 -0
  34. package/Docs/UnityIntegration.md +3 -1
  35. package/Docs/VisualGuide.md +206 -157
  36. package/Editor/CustomEditors/MessagingComponentEditor.cs +15 -6
  37. package/README.md +148 -26
  38. package/Runtime/AssemblyInfo.cs +4 -0
  39. package/Runtime/Core/Extensions/MessageBusExtensions.cs +253 -0
  40. package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -0
  41. package/Runtime/Core/Extensions/MessageExtensions.cs +137 -89
  42. package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs +23 -0
  43. package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -0
  44. package/Runtime/Core/MessageBus/IMessageBusProvider.cs +14 -0
  45. package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -0
  46. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +18 -0
  47. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -0
  48. package/Runtime/Core/MessageBus/MessageBusRebindMode.cs +26 -0
  49. package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -0
  50. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +383 -0
  51. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -0
  52. package/Runtime/Core/MessageHandler.cs +198 -27
  53. package/Runtime/Core/MessageRegistrationToken.cs +67 -25
  54. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs +31 -0
  55. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -0
  56. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs +38 -0
  57. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -0
  58. package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs +11 -0
  59. package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +3 -0
  60. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
  61. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -0
  62. package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -0
  63. package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -0
  64. package/Runtime/Unity/Integrations/Reflex.meta +8 -0
  65. package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs +11 -0
  66. package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +3 -0
  67. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +46 -0
  68. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -0
  69. package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -0
  70. package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -0
  71. package/Runtime/Unity/Integrations/VContainer.meta +8 -0
  72. package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs +11 -0
  73. package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +3 -0
  74. package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -0
  75. package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -0
  76. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +55 -0
  77. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -0
  78. package/Runtime/Unity/Integrations/Zenject.meta +8 -0
  79. package/Runtime/Unity/Integrations.meta +8 -0
  80. package/Runtime/Unity/MessageAwareComponent.cs +102 -0
  81. package/Runtime/Unity/MessageBusProviderHandle.cs +97 -0
  82. package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -0
  83. package/Runtime/Unity/MessagingComponent.cs +164 -2
  84. package/Runtime/Unity/MessagingComponentInstaller.cs +120 -0
  85. package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -0
  86. package/Runtime/Unity/ScriptableMessageBusProvider.cs +14 -0
  87. package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -0
  88. package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -0
  89. package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -0
  90. package/Samples~/DI/Prefabs.meta +8 -0
  91. package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -0
  92. package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -0
  93. package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -0
  94. package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -0
  95. package/Samples~/DI/Providers.meta +8 -0
  96. package/Samples~/DI/README.md +51 -0
  97. package/Samples~/DI/README.md.meta +7 -0
  98. package/Samples~/DI/Reflex/SampleInstaller.cs +75 -0
  99. package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -0
  100. package/Samples~/DI/Reflex.meta +8 -0
  101. package/Samples~/DI/VContainer/SampleLifetimeScope.cs +81 -0
  102. package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -0
  103. package/Samples~/DI/VContainer.meta +8 -0
  104. package/Samples~/DI/Zenject/SampleInstaller.cs +67 -0
  105. package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -0
  106. package/Samples~/DI/Zenject.meta +8 -0
  107. package/Samples~/DI.meta +8 -0
  108. package/Samples~/Mini Combat/README.md +5 -7
  109. package/Samples~/Mini Combat/Walkthrough.md +18 -24
  110. package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs +12 -2
  111. package/Samples~/UI Buttons + Inspector/README.md.meta +7 -0
  112. package/Tests/Runtime/Benchmarks/BenchmarkSession.cs +444 -0
  113. package/Tests/Runtime/Benchmarks/BenchmarkSession.cs.meta +11 -0
  114. package/Tests/Runtime/Benchmarks/BenchmarkTestBase.cs +94 -0
  115. package/Tests/Runtime/Benchmarks/BenchmarkTestBase.cs.meta +11 -0
  116. package/Tests/Runtime/Benchmarks/ComparisonPerformanceTests.cs +395 -0
  117. package/Tests/Runtime/Benchmarks/ComparisonPerformanceTests.cs.meta +11 -0
  118. package/Tests/Runtime/Benchmarks/PerformanceTests.cs +77 -429
  119. package/Tests/Runtime/Benchmarks/ProviderResolutionBenchmarks.cs +142 -0
  120. package/Tests/Runtime/Benchmarks/ProviderResolutionBenchmarks.cs.meta +12 -0
  121. package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef +50 -0
  122. package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef.meta +7 -0
  123. package/Tests/Runtime/Core/DefaultBusFallbackTests.cs +333 -0
  124. package/Tests/Runtime/Core/DefaultBusFallbackTests.cs.meta +11 -0
  125. package/Tests/Runtime/Core/Extensions/MessageBusExtensionsTests.cs +278 -0
  126. package/Tests/Runtime/Core/Extensions/MessageBusExtensionsTests.cs.meta +11 -0
  127. package/Tests/Runtime/Core/Extensions/MessageExtensionsProviderTests.cs +289 -0
  128. package/Tests/Runtime/Core/Extensions/MessageExtensionsProviderTests.cs.meta +11 -0
  129. package/Tests/Runtime/Core/Extensions.meta +8 -0
  130. package/Tests/Runtime/Core/IntegrationShimSmokeTests.cs +57 -0
  131. package/Tests/Runtime/Core/IntegrationShimSmokeTests.cs.meta +11 -0
  132. package/Tests/Runtime/Core/MessageHandlerGlobalBusTests.cs +219 -0
  133. package/Tests/Runtime/Core/MessageHandlerGlobalBusTests.cs.meta +11 -0
  134. package/Tests/Runtime/Core/MessageRegistrationBuilderTests.cs +204 -0
  135. package/Tests/Runtime/Core/MessageRegistrationBuilderTests.cs.meta +11 -0
  136. package/Tests/Runtime/Core/MessagingTestBase.cs +4 -4
  137. package/Tests/Runtime/Core/NominalTests.cs +2 -2
  138. package/Tests/Runtime/Core/TypedShorthandTests.cs +2 -2
  139. package/Tests/Runtime/Core/UntargetedEquivalenceTests.cs +1 -1
  140. package/Tests/Runtime/Core/UntargetedPrefreezeTests.cs +2 -4
  141. package/Tests/Runtime/Integrations/Reflex/ReflexIntegrationTests.cs +162 -0
  142. package/Tests/Runtime/Integrations/Reflex/ReflexIntegrationTests.cs.meta +11 -0
  143. package/Tests/Runtime/Integrations/Reflex/Resources/ReflexSettings.asset +16 -0
  144. package/Tests/Runtime/Integrations/Reflex/Resources/ReflexSettings.asset.meta +8 -0
  145. package/Tests/Runtime/Integrations/Reflex/Resources.meta +8 -0
  146. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef +27 -0
  147. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef.meta +7 -0
  148. package/Tests/Runtime/Integrations/Reflex.meta +8 -0
  149. package/Tests/Runtime/Integrations/VContainer/VContainerIntegrationTests.cs +140 -0
  150. package/Tests/Runtime/Integrations/VContainer/VContainerIntegrationTests.cs.meta +11 -0
  151. package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef +37 -0
  152. package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef.meta +7 -0
  153. package/Tests/Runtime/Integrations/VContainer.meta +8 -0
  154. package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef +37 -0
  155. package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef.meta +7 -0
  156. package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs +140 -0
  157. package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs.meta +11 -0
  158. package/Tests/Runtime/Integrations/Zenject.meta +8 -0
  159. package/Tests/Runtime/Integrations.meta +8 -0
  160. package/Tests/Runtime/Scripts/Components/EmptyMessageAwareComponent.cs +1 -1
  161. package/Tests/Runtime/Scripts/Components/GenericMessageAwareComponent.cs +1 -1
  162. package/Tests/Runtime/Scripts/Components/SimpleMessageAwareComponent.cs +1 -1
  163. package/Tests/Runtime/TestUtilities/UnityFixtureBase.cs +64 -0
  164. package/Tests/Runtime/TestUtilities/UnityFixtureBase.cs.meta +12 -0
  165. package/Tests/Runtime/TestUtilities.meta +9 -0
  166. package/Tests/Runtime/Unity/MessageBusProviderAssetTests.cs +57 -0
  167. package/Tests/Runtime/Unity/MessageBusProviderAssetTests.cs.meta +11 -0
  168. package/Tests/Runtime/Unity/MessageBusProviderHandleTests.cs +107 -0
  169. package/Tests/Runtime/Unity/MessageBusProviderHandleTests.cs.meta +12 -0
  170. package/Tests/Runtime/Unity/MessagingComponentProviderIntegrationTests.cs +210 -0
  171. package/Tests/Runtime/Unity/MessagingComponentProviderIntegrationTests.cs.meta +12 -0
  172. package/Tests/Runtime/Unity.meta +9 -0
  173. package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.asmdef +3 -1
  174. package/package.json +1 -1
@@ -1,14 +1,7 @@
1
1
  namespace DxMessaging.Tests.Runtime.Benchmarks
2
2
  {
3
3
  using System;
4
- using System.Collections.Generic;
5
4
  using System.Diagnostics;
6
- using System.Globalization;
7
- using System.IO;
8
- using System.Runtime.InteropServices;
9
- using System.Text;
10
- using System.Text.RegularExpressions;
11
- using Core;
12
5
  using DxMessaging.Core;
13
6
  using DxMessaging.Core.Extensions;
14
7
  using DxMessaging.Core.Messages;
@@ -17,125 +10,80 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
17
10
  using Scripts.Messages;
18
11
  using UnityEngine;
19
12
  using UnityEngine.TestTools.Constraints;
20
- using Debug = UnityEngine.Debug;
21
13
  using Is = NUnit.Framework.Is;
22
- using Object = UnityEngine.Object;
23
14
 
24
- public sealed class PerformanceTests : MessagingTestBase
15
+ public sealed class PerformanceTests : BenchmarkTestBase
25
16
  {
26
- private const int NumInvocationsPerIteration = 1_000;
27
-
28
- private static readonly List<BenchmarkResult> BenchmarkResults = new();
29
-
30
- private readonly struct BenchmarkResult
31
- {
32
- internal BenchmarkResult(string messageTech, long operationsPerSecond, bool allocating)
33
- {
34
- MessageTech = messageTech;
35
- OperationsPerSecond = operationsPerSecond;
36
- Allocating = allocating;
37
- }
38
-
39
- internal string MessageTech { get; }
40
-
41
- internal long OperationsPerSecond { get; }
42
-
43
- internal bool Allocating { get; }
44
- }
45
-
46
17
  protected override bool MessagingDebugEnabled => false;
47
18
 
48
19
  [Test]
49
20
  public void Benchmark()
50
21
  {
51
- BenchmarkResults.Clear();
52
- try
53
- {
54
- TimeSpan timeout = TimeSpan.FromSeconds(5);
55
-
56
- Debug.Log("| Message Tech | Operations / Second | Allocations? |");
57
- Debug.Log("| ------------ | ------------------- | ------------ | ");
58
-
59
- ComplexTargetedMessage message = new(Guid.NewGuid());
60
- ReflexiveMessage reflexiveMessage = new(
61
- nameof(SimpleMessageAwareComponent.HandleSlowComplexTargetedMessage),
62
- ReflexiveSendMode.Flat,
63
- message
64
- );
65
-
66
- Stopwatch timer = Stopwatch.StartNew();
67
-
68
- RunTest(component => Unity(timer, timeout, component.gameObject, message));
69
-
70
- RunTest(component => NormalGameObject(timer, timeout, component, message));
71
- RunTest(component => NormalComponent(timer, timeout, component, message));
72
- RunTest(component => NoCopyGameObject(timer, timeout, component, message));
73
- RunTest(component => NoCopyComponent(timer, timeout, component, message));
22
+ string operatingSystemSection = BenchmarkDocumentation.GetOperatingSystemSection();
23
+ BenchmarkSession session = new(
24
+ operatingSystemSection,
25
+ "## ",
26
+ new Func<string>[]
27
+ {
28
+ BenchmarkDocumentation.TryFindPerformanceDocPath,
29
+ BenchmarkDocumentation.TryFindReadmePath,
30
+ }
31
+ );
74
32
 
75
- SimpleUntargetedMessage untargetedMessage = new();
76
- RunTest(component =>
77
- NoCopyUntargeted(timer, timeout, component, untargetedMessage)
78
- );
79
- RunTest(component =>
80
- ReflexiveOneArgument(timer, timeout, component.gameObject, reflexiveMessage)
81
- );
82
- RunTest(component => ReflexiveTwoArguments(timer, timeout, component.gameObject));
83
- RunTest(component => ReflexiveThreeArguments(timer, timeout, component.gameObject));
33
+ RunWithSession(
34
+ session,
35
+ () =>
36
+ {
37
+ TimeSpan timeout = TimeSpan.FromSeconds(5);
38
+ Stopwatch timer = Stopwatch.StartNew();
84
39
 
85
- UpdateReadmeWithBenchmarks();
86
- }
87
- finally
88
- {
89
- BenchmarkResults.Clear();
90
- }
91
- }
40
+ ComplexTargetedMessage message = new(Guid.NewGuid());
41
+ ReflexiveMessage reflexiveMessage = new(
42
+ nameof(SimpleMessageAwareComponent.HandleSlowComplexTargetedMessage),
43
+ ReflexiveSendMode.Flat,
44
+ message
45
+ );
92
46
 
93
- private GameObject CreateGameObject()
94
- {
95
- GameObject target = new(
96
- nameof(Benchmark),
97
- typeof(EmptyMessageAwareComponent),
98
- typeof(SpriteRenderer),
99
- typeof(Rigidbody2D),
100
- typeof(CircleCollider2D),
101
- typeof(LineRenderer)
102
- );
103
- _spawned.Add(target);
47
+ RunWithComponent(component =>
48
+ Unity(timer, timeout, component.gameObject, message)
49
+ );
104
50
 
105
- return target;
106
- }
51
+ RunWithComponent(component =>
52
+ NormalGameObject(timer, timeout, component, message)
53
+ );
54
+ RunWithComponent(component =>
55
+ NormalComponent(timer, timeout, component, message)
56
+ );
57
+ RunWithComponent(component =>
58
+ NoCopyGameObject(timer, timeout, component, message)
59
+ );
60
+ RunWithComponent(component =>
61
+ NoCopyComponent(timer, timeout, component, message)
62
+ );
107
63
 
108
- private static void DisplayCount(
109
- string testName,
110
- int count,
111
- TimeSpan timeout,
112
- bool allocating
113
- )
114
- {
115
- long operationsPerSecond = (long)Math.Floor(count / timeout.TotalSeconds);
116
- BenchmarkResults.Add(new BenchmarkResult(testName, operationsPerSecond, allocating));
117
- string formattedOperations = operationsPerSecond.ToString(
118
- "N0",
119
- CultureInfo.InvariantCulture
64
+ SimpleUntargetedMessage untargetedMessage = new();
65
+ RunWithComponent(component =>
66
+ NoCopyUntargeted(timer, timeout, component, untargetedMessage)
67
+ );
68
+ RunWithComponent(component =>
69
+ ReflexiveOneArgument(timer, timeout, component.gameObject, reflexiveMessage)
70
+ );
71
+ RunWithComponent(component =>
72
+ ReflexiveTwoArguments(timer, timeout, component.gameObject)
73
+ );
74
+ RunWithComponent(component =>
75
+ ReflexiveThreeArguments(timer, timeout, component.gameObject)
76
+ );
77
+ }
120
78
  );
121
- Debug.Log($"| {testName} | {formattedOperations} | {(allocating ? "Yes" : "No")} |");
122
79
  }
123
80
 
124
- private void RunTest(Action<EmptyMessageAwareComponent> test)
81
+ private void DisplayCount(string testName, int count, TimeSpan timeout, bool allocating)
125
82
  {
126
- GameObject go = CreateGameObject();
127
- try
128
- {
129
- test(go.GetComponent<EmptyMessageAwareComponent>());
130
- }
131
- finally
132
- {
133
- _spawned.Remove(go);
134
- Object.Destroy(go);
135
- }
83
+ RecordBenchmark(testName, count, timeout, allocating);
136
84
  }
137
85
 
138
- private static void Unity(
86
+ private void Unity(
139
87
  Stopwatch timer,
140
88
  TimeSpan timeout,
141
89
  GameObject target,
@@ -147,6 +95,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
147
95
  {
148
96
  component = target.AddComponent<SimpleMessageAwareComponent>();
149
97
  }
98
+
150
99
  component.slowComplexTargetedHandler = () => ++count;
151
100
  // Pre-warm
152
101
  target.SendMessage(
@@ -183,20 +132,18 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
183
132
  {
184
133
  allocating = true;
185
134
  }
135
+
186
136
  DisplayCount("Unity", count, timeout, allocating);
187
137
  }
188
138
 
189
- private static void ReflexiveThreeArguments(
190
- Stopwatch timer,
191
- TimeSpan timeout,
192
- GameObject go
193
- )
139
+ private void ReflexiveThreeArguments(Stopwatch timer, TimeSpan timeout, GameObject go)
194
140
  {
195
141
  int count = 0;
196
142
  if (!go.TryGetComponent(out SimpleMessageAwareComponent component))
197
143
  {
198
144
  component = go.AddComponent<SimpleMessageAwareComponent>();
199
145
  }
146
+
200
147
  component.reflexiveThreeArgumentHandler = () => ++count;
201
148
  ReflexiveMessage message = new(
202
149
  nameof(SimpleMessageAwareComponent.HandleReflexiveMessageThreeArguments),
@@ -217,6 +164,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
217
164
  message.EmitTargeted(target);
218
165
  }
219
166
  } while (timer.Elapsed < timeout);
167
+
220
168
  bool allocating;
221
169
  try
222
170
  {
@@ -231,13 +179,14 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
231
179
  DisplayCount("Reflexive (Three Arguments)", count, timeout, allocating);
232
180
  }
233
181
 
234
- private static void ReflexiveTwoArguments(Stopwatch timer, TimeSpan timeout, GameObject go)
182
+ private void ReflexiveTwoArguments(Stopwatch timer, TimeSpan timeout, GameObject go)
235
183
  {
236
184
  int count = 0;
237
185
  if (!go.TryGetComponent(out SimpleMessageAwareComponent component))
238
186
  {
239
187
  component = go.AddComponent<SimpleMessageAwareComponent>();
240
188
  }
189
+
241
190
  component.reflexiveTwoArgumentHandler = () => ++count;
242
191
  ReflexiveMessage message = new(
243
192
  nameof(SimpleMessageAwareComponent.HandleReflexiveMessageTwoArguments),
@@ -257,6 +206,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
257
206
  message.EmitTargeted(target);
258
207
  }
259
208
  } while (timer.Elapsed < timeout);
209
+
260
210
  bool allocating;
261
211
  try
262
212
  {
@@ -271,7 +221,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
271
221
  DisplayCount("Reflexive (Two Arguments)", count, timeout, allocating);
272
222
  }
273
223
 
274
- private static void ReflexiveOneArgument(
224
+ private void ReflexiveOneArgument(
275
225
  Stopwatch timer,
276
226
  TimeSpan timeout,
277
227
  GameObject go,
@@ -283,6 +233,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
283
233
  {
284
234
  component = go.AddComponent<SimpleMessageAwareComponent>();
285
235
  }
236
+
286
237
  component.slowComplexTargetedHandler = () => ++count;
287
238
  InstanceId target = go;
288
239
  // Pre-warm
@@ -296,6 +247,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
296
247
  message.EmitTargeted(target);
297
248
  }
298
249
  } while (timer.Elapsed < timeout);
250
+
299
251
  bool allocating;
300
252
  try
301
253
  {
@@ -310,7 +262,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
310
262
  DisplayCount("Reflexive (One Argument)", count, timeout, allocating);
311
263
  }
312
264
 
313
- private static void NormalGameObject(
265
+ private void NormalGameObject(
314
266
  Stopwatch timer,
315
267
  TimeSpan timeout,
316
268
  EmptyMessageAwareComponent component,
@@ -334,6 +286,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
334
286
  message.EmitTargeted(target);
335
287
  }
336
288
  } while (timer.Elapsed < timeout);
289
+
337
290
  bool allocating;
338
291
  try
339
292
  {
@@ -354,7 +307,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
354
307
  }
355
308
  }
356
309
 
357
- private static void NormalComponent(
310
+ private void NormalComponent(
358
311
  Stopwatch timer,
359
312
  TimeSpan timeout,
360
313
  EmptyMessageAwareComponent component,
@@ -388,6 +341,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
388
341
  {
389
342
  allocating = true;
390
343
  }
344
+
391
345
  DisplayCount("DxMessaging (Component) - Normal", count, timeout, allocating);
392
346
  return;
393
347
 
@@ -397,7 +351,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
397
351
  }
398
352
  }
399
353
 
400
- private static void NoCopyGameObject(
354
+ private void NoCopyGameObject(
401
355
  Stopwatch timer,
402
356
  TimeSpan timeout,
403
357
  EmptyMessageAwareComponent component,
@@ -421,6 +375,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
421
375
  message.EmitTargeted(target);
422
376
  }
423
377
  } while (timer.Elapsed < timeout);
378
+
424
379
  bool allocating;
425
380
  try
426
381
  {
@@ -431,6 +386,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
431
386
  {
432
387
  allocating = true;
433
388
  }
389
+
434
390
  DisplayCount("DxMessaging (GameObject) - No-Copy", count, timeout, allocating);
435
391
  return;
436
392
 
@@ -440,7 +396,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
440
396
  }
441
397
  }
442
398
 
443
- private static void NoCopyComponent(
399
+ private void NoCopyComponent(
444
400
  Stopwatch timer,
445
401
  TimeSpan timeout,
446
402
  EmptyMessageAwareComponent component,
@@ -474,6 +430,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
474
430
  {
475
431
  allocating = true;
476
432
  }
433
+
477
434
  DisplayCount("DxMessaging (Component) - No-Copy", count, timeout, allocating);
478
435
  return;
479
436
 
@@ -483,7 +440,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
483
440
  }
484
441
  }
485
442
 
486
- private static void NoCopyUntargeted(
443
+ private void NoCopyUntargeted(
487
444
  Stopwatch timer,
488
445
  TimeSpan timeout,
489
446
  EmptyMessageAwareComponent component,
@@ -516,6 +473,7 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
516
473
  {
517
474
  allocating = true;
518
475
  }
476
+
519
477
  DisplayCount("DxMessaging (Untargeted) - No-Copy", count, timeout, allocating);
520
478
  return;
521
479
 
@@ -524,315 +482,5 @@ namespace DxMessaging.Tests.Runtime.Benchmarks
524
482
  ++count;
525
483
  }
526
484
  }
527
-
528
- private static void UpdateReadmeWithBenchmarks()
529
- {
530
- if (BenchmarkResults.Count == 0)
531
- {
532
- return;
533
- }
534
-
535
- if (IsRunningInContinuousIntegration())
536
- {
537
- Debug.Log("Skipping README update because the benchmarks are running in CI.");
538
- return;
539
- }
540
-
541
- string operatingSystemSection = GetOperatingSystemSection();
542
- if (string.IsNullOrEmpty(operatingSystemSection))
543
- {
544
- Debug.LogWarning(
545
- "Skipping README update because the operating system could not be determined."
546
- );
547
- return;
548
- }
549
-
550
- // Prefer writing to Docs/Performance.md; fall back to README.md if not found.
551
- string performancePath = FindPerformanceDocPath();
552
- string readmePath = FindReadmePath();
553
- string targetPath = !string.IsNullOrEmpty(performancePath)
554
- ? performancePath
555
- : readmePath;
556
- if (string.IsNullOrEmpty(targetPath))
557
- {
558
- Debug.LogWarning(
559
- "Skipping benchmarks update because neither Docs/Performance.md nor README.md could be located."
560
- );
561
- return;
562
- }
563
-
564
- try
565
- {
566
- string table = BuildBenchmarkTable();
567
- string originalContent = File.ReadAllText(targetPath);
568
- string updatedContent = ReplaceOperatingSystemSection(
569
- originalContent,
570
- operatingSystemSection,
571
- table
572
- );
573
-
574
- if (string.Equals(originalContent, updatedContent, StringComparison.Ordinal))
575
- {
576
- Debug.Log(
577
- $"Benchmark section for {operatingSystemSection} is already up to date in {targetPath}."
578
- );
579
- return;
580
- }
581
-
582
- File.WriteAllText(targetPath, updatedContent, new UTF8Encoding(false));
583
- Debug.Log($"Updated benchmarks for {operatingSystemSection} in {targetPath}.");
584
- }
585
- catch (Exception exception)
586
- {
587
- Debug.LogWarning($"Failed to update benchmark documentation: {exception}");
588
- }
589
- }
590
-
591
- private static string BuildBenchmarkTable()
592
- {
593
- StringBuilder builder = new();
594
- builder.AppendLine("| Message Tech | Operations / Second | Allocations? |");
595
- builder.AppendLine("| ------------ | ------------------- | ------------ |");
596
-
597
- foreach (BenchmarkResult result in BenchmarkResults)
598
- {
599
- builder
600
- .Append("| ")
601
- .Append(result.MessageTech)
602
- .Append(" | ")
603
- .Append(result.OperationsPerSecond.ToString("N0", CultureInfo.InvariantCulture))
604
- .Append(" | ")
605
- .Append(result.Allocating ? "Yes" : "No")
606
- .AppendLine(" |");
607
- }
608
-
609
- return builder.ToString().TrimEnd('\r', '\n');
610
- }
611
-
612
- private static string ReplaceOperatingSystemSection(
613
- string content,
614
- string sectionName,
615
- string tableContent
616
- )
617
- {
618
- string replacement = $"## {sectionName}\n\n{tableContent}\n";
619
- string pattern =
620
- $@"## {Regex.Escape(sectionName)}\r?\n(?:\r?\n)*[\s\S]*?(?=\r?\n## |\r?\n# |\Z)";
621
- Regex regex = new(pattern, RegexOptions.CultureInvariant);
622
- string updated = regex.Replace(content, replacement, 1);
623
-
624
- if (string.Equals(content, updated, StringComparison.Ordinal))
625
- {
626
- string prefix = content.EndsWith("\n", StringComparison.Ordinal)
627
- ? string.Empty
628
- : "\n";
629
- updated = $"{content}{prefix}\n{replacement}";
630
- }
631
-
632
- if (!updated.EndsWith("\n", StringComparison.Ordinal))
633
- {
634
- updated += "\n";
635
- }
636
-
637
- return updated;
638
- }
639
-
640
- private static string GetOperatingSystemSection()
641
- {
642
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
643
- {
644
- return "Windows";
645
- }
646
-
647
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
648
- {
649
- return "macOS";
650
- }
651
-
652
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
653
- {
654
- return "Linux";
655
- }
656
-
657
- return null;
658
- }
659
-
660
- private static bool IsRunningInContinuousIntegration()
661
- {
662
- if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_ACTIONS")))
663
- {
664
- return true;
665
- }
666
-
667
- if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CI")))
668
- {
669
- return true;
670
- }
671
-
672
- if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("JENKINS_URL")))
673
- {
674
- return true;
675
- }
676
-
677
- if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITLAB_CI")))
678
- {
679
- return true;
680
- }
681
-
682
- return false;
683
- }
684
-
685
- private static string FindReadmePath()
686
- {
687
- string current = Directory.GetCurrentDirectory();
688
- while (!string.IsNullOrEmpty(current))
689
- {
690
- string candidate = Path.Combine(current, "README.md");
691
- if (File.Exists(candidate))
692
- {
693
- return candidate;
694
- }
695
-
696
- string packageCandidate = Path.Combine(
697
- current,
698
- "Packages",
699
- "com.wallstop-studios.dxmessaging",
700
- "README.md"
701
- );
702
- if (File.Exists(packageCandidate))
703
- {
704
- return packageCandidate;
705
- }
706
-
707
- DirectoryInfo parent = Directory.GetParent(current);
708
- current = parent?.FullName;
709
- }
710
-
711
- string assemblyLocation = typeof(PerformanceTests).Assembly.Location;
712
- if (string.IsNullOrEmpty(assemblyLocation))
713
- {
714
- return null;
715
- }
716
-
717
- string assemblyDirectoryPath = Path.GetDirectoryName(assemblyLocation);
718
- if (string.IsNullOrEmpty(assemblyDirectoryPath))
719
- {
720
- return null;
721
- }
722
-
723
- DirectoryInfo directory = new(assemblyDirectoryPath);
724
- while (directory != null)
725
- {
726
- string candidate = Path.Combine(directory.FullName, "README.md");
727
- if (File.Exists(candidate))
728
- {
729
- return candidate;
730
- }
731
-
732
- string packageCandidate = Path.Combine(
733
- directory.FullName,
734
- "Packages",
735
- "com.wallstop-studios.dxmessaging",
736
- "README.md"
737
- );
738
- if (File.Exists(packageCandidate))
739
- {
740
- return packageCandidate;
741
- }
742
-
743
- directory = directory.Parent;
744
- }
745
-
746
- return null;
747
- }
748
-
749
- private static string FindPerformanceDocPath()
750
- {
751
- // Try to locate an existing Docs/Performance.md near the working directory
752
- string current = Directory.GetCurrentDirectory();
753
- while (!string.IsNullOrEmpty(current))
754
- {
755
- string candidate = Path.Combine(current, "Docs", "Performance.md");
756
- if (File.Exists(candidate))
757
- {
758
- return candidate;
759
- }
760
-
761
- string packageCandidate = Path.Combine(
762
- current,
763
- "Packages",
764
- "com.wallstop-studios.dxmessaging",
765
- "Docs",
766
- "Performance.md"
767
- );
768
- if (File.Exists(packageCandidate))
769
- {
770
- return packageCandidate;
771
- }
772
-
773
- DirectoryInfo parent = Directory.GetParent(current);
774
- current = parent?.FullName;
775
- }
776
-
777
- // If not found, try to create it next to the discovered README.md
778
- string readmePath = FindReadmePath();
779
- if (!string.IsNullOrEmpty(readmePath))
780
- {
781
- string baseDir = Path.GetDirectoryName(readmePath);
782
- if (!string.IsNullOrEmpty(baseDir))
783
- {
784
- string docsDir = Path.Combine(baseDir, "Docs");
785
- string perfPath = Path.Combine(docsDir, "Performance.md");
786
-
787
- try
788
- {
789
- if (!Directory.Exists(docsDir))
790
- {
791
- Directory.CreateDirectory(docsDir);
792
- }
793
-
794
- if (!File.Exists(perfPath))
795
- {
796
- const string header =
797
- "# Performance Benchmarks\n\n"
798
- + "This page is auto-updated by the Unity PlayMode benchmark tests.\n\n"
799
- + "Sections below are OS-specific. Run tests locally to refresh.\n\n";
800
- File.WriteAllText(perfPath, header, new UTF8Encoding(false));
801
- }
802
-
803
- return perfPath;
804
- }
805
- catch
806
- {
807
- // Fall back silently; caller will try README.md
808
- }
809
- }
810
- }
811
-
812
- // Last resort: try to create under the current working directory
813
- try
814
- {
815
- string cwd = Directory.GetCurrentDirectory();
816
- string docsDir = Path.Combine(cwd, "Docs");
817
- string perfPath = Path.Combine(docsDir, "Performance.md");
818
- if (!Directory.Exists(docsDir))
819
- {
820
- Directory.CreateDirectory(docsDir);
821
- }
822
- if (!File.Exists(perfPath))
823
- {
824
- const string header =
825
- "# Performance Benchmarks\n\n"
826
- + "This page is auto-updated by the Unity PlayMode benchmark tests.\n\n"
827
- + "Sections below are OS-specific. Run tests locally to refresh.\n\n";
828
- File.WriteAllText(perfPath, header, new UTF8Encoding(false));
829
- }
830
- return perfPath;
831
- }
832
- catch
833
- {
834
- return null;
835
- }
836
- }
837
485
  }
838
486
  }