ffa5b2a4fa
Review 07 U7 (AbLegacy part): probe/read/write had no OperationCanceledException arm, so cancelling a slow read printed an unhandled-exception stack trace. The driver work now runs through AbLegacyCommandBase.RunCancellableAsync, which reports an operator cancellation as "Cancelled." and still propagates anything else. (The AbCip subscribe lock and SnapshotFormatter array rendering are other lanes' files.) Refs #738 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
53 lines
1.7 KiB
C#
53 lines
1.7 KiB
C#
using CliFx.Infrastructure;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli.Tests;
|
|
|
|
/// <summary>
|
|
/// Review 07 U7: the AbLegacy probe/read/write commands had no <see cref="OperationCanceledException"/> arm, so a
|
|
/// Ctrl-C printed a stack trace. They now run their driver work through
|
|
/// <see cref="AbLegacyCommandBase.RunCancellableAsync"/>.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class CancellationHandlingTests
|
|
{
|
|
[Fact]
|
|
public async Task Operator_cancellation_prints_Cancelled_instead_of_throwing()
|
|
{
|
|
using var console = new FakeInMemoryConsole();
|
|
using var cts = new CancellationTokenSource();
|
|
cts.Cancel();
|
|
|
|
await Should.NotThrowAsync(async () => await AbLegacyCommandBase.RunCancellableAsync(
|
|
console, cts.Token, () => Task.FromCanceled(cts.Token)));
|
|
|
|
console.ReadErrorString().ShouldContain("Cancelled.");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_cancellation_the_operator_did_not_request_still_propagates()
|
|
{
|
|
using var console = new FakeInMemoryConsole();
|
|
|
|
await Should.ThrowAsync<OperationCanceledException>(async () => await AbLegacyCommandBase.RunCancellableAsync(
|
|
console, CancellationToken.None, () => throw new OperationCanceledException()));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_completed_body_runs_to_completion()
|
|
{
|
|
using var console = new FakeInMemoryConsole();
|
|
var ran = false;
|
|
|
|
await AbLegacyCommandBase.RunCancellableAsync(console, CancellationToken.None, () =>
|
|
{
|
|
ran = true;
|
|
return Task.CompletedTask;
|
|
});
|
|
|
|
ran.ShouldBeTrue(); // positive control
|
|
console.ReadErrorString().ShouldBeEmpty();
|
|
}
|
|
}
|