test(s7): assert the stale handle is disposed, not just replaced, in the cancellation-reopen pins
This commit is contained in:
@@ -473,12 +473,13 @@ public sealed class S7SpanReadParityTests
|
||||
|
||||
await Should.ThrowAsync<OperationCanceledException>(async () => await reading);
|
||||
|
||||
// The reopen is the observable half of the contract: the desynced handle was discarded, so
|
||||
// the next read builds a NEW connection rather than reusing it.
|
||||
// The reopen is the observable half of the contract: the desynced handle was DISPOSED — not
|
||||
// merely leaked behind a second connection — and the next read builds a new one.
|
||||
factory.HangReadBytes = false;
|
||||
var recovered = await driver.ReadAsync(refs, ct);
|
||||
recovered.ShouldAllBe(s => s.StatusCode == 0u);
|
||||
factory.CreatedPlcCount.ShouldBe(2);
|
||||
factory.Plcs[0].Disposed.ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -507,6 +508,7 @@ public sealed class S7SpanReadParityTests
|
||||
var recovered = await driver.ReadAsync(refs, ct);
|
||||
recovered.ShouldAllBe(s => s.StatusCode == 0u);
|
||||
factory.CreatedPlcCount.ShouldBe(2);
|
||||
factory.Plcs[0].Disposed.ShouldBeTrue();
|
||||
}
|
||||
|
||||
// ── Configurability ──────────────────────────────────────────────────────────────────
|
||||
@@ -640,18 +642,30 @@ public sealed class S7SpanReadParityTests
|
||||
/// <summary>How many connections this factory has been asked to build — the reopen observable.</summary>
|
||||
public int CreatedPlcCount;
|
||||
|
||||
/// <summary>
|
||||
/// Every connection this factory built, in build order, so a test can assert what became
|
||||
/// of the STALE one — a driver that leaked the old handle and merely opened a second
|
||||
/// would satisfy <see cref="CreatedPlcCount"/> alone.
|
||||
/// </summary>
|
||||
public List<ImageS7Plc> Plcs { get; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IS7Plc Create(S7CpuType cpuType, string host, int port, short rack, short slot, TimeSpan timeout)
|
||||
{
|
||||
Interlocked.Increment(ref CreatedPlcCount);
|
||||
return new ImageS7Plc(this);
|
||||
var plc = new ImageS7Plc(this);
|
||||
lock (Plcs) Plcs.Add(plc);
|
||||
return plc;
|
||||
}
|
||||
|
||||
private sealed class ImageS7Plc(ImageS7PlcFactory owner) : IS7Plc
|
||||
public sealed class ImageS7Plc(ImageS7PlcFactory owner) : IS7Plc
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
/// <summary>True once the driver has disposed this handle — the discard half of a reopen.</summary>
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task OpenAsync(CancellationToken ct) { IsConnected = true; return Task.CompletedTask; }
|
||||
|
||||
@@ -771,7 +785,11 @@ public sealed class S7SpanReadParityTests
|
||||
public Task ReadStatusAsync(CancellationToken ct) => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => IsConnected = false;
|
||||
public void Dispose()
|
||||
{
|
||||
IsConnected = false;
|
||||
Disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user