docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)

Comment/XML-doc changes only:
- add summaries to ~10k test methods, fixtures, fakes and helpers
  (+ <param> on theories, <returns> on async Task methods,
  <inheritdoc/> on interface implementations the checker can resolve)
- strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N,
  #NN, Component-0NN) from comments, keeping the explanatory text;
  reword false positives in comments only
- real docs on fakes implementing BCL/library interfaces
  (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime)
- reword the last 5 src/ false positives (CWE-1236, failures-1,
  year-0001, M365)

Only non-comment edits: four one-line nested classes split across lines
so members can carry docs. Build 0 errors / 0 warnings.
This commit is contained in:
Joseph Doherty
2026-10-07 15:05:49 -04:00
parent 0ffd4bdbea
commit 513fa1aa16
998 changed files with 17896 additions and 2395 deletions
@@ -1,20 +1,20 @@
namespace ZB.MOM.WW.ScadaBridge.LoadHarness;
/// <summary>
/// Scale and duration knobs for a load-harness run. Defaults are the Phase-8 WP-4
/// Scale and duration knobs for a load-harness run. Defaults are the
/// target scale (10 sites x 500 instances x 75 tags = 375,000 subscriptions).
/// Every value is overridable from the command line so the same binary serves both
/// the full-scale protocol run and the scaled-down CI smoke.
/// </summary>
public sealed record HarnessConfig
{
/// <summary>Number of simulated sites (WP-4 acceptance criterion <c>[2.5-1]</c>).</summary>
/// <summary>Number of simulated sites (acceptance criterion <c>[2.5-1]</c>).</summary>
public int Sites { get; init; } = 10;
/// <summary>Instance Actors per site (WP-4 <c>[2.5-2]</c>).</summary>
/// <summary>Instance Actors per site (acceptance criterion <c>[2.5-2]</c>).</summary>
public int InstancesPerSite { get; init; } = 500;
/// <summary>Data-sourced attributes ("live tags") per instance (WP-4 <c>[2.5-3]</c>).</summary>
/// <summary>Data-sourced attributes ("live tags") per instance (acceptance criterion <c>[2.5-3]</c>).</summary>
public int TagsPerInstance { get; init; } = 75;
/// <summary>
@@ -27,8 +27,8 @@ namespace ZB.MOM.WW.ScadaBridge.LoadHarness;
/// <param name="ResourceSamples">
/// Every raw resource sample. Retained because the summary alone cannot distinguish a
/// slow leak from a heap that simply sawtooths — especially when the run records zero
/// gen-2 collections, where a positive least-squares slope may be nothing more than
/// gen-2 garbage that was never collected.
/// gen 2 collections, where a positive least-squares slope may be nothing more than
/// gen 2 garbage that was never collected.
/// </param>
/// <param name="HealthReportLatency">Health report collect+ingest latency.</param>
/// <param name="HealthReportsDelivered">Health reports ingested by the central aggregator.</param>
@@ -112,7 +112,7 @@ public sealed record EnvironmentInfo(
/// <summary>
/// Orchestrates a full run: build sites, ramp instances, attach subscribers, drive
/// tag updates for the sustained window while sampling resources and probing
/// observability, then run the two register-row-50 scenarios on a dedicated site.
/// observability, then run the two deferred-register scenarios on a dedicated site.
/// </summary>
public static class HarnessRun
{
@@ -251,7 +251,7 @@ public static class HarnessRun
var probeReceived = sites.Sum(s => s.Probes.Sum(p => p.ReceivedEvents)) - receivedAtWindowStart;
var probeDropped = sites.Sum(s => s.Probes.Sum(p => p.DroppedEvents)) - droppedAtWindowStart;
// Wire-path window deltas, captured BEFORE the row-50 scenarios below so
// Wire-path window deltas, captured BEFORE the deferred-register scenarios below so
// their publishing cannot contaminate the wire figures.
WireStreamResult? wireResult = null;
if (batchAggregator is not null)
@@ -61,7 +61,7 @@ public sealed class BatchSizeAggregator : IDisposable
return copy;
}
/// <inheritdoc />
/// <summary>Disposes the underlying meter listener.</summary>
public void Dispose() => _listener.Dispose();
}
@@ -10,7 +10,7 @@ namespace ZB.MOM.WW.ScadaBridge.LoadHarness.Metrics;
/// <param name="ManagedHeapMb"><see cref="GC.GetTotalMemory(bool)"/> without forcing a collection.</param>
/// <param name="CpuPercent">Mean CPU utilization since the previous sample, as a percentage of ONE core.</param>
/// <param name="ThreadCount">OS threads in the process.</param>
/// <param name="Gen2Collections">Cumulative gen-2 collections.</param>
/// <param name="Gen2Collections">Cumulative generation 2 collections.</param>
public sealed record ResourceSample(
double ElapsedSeconds,
double WorkingSetMb,
@@ -150,7 +150,8 @@ public sealed class ResourceSampler : IAsyncDisposable
private int _disposed;
/// <inheritdoc />
/// <summary>Stops the sampling loop and releases the sampler's resources.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async ValueTask DisposeAsync()
{
if (Interlocked.Exchange(ref _disposed, 1) != 0)
@@ -184,7 +185,7 @@ public sealed class ResourceSampler : IAsyncDisposable
/// <param name="MeanCpuPercentOfOneCore">Mean CPU as a percentage of one core (1400% = 14 cores saturated).</param>
/// <param name="PeakCpuPercentOfOneCore">Peak single-sample CPU as a percentage of one core.</param>
/// <param name="MeanThreadCount">Mean OS thread count.</param>
/// <param name="Gen2Collections">Gen-2 collections during the window.</param>
/// <param name="Gen2Collections">Generation 2 collections during the window.</param>
public sealed record ResourceWindowSummary(
int SampleCount,
double DurationSeconds,
@@ -19,7 +19,7 @@ namespace ZB.MOM.WW.ScadaBridge.LoadHarness.Probes;
/// <para>
/// This is the hop <see cref="StreamSubscriberProbe"/> deliberately substitutes with a
/// controllable in-process reader (which the slow-subscriber scenario needs), and
/// therefore the hop the original WP-4 runs never exercised: the
/// therefore the hop the original runs never exercised: the
/// <c>SiteStreamEventBatcher</c> pump (R2), protobuf serialization,
/// <c>IServerStreamWriter.WriteAsync</c>, and HTTP/2 flow control. The server is built
/// through its production DI constructor with default <see cref="CommunicationOptions"/>,
@@ -84,7 +84,8 @@ public sealed class SiteWireStreamHost : IAsyncDisposable
return new SiteWireStreamHost(app, server, address);
}
/// <inheritdoc />
/// <summary>Cancels active streams and stops and disposes the underlying listener.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async ValueTask DisposeAsync()
{
// Mirrors the production shutdown ordering: cancel active streams first so
@@ -1,9 +1,9 @@
using System.Text.Json;
using ZB.MOM.WW.ScadaBridge.LoadHarness;
// Target-scale load harness (Phase-8 WP-4 / deferred-work register #25 + row 50).
// Target-scale load harness.
//
// Full-scale protocol run (the WP-4 numbers):
// Full-scale protocol run (the target-scale numbers):
// dotnet run -c Release --project tests/ZB.MOM.WW.ScadaBridge.LoadHarness -- \
// --sustain-minutes 20 --results loadharness-results.json
//
@@ -7,7 +7,7 @@ using ZB.MOM.WW.ScadaBridge.LoadHarness.Metrics;
namespace ZB.MOM.WW.ScadaBridge.LoadHarness.Scenarios;
/// <summary>
/// The two "does observability still work at scale?" probes WP-4's test protocol
/// The two "does observability still work at scale?" probes the target-scale test protocol
/// names — health report delivery timing and debug view latency — run continuously
/// alongside the sustained load rather than after it, so both are measured against a
/// site that is actually busy.
@@ -137,7 +137,8 @@ public sealed class ObservabilityProbes : IAsyncDisposable
private int _disposed;
/// <inheritdoc />
/// <summary>Stops the probes and releases their resources.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async ValueTask DisposeAsync()
{
// Idempotent: the orchestrator stops the probes early (so the register row 50
@@ -49,7 +49,7 @@ public sealed record DrainProgressSample(double ElapsedSeconds, long Delivered,
/// Phase 1 buffers <paramref name="messageCount"/> messages with
/// <c>attemptImmediateDelivery: false</c>, spread across many origin instance names
/// and issued from many concurrent tasks — the "concurrent buffering from multiple
/// instances" WP-4 asks about (<c>[xc-7]</c>). Phase 2 registers a delivery handler
/// instances" case the load test targets. Phase 2 registers a delivery handler
/// that always succeeds and drives sweeps to completion, timing the drain.
/// </para>
/// <para>
@@ -44,10 +44,11 @@ public sealed class SimulatedDataConnection : IDataConnection, IBatchSubscribabl
/// <summary>Key under which the connection name travels in the connection details.</summary>
public const string ConnectionNameKey = "connectionName";
// Gets the current health of the simulated connection.
/// <inheritdoc />
public ConnectionHealth Status { get; private set; } = ConnectionHealth.Disconnected;
/// <inheritdoc />
/// <summary>Raised when the simulated connection is disconnected.</summary>
public event Action? Disconnected;
/// <summary>
@@ -60,6 +61,7 @@ public sealed class SimulatedDataConnection : IDataConnection, IBatchSubscribabl
/// <summary>Number of tag paths this connection has accepted subscriptions for.</summary>
public int SubscribedTagCount => _subscriptionCounter;
// Marks the connection as connected and records the connection name from the details, if present.
/// <inheritdoc />
public Task ConnectAsync(IDictionary<string, string> connectionDetails, CancellationToken cancellationToken = default)
{
@@ -70,6 +72,7 @@ public sealed class SimulatedDataConnection : IDataConnection, IBatchSubscribabl
return Task.CompletedTask;
}
// Marks the connection as disconnected and raises the Disconnected event.
/// <inheritdoc />
public Task DisconnectAsync(CancellationToken cancellationToken = default)
{
@@ -78,6 +81,7 @@ public sealed class SimulatedDataConnection : IDataConnection, IBatchSubscribabl
return Task.CompletedTask;
}
// Captures the callback and returns a new subscription id.
/// <inheritdoc />
public Task<string> SubscribeAsync(string tagPath, SubscriptionCallback callback, CancellationToken cancellationToken = default)
{
@@ -85,6 +89,7 @@ public sealed class SimulatedDataConnection : IDataConnection, IBatchSubscribabl
return Task.FromResult($"sub-{Interlocked.Increment(ref _subscriptionCounter)}");
}
// Captures the callback and accepts every tag path in the batch.
/// <inheritdoc />
public Task<IReadOnlyList<TagSubscribeResult>> SubscribeBatchAsync(
IReadOnlyList<string> tagPaths,
@@ -102,18 +107,22 @@ public sealed class SimulatedDataConnection : IDataConnection, IBatchSubscribabl
return Task.FromResult<IReadOnlyList<TagSubscribeResult>>(results);
}
// Does nothing; the simulated connection keeps no per-subscription state.
/// <inheritdoc />
public Task UnsubscribeAsync(string subscriptionId, CancellationToken cancellationToken = default)
=> Task.CompletedTask;
// Does nothing; the simulated connection keeps no per-subscription state.
/// <inheritdoc />
public Task UnsubscribeBatchAsync(IReadOnlyList<string> subscriptionIds, CancellationToken cancellationToken = default)
=> Task.CompletedTask;
// Returns a good-quality zero value for any tag.
/// <inheritdoc />
public Task<ReadResult> ReadAsync(string tagPath, CancellationToken cancellationToken = default)
=> Task.FromResult(new ReadResult(true, new TagValue(0d, QualityCode.Good, DateTimeOffset.UtcNow), null));
// Returns a good-quality zero value for every requested tag.
/// <inheritdoc />
public Task<IReadOnlyDictionary<string, ReadResult>> ReadBatchAsync(
IEnumerable<string> tagPaths, CancellationToken cancellationToken = default)
@@ -126,10 +135,12 @@ public sealed class SimulatedDataConnection : IDataConnection, IBatchSubscribabl
return Task.FromResult<IReadOnlyDictionary<string, ReadResult>>(results);
}
// Accepts the write without storing it.
/// <inheritdoc />
public Task<WriteResult> WriteAsync(string tagPath, object? value, CancellationToken cancellationToken = default)
=> Task.FromResult(new WriteResult(true, null));
// Accepts every write in the batch without storing it.
/// <inheritdoc />
public Task<IReadOnlyDictionary<string, WriteResult>> WriteBatchAsync(
IDictionary<string, object?> values, CancellationToken cancellationToken = default)
@@ -141,6 +152,7 @@ public sealed class SimulatedDataConnection : IDataConnection, IBatchSubscribabl
return Task.FromResult<IReadOnlyDictionary<string, WriteResult>>(results);
}
// Accepts the batch and reports that the awaited response matched immediately.
/// <inheritdoc />
public Task<bool> WriteBatchAndWaitAsync(
IDictionary<string, object?> values, string flagPath, object? flagValue,
@@ -148,6 +160,7 @@ public sealed class SimulatedDataConnection : IDataConnection, IBatchSubscribabl
CancellationToken cancellationToken = default)
=> Task.FromResult(true);
/// <inheritdoc />
/// <summary>Releases the connection; the simulated connection holds no resources.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}
@@ -32,7 +32,7 @@ namespace ZB.MOM.WW.ScadaBridge.LoadHarness;
/// The sites are separate, non-clustered ActorSystems rather than 10 real two-node
/// Akka clusters. Cluster membership, singleton placement and failover timing are
/// already measured on a real two-node rig by
/// <c>PerformanceTests/Failover/FailoverTimingTests.cs</c>; what WP-4 asks about is
/// <c>PerformanceTests/Failover/FailoverTimingTests.cs</c>; what matters here is
/// the load-bearing hierarchy under each singleton, which is what this builds.
/// </para>
/// </summary>
@@ -50,7 +50,7 @@ public sealed class SiteRuntimeFixture : IAsyncDisposable
private SiteStreamGrpcClient? _wireClient;
private CancellationTokenSource? _wireCts;
/// <summary>Site identifier, e.g. <c>site-01</c>.</summary>
/// <summary>Site identifier, for example a zero-padded numbered name for the first site.</summary>
public string SiteId { get; }
/// <summary>This site's actor system.</summary>
@@ -393,7 +393,8 @@ public sealed class SiteRuntimeFixture : IAsyncDisposable
/// <summary>Total tag paths registered across this site's connections.</summary>
public int TotalTagPaths => TagPathsByConnection.Sum(list => list.Count);
/// <inheritdoc />
/// <summary>Stops the wire path, the actor system and the data directory in production teardown order.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async ValueTask DisposeAsync()
{
// Wire path first: cancel the client streams, wait for their loops to exit,
@@ -13,7 +13,7 @@ namespace ZB.MOM.WW.ScadaBridge.LoadHarness;
/// <c>DataConnectionActor</c> has exactly one adapter feeding it, and every update
/// for that connection funnels through that actor's single mailbox. Sharding the
/// emitters differently would hide the per-connection-actor serialization point,
/// which is one of the things WP-4 needs to characterize.
/// which is one of the things the load test needs to characterize.
/// </para>
/// <para>
/// The emitter walks the connection's tag list in slices sized so that one full pass
@@ -154,7 +154,8 @@ public sealed class TagUpdateDriver : IAsyncDisposable
private int _disposed;
/// <inheritdoc />
/// <summary>Cancels the emitter tasks, waits for them to finish and releases the cancellation source.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async ValueTask DisposeAsync()
{
if (Interlocked.Exchange(ref _disposed, 1) != 0)