// 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 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(); 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(); 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(); 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."); } } }