181 lines
4.5 KiB
C#
181 lines
4.5 KiB
C#
|
|
// 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.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|