Files
lmxopcua/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipReinitializeReleasesStateTests.cs
T
Joseph Doherty 461133235a fix(drivers): InitializeAsync releases previous device/session state before overwrite (#630)
FOCAS, AbLegacy, AbCip, TwinCAT, S7 and OpcUaClient orphaned the previous
probe/recycle/fixed-tree loops and clients when re-initialised on a live driver.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:28:55 -04:00

55 lines
2.1 KiB
C#

using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests;
/// <summary>
/// #630 (same pattern) — <c>InitializeAsync</c> on a live driver overwrote <c>_devices[host]</c> and
/// orphaned the previous state's probe loop + handles. It now releases them first.
/// </summary>
[Trait("Category", "Unit")]
public sealed class AbCipReinitializeReleasesStateTests
{
/// <summary>Initialize twice: the first state's probe loop has stopped and its probe handle is disposed.</summary>
[Fact]
public async Task SecondInitialize_StopsFirstStatesLoop_AndDisposesItsHandles()
{
const string host = "ab://10.0.0.5/1,0";
var factory = new FakeAbCipTagFactory();
var drv = new AbCipDriver(new AbCipDriverOptions
{
Devices = [new AbCipDeviceOptions(host)],
Probe = new AbCipProbeOptions
{
Enabled = true,
Interval = TimeSpan.FromMilliseconds(20),
Timeout = TimeSpan.FromMilliseconds(200),
ProbeTagPath = "@raw_cpu_type",
},
}, "drv-reinit", factory);
await drv.InitializeAsync("{}", CancellationToken.None);
var first = drv.GetDeviceState(host)!;
// Positive control: the first probe loop is running and has initialised its runtime.
var deadline = DateTime.UtcNow.AddSeconds(5);
while (!(factory.Tags.TryGetValue("@raw_cpu_type", out var t) && t.InitializeCount > 0))
{
if (DateTime.UtcNow > deadline) throw new TimeoutException("probe never started");
await Task.Delay(10);
}
var firstTag = factory.Tags["@raw_cpu_type"];
var firstLoop = first.ProbeTask;
firstLoop.ShouldNotBeNull();
firstLoop!.IsCompleted.ShouldBeFalse();
firstTag.Disposed.ShouldBeFalse();
await drv.InitializeAsync("{}", CancellationToken.None);
firstLoop.IsCompleted.ShouldBeTrue(); // joined
firstTag.Disposed.ShouldBeTrue();
drv.GetDeviceState(host).ShouldNotBeSameAs(first);
await drv.ShutdownAsync(CancellationToken.None);
}
}