test(opcua): #870 review — pin the 1000th-newest duplicate-window boundary; serialise the ring-queue switch users

Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk
This commit is contained in:
Joseph Doherty
2026-11-11 15:20:31 -05:00
parent bcde93056a
commit 36583ee6f4
3 changed files with 34 additions and 0 deletions
@@ -35,6 +35,9 @@ namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
/// compiles against the pre-#855 tree for a before/after comparison.</para>
/// </remarks>
[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";
@@ -9,6 +9,9 @@ namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
/// item's queue is a <see cref="RingEventMonitoredItemQueue"/>; the SDK's <see cref="EventQueueHandler"/> always asks
/// the server's factory), and data-change queues are still the SDK's own.
/// </summary>
// 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<DataChangeMonitoredItemQueue>();
}
}
/// <summary>Serialises the tests that read or flip <c>OtOpcUaSdkServer.RingEventQueues</c>.</summary>
[CollectionDefinition(Name)]
public sealed class RingEventQueueSwitchCollection
{
/// <summary>The collection name.</summary>
public const string Name = "RingEventQueueSwitch";
}
@@ -143,6 +143,26 @@ public sealed class RingEventMonitoredItemQueueParityTests
Both(q => q.IsEventContainedInQueue(Targets[1]), sdk, ring, "absent").ShouldBeFalse();
}
/// <summary>Upper boundary of the window: the entry exactly 1000th from newest is still found; 1001st is not.</summary>
[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();
}
/// <summary>Random interleavings of every queue operation, at small sizes (wrap-around, grow, shrink, size 0).</summary>
[Theory]
[MemberData(nameof(Seeds))]