f9fbd4ee0d
R07 U7: Driver.Cli.Common gains RunCancellableAsync (moved up from AbLegacy) and CreateChangeLineWriter (one lock-serialised line per change; a failing console write is logged and swallowed so it cannot fault the poll loop). AbCip probe/read/write print Cancelled. on Ctrl-C instead of a stack trace, and AbCip subscribe uses the shared writer. SnapshotFormatter renders arrays/lists as [a, b, c] instead of System.Int32[]. Refs #747 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
51 lines
1.8 KiB
C#
51 lines
1.8 KiB
C#
using CliFx.Infrastructure;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli.Commands;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli.Tests;
|
|
|
|
/// <summary>
|
|
/// Review 07 U7: the AbCip probe/read/write commands had no <see cref="OperationCanceledException"/> arm, so a
|
|
/// Ctrl-C printed a stack trace. A command cancelled by the operator now exits cleanly with <c>Cancelled.</c>.
|
|
/// The gateway is TEST-NET (never answers), so nothing is sent to a real device.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class CancellationHandlingTests
|
|
{
|
|
private const string Blackhole = "ab://192.0.2.1/1,0";
|
|
|
|
private static async Task<string> RunCancelledAsync(AbCipCommandBase command)
|
|
{
|
|
using var console = new FakeInMemoryConsole();
|
|
console.RequestCancellation();
|
|
|
|
await Should.NotThrowAsync(async () => await command.ExecuteAsync(console));
|
|
return console.ReadErrorString();
|
|
}
|
|
|
|
/// <summary>A cancelled read prints Cancelled. and does not throw.</summary>
|
|
[Fact]
|
|
public async Task A_cancelled_read_prints_Cancelled()
|
|
{
|
|
(await RunCancelledAsync(new ReadCommand { Gateway = Blackhole, TagPath = "Motor01" }))
|
|
.ShouldContain("Cancelled.");
|
|
}
|
|
|
|
/// <summary>A cancelled probe prints Cancelled. and does not throw.</summary>
|
|
[Fact]
|
|
public async Task A_cancelled_probe_prints_Cancelled()
|
|
{
|
|
(await RunCancelledAsync(new ProbeCommand { Gateway = Blackhole, TagPath = "Motor01" }))
|
|
.ShouldContain("Cancelled.");
|
|
}
|
|
|
|
/// <summary>A cancelled write prints Cancelled. and does not throw.</summary>
|
|
[Fact]
|
|
public async Task A_cancelled_write_prints_Cancelled()
|
|
{
|
|
(await RunCancelledAsync(new WriteCommand { Gateway = Blackhole, TagPath = "Motor01", Value = "1" }))
|
|
.ShouldContain("Cancelled.");
|
|
}
|
|
}
|