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))]