process/src/process.tests/PipingTests.cs

672 lines
16 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 System.IO.Pipelines;
using System.Buffers;
2026-01-20 22:41:16 +01:00
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_pipe_reader()
{
var pipe = new Pipe();
await pipe.Writer.WriteAsync("Hello World!"u8.ToArray());
await pipe.Writer.CompleteAsync();
var cmd = PipeSource.FromPipeReader(pipe.Reader) |
new Command(Testing.Fixture.Program.FilePath)
.WithArguments("echo-stdin");
var result = await cmd.ExecuteBufferedAsync();
await Assert.That(result.StandardOutput.Trim()).IsEqualTo("Hello World!");
await pipe.Reader.CompleteAsync();
}
[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!");
}
2026-01-20 22:41:16 +01:00
[Test]
public async Task I_can_execute_a_command_and_pipe_the_stdin_from_memory()
{
// Arrange
var data = new ReadOnlyMemory<byte>("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);
}
2026-01-20 22:41:16 +01:00
[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_to_a_pipe_writer()
{
// Arrange
var pipe = new Pipe();
var cmd = new Command(Testing.Fixture.Program.FilePath)
.WithArguments(["generate", "blob", "--length", "100000"]) |
PipeTarget.ToPipeWriter(pipe.Writer);
// Act
using var output = new MemoryStream();
var executionTask = cmd.ExecuteAsync();
while (true)
{
var result = await pipe.Reader.ReadAsync();
foreach (var segment in result.Buffer)
{
await output.WriteAsync(segment);
}
pipe.Reader.AdvanceTo(result.Buffer.End);
if (result.IsCompleted)
{
break;
}
}
await executionTask;
await pipe.Reader.CompleteAsync();
await Assert.That(output.Length).IsEqualTo(100_000);
}
[Test]
public async Task I_can_execute_a_command_and_pipe_the_stdout_to_a_buffer_writer()
{
var output = new ArrayBufferWriter<byte>();
var cmd = new Command(Testing.Fixture.Program.FilePath)
.WithArguments(["generate", "blob", "--length", "100000"]) |
PipeTarget.ToBufferWriter(output);
await cmd.ExecuteAsync();
await Assert.That(output.WrittenCount).IsEqualTo(100_000);
}
2026-01-20 22:41:16 +01:00
[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());
}
}
}