process/src/process.tests/CancellationTests.cs

181 lines
4.5 KiB
C#
Raw Normal View History

2026-01-20 22:41:16 +01:00
// Copyright (c) The Geekeey Authors
// SPDX-License-Identifier: EUPL-1.2
using System.Text;
using Geekeey.Process.Buffered;
namespace Geekeey.Process.Tests;
internal sealed class CancellationTests
{
private static Action<string> NotifyOnStart(out TaskCompletionSource tcs)
{
// run the continuation async on the thread pool to allow the io reader to complete
var source = tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
return line =>
{
if (line.Contains("Sleeping for", StringComparison.OrdinalIgnoreCase))
{
source.TrySetResult();
}
};
}
[Test]
public async Task I_can_execute_a_command_and_cancel_it_immediately()
{
// Arrange
using var cts = new CancellationTokenSource();
var stdout = new StringBuilder();
var target = PipeTarget.Merge(
PipeTarget.ToDelegate(NotifyOnStart(out var tcs)),
PipeTarget.ToStringBuilder(stdout)
);
var cmd = new Command(Testing.Fixture.Program.FilePath)
.WithArguments(["sleep", "00:00:30"]) |
target;
// Act
var task = cmd.ExecuteAsync(cts.Token);
await tcs.Task;
await cts.CancelAsync();
// Assert
await Assert.That(async () => await task).Throws<OperationCanceledException>();
using (Assert.Multiple())
{
await Assert.That(ProcessTree.HasExited(task.ProcessId)).IsTrue();
await Assert.That(stdout.ToString()).Contains("Sleeping for");
await Assert.That(stdout.ToString()).DoesNotContain("Done.");
}
}
[Test]
public async Task I_can_execute_a_command_and_kill_it_immediately()
{
// Arrange
var stdout = new StringBuilder();
var target = PipeTarget.Merge(
PipeTarget.ToDelegate(NotifyOnStart(out var tcs)),
PipeTarget.ToStringBuilder(stdout)
);
var cmd = new Command(Testing.Fixture.Program.FilePath)
.WithArguments(["sleep", "00:00:30"]) |
target;
// Act
var task = cmd.ExecuteAsync();
await tcs.Task;
task.Kill();
// Assert
await Assert.That(async () => await task).Throws<CommandExecutionException>();
using (Assert.Multiple())
{
await Assert.That(ProcessTree.HasExited(task.ProcessId)).IsTrue();
await Assert.That(stdout.ToString()).Contains("Sleeping for");
await Assert.That(stdout.ToString()).DoesNotContain("Done.");
}
}
[Test]
public async Task I_can_execute_a_command_with_buffering_and_kill_it_immediately()
{
// Arrange
var stdout = new StringBuilder();
var target = PipeTarget.Merge(
PipeTarget.ToDelegate(NotifyOnStart(out var tcs)),
PipeTarget.ToStringBuilder(stdout)
);
var cmd = new Command(Testing.Fixture.Program.FilePath)
.WithArguments(["sleep", "00:00:30"]) |
target;
// Act
var task = cmd.ExecuteBufferedAsync();
await tcs.Task;
task.Kill();
// Assert
await Assert.That(async () => await task).Throws<CommandExecutionException>();
using (Assert.Multiple())
{
await Assert.That(ProcessTree.HasExited(task.ProcessId)).IsTrue();
await Assert.That(stdout.ToString()).Contains("Sleeping for");
await Assert.That(stdout.ToString()).DoesNotContain("Done.");
}
}
[Test]
public async Task I_can_execute_a_command_and_interrupt_it_immediately()
{
// Arrange
var stdout = new StringBuilder();
var target = PipeTarget.Merge(
PipeTarget.ToDelegate(NotifyOnStart(out var tcs)),
PipeTarget.ToStringBuilder(stdout)
);
var cmd = new Command(Testing.Fixture.Program.FilePath)
.WithArguments(["sleep", "00:00:30"]) |
target;
// Act
var task = cmd.ExecuteAsync();
await tcs.Task;
task.Interrupt();
// Assert
await Assert.That(async () => await task).ThrowsNothing();
using (Assert.Multiple())
{
await Assert.That(ProcessTree.HasExited(task.ProcessId)).IsTrue();
await Assert.That(stdout.ToString()).Contains("Sleeping for");
await Assert.That(stdout.ToString()).Contains("Done.");
}
}
[Test]
public async Task I_can_execute_a_command_with_buffering_and_interrupt_it_immediately()
{
// Arrange
var stdout = new StringBuilder();
var target = PipeTarget.Merge(
PipeTarget.ToDelegate(NotifyOnStart(out var tcs)),
PipeTarget.ToStringBuilder(stdout)
);
var cmd = new Command(Testing.Fixture.Program.FilePath)
.WithArguments(["sleep", "00:00:30"]) |
target;
// Act
var task = cmd.ExecuteBufferedAsync();
await tcs.Task;
task.Interrupt();
// Assert
await Assert.That(async () => await task).ThrowsNothing();
using (Assert.Multiple())
{
await Assert.That(ProcessTree.HasExited(task.ProcessId)).IsTrue();
await Assert.That(stdout.ToString()).Contains("Sleeping for");
await Assert.That(stdout.ToString()).Contains("Done.");
}
}
}