// 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 PipingTests { #region Stdin [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_an_async_anonymous_source() { // Arrange var source = PipeSource.Create(async (destination, cancellationToken) => await destination.WriteAsync("Hello World!"u8.ToArray(), cancellationToken)); var cmd = source | new Command(Testing.Fixture.Program.FilePath) .WithArguments("echo-stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("Hello World!"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_sync_anonymous_source() { // Arrange var source = PipeSource.Create(destination => destination.Write("Hello World!"u8.ToArray())); var cmd = source | new Command(Testing.Fixture.Program.FilePath) .WithArguments("echo-stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("Hello World!"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_stream() { // Arrange using var source = new MemoryStream("Hello World!"u8.ToArray()); var cmd = source | new Command(Testing.Fixture.Program.FilePath) .WithArguments("echo-stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("Hello World!"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_stream_with_a_custom_buffer_size() { // Arrange using var source = new MemoryStream("Hello World!"u8.ToArray()); var cmd = PipeSource.FromStream(source, 1) | new Command(Testing.Fixture.Program.FilePath) .WithArguments("echo-stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("Hello World!"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_file_with_a_custom_buffer_size() { // Arrange using var dir = TestTempDirectory.Create(); var filePath = Path.Combine(dir.Path, "input.txt"); await File.WriteAllTextAsync(filePath, "Hello World!"); var cmd = PipeSource.FromFile(filePath, 1) | new Command(Testing.Fixture.Program.FilePath) .WithArguments("echo-stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("Hello World!"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_memory() { // Arrange var data = new ReadOnlyMemory("Hello World!"u8.ToArray()); var cmd = data | new Command(Testing.Fixture.Program.FilePath) .WithArguments("echo-stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("Hello World!"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_byte_array() { // Arrange var data = "Hello World!"u8.ToArray(); var cmd = data | new Command(Testing.Fixture.Program.FilePath) .WithArguments("echo-stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("Hello World!"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_string() { // Arrange var data = "Hello World!"; var cmd = data | new Command(Testing.Fixture.Program.FilePath) .WithArguments("echo-stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("Hello World!"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_another_command() { // Arrange var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "blob", "--length", "100000"]) | new Command(Testing.Fixture.Program.FilePath) .WithArguments("length"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("100000"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_chain_of_commands() { // Arrange var cmd = "Hello world" | new Command(Testing.Fixture.Program.FilePath) .WithArguments("echo-stdin") | new Command(Testing.Fixture.Program.FilePath) .WithArguments(["echo-stdin", "--length", "5"]) | new Command(Testing.Fixture.Program.FilePath) .WithArguments("length"); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert await Assert.That(result.StandardOutput.Trim()).IsEqualTo("5"); } #endregion #region Stdout [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_an_async_anonymous_target() { // Arrange using var stream = new MemoryStream(); var target = PipeTarget.Create(async (origin, cancellationToken) => // ReSharper disable once AccessToDisposedClosure await origin.CopyToAsync(stream, cancellationToken) ); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "blob", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); // Assert await Assert.That(stream.Length).IsEqualTo(100_000); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_anonymous_target() { // Arrange using var stream = new MemoryStream(); var target = PipeTarget.Create(origin => // ReSharper disable once AccessToDisposedClosure origin.CopyTo(stream) ); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "blob", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); // Assert await Assert.That(stream.Length).IsEqualTo(100_000); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_stream() { // Arrange using var stream = new MemoryStream(); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "blob", "--length", "100000"]) | stream; // Act await cmd.ExecuteAsync(); // Assert await Assert.That(stream.Length).IsEqualTo(100_000); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_stream_with_a_custom_buffer_size() { // Arrange using var stream = new MemoryStream(); var target = PipeTarget.ToStream(stream, 1); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "blob", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); // Assert await Assert.That(stream.Length).IsEqualTo(100_000); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_file_with_a_custom_buffer_size() { // Arrange using var dir = TestTempDirectory.Create(); var filePath = Path.Combine(dir.Path, "output.bin"); var target = PipeTarget.ToFile(filePath, 1); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "blob", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); // Assert await Assert.That(new FileInfo(filePath).Length).IsEqualTo(100_000); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_string_builder() { // Arrange var buffer = new StringBuilder(); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["echo", "Hello World!"]) | buffer; // Act await cmd.ExecuteAsync(); // Assert await Assert.That(buffer.ToString().Trim()).IsEqualTo("Hello World!"); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_an_async_delegate() { // Arrange var stdOutLinesCount = 0; async Task HandleStdOutAsync(string line) { await Task.Yield(); stdOutLinesCount++; } var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "clob", "--lines", "100"]) | HandleStdOutAsync; // Act await cmd.ExecuteAsync(); // Assert await Assert.That(stdOutLinesCount).IsEqualTo(100); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_an_async_delegate_with_cancellation() { // Arrange var stdOutLinesCount = 0; async Task HandleStdOutAsync(string line, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); stdOutLinesCount++; } var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "clob", "--lines", "100"]) | HandleStdOutAsync; // Act await cmd.ExecuteAsync(); // Assert await Assert.That(stdOutLinesCount).IsEqualTo(100); } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_delegate() { // Arrange var stdOutLinesCount = 0; void HandleStdOut(string line) { stdOutLinesCount++; } var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "clob", "--lines", "100"]) | HandleStdOut; // Act await cmd.ExecuteAsync(); // Assert await Assert.That(stdOutLinesCount).IsEqualTo(100); } #endregion #region Stdout & Stderr [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_separate_stream() { // Arrange using var stdOut = new MemoryStream(); using var stdErr = new MemoryStream(); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "blob", "--target", "all", "--length", "100000"]) | (stdOut, stdErr); // Act await cmd.ExecuteAsync(); // Assert using (Assert.Multiple()) { await Assert.That(stdOut.Length).IsEqualTo(100_000); await Assert.That(stdErr.Length).IsEqualTo(100_000); } } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_string_builder() { // Arrange var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["echo", "Hello world!", "--target", "all"]) | (stdOutBuffer, stdErrBuffer); // Act await cmd.ExecuteAsync(); // Assert using (Assert.Multiple()) { await Assert.That(stdOutBuffer.ToString().Trim()).IsEqualTo("Hello world!"); await Assert.That(stdErrBuffer.ToString().Trim()).IsEqualTo("Hello world!"); } } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_separate_async_delegate() { // Arrange var stdOutLinesCount = 0; var stdErrLinesCount = 0; async Task HandleStdOutAsync(string line) { await Task.Yield(); stdOutLinesCount++; } async Task HandleStdErrAsync(string line) { await Task.Yield(); stdErrLinesCount++; } var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "clob", "--target", "all", "--lines", "100"]) | (HandleStdOutAsync, HandleStdErrAsync); // Act await cmd.ExecuteAsync(); // Assert using (Assert.Multiple()) { await Assert.That(stdOutLinesCount).IsEqualTo(100); await Assert.That(stdErrLinesCount).IsEqualTo(100); } } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_separate_async_delegate_with_cancellation() { // Arrange var stdOutLinesCount = 0; var stdErrLinesCount = 0; async Task HandleStdOutAsync(string line, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); stdOutLinesCount++; } async Task HandleStdErrAsync(string line, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); stdErrLinesCount++; } var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "clob", "--target", "all", "--lines", "100"]) | (HandleStdOutAsync, HandleStdErrAsync); // Act await cmd.ExecuteAsync(); // Assert using (Assert.Multiple()) { await Assert.That(stdOutLinesCount).IsEqualTo(100); await Assert.That(stdErrLinesCount).IsEqualTo(100); } } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_separate_sync_delegate() { // Arrange var stdOutLinesCount = 0; var stdErrLinesCount = 0; void HandleStdOut(string line) { stdOutLinesCount++; } void HandleStdErr(string line) { stdErrLinesCount++; } var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "clob", "--target", "all", "--lines", "100"]) | (HandleStdOut, HandleStdErr); // Act await cmd.ExecuteAsync(); // Assert using (Assert.Multiple()) { await Assert.That(stdOutLinesCount).IsEqualTo(100); await Assert.That(stdErrLinesCount).IsEqualTo(100); } } #endregion [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targets() { // Arrange using var stream1 = new MemoryStream(); using var stream2 = new MemoryStream(); using var stream3 = new MemoryStream(); var target = PipeTarget.Merge( PipeTarget.ToStream(stream1), PipeTarget.ToStream(stream2), PipeTarget.ToStream(stream3) ); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "blob", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); // Assert using (Assert.Multiple()) { await Assert.That(stream1.Length).IsEqualTo(100_000); await Assert.That(stream2.Length).IsEqualTo(100_000); await Assert.That(stream3.Length).IsEqualTo(100_000); await Assert.That(stream1.ToArray()).IsEquivalentTo(stream2.ToArray()); await Assert.That(stream2.ToArray()).IsEquivalentTo(stream3.ToArray()); } } [Test] public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_hierarchical_targets() { // Arrange using var stream1 = new MemoryStream(); using var stream2 = new MemoryStream(); using var stream3 = new MemoryStream(); using var stream4 = new MemoryStream(); var target = PipeTarget.Merge( PipeTarget.ToStream(stream1), PipeTarget.Merge( PipeTarget.ToStream(stream2), PipeTarget.Merge( PipeTarget.ToStream(stream3), PipeTarget.ToStream(stream4)))); var cmd = new Command(Testing.Fixture.Program.FilePath) .WithArguments(["generate", "blob", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); // Assert using (Assert.Multiple()) { await Assert.That(stream1.Length).IsEqualTo(100_000); await Assert.That(stream2.Length).IsEqualTo(100_000); await Assert.That(stream3.Length).IsEqualTo(100_000); await Assert.That(stream4.Length).IsEqualTo(100_000); await Assert.That(stream1.ToArray()).IsEquivalentTo(stream2.ToArray()); await Assert.That(stream2.ToArray()).IsEquivalentTo(stream3.ToArray()); await Assert.That(stream3.ToArray()).IsEquivalentTo(stream4.ToArray()); } } }