From 36583ee6f4c9b03cbe8b9523aa6ed7937c7e3174 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 11 Nov 2026 15:20:31 -0500 Subject: [PATCH] =?UTF-8?q?test(opcua):=20#870=20review=20=E2=80=94=20pin?= =?UTF-8?q?=20the=201000th-newest=20duplicate-window=20boundary;=20seriali?= =?UTF-8?q?se=20the=20ring-queue=20switch=20users?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk --- .../FolderEventDeliveryPerfTests.cs | 3 +++ .../RingEventQueueWiringTests.cs | 11 ++++++++++ .../RingEventMonitoredItemQueueParityTests.cs | 20 +++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/FolderEventDeliveryPerfTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/FolderEventDeliveryPerfTests.cs index b101f5de6..b81ac7f0a 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/FolderEventDeliveryPerfTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/FolderEventDeliveryPerfTests.cs @@ -35,6 +35,9 @@ namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests; /// compiles against the pre-#855 tree for a before/after comparison. /// [Trait("Category", "Perf")] +// Shares a collection with the other user of OtOpcUaSdkServer.RingEventQueues (a process-wide switch the perf +// harness flips), so the wiring assertion never observes it switched off (#870 review). +[Collection(RingEventQueueSwitchCollection.Name)] public sealed class FolderEventDeliveryPerfTests { private const string OptInVariable = "OTOPCUA_EVENT_PERF"; diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/RingEventQueueWiringTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/RingEventQueueWiringTests.cs index f654cc186..2174f71e7 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/RingEventQueueWiringTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/RingEventQueueWiringTests.cs @@ -9,6 +9,9 @@ namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests; /// item's queue is a ; the SDK's always asks /// the server's factory), and data-change queues are still the SDK's own. /// +// Shares a collection with the other user of OtOpcUaSdkServer.RingEventQueues (a process-wide switch the perf +// harness flips), so the wiring assertion never observes it switched off (#870 review). +[Collection(RingEventQueueSwitchCollection.Name)] public sealed class RingEventQueueWiringTests { [Fact] @@ -23,3 +26,11 @@ public sealed class RingEventQueueWiringTests factory.CreateDataChangeQueue(false, 2).ShouldBeOfType(); } } + +/// Serialises the tests that read or flip OtOpcUaSdkServer.RingEventQueues. +[CollectionDefinition(Name)] +public sealed class RingEventQueueSwitchCollection +{ + /// The collection name. + public const string Name = "RingEventQueueSwitch"; +} diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/RingEventMonitoredItemQueueParityTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/RingEventMonitoredItemQueueParityTests.cs index b2d264ccf..677b1f573 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/RingEventMonitoredItemQueueParityTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/RingEventMonitoredItemQueueParityTests.cs @@ -143,6 +143,26 @@ public sealed class RingEventMonitoredItemQueueParityTests Both(q => q.IsEventContainedInQueue(Targets[1]), sdk, ring, "absent").ShouldBeFalse(); } + /// Upper boundary of the window: the entry exactly 1000th from newest is still found; 1001st is not. + [Fact] + public void Duplicate_check_includes_the_1000th_newest_entry() + { + var sdk = new EventMonitoredItemQueue(false, 1, Telemetry); + var ring = new RingEventMonitoredItemQueue(1); + sdk.SetQueueSize(3000, true); + ring.SetQueueSize(3000, true); + var outside = new Target(); + var boundary = new Target(); + void Add(EventFieldList e) { sdk.Enqueue(e); ring.Enqueue(e); } + Add(new EventFieldList { Handle = outside }); + Add(new EventFieldList { Handle = boundary }); + for (var i = 0; i < 999; i++) Add(new EventFieldList { Handle = Targets[0] }); + + // 1001 entries: `boundary` is the 1000th newest (inside), `outside` the 1001st. + Both(q => q.IsEventContainedInQueue(boundary), sdk, ring, "boundary").ShouldBeTrue(); + Both(q => q.IsEventContainedInQueue(outside), sdk, ring, "outside").ShouldBeFalse(); + } + /// Random interleavings of every queue operation, at small sizes (wrap-around, grow, shrink, size 0). [Theory] [MemberData(nameof(Seeds))]