build: initial project release
All checks were successful
release / dotnet-release-workflow (push) Successful in 1m23s
All checks were successful
release / dotnet-release-workflow (push) Successful in 1m23s
This commit is contained in:
commit
48c483c568
62 changed files with 4957 additions and 0 deletions
6
src/process.tests/.editorconfig
Normal file
6
src/process.tests/.editorconfig
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
[*.{cs,vb}]
|
||||
# disable IDE0060: Remove unused parameter
|
||||
dotnet_diagnostic.IDE0060.severity = none
|
||||
# disable IDE0005: Unnecessary using directive
|
||||
dotnet_diagnostic.IDE0005.severity = none
|
||||
181
src/process.tests/CancellationTests.cs
Normal file
181
src/process.tests/CancellationTests.cs
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
// 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
273
src/process.tests/CommandTests.cs
Normal file
273
src/process.tests/CommandTests.cs
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
namespace Geekeey.Process.Tests;
|
||||
|
||||
internal sealed class CommandTests
|
||||
{
|
||||
[Test]
|
||||
public async Task I_can_create_a_command_with_the_default_configuration()
|
||||
{
|
||||
var cmd = new Command("foo");
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(cmd.TargetFilePath).IsEqualTo("foo");
|
||||
await Assert.That(cmd.Arguments).IsEmpty();
|
||||
await Assert.That(cmd.WorkingDirPath).IsEqualTo(Directory.GetCurrentDirectory());
|
||||
await Assert.That(cmd.Environment).IsEmpty();
|
||||
await Assert.That(cmd.Validation).HasFlag(ValidationMode.ZeroExitCode);
|
||||
await Assert.That(cmd.StandardInputPipe).IsEqualTo(PipeSource.Null);
|
||||
await Assert.That(cmd.StandardOutputPipe).IsEqualTo(PipeTarget.Null);
|
||||
await Assert.That(cmd.StandardErrorPipe).IsEqualTo(PipeTarget.Null);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_target_file()
|
||||
{
|
||||
var cmd = new Command("foo");
|
||||
var modified = cmd.WithTargetFile("bar");
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo("bar");
|
||||
await Assert.That(modified.Arguments).IsEqualTo(cmd.Arguments);
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEqualTo(cmd.Environment);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.TargetFilePath).IsNotEqualTo("bar");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_command_line_arguments()
|
||||
{
|
||||
var cmd = new Command("foo").WithArguments("xxx");
|
||||
var modified = cmd.WithArguments("abc def");
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo("abc def");
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEqualTo(cmd.Environment);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.Arguments).IsNotEqualTo("abc def");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_command_line_arguments_by_passing_an_array()
|
||||
{
|
||||
var cmd = new Command("foo").WithArguments("xxx");
|
||||
var modified = cmd.WithArguments(["abc", "def"]);
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo("abc def");
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEqualTo(cmd.Environment);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.Arguments).IsNotEqualTo("abc def");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_command_line_arguments_using_a_builder()
|
||||
{
|
||||
var cmd = new Command("foo").WithArguments("xxx");
|
||||
var modified = cmd.WithArguments(args => args
|
||||
.Add("-a")
|
||||
.Add("foo bar")
|
||||
.Add("\"foo\\\\bar\"")
|
||||
.Add(3.14)
|
||||
.Add(["foo", "bar"])
|
||||
.Add([-10, 12.12]));
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo("-a \"foo bar\" \"\\\"foo\\\\bar\\\"\" 3.14 foo bar -10 12.12");
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEqualTo(cmd.Environment);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.Arguments).IsNotEqualTo("-a \"foo bar\" \"\\\"foo\\\\bar\\\"\" 3.14 foo bar -10 12.12");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_working_directory()
|
||||
{
|
||||
var cmd = new Command("foo").WithWorkingDirectory("xxx");
|
||||
var modified = cmd.WithWorkingDirectory("new");
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo(cmd.Arguments);
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo("new");
|
||||
await Assert.That(modified.Environment).IsEqualTo(cmd.Environment);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.WorkingDirPath).IsNotEqualTo("new");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_environment_variables()
|
||||
{
|
||||
var cmd = new Command("foo").WithEnvironment(e => e.Set("xxx", "xxx"));
|
||||
var vars = new Dictionary<string, string?>
|
||||
{
|
||||
["name"] = "value",
|
||||
["key"] = "door",
|
||||
};
|
||||
var modified = cmd.WithEnvironment(vars);
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo(cmd.Arguments);
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEqualTo(vars);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.Environment).IsNotEqualTo(vars);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_environment_variables_using_a_builder()
|
||||
{
|
||||
var cmd = new Command("foo").WithEnvironment(e => e.Set("xxx", "xxx"));
|
||||
var modified = cmd.WithEnvironment(env => env
|
||||
.Set("name", "value")
|
||||
.Set("key", "door")
|
||||
.Set(new Dictionary<string, string?>
|
||||
{
|
||||
["zzz"] = "yyy",
|
||||
["aaa"] = "bbb",
|
||||
}));
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
var vars = new Dictionary<string, string?>
|
||||
{
|
||||
["name"] = "value",
|
||||
["key"] = "door",
|
||||
["zzz"] = "yyy",
|
||||
["aaa"] = "bbb",
|
||||
};
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo(cmd.Arguments);
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEquivalentTo(vars);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.Environment).IsNotEqualTo(vars);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_result_validation_strategy()
|
||||
{
|
||||
var cmd = new Command("foo").WithExitValidation(ValidationMode.ZeroExitCode);
|
||||
var modified = cmd.WithExitValidation(ValidationMode.None);
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo(cmd.Arguments);
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEqualTo(cmd.Environment);
|
||||
await Assert.That(modified.Validation).IsEqualTo(ValidationMode.None);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.Validation).IsNotEqualTo(ValidationMode.None);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_stdin_pipe()
|
||||
{
|
||||
var cmd = new Command("foo").WithStandardInputPipe(PipeSource.Null);
|
||||
var pipeSource = PipeSource.FromStream(Stream.Null);
|
||||
var modified = cmd.WithStandardInputPipe(pipeSource);
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo(cmd.Arguments);
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEqualTo(cmd.Environment);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(pipeSource);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.StandardInputPipe).IsNotEqualTo(pipeSource);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_stdout_pipe()
|
||||
{
|
||||
var cmd = new Command("foo").WithStandardOutputPipe(PipeTarget.Null);
|
||||
var pipeTarget = PipeTarget.ToStream(Stream.Null);
|
||||
var modified = cmd.WithStandardOutputPipe(pipeTarget);
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo(cmd.Arguments);
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEqualTo(cmd.Environment);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(pipeTarget);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(cmd.StandardErrorPipe);
|
||||
await Assert.That(cmd.StandardOutputPipe).IsNotEqualTo(pipeTarget);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_configure_the_stderr_pipe()
|
||||
{
|
||||
var cmd = new Command("foo").WithStandardErrorPipe(PipeTarget.Null);
|
||||
var pipeTarget = PipeTarget.ToStream(Stream.Null);
|
||||
var modified = cmd.WithStandardErrorPipe(pipeTarget);
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(modified.TargetFilePath).IsEqualTo(cmd.TargetFilePath);
|
||||
await Assert.That(modified.Arguments).IsEqualTo(cmd.Arguments);
|
||||
await Assert.That(modified.WorkingDirPath).IsEqualTo(cmd.WorkingDirPath);
|
||||
await Assert.That(modified.Environment).IsEqualTo(cmd.Environment);
|
||||
await Assert.That(modified.Validation).IsEqualTo(cmd.Validation);
|
||||
await Assert.That(modified.StandardInputPipe).IsEqualTo(cmd.StandardInputPipe);
|
||||
await Assert.That(modified.StandardOutputPipe).IsEqualTo(cmd.StandardOutputPipe);
|
||||
await Assert.That(modified.StandardErrorPipe).IsEqualTo(pipeTarget);
|
||||
await Assert.That(cmd.StandardErrorPipe).IsNotEqualTo(pipeTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
139
src/process.tests/ExecuteTests.cs
Normal file
139
src/process.tests/ExecuteTests.cs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
using Geekeey.Process.Buffered;
|
||||
|
||||
namespace Geekeey.Process.Tests;
|
||||
|
||||
internal sealed class ExecuteTests
|
||||
{
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_and_get_the_exit_code_and_execution_time()
|
||||
{
|
||||
// Arrange
|
||||
var cmd = new Command(Testing.Fixture.Program.FilePath)
|
||||
.WithArguments(["echo"]);
|
||||
|
||||
// Act
|
||||
var result = await cmd.ExecuteAsync();
|
||||
|
||||
await Assert.That(result.ExitCode).IsZero();
|
||||
|
||||
// Assert
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(result.ExitCode).IsZero();
|
||||
await Assert.That(result.IsSuccess).IsTrue();
|
||||
await Assert.That(result.RunTime).IsGreaterThan(TimeSpan.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_and_get_the_associated_process_id()
|
||||
{
|
||||
// Arrange
|
||||
var cmd = new Command(Testing.Fixture.Program.FilePath)
|
||||
.WithArguments(["echo"]);
|
||||
|
||||
// Act
|
||||
var task = cmd.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
await Assert.That(task.ProcessId).IsNotZero();
|
||||
|
||||
await task;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_with_a_configured_awaiter()
|
||||
{
|
||||
// Arrange
|
||||
var cmd = new Command(Testing.Fixture.Program.FilePath)
|
||||
.WithArguments(["echo"]);
|
||||
|
||||
// Act + Assert
|
||||
await cmd.ExecuteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_try_to_execute_a_command_and_get_an_error_if_the_target_file_does_not_exist()
|
||||
{
|
||||
// Arrange
|
||||
var cmd = new Command("some_exe_with_does_not_exits");
|
||||
|
||||
// Act + Assert
|
||||
await Assert.That(() => cmd.ExecuteAsync()).Throws<InvalidOperationException>()
|
||||
.WithInnerException();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_with_a_custom_working_directory()
|
||||
{
|
||||
// Arrange
|
||||
using var dir = TestTempDirectory.Create();
|
||||
|
||||
var cmd = new Command(Testing.Fixture.Program.FilePath)
|
||||
.WithArguments("cwd")
|
||||
.WithWorkingDirectory(dir.Path);
|
||||
|
||||
// Act
|
||||
var result = await cmd.ExecuteBufferedAsync();
|
||||
|
||||
await Assert.That(result.ExitCode).IsZero();
|
||||
|
||||
// Assert
|
||||
var lines = result.StandardOutput.Split(Environment.NewLine);
|
||||
await Assert.That(lines).Contains(dir.Path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_with_additional_environment_variables()
|
||||
{
|
||||
// Arrange
|
||||
var cmd = new Command(Testing.Fixture.Program.FilePath)
|
||||
.WithArguments(["env", "foo", "bar"])
|
||||
.WithEnvironment(env => env
|
||||
.Set("foo", "hello")
|
||||
.Set("bar", "world"));
|
||||
|
||||
// Act
|
||||
var result = await cmd.ExecuteBufferedAsync();
|
||||
|
||||
await Assert.That(result.ExitCode).IsZero();
|
||||
|
||||
// Assert
|
||||
var lines = result.StandardOutput.Split(Environment.NewLine);
|
||||
await Assert.That(lines).Contains("hello");
|
||||
await Assert.That(lines).Contains("world");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_with_some_environment_variables_overwritten()
|
||||
{
|
||||
// Arrange
|
||||
var key = Guid.NewGuid();
|
||||
var variableToKeep = $"GKY_TEST_KEEP_{key}";
|
||||
var variableToOverwrite = $"GKY_TEST_OVERWRITE_{key}";
|
||||
var variableToUnset = $"GKY_TEST_UNSET_{key}";
|
||||
|
||||
using var a = TestEnvironment.Create(variableToKeep, "keep");
|
||||
using var b = TestEnvironment.Create(variableToOverwrite, "overwrite");
|
||||
using var c = TestEnvironment.Create(variableToUnset, "unset");
|
||||
|
||||
var cmd = new Command(Testing.Fixture.Program.FilePath)
|
||||
.WithArguments(["env", variableToKeep, variableToOverwrite, variableToUnset])
|
||||
.WithEnvironment(env => env
|
||||
.Set(variableToOverwrite, "overwritten")
|
||||
.Set(variableToUnset, null));
|
||||
|
||||
// Act
|
||||
var result = await cmd.ExecuteBufferedAsync();
|
||||
|
||||
await Assert.That(result.ExitCode).IsZero();
|
||||
|
||||
// Assert
|
||||
var lines = result.StandardOutput.Split(Environment.NewLine);
|
||||
await Assert.That(lines).Contains("keep");
|
||||
await Assert.That(lines).Contains("overwritten");
|
||||
}
|
||||
}
|
||||
18
src/process.tests/Geekeey.Process.Tests.csproj
Normal file
18
src/process.tests/Geekeey.Process.Tests.csproj
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TUnit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\process\Geekeey.Process.csproj" />
|
||||
<ProjectReference Include="..\process.dummy.app\Geekeey.Process.Dummy.App.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
81
src/process.tests/LineBreakTests.cs
Normal file
81
src/process.tests/LineBreakTests.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
namespace Geekeey.Process.Tests;
|
||||
|
||||
internal sealed class LineBreakTests
|
||||
{
|
||||
private static Command Echo()
|
||||
{
|
||||
return new Command(Testing.Fixture.Program.FilePath)
|
||||
.WithArguments("echo-stdin");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_and_split_the_stdout_by_newline()
|
||||
{
|
||||
// Arrange
|
||||
const string data = "Foo\nBar\nBaz";
|
||||
|
||||
var stdOutLines = new List<string>();
|
||||
|
||||
var cmd = data | Echo() | stdOutLines.Add;
|
||||
|
||||
// Act
|
||||
await cmd.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
await Assert.That(stdOutLines).IsEquivalentTo(["Foo", "Bar", "Baz"]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_and_split_the_stdout_by_caret_return()
|
||||
{
|
||||
// Arrange
|
||||
const string data = "Foo\rBar\rBaz";
|
||||
|
||||
var stdOutLines = new List<string>();
|
||||
|
||||
var cmd = data | Echo() | stdOutLines.Add;
|
||||
|
||||
// Act
|
||||
await cmd.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
await Assert.That(stdOutLines).IsEquivalentTo(["Foo", "Bar", "Baz"]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_and_split_the_stdout_by_caret_return_followed_by_newline()
|
||||
{
|
||||
// Arrange
|
||||
const string data = "Foo\r\nBar\r\nBaz";
|
||||
|
||||
var stdOutLines = new List<string>();
|
||||
|
||||
var cmd = data | Echo() | stdOutLines.Add;
|
||||
|
||||
// Act
|
||||
await cmd.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
await Assert.That(stdOutLines).IsEquivalentTo(["Foo", "Bar", "Baz"]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_and_split_the_stdout_by_newline_while_including_empty_lines()
|
||||
{
|
||||
// Arrange
|
||||
const string data = "Foo\r\rBar\n\nBaz";
|
||||
|
||||
var stdOutLines = new List<string>();
|
||||
|
||||
var cmd = data | Echo() | stdOutLines.Add;
|
||||
|
||||
// Act
|
||||
await cmd.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
await Assert.That(stdOutLines).IsEquivalentTo(["Foo", "", "Bar", "", "Baz"]);
|
||||
}
|
||||
}
|
||||
50
src/process.tests/PathResolutionTests.cs
Normal file
50
src/process.tests/PathResolutionTests.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
using Geekeey.Process.Buffered;
|
||||
|
||||
namespace Geekeey.Process.Tests;
|
||||
|
||||
internal sealed class PathResolutionTests
|
||||
{
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_on_an_executable_using_its_short_name()
|
||||
{
|
||||
// Arrange
|
||||
var cmd = new Command("dotnet")
|
||||
.WithArguments("--version");
|
||||
|
||||
// Act
|
||||
var result = await cmd.ExecuteBufferedAsync();
|
||||
|
||||
// Assert
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(result.ExitCode).IsEqualTo(0);
|
||||
await Assert.That(result.StandardOutput.Trim()).Matches(@"^\d+\.\d+\.\d+$");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Platform(PlatformAttribute.Windows)]
|
||||
public async Task I_can_execute_a_command_on_a_script_using_its_short_name()
|
||||
{
|
||||
// Arrange
|
||||
using var dir = TestTempDirectory.Create();
|
||||
await File.WriteAllTextAsync(Path.Combine(dir.Path, "script.cmd"), "@echo hi");
|
||||
|
||||
using var _1 = TestEnvironment.ExtendPath(dir.Path);
|
||||
var cmd = new Command("script");
|
||||
|
||||
// Act
|
||||
var result = await cmd.ExecuteBufferedAsync();
|
||||
|
||||
// Assert
|
||||
// Assert
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(result.ExitCode).IsEqualTo(0);
|
||||
await Assert.That(result.StandardOutput.Trim()).IsEqualTo("hi");
|
||||
}
|
||||
}
|
||||
}
|
||||
536
src/process.tests/PipingTests.cs
Normal file
536
src/process.tests/PipingTests.cs
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
// 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_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_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());
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/process.tests/ValidationTests.cs
Normal file
58
src/process.tests/ValidationTests.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
using Geekeey.Process.Buffered;
|
||||
|
||||
namespace Geekeey.Process.Tests;
|
||||
|
||||
internal sealed class ValidationTests
|
||||
{
|
||||
private static Command Exit()
|
||||
{
|
||||
return new Command(Testing.Fixture.Program.FilePath)
|
||||
.WithArguments(["exit", "1"]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_try_to_execute_a_command_and_get_an_error_if_it_returns_a_non_zero_exit_code()
|
||||
{
|
||||
// Arrange
|
||||
var cmd = Exit();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.That(async () => await cmd.ExecuteAsync()).Throws<CommandExecutionException>().And
|
||||
.Member(static exception => exception.Message, static source => source.Contains("a non-zero exit code (1)")).And
|
||||
.Member(static exception => exception.ExitCode, static source => source.IsEqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_try_to_execute_a_command_with_buffering_and_get_a_detailed_error_if_it_returns_a_non_zero_exit_code()
|
||||
{
|
||||
// Arrange
|
||||
var cmd = Exit();
|
||||
|
||||
|
||||
// Act & Assert
|
||||
await Assert.That(async () => await cmd.ExecuteBufferedAsync()).Throws<CommandExecutionException>().And
|
||||
.Member(static exception => exception.Message, static source => source.Contains("Exit code set to 1")).And
|
||||
.Member(static exception => exception.ExitCode, static source => source.IsEqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_execute_a_command_without_validating_the_exit_code()
|
||||
{
|
||||
// Arrange
|
||||
var cmd = Exit()
|
||||
.WithExitValidation(ValidationMode.None);
|
||||
|
||||
// Act
|
||||
var result = await cmd.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(result.ExitCode).IsEqualTo(1);
|
||||
await Assert.That(result.IsSuccess).IsFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/process.tests/_fixture/PlatformAttribute.cs
Normal file
35
src/process.tests/_fixture/PlatformAttribute.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
namespace Geekeey.Process.Tests;
|
||||
|
||||
internal sealed class PlatformAttribute : SkipAttribute
|
||||
{
|
||||
// from the OperatingSystem definitions
|
||||
|
||||
public const string Browser = "BROWSER";
|
||||
public const string Wasi = "WASI";
|
||||
public const string Windows = "WINDOWS";
|
||||
public const string Osx = "OSX";
|
||||
public const string MacCatalyst = "MACCATALYST";
|
||||
public const string Ios = "IOS";
|
||||
public const string Tvos = "TVOS";
|
||||
public const string Android = "ANDROID";
|
||||
public const string Linux = "LINUX";
|
||||
public const string Freebsd = "FREEBSD";
|
||||
public const string Netbsd = "NETBSD";
|
||||
public const string Illumos = "ILLUMOS";
|
||||
public const string Solaris = "SOLARIS";
|
||||
|
||||
private readonly string[] _os;
|
||||
|
||||
public PlatformAttribute(params string[] os) : base("Test skipped on unsupported platform.")
|
||||
{
|
||||
_os = os;
|
||||
}
|
||||
|
||||
public override Task<bool> ShouldSkip(TestRegisteredContext context)
|
||||
{
|
||||
return Task.FromResult(!_os.Any(OperatingSystem.IsOSPlatform));
|
||||
}
|
||||
}
|
||||
21
src/process.tests/_fixture/ProcessTree.cs
Normal file
21
src/process.tests/_fixture/ProcessTree.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
namespace Geekeey.Process.Tests;
|
||||
|
||||
internal static class ProcessTree
|
||||
{
|
||||
public static bool HasExited(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var process = System.Diagnostics.Process.GetProcessById(id);
|
||||
return process.HasExited;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// GetProcessById throws if the process can not be found, which means it is not running!
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/process.tests/_fixture/TestEnvironment.cs
Normal file
32
src/process.tests/_fixture/TestEnvironment.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
namespace Geekeey.Process.Tests;
|
||||
|
||||
internal sealed class TestEnvironment : IDisposable
|
||||
{
|
||||
private readonly Action _action;
|
||||
|
||||
private TestEnvironment(Action action)
|
||||
{
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public static TestEnvironment Create(string name, string? value)
|
||||
{
|
||||
var lastValue = Environment.GetEnvironmentVariable(name);
|
||||
Environment.SetEnvironmentVariable(name, value);
|
||||
|
||||
return new TestEnvironment(() => Environment.SetEnvironmentVariable(name, lastValue));
|
||||
}
|
||||
|
||||
public static TestEnvironment ExtendPath(string path)
|
||||
{
|
||||
return Create("PATH", Environment.GetEnvironmentVariable("PATH") + Path.PathSeparator + path);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_action();
|
||||
}
|
||||
}
|
||||
34
src/process.tests/_fixture/TestTempDirectory.cs
Normal file
34
src/process.tests/_fixture/TestTempDirectory.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
namespace Geekeey.Process.Tests;
|
||||
|
||||
internal sealed class TestTempDirectory : IDisposable
|
||||
{
|
||||
private TestTempDirectory(string path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public static TestTempDirectory Create()
|
||||
{
|
||||
var location = System.Reflection.Assembly.GetExecutingAssembly().Location;
|
||||
var pwd = System.IO.Path.GetDirectoryName(location) ?? Directory.GetCurrentDirectory();
|
||||
var dirPath = System.IO.Path.Combine(pwd, "Temp", Guid.NewGuid().ToString());
|
||||
|
||||
Directory.CreateDirectory(dirPath);
|
||||
|
||||
return new TestTempDirectory(dirPath);
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(Path, recursive: true);
|
||||
}
|
||||
catch (DirectoryNotFoundException) { }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue