ba62022e88
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
112 lines
3.8 KiB
C#
112 lines
3.8 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
|
|
|
|
/// <summary>#276 — counters are thread-safe and surface through <c>DriverHealth.Diagnostics</c>.</summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class OpcUaClientDiagnosticsTests
|
|
{
|
|
[Fact]
|
|
public void Fresh_snapshot_is_all_zero_with_the_full_key_set()
|
|
{
|
|
var snap = new OpcUaClientDiagnostics().Snapshot();
|
|
|
|
snap.Keys.ShouldBe(
|
|
[
|
|
"PublishResponseCount", "NotificationCount", "DataChangeCount", "BadStatusValueCount",
|
|
"PublishErrorCount", "KeepAliveMissCount", "ReconnectAttemptCount", "ReconnectSuccessCount",
|
|
"ReconnectExhaustedCount", "LastReconnectUtcTicks", "ReadCallCount", "ReadFailureCount",
|
|
"WriteCallCount", "WriteFailureCount", "ChunkedRequestCount",
|
|
], ignoreOrder: true);
|
|
snap.Values.ShouldAllBe(v => v == 0);
|
|
}
|
|
|
|
[Fact]
|
|
public void Each_recorder_moves_exactly_its_own_counter()
|
|
{
|
|
var d = new OpcUaClientDiagnostics();
|
|
d.RecordPublishResponse();
|
|
d.RecordNotifications(3);
|
|
d.RecordDataChange(bad: false);
|
|
d.RecordDataChange(bad: true);
|
|
d.RecordPublishError();
|
|
d.RecordKeepAliveMiss();
|
|
d.RecordReconnectAttempt();
|
|
d.RecordReconnectSucceeded();
|
|
d.RecordReconnectExhausted();
|
|
d.RecordRead(failed: true);
|
|
d.RecordWrite(failed: false);
|
|
d.RecordChunkedRequest();
|
|
|
|
var s = d.Snapshot();
|
|
s["PublishResponseCount"].ShouldBe(1);
|
|
s["NotificationCount"].ShouldBe(3);
|
|
s["DataChangeCount"].ShouldBe(2);
|
|
s["BadStatusValueCount"].ShouldBe(1);
|
|
s["PublishErrorCount"].ShouldBe(1);
|
|
s["KeepAliveMissCount"].ShouldBe(1);
|
|
s["ReconnectAttemptCount"].ShouldBe(1);
|
|
s["ReconnectSuccessCount"].ShouldBe(1);
|
|
s["ReconnectExhaustedCount"].ShouldBe(1);
|
|
s["LastReconnectUtcTicks"].ShouldBeGreaterThan(0);
|
|
s["ReadCallCount"].ShouldBe(1);
|
|
s["ReadFailureCount"].ShouldBe(1);
|
|
s["WriteCallCount"].ShouldBe(1);
|
|
s["WriteFailureCount"].ShouldBe(0);
|
|
s["ChunkedRequestCount"].ShouldBe(1);
|
|
}
|
|
|
|
[Fact]
|
|
public void Concurrent_increments_do_not_lose_updates()
|
|
{
|
|
var d = new OpcUaClientDiagnostics();
|
|
const int threads = 8, each = 5_000;
|
|
|
|
Parallel.For(0, threads, _ =>
|
|
{
|
|
for (var i = 0; i < each; i++) { d.RecordDataChange(bad: i % 2 == 0); d.RecordNotifications(2); }
|
|
});
|
|
|
|
var s = d.Snapshot();
|
|
s["DataChangeCount"].ShouldBe(threads * each);
|
|
s["BadStatusValueCount"].ShouldBe(threads * each / 2);
|
|
s["NotificationCount"].ShouldBe(threads * each * 2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Snapshot_is_a_point_in_time_copy()
|
|
{
|
|
var d = new OpcUaClientDiagnostics();
|
|
var before = d.Snapshot();
|
|
d.RecordKeepAliveMiss();
|
|
|
|
before["KeepAliveMissCount"].ShouldBe(0);
|
|
d.Snapshot()["KeepAliveMissCount"].ShouldBe(1);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetHealth_carries_the_diagnostics_and_keeps_state_fields()
|
|
{
|
|
using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-diag");
|
|
drv.DiagnosticsForTest.RecordKeepAliveMiss();
|
|
drv.DiagnosticsForTest.RecordKeepAliveMiss();
|
|
|
|
var h = drv.GetHealth();
|
|
|
|
h.State.ShouldBe(DriverState.Unknown);
|
|
h.Diagnostics.ShouldNotBeNull();
|
|
h.Diagnostics!["KeepAliveMissCount"].ShouldBe(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void DriverHealth_without_diagnostics_stays_back_compatible()
|
|
{
|
|
var h = new DriverHealth(DriverState.Healthy, null, null);
|
|
|
|
h.Diagnostics.ShouldBeNull();
|
|
h.ShouldBe(new DriverHealth(DriverState.Healthy, null, null));
|
|
}
|
|
}
|