From 08369cd4c5b4b0912473c95ccb84d1ec73b76024 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Fri, 23 Jun 2023 19:51:08 +0300 Subject: [PATCH 01/57] Rename test --- CliWrap.Tests/PipingSpecs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index 8bbb3ec9..e08c17a7 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -580,7 +580,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ } [Fact(Timeout = 15000)] - public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_complicated_target_hierarchy() + public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_hierarchical_targets() { // Arrange await using var stream1 = new MemoryStream(); From 2be737217159c64e7c9b00b96b5516275388607e Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:39:27 +0300 Subject: [PATCH 02/57] Clarify wording in the changelog --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 9edaa617..ad30bdb4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ ## v3.6.4 (23-Jun-2023) -- Renamed `PipeSource.FromMemory(...)` to `PipeSource.FromBytes(...)` for consistency with similar methods in .NET. The old method is still available, but is marked as obsolete. +- Renamed `PipeSource.FromMemory(...)` to `PipeSource.FromBytes(...)` for consistency with similar methods in the wider .NET framework. The old method is still available but marked as obsolete. - Fixed an issue where piping very large output through `PipeTarget.Merge(...)` could result in a deadlock if one of the underlying targets failed with an exception. ## v3.6.3 (18-May-2023) From 62eb99f860d7108c26ae9d3d040abbf814eebba2 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:40:26 +0300 Subject: [PATCH 03/57] Update NuGet packages --- CliWrap.Benchmarks/CliWrap.Benchmarks.csproj | 2 +- CliWrap.Tests/CliWrap.Tests.csproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj index dac87b1a..bc652de1 100644 --- a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj +++ b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj @@ -6,7 +6,7 @@ - + diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index 63c84d5c..74b1841e 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -11,10 +11,10 @@ - + - - + + From fd413014bf0b2eb3f2b8f7106b91fd656a3051a4 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 11 Jul 2023 23:56:48 +0300 Subject: [PATCH 04/57] Hack around xUnit warnings --- CliWrap.Tests/ExecutionSpecs.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CliWrap.Tests/ExecutionSpecs.cs b/CliWrap.Tests/ExecutionSpecs.cs index 411e2fb9..9eead94d 100644 --- a/CliWrap.Tests/ExecutionSpecs.cs +++ b/CliWrap.Tests/ExecutionSpecs.cs @@ -73,6 +73,11 @@ public void I_cannot_execute_a_command_on_a_file_that_does_not_exist() // Should throw synchronously // https://github.com/Tyrrrz/CliWrap/issues/139 - Assert.ThrowsAny(() => cmd.ExecuteAsync()); + Assert.ThrowsAny(() => + // xUnit tells us to use ThrowsAnyAsync(...) instead for async methods, + // but we're actually interested in the sync portion of this method. + // So cast the result to object to avoid the warning. + (object)cmd.ExecuteAsync() + ); } } \ No newline at end of file From 417aaffe171b9897799ed2a28a6467c11d69c296 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Mon, 17 Jul 2023 00:57:02 +0300 Subject: [PATCH 05/57] Update readme --- Readme.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index bc89071f..4d285dfe 100644 --- a/Readme.md +++ b/Readme.md @@ -1,15 +1,19 @@ # CliWrap +[![Status](https://img.shields.io/badge/status-active-47c219.svg)](https://github.com/Tyrrrz/.github/blob/master/docs/project-status.md) [![Made in Ukraine](https://img.shields.io/badge/made_in-ukraine-ffd700.svg?labelColor=0057b7)](https://tyrrrz.me/ukraine) [![Build](https://img.shields.io/github/actions/workflow/status/Tyrrrz/CliWrap/main.yml?branch=master)](https://github.com/Tyrrrz/CliWrap/actions) [![Coverage](https://img.shields.io/codecov/c/github/Tyrrrz/CliWrap/master)](https://codecov.io/gh/Tyrrrz/CliWrap) [![Version](https://img.shields.io/nuget/v/CliWrap.svg)](https://nuget.org/packages/CliWrap) [![Downloads](https://img.shields.io/nuget/dt/CliWrap.svg)](https://nuget.org/packages/CliWrap) [![Discord](https://img.shields.io/discord/869237470565392384?label=discord)](https://discord.gg/2SUWKFnHSm) -[![Donate](https://img.shields.io/badge/donate-$$$-8a2be2.svg)](https://tyrrrz.me/donate) [![Fuck Russia](https://img.shields.io/badge/fuck-russia-e4181c.svg?labelColor=000000)](https://twitter.com/tyrrrz/status/1495972128977571848) -> 🟢 **Project status**: active[[?]](https://github.com/Tyrrrz/.github/blob/master/docs/project-status.md) + + + + +
Development of this project is entirely funded by the community. Consider donating to support!

Icon From ce5a669c54844feb1337ca0068514622e48e5636 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Wed, 26 Jul 2023 22:29:51 +0300 Subject: [PATCH 06/57] Add dispatchable prerelease workflow --- .github/workflows/main.yml | 2 +- .github/workflows/prerelease.yml | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/prerelease.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f72ec74d..8fe71abc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,4 +10,4 @@ jobs: secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} - DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} \ No newline at end of file + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml new file mode 100644 index 00000000..364b2535 --- /dev/null +++ b/.github/workflows/prerelease.yml @@ -0,0 +1,25 @@ +name: prerelease + +on: + workflow_dispatch: + inputs: + package-version-base: + type: string + description: Package version base + required: true + package-version-suffix: + type: string + description: Package version suffix + required: true + +jobs: + prerelease: + uses: Tyrrrz/.github/.github/workflows/nuget.yml@master + with: + dotnet-version: 7.0.x + package-version: ${{ format('{0}-{1}', inputs.package-version-base, inputs.package-version-suffix) }} + deploy-on-tags-only: false + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} From 49daa778bb690a82ccff487dd394f5ec9e952555 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 5 Aug 2023 23:09:42 +0300 Subject: [PATCH 07/57] Delegate running the test dummy to a script --- CliWrap.Tests/BufferedSpecs.cs | 13 +-- CliWrap.Tests/CancellationSpecs.cs | 36 ++---- CliWrap.Tests/CliWrap.Tests.csproj | 2 + CliWrap.Tests/CredentialsSpecs.cs | 10 +- CliWrap.Tests/EnvironmentSpecs.cs | 27 ++--- CliWrap.Tests/EventStreamSpecs.cs | 7 +- CliWrap.Tests/ExecutionSpecs.cs | 10 +- CliWrap.Tests/LineBreakSpecs.cs | 37 +++--- CliWrap.Tests/PipingSpecs.cs | 174 ++++++++++------------------- CliWrap.Tests/Utils/DummyScript.cs | 17 +++ CliWrap.Tests/ValidationSpecs.cs | 10 +- CliWrap.Tests/run-dummy.bat | 10 ++ CliWrap.Tests/run-dummy.sh | 2 + 13 files changed, 149 insertions(+), 206 deletions(-) create mode 100644 CliWrap.Tests/Utils/DummyScript.cs create mode 100644 CliWrap.Tests/run-dummy.bat create mode 100644 CliWrap.Tests/run-dummy.sh diff --git a/CliWrap.Tests/BufferedSpecs.cs b/CliWrap.Tests/BufferedSpecs.cs index 2e5abfa5..6658a780 100644 --- a/CliWrap.Tests/BufferedSpecs.cs +++ b/CliWrap.Tests/BufferedSpecs.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using CliWrap.Buffered; +using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; @@ -11,9 +12,8 @@ public class BufferedSpecs public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("echo") .Add("Hello stdout") .Add("--target").Add("stdout") @@ -31,9 +31,8 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout() public async Task I_can_execute_a_command_with_buffering_and_get_the_stderr() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("echo") .Add("Hello stderr") .Add("--target").Add("stderr") @@ -51,9 +50,8 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stderr() public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout_and_stderr() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("echo") .Add("Hello stdout and stderr") .Add("--target").Add("all") @@ -71,9 +69,8 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout_and_ public async Task I_can_execute_a_command_with_buffering_and_not_hang_on_large_stdout_and_stderr() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--target").Add("all") .Add("--length").Add(100_000) diff --git a/CliWrap.Tests/CancellationSpecs.cs b/CliWrap.Tests/CancellationSpecs.cs index b788c974..12abb180 100644 --- a/CliWrap.Tests/CancellationSpecs.cs +++ b/CliWrap.Tests/CancellationSpecs.cs @@ -22,9 +22,8 @@ public async Task I_can_execute_a_command_and_cancel_it_immediately() var stdOutBuffer = new StringBuilder(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ) | stdOutBuffer; @@ -49,9 +48,8 @@ public async Task I_can_execute_a_command_and_cancel_it_after_a_delay() var stdOutBuffer = new StringBuilder(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ) | stdOutBuffer; @@ -89,9 +87,8 @@ void HandleStdOut(string line) PipeTarget.ToStringBuilder(stdOutBuffer) ); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ) | target; @@ -114,9 +111,8 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_immediate using var cts = new CancellationTokenSource(); cts.Cancel(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ); @@ -136,9 +132,8 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_after_a_d using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ); @@ -158,9 +153,8 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_gracefull using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ); @@ -185,9 +179,8 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.Cancel(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ); @@ -212,9 +205,8 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ); @@ -239,9 +231,8 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ); @@ -270,9 +261,8 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.Cancel(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ); @@ -296,9 +286,8 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ); @@ -322,9 +311,8 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("sleep") .Add("--duration").Add("00:00:20") ); diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index 74b1841e..233c3e46 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -6,6 +6,8 @@ + + diff --git a/CliWrap.Tests/CredentialsSpecs.cs b/CliWrap.Tests/CredentialsSpecs.cs index 843621d1..1f3978a4 100644 --- a/CliWrap.Tests/CredentialsSpecs.cs +++ b/CliWrap.Tests/CredentialsSpecs.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using System.Runtime.InteropServices; using System.Threading.Tasks; +using CliWrap.Tests.Utils; using Xunit; namespace CliWrap.Tests; @@ -20,8 +21,7 @@ public async Task I_can_execute_a_command_as_a_different_user() // that the credentials have been passed by getting an exception. // Arrange - var cmd = Cli.Wrap("dotnet") - .WithArguments(a => a.Add(Dummy.Program.FilePath)) + var cmd = Cli.Wrap(DummyScript.FilePath) .WithCredentials(c => c .SetUserName("user123") .SetPassword("pass123") @@ -44,8 +44,7 @@ public async Task I_can_execute_a_command_as_a_different_user_under_the_specifie // that the credentials have been passed by getting an exception. // Arrange - var cmd = Cli.Wrap("dotnet") - .WithArguments(a => a.Add(Dummy.Program.FilePath)) + var cmd = Cli.Wrap(DummyScript.FilePath) .WithCredentials(c => c .SetDomain("domain123") .SetUserName("user123") @@ -66,8 +65,7 @@ public async Task I_cannot_execute_a_command_as_a_different_user_on_a_system_tha ); // Arrange - var cmd = Cli.Wrap("dotnet") - .WithArguments(a => a.Add(Dummy.Program.FilePath)) + var cmd = Cli.Wrap(DummyScript.FilePath) .WithCredentials(c => c .SetUserName("user123") .SetPassword("pass123") diff --git a/CliWrap.Tests/EnvironmentSpecs.cs b/CliWrap.Tests/EnvironmentSpecs.cs index 8f383569..9157d4c0 100644 --- a/CliWrap.Tests/EnvironmentSpecs.cs +++ b/CliWrap.Tests/EnvironmentSpecs.cs @@ -16,11 +16,8 @@ public async Task I_can_execute_a_command_with_a_custom_working_directory() // Arrange using var dir = TempDir.Create(); - var cmd = Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("print cwd") - ) + var cmd = Cli.Wrap(DummyScript.FilePath) + .WithArguments("print cwd") .WithWorkingDirectory(dir.Path); // Act @@ -37,15 +34,11 @@ public async Task I_can_execute_a_command_with_additional_environment_variables( var env = new Dictionary { ["foo"] = "bar", - ["hello"] = "world", - ["Path"] = "there" + ["hello"] = "world" }; - var cmd = Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("print env") - ) + var cmd = Cli.Wrap(DummyScript.FilePath) + .WithArguments("print env") .WithEnvironmentVariables(env); // Act @@ -54,8 +47,7 @@ public async Task I_can_execute_a_command_with_additional_environment_variables( // Assert result.StandardOutput.Trim().Should().ContainAll( "[foo] = bar", - "[hello] = world", - "[Path] = there" + "[hello] = world" ); } @@ -72,11 +64,8 @@ public async Task I_can_execute_a_command_with_some_environment_variables_overwr using (TempEnvironmentVariable.Set(variableToOverwrite, "overwrite")) // will be overwritten using (TempEnvironmentVariable.Set(variableToUnset, "unset")) // will be unset { - var cmd = Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("print env") - ) + var cmd = Cli.Wrap(DummyScript.FilePath) + .WithArguments("print env") .WithEnvironmentVariables(e => e .Set(variableToOverwrite, "overwritten") .Set(variableToUnset, null) diff --git a/CliWrap.Tests/EventStreamSpecs.cs b/CliWrap.Tests/EventStreamSpecs.cs index c9350fd6..92a27749 100644 --- a/CliWrap.Tests/EventStreamSpecs.cs +++ b/CliWrap.Tests/EventStreamSpecs.cs @@ -3,6 +3,7 @@ using System.Reactive.Linq; using System.Threading.Tasks; using CliWrap.EventStream; +using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; @@ -14,9 +15,8 @@ public class EventStreamSpecs public async Task I_can_execute_a_command_as_a_pull_based_event_stream() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--target").Add("all") .Add("--lines").Add(100) @@ -40,9 +40,8 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream() public async Task I_can_execute_a_command_as_a_push_based_event_stream() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--target").Add("all") .Add("--lines").Add(100) diff --git a/CliWrap.Tests/ExecutionSpecs.cs b/CliWrap.Tests/ExecutionSpecs.cs index 9eead94d..c0f6bd20 100644 --- a/CliWrap.Tests/ExecutionSpecs.cs +++ b/CliWrap.Tests/ExecutionSpecs.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using System.Threading.Tasks; +using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; @@ -12,7 +13,7 @@ public class ExecutionSpecs public async Task I_can_execute_a_command_and_get_the_exit_code_and_execution_time() { // Arrange - var cmd = Cli.Wrap("dotnet").WithArguments(Dummy.Program.FilePath); + var cmd = Cli.Wrap(DummyScript.FilePath); // Act var result = await cmd.ExecuteAsync(); @@ -26,7 +27,7 @@ public async Task I_can_execute_a_command_and_get_the_exit_code_and_execution_ti public async Task I_can_execute_a_command_and_get_the_associated_process_ID() { // Arrange - var cmd = Cli.Wrap("dotnet").WithArguments(Dummy.Program.FilePath); + var cmd = Cli.Wrap(DummyScript.FilePath); // Act var task = cmd.ExecuteAsync(); @@ -41,7 +42,7 @@ public async Task I_can_execute_a_command_and_get_the_associated_process_ID() public async Task I_can_execute_a_command_with_a_configured_awaiter() { // Arrange - var cmd = Cli.Wrap("dotnet").WithArguments(Dummy.Program.FilePath); + var cmd = Cli.Wrap(DummyScript.FilePath); // Act & assert await cmd.ExecuteAsync().ConfigureAwait(false); @@ -51,9 +52,8 @@ public async Task I_can_execute_a_command_with_a_configured_awaiter() public async Task I_can_execute_a_command_and_not_hang_on_large_stdout_and_stderr() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--target").Add("all") .Add("--length").Add(100_000) diff --git a/CliWrap.Tests/LineBreakSpecs.cs b/CliWrap.Tests/LineBreakSpecs.cs index b9ef0a94..18645057 100644 --- a/CliWrap.Tests/LineBreakSpecs.cs +++ b/CliWrap.Tests/LineBreakSpecs.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; +using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; @@ -15,11 +16,10 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_newline() var stdOutLines = new List(); - var cmd = data | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ) | stdOutLines.Add; + var cmd = + data | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin") | + stdOutLines.Add; // Act await cmd.ExecuteAsync(); @@ -40,11 +40,10 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_caret_return() var stdOutLines = new List(); - var cmd = data | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ) | stdOutLines.Add; + var cmd = + data | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin") | + stdOutLines.Add; // Act await cmd.ExecuteAsync(); @@ -65,11 +64,10 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_caret_return_f var stdOutLines = new List(); - var cmd = data | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ) | stdOutLines.Add; + var cmd = + data | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin") | + stdOutLines.Add; // Act await cmd.ExecuteAsync(); @@ -90,11 +88,10 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_newline_while_ var stdOutLines = new List(); - var cmd = data | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ) | stdOutLines.Add; + var cmd = + data | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin") | + stdOutLines.Add; // Act await cmd.ExecuteAsync(); diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index e08c17a7..241c010d 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -21,11 +21,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_an_async_anony await destination.WriteAsync("Hello world!"u8.ToArray(), cancellationToken) ); - var cmd = source | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = + source | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -42,11 +40,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_sync_anonymo destination.Write("Hello world!"u8) ); - var cmd = source | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = + source | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -61,11 +57,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_stream() // Arrange await using var stream = new MemoryStream("Hello world!"u8.ToArray()); - var cmd = stream | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = + stream | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -81,11 +75,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_file() using var file = TempFile.Create(); await File.WriteAllTextAsync(file.Path, "Hello world!"); - var cmd = PipeSource.FromFile(file.Path) | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = + PipeSource.FromFile(file.Path) | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -100,11 +92,9 @@ 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 | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = + data | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -119,11 +109,9 @@ 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 | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = + data | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -136,11 +124,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_byte_array() public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_string() { // Arrange - var cmd = "Hello world!" | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = + "Hello world!" | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -154,14 +140,12 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_another_comman { // Arrange var cmd = - Cli.Wrap("dotnet").WithArguments(a => a - .Add(Dummy.Program.FilePath) + Cli.Wrap(DummyScript.FilePath).WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000) ) | - Cli.Wrap("dotnet").WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("print length stdin") + Cli.Wrap(DummyScript.FilePath).WithArguments( + "print length stdin" ); // Act @@ -177,18 +161,15 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_chain_of_com // Arrange var cmd = "Hello world" | - Cli.Wrap("dotnet").WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") + Cli.Wrap(DummyScript.FilePath).WithArguments( + "echo stdin" ) | - Cli.Wrap("dotnet").WithArguments(a => a - .Add(Dummy.Program.FilePath) + Cli.Wrap(DummyScript.FilePath).WithArguments(a => a .Add("echo stdin") .Add("--length").Add(5) ) | - Cli.Wrap("dotnet").WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("print length stdin") + Cli.Wrap(DummyScript.FilePath).WithArguments( + "print length stdin" ); // Act @@ -209,9 +190,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_an_async_anon await origin.CopyToAsync(stream, cancellationToken) ); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--length").Add(100_000) ) | target; @@ -234,9 +214,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_anonym origin.CopyTo(stream) ); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--length").Add(100_000) ) | target; @@ -254,9 +233,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_stream() // Arrange await using var stream = new MemoryStream(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--length").Add(100_000) ) | stream; @@ -274,9 +252,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_file() // Arrange using var file = TempFile.Create(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--length").Add(100_000) ) | PipeTarget.ToFile(file.Path); @@ -295,9 +272,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_string_buil // Arrange var buffer = new StringBuilder(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("echo") .Add("Hello world!") ) | buffer; @@ -321,9 +297,8 @@ async Task HandleStdOutAsync(string line) stdOutLinesCount++; } - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--lines").Add(100) ) | HandleStdOutAsync; @@ -347,9 +322,8 @@ async Task HandleStdOutAsync(string line, CancellationToken cancellationToken = stdOutLinesCount++; } - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--lines").Add(100) ) | HandleStdOutAsync; @@ -369,9 +343,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_delega void HandleStdOut(string line) => stdOutLinesCount++; - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--lines").Add(100) ) | HandleStdOut; @@ -390,9 +363,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se await using var stdOut = new MemoryStream(); await using var stdErr = new MemoryStream(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--target").Add("all") .Add("--length").Add(100_000) @@ -413,9 +385,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_st var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("echo").Add("Hello world!") .Add("--target").Add("all") ) | (stdOutBuffer, stdErrBuffer); @@ -447,9 +418,8 @@ async Task HandleStdErrAsync(string line) stdErrLinesCount++; } - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--target").Add("all") .Add("--lines").Add(100) @@ -482,9 +452,8 @@ async Task HandleStdErrAsync(string line, CancellationToken cancellationToken = stdErrLinesCount++; } - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--target").Add("all") .Add("--lines").Add(100) @@ -508,9 +477,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se void HandleStdOut(string line) => stdOutLinesCount++; void HandleStdErr(string line) => stdErrLinesCount++; - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--target").Add("all") .Add("--lines").Add(100) @@ -538,9 +506,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ PipeTarget.ToStream(stream3) ); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--length").Add(100_000) ) | target; @@ -567,9 +534,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ PipeTarget.ToDelegate(_ => throw new Exception("Expected exception.")) ); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--length").Add(100_000) ) | target; @@ -599,9 +565,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_hier ) ); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--length").Add(100_000)) | target; @@ -624,9 +589,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_stre // https://github.com/Tyrrrz/CliWrap/issues/81 // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--length").Add(1_000_000) // Buffer needs to be >= BufferSizes.Stream to fail @@ -658,9 +622,8 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou // Arrange await using var stream = new MemoryStream(); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--length").Add(100_000) ) | stream; @@ -685,9 +648,8 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou var delegateLines = new List(); void HandleStdOut(string line) => delegateLines.Add(line); - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate text") .Add("--lines").Add(100) ) | HandleStdOut; @@ -707,11 +669,9 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_source_throws_an_exception() { // Arrange - var cmd = PipeSource.FromFile("non-existing-file.txt") | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = + PipeSource.FromFile("non-existing-file.txt") | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act & assert await Assert.ThrowsAnyAsync(async () => await cmd.ExecuteAsync()); @@ -721,9 +681,8 @@ public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_source_th public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_target_throws_an_exception() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("generate binary") .Add("--length").Add(100_000) ) | PipeTarget.ToFile("non-existing-directory/file.txt"); @@ -736,11 +695,7 @@ public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_target_th public async Task I_can_execute_a_command_and_not_hang_if_the_process_expects_stdin_but_none_is_provided() { // Arrange - var cmd = Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act await cmd.ExecuteAsync(); @@ -750,11 +705,9 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_expects_st public async Task I_can_execute_a_command_and_not_hang_if_the_process_expects_stdin_but_empty_data_is_provided() { // Arrange - var cmd = Array.Empty() | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = + Array.Empty() | + Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act await cmd.ExecuteAsync(); @@ -780,9 +733,8 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_only_parti // ReSharper disable once FunctionNeverReturns }); - var cmd = source | Cli.Wrap("dotnet") + var cmd = source | Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("echo stdin") .Add("--length").Add(100_000) ); @@ -803,9 +755,8 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_does_not_c await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken); }); - var cmd = source | Cli.Wrap("dotnet") + var cmd = source | Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("echo stdin") .Add("--length").Add(0) ); @@ -825,9 +776,8 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_does_not_c await Task.Delay(TimeSpan.FromSeconds(20), CancellationToken.None) ); - var cmd = source | Cli.Wrap("dotnet") + var cmd = source | Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("echo stdin") .Add("--length").Add(0) ); @@ -859,11 +809,7 @@ public async Task I_can_execute_a_command_and_not_hang_on_large_stdin_while_also } }); - var cmd = source | Cli.Wrap("dotnet") - .WithArguments(a => a - .Add(Dummy.Program.FilePath) - .Add("echo stdin") - ); + var cmd = source | Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); // Act & assert await cmd.ExecuteAsync(); diff --git a/CliWrap.Tests/Utils/DummyScript.cs b/CliWrap.Tests/Utils/DummyScript.cs new file mode 100644 index 00000000..f3e76817 --- /dev/null +++ b/CliWrap.Tests/Utils/DummyScript.cs @@ -0,0 +1,17 @@ +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace CliWrap.Tests.Utils; + +// Runs the dummy program either through the EXE file or via .NET CLI, depending on the platform +internal static class DummyScript +{ + public static string DirPath { get; } = + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? + Directory.GetCurrentDirectory(); + + public static string FilePath { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? Path.Combine(DirPath, "run-dummy.bat") + : Path.Combine(DirPath, "run-dummy.sh"); +} \ No newline at end of file diff --git a/CliWrap.Tests/ValidationSpecs.cs b/CliWrap.Tests/ValidationSpecs.cs index c987ae5e..1d94f4fa 100644 --- a/CliWrap.Tests/ValidationSpecs.cs +++ b/CliWrap.Tests/ValidationSpecs.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using CliWrap.Buffered; using CliWrap.Exceptions; +using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; using Xunit.Abstractions; @@ -17,9 +18,8 @@ public class ValidationSpecs public async Task I_can_execute_a_command_and_get_an_error_if_it_returns_a_non_zero_exit_code() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("exit") .Add("--code").Add(1) ); @@ -39,9 +39,8 @@ await cmd.ExecuteAsync() public async Task I_can_execute_a_command_with_buffering_and_get_a_detailed_exception_if_it_returns_a_non_zero_exit_code() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("exit") .Add("--code").Add(1) ); @@ -62,9 +61,8 @@ await cmd.ExecuteBufferedAsync() public async Task I_can_execute_a_command_without_validating_the_exit_code() { // Arrange - var cmd = Cli.Wrap("dotnet") + var cmd = Cli.Wrap(DummyScript.FilePath) .WithArguments(a => a - .Add(Dummy.Program.FilePath) .Add("exit") .Add("--code").Add(1) ) diff --git a/CliWrap.Tests/run-dummy.bat b/CliWrap.Tests/run-dummy.bat new file mode 100644 index 00000000..013140d0 --- /dev/null +++ b/CliWrap.Tests/run-dummy.bat @@ -0,0 +1,10 @@ +@ECHO OFF + +SET BASEDIR=%~dp0 +SET DUMMYNAME=CliWrap.Tests.Dummy + +IF EXIST "%BASEDIR%/%DUMMYNAME%.exe" ( + "%BASEDIR%/%DUMMYNAME%.exe" %* +) ELSE ( + dotnet "%BASEDIR%/%DUMMYNAME%.dll" %* +) \ No newline at end of file diff --git a/CliWrap.Tests/run-dummy.sh b/CliWrap.Tests/run-dummy.sh new file mode 100644 index 00000000..580537e0 --- /dev/null +++ b/CliWrap.Tests/run-dummy.sh @@ -0,0 +1,2 @@ +BASEDIR=$(dirname "$0") +dotnet "$BASEDIR/CliWrap.Tests.Dummy.dll" "$@" \ No newline at end of file From ea3ba2e7ae0f5728873624030c14bdeba68879fb Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 5 Aug 2023 23:13:02 +0300 Subject: [PATCH 08/57] Set execute permission on the dummy script --- CliWrap.Tests/CliWrap.Tests.csproj | 4 ++-- CliWrap.Tests/Utils/DummyScript.cs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index 233c3e46..e378f23a 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/CliWrap.Tests/Utils/DummyScript.cs b/CliWrap.Tests/Utils/DummyScript.cs index f3e76817..4fab1bc8 100644 --- a/CliWrap.Tests/Utils/DummyScript.cs +++ b/CliWrap.Tests/Utils/DummyScript.cs @@ -14,4 +14,10 @@ internal static class DummyScript public static string FilePath { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Path.Combine(DirPath, "run-dummy.bat") : Path.Combine(DirPath, "run-dummy.sh"); + + static DummyScript() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + File.SetUnixFileMode(FilePath, UnixFileMode.UserExecute); + } } \ No newline at end of file From d028359136bd41cf16db52905df32f29a35b9e0c Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 5 Aug 2023 23:19:53 +0300 Subject: [PATCH 09/57] Add shebang to the bash script --- CliWrap.Tests/run-dummy.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CliWrap.Tests/run-dummy.sh b/CliWrap.Tests/run-dummy.sh index 580537e0..b31bcf92 100644 --- a/CliWrap.Tests/run-dummy.sh +++ b/CliWrap.Tests/run-dummy.sh @@ -1,2 +1,4 @@ +#!/usr/bin/env bash + BASEDIR=$(dirname "$0") dotnet "$BASEDIR/CliWrap.Tests.Dummy.dll" "$@" \ No newline at end of file From 775068a49ce2e5e42f88886830b85787af33e94a Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 5 Aug 2023 23:26:44 +0300 Subject: [PATCH 10/57] Keep existing permissions on bash script file --- CliWrap.Tests/Utils/DummyScript.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CliWrap.Tests/Utils/DummyScript.cs b/CliWrap.Tests/Utils/DummyScript.cs index 4fab1bc8..3ee85681 100644 --- a/CliWrap.Tests/Utils/DummyScript.cs +++ b/CliWrap.Tests/Utils/DummyScript.cs @@ -18,6 +18,11 @@ internal static class DummyScript static DummyScript() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - File.SetUnixFileMode(FilePath, UnixFileMode.UserExecute); + { + File.SetUnixFileMode( + FilePath, + File.GetUnixFileMode(FilePath) | UnixFileMode.UserExecute + ); + } } } \ No newline at end of file From a77a16a88e84eecd2b8e156b8f65042ecd4b3863 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Mon, 7 Aug 2023 20:00:19 +0300 Subject: [PATCH 11/57] Use the dummy apphost directly, don't use scripts --- CliWrap.Tests.Dummy/Program.cs | 8 ++- CliWrap.Tests/BufferedSpecs.cs | 9 ++-- CliWrap.Tests/CancellationSpecs.cs | 24 ++++----- CliWrap.Tests/CliWrap.Tests.csproj | 2 - CliWrap.Tests/CredentialsSpecs.cs | 7 ++- CliWrap.Tests/EnvironmentSpecs.cs | 6 +-- CliWrap.Tests/EventStreamSpecs.cs | 5 +- CliWrap.Tests/ExecutionSpecs.cs | 9 ++-- CliWrap.Tests/LineBreakSpecs.cs | 9 ++-- CliWrap.Tests/PipingSpecs.cs | 78 +++++++++++++++--------------- CliWrap.Tests/Utils/DummyScript.cs | 28 ----------- CliWrap.Tests/ValidationSpecs.cs | 7 ++- CliWrap.Tests/run-dummy.bat | 10 ---- CliWrap.Tests/run-dummy.sh | 4 -- 14 files changed, 81 insertions(+), 125 deletions(-) delete mode 100644 CliWrap.Tests/Utils/DummyScript.cs delete mode 100644 CliWrap.Tests/run-dummy.bat delete mode 100644 CliWrap.Tests/run-dummy.sh diff --git a/CliWrap.Tests.Dummy/Program.cs b/CliWrap.Tests.Dummy/Program.cs index 6dd2c324..5b2db914 100644 --- a/CliWrap.Tests.Dummy/Program.cs +++ b/CliWrap.Tests.Dummy/Program.cs @@ -1,5 +1,7 @@ using System; +using System.IO; using System.Reflection; +using System.Runtime.InteropServices; using System.Threading.Tasks; using CliFx; @@ -7,7 +9,11 @@ namespace CliWrap.Tests.Dummy; public static class Program { - public static string FilePath { get; } = Assembly.GetExecutingAssembly().Location; + // Path to the apphost + public static string FilePath { get; } = Path.ChangeExtension( + Assembly.GetExecutingAssembly().Location, + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "exe" : null + ); public static async Task Main(string[] args) { diff --git a/CliWrap.Tests/BufferedSpecs.cs b/CliWrap.Tests/BufferedSpecs.cs index 6658a780..44f7ca20 100644 --- a/CliWrap.Tests/BufferedSpecs.cs +++ b/CliWrap.Tests/BufferedSpecs.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using CliWrap.Buffered; -using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; @@ -12,7 +11,7 @@ public class BufferedSpecs public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("echo") .Add("Hello stdout") @@ -31,7 +30,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout() public async Task I_can_execute_a_command_with_buffering_and_get_the_stderr() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("echo") .Add("Hello stderr") @@ -50,7 +49,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stderr() public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout_and_stderr() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("echo") .Add("Hello stdout and stderr") @@ -69,7 +68,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout_and_ public async Task I_can_execute_a_command_with_buffering_and_not_hang_on_large_stdout_and_stderr() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--target").Add("all") diff --git a/CliWrap.Tests/CancellationSpecs.cs b/CliWrap.Tests/CancellationSpecs.cs index 12abb180..3b649294 100644 --- a/CliWrap.Tests/CancellationSpecs.cs +++ b/CliWrap.Tests/CancellationSpecs.cs @@ -22,7 +22,7 @@ public async Task I_can_execute_a_command_and_cancel_it_immediately() var stdOutBuffer = new StringBuilder(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -48,7 +48,7 @@ public async Task I_can_execute_a_command_and_cancel_it_after_a_delay() var stdOutBuffer = new StringBuilder(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -87,7 +87,7 @@ void HandleStdOut(string line) PipeTarget.ToStringBuilder(stdOutBuffer) ); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -111,7 +111,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_immediate using var cts = new CancellationTokenSource(); cts.Cancel(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -132,7 +132,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_after_a_d using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -153,7 +153,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_gracefull using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -179,7 +179,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.Cancel(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -205,7 +205,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -231,7 +231,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -261,7 +261,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.Cancel(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -286,7 +286,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") @@ -311,7 +311,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("sleep") .Add("--duration").Add("00:00:20") diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index e378f23a..74b1841e 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -6,8 +6,6 @@ - - diff --git a/CliWrap.Tests/CredentialsSpecs.cs b/CliWrap.Tests/CredentialsSpecs.cs index 1f3978a4..41835eca 100644 --- a/CliWrap.Tests/CredentialsSpecs.cs +++ b/CliWrap.Tests/CredentialsSpecs.cs @@ -2,7 +2,6 @@ using System.ComponentModel; using System.Runtime.InteropServices; using System.Threading.Tasks; -using CliWrap.Tests.Utils; using Xunit; namespace CliWrap.Tests; @@ -21,7 +20,7 @@ public async Task I_can_execute_a_command_as_a_different_user() // that the credentials have been passed by getting an exception. // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithCredentials(c => c .SetUserName("user123") .SetPassword("pass123") @@ -44,7 +43,7 @@ public async Task I_can_execute_a_command_as_a_different_user_under_the_specifie // that the credentials have been passed by getting an exception. // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithCredentials(c => c .SetDomain("domain123") .SetUserName("user123") @@ -65,7 +64,7 @@ public async Task I_cannot_execute_a_command_as_a_different_user_on_a_system_tha ); // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithCredentials(c => c .SetUserName("user123") .SetPassword("pass123") diff --git a/CliWrap.Tests/EnvironmentSpecs.cs b/CliWrap.Tests/EnvironmentSpecs.cs index 9157d4c0..b40e778f 100644 --- a/CliWrap.Tests/EnvironmentSpecs.cs +++ b/CliWrap.Tests/EnvironmentSpecs.cs @@ -16,7 +16,7 @@ public async Task I_can_execute_a_command_with_a_custom_working_directory() // Arrange using var dir = TempDir.Create(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments("print cwd") .WithWorkingDirectory(dir.Path); @@ -37,7 +37,7 @@ public async Task I_can_execute_a_command_with_additional_environment_variables( ["hello"] = "world" }; - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments("print env") .WithEnvironmentVariables(env); @@ -64,7 +64,7 @@ public async Task I_can_execute_a_command_with_some_environment_variables_overwr using (TempEnvironmentVariable.Set(variableToOverwrite, "overwrite")) // will be overwritten using (TempEnvironmentVariable.Set(variableToUnset, "unset")) // will be unset { - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments("print env") .WithEnvironmentVariables(e => e .Set(variableToOverwrite, "overwritten") diff --git a/CliWrap.Tests/EventStreamSpecs.cs b/CliWrap.Tests/EventStreamSpecs.cs index 92a27749..d5370180 100644 --- a/CliWrap.Tests/EventStreamSpecs.cs +++ b/CliWrap.Tests/EventStreamSpecs.cs @@ -3,7 +3,6 @@ using System.Reactive.Linq; using System.Threading.Tasks; using CliWrap.EventStream; -using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; @@ -15,7 +14,7 @@ public class EventStreamSpecs public async Task I_can_execute_a_command_as_a_pull_based_event_stream() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--target").Add("all") @@ -40,7 +39,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream() public async Task I_can_execute_a_command_as_a_push_based_event_stream() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--target").Add("all") diff --git a/CliWrap.Tests/ExecutionSpecs.cs b/CliWrap.Tests/ExecutionSpecs.cs index c0f6bd20..0afa302e 100644 --- a/CliWrap.Tests/ExecutionSpecs.cs +++ b/CliWrap.Tests/ExecutionSpecs.cs @@ -1,7 +1,6 @@ using System; using System.ComponentModel; using System.Threading.Tasks; -using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; @@ -13,7 +12,7 @@ public class ExecutionSpecs public async Task I_can_execute_a_command_and_get_the_exit_code_and_execution_time() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath); + var cmd = Cli.Wrap(Dummy.Program.FilePath); // Act var result = await cmd.ExecuteAsync(); @@ -27,7 +26,7 @@ public async Task I_can_execute_a_command_and_get_the_exit_code_and_execution_ti public async Task I_can_execute_a_command_and_get_the_associated_process_ID() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath); + var cmd = Cli.Wrap(Dummy.Program.FilePath); // Act var task = cmd.ExecuteAsync(); @@ -42,7 +41,7 @@ public async Task I_can_execute_a_command_and_get_the_associated_process_ID() public async Task I_can_execute_a_command_with_a_configured_awaiter() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath); + var cmd = Cli.Wrap(Dummy.Program.FilePath); // Act & assert await cmd.ExecuteAsync().ConfigureAwait(false); @@ -52,7 +51,7 @@ public async Task I_can_execute_a_command_with_a_configured_awaiter() public async Task I_can_execute_a_command_and_not_hang_on_large_stdout_and_stderr() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--target").Add("all") diff --git a/CliWrap.Tests/LineBreakSpecs.cs b/CliWrap.Tests/LineBreakSpecs.cs index 18645057..f96fb43b 100644 --- a/CliWrap.Tests/LineBreakSpecs.cs +++ b/CliWrap.Tests/LineBreakSpecs.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Threading.Tasks; -using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; @@ -18,7 +17,7 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_newline() var cmd = data | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin") | + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | stdOutLines.Add; // Act @@ -42,7 +41,7 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_caret_return() var cmd = data | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin") | + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | stdOutLines.Add; // Act @@ -66,7 +65,7 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_caret_return_f var cmd = data | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin") | + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | stdOutLines.Add; // Act @@ -90,7 +89,7 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_newline_while_ var cmd = data | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin") | + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | stdOutLines.Add; // Act diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index 241c010d..f7292e97 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -23,7 +23,7 @@ await destination.WriteAsync("Hello world!"u8.ToArray(), cancellationToken) var cmd = source | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -42,7 +42,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_sync_anonymo var cmd = source | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -59,7 +59,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_stream() var cmd = stream | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -77,7 +77,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_file() var cmd = PipeSource.FromFile(file.Path) | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -94,7 +94,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_memory() var cmd = data | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -111,7 +111,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_byte_array() var cmd = data | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -126,7 +126,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_string() // Arrange var cmd = "Hello world!" | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -140,11 +140,11 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_another_comman { // Arrange var cmd = - Cli.Wrap(DummyScript.FilePath).WithArguments(a => a + Cli.Wrap(Dummy.Program.FilePath).WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000) ) | - Cli.Wrap(DummyScript.FilePath).WithArguments( + Cli.Wrap(Dummy.Program.FilePath).WithArguments( "print length stdin" ); @@ -161,14 +161,14 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_chain_of_com // Arrange var cmd = "Hello world" | - Cli.Wrap(DummyScript.FilePath).WithArguments( + Cli.Wrap(Dummy.Program.FilePath).WithArguments( "echo stdin" ) | - Cli.Wrap(DummyScript.FilePath).WithArguments(a => a + Cli.Wrap(Dummy.Program.FilePath).WithArguments(a => a .Add("echo stdin") .Add("--length").Add(5) ) | - Cli.Wrap(DummyScript.FilePath).WithArguments( + Cli.Wrap(Dummy.Program.FilePath).WithArguments( "print length stdin" ); @@ -190,7 +190,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_an_async_anon await origin.CopyToAsync(stream, cancellationToken) ); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000) @@ -214,7 +214,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_anonym origin.CopyTo(stream) ); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000) @@ -233,7 +233,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_stream() // Arrange await using var stream = new MemoryStream(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000) @@ -252,7 +252,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_file() // Arrange using var file = TempFile.Create(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000) @@ -272,7 +272,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_string_buil // Arrange var buffer = new StringBuilder(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("echo") .Add("Hello world!") @@ -297,7 +297,7 @@ async Task HandleStdOutAsync(string line) stdOutLinesCount++; } - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--lines").Add(100) @@ -322,7 +322,7 @@ async Task HandleStdOutAsync(string line, CancellationToken cancellationToken = stdOutLinesCount++; } - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--lines").Add(100) @@ -343,7 +343,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_delega void HandleStdOut(string line) => stdOutLinesCount++; - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--lines").Add(100) @@ -363,7 +363,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se await using var stdOut = new MemoryStream(); await using var stdErr = new MemoryStream(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--target").Add("all") @@ -385,7 +385,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_st var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("echo").Add("Hello world!") .Add("--target").Add("all") @@ -418,7 +418,7 @@ async Task HandleStdErrAsync(string line) stdErrLinesCount++; } - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--target").Add("all") @@ -452,7 +452,7 @@ async Task HandleStdErrAsync(string line, CancellationToken cancellationToken = stdErrLinesCount++; } - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--target").Add("all") @@ -477,7 +477,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se void HandleStdOut(string line) => stdOutLinesCount++; void HandleStdErr(string line) => stdErrLinesCount++; - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--target").Add("all") @@ -506,7 +506,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ PipeTarget.ToStream(stream3) ); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000) @@ -534,7 +534,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ PipeTarget.ToDelegate(_ => throw new Exception("Expected exception.")) ); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000) @@ -565,7 +565,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_hier ) ); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000)) | target; @@ -589,7 +589,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_stre // https://github.com/Tyrrrz/CliWrap/issues/81 // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--length").Add(1_000_000) @@ -622,7 +622,7 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou // Arrange await using var stream = new MemoryStream(); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--length").Add(100_000) @@ -648,7 +648,7 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou var delegateLines = new List(); void HandleStdOut(string line) => delegateLines.Add(line); - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate text") .Add("--lines").Add(100) @@ -671,7 +671,7 @@ public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_source_th // Arrange var cmd = PipeSource.FromFile("non-existing-file.txt") | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act & assert await Assert.ThrowsAnyAsync(async () => await cmd.ExecuteAsync()); @@ -681,7 +681,7 @@ public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_source_th public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_target_throws_an_exception() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("generate binary") .Add("--length").Add(100_000) @@ -695,7 +695,7 @@ public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_target_th public async Task I_can_execute_a_command_and_not_hang_if_the_process_expects_stdin_but_none_is_provided() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act await cmd.ExecuteAsync(); @@ -707,7 +707,7 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_expects_st // Arrange var cmd = Array.Empty() | - Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act await cmd.ExecuteAsync(); @@ -733,7 +733,7 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_only_parti // ReSharper disable once FunctionNeverReturns }); - var cmd = source | Cli.Wrap(DummyScript.FilePath) + var cmd = source | Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("echo stdin") .Add("--length").Add(100_000) @@ -755,7 +755,7 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_does_not_c await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken); }); - var cmd = source | Cli.Wrap(DummyScript.FilePath) + var cmd = source | Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("echo stdin") .Add("--length").Add(0) @@ -776,7 +776,7 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_does_not_c await Task.Delay(TimeSpan.FromSeconds(20), CancellationToken.None) ); - var cmd = source | Cli.Wrap(DummyScript.FilePath) + var cmd = source | Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("echo stdin") .Add("--length").Add(0) @@ -809,7 +809,7 @@ public async Task I_can_execute_a_command_and_not_hang_on_large_stdin_while_also } }); - var cmd = source | Cli.Wrap(DummyScript.FilePath).WithArguments("echo stdin"); + var cmd = source | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act & assert await cmd.ExecuteAsync(); diff --git a/CliWrap.Tests/Utils/DummyScript.cs b/CliWrap.Tests/Utils/DummyScript.cs deleted file mode 100644 index 3ee85681..00000000 --- a/CliWrap.Tests/Utils/DummyScript.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; - -namespace CliWrap.Tests.Utils; - -// Runs the dummy program either through the EXE file or via .NET CLI, depending on the platform -internal static class DummyScript -{ - public static string DirPath { get; } = - Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? - Directory.GetCurrentDirectory(); - - public static string FilePath { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? Path.Combine(DirPath, "run-dummy.bat") - : Path.Combine(DirPath, "run-dummy.sh"); - - static DummyScript() - { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - File.SetUnixFileMode( - FilePath, - File.GetUnixFileMode(FilePath) | UnixFileMode.UserExecute - ); - } - } -} \ No newline at end of file diff --git a/CliWrap.Tests/ValidationSpecs.cs b/CliWrap.Tests/ValidationSpecs.cs index 1d94f4fa..c39b47d7 100644 --- a/CliWrap.Tests/ValidationSpecs.cs +++ b/CliWrap.Tests/ValidationSpecs.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using CliWrap.Buffered; using CliWrap.Exceptions; -using CliWrap.Tests.Utils; using FluentAssertions; using Xunit; using Xunit.Abstractions; @@ -18,7 +17,7 @@ public class ValidationSpecs public async Task I_can_execute_a_command_and_get_an_error_if_it_returns_a_non_zero_exit_code() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("exit") .Add("--code").Add(1) @@ -39,7 +38,7 @@ await cmd.ExecuteAsync() public async Task I_can_execute_a_command_with_buffering_and_get_a_detailed_exception_if_it_returns_a_non_zero_exit_code() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("exit") .Add("--code").Add(1) @@ -61,7 +60,7 @@ await cmd.ExecuteBufferedAsync() public async Task I_can_execute_a_command_without_validating_the_exit_code() { // Arrange - var cmd = Cli.Wrap(DummyScript.FilePath) + var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a .Add("exit") .Add("--code").Add(1) diff --git a/CliWrap.Tests/run-dummy.bat b/CliWrap.Tests/run-dummy.bat deleted file mode 100644 index 013140d0..00000000 --- a/CliWrap.Tests/run-dummy.bat +++ /dev/null @@ -1,10 +0,0 @@ -@ECHO OFF - -SET BASEDIR=%~dp0 -SET DUMMYNAME=CliWrap.Tests.Dummy - -IF EXIST "%BASEDIR%/%DUMMYNAME%.exe" ( - "%BASEDIR%/%DUMMYNAME%.exe" %* -) ELSE ( - dotnet "%BASEDIR%/%DUMMYNAME%.dll" %* -) \ No newline at end of file diff --git a/CliWrap.Tests/run-dummy.sh b/CliWrap.Tests/run-dummy.sh deleted file mode 100644 index b31bcf92..00000000 --- a/CliWrap.Tests/run-dummy.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -BASEDIR=$(dirname "$0") -dotnet "$BASEDIR/CliWrap.Tests.Dummy.dll" "$@" \ No newline at end of file From d3292808bf6137889d86ccec41e81990fce7c3ee Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Mon, 7 Aug 2023 20:48:14 +0300 Subject: [PATCH 12/57] Run tests on .NET Framework too --- CliWrap.Benchmarks/CliWrap.Benchmarks.csproj | 2 +- .../CliWrap.Tests.Dummy.csproj | 5 ++- CliWrap.Tests/CliWrap.Tests.csproj | 8 +++-- CliWrap.Tests/PathResolutionSpecs.cs | 5 +-- CliWrap.Tests/PipingSpecs.cs | 36 +++++++++---------- CliWrap/CliWrap.csproj | 2 +- 6 files changed, 30 insertions(+), 28 deletions(-) diff --git a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj index bc652de1..9ab808bd 100644 --- a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj +++ b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj @@ -6,7 +6,7 @@ - + diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj index 1941a55a..f618a61d 100644 --- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj @@ -2,11 +2,14 @@ Exe - net7.0 + net7.0 + $(TargetFrameworks);net48 + + \ No newline at end of file diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index 74b1841e..476b26ef 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -1,7 +1,8 @@  - net7.0 + net7.0 + $(TargetFrameworks);net48 @@ -9,14 +10,15 @@ + - + + - diff --git a/CliWrap.Tests/PathResolutionSpecs.cs b/CliWrap.Tests/PathResolutionSpecs.cs index 6c4e3087..64769341 100644 --- a/CliWrap.Tests/PathResolutionSpecs.cs +++ b/CliWrap.Tests/PathResolutionSpecs.cs @@ -34,10 +34,7 @@ public async Task I_can_execute_a_command_on_a_script_using_its_short_name() // Arrange using var dir = TempDir.Create(); - await File.WriteAllTextAsync( - Path.Combine(dir.Path, "test-script.cmd"), - "@echo hello" - ); + File.WriteAllText(Path.Combine(dir.Path, "test-script.cmd"), "@echo hello"); using (TempEnvironmentVariable.ExtendPath(dir.Path)) { diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index f7292e97..b086d6c3 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -55,7 +55,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_sync_anonymo public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_stream() { // Arrange - await using var stream = new MemoryStream("Hello world!"u8.ToArray()); + using var stream = new MemoryStream("Hello world!"u8.ToArray()); var cmd = stream | @@ -73,7 +73,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_file() { // Arrange using var file = TempFile.Create(); - await File.WriteAllTextAsync(file.Path, "Hello world!"); + File.WriteAllText(file.Path, "Hello world!"); var cmd = PipeSource.FromFile(file.Path) | @@ -183,7 +183,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_chain_of_com public async Task I_can_execute_a_command_and_pipe_the_stdout_into_an_async_anonymous_target() { // Arrange - await using var stream = new MemoryStream(); + using var stream = new MemoryStream(); var target = PipeTarget.Create(async (origin, cancellationToken) => // ReSharper disable once AccessToDisposedClosure @@ -207,7 +207,7 @@ await origin.CopyToAsync(stream, cancellationToken) public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_anonymous_target() { // Arrange - await using var stream = new MemoryStream(); + using var stream = new MemoryStream(); var target = PipeTarget.Create(origin => // ReSharper disable once AccessToDisposedClosure @@ -231,7 +231,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_anonym public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_stream() { // Arrange - await using var stream = new MemoryStream(); + using var stream = new MemoryStream(); var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a @@ -360,8 +360,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_delega public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_separate_streams() { // Arrange - await using var stdOut = new MemoryStream(); - await using var stdErr = new MemoryStream(); + using var stdOut = new MemoryStream(); + using var stdErr = new MemoryStream(); var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a @@ -496,9 +496,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targets() { // Arrange - await using var stream1 = new MemoryStream(); - await using var stream2 = new MemoryStream(); - await using var stream3 = new MemoryStream(); + using var stream1 = new MemoryStream(); + using var stream2 = new MemoryStream(); + using var stream3 = new MemoryStream(); var target = PipeTarget.Merge( PipeTarget.ToStream(stream1), @@ -549,10 +549,10 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_hierarchical_targets() { // Arrange - await using var stream1 = new MemoryStream(); - await using var stream2 = new MemoryStream(); - await using var stream3 = new MemoryStream(); - await using var stream4 = new MemoryStream(); + 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), @@ -598,8 +598,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_stre ); // Act - await using var mergedStream1 = new MemoryStream(); - await using var mergedStream2 = new MemoryStream(); + using var mergedStream1 = new MemoryStream(); + using var mergedStream2 = new MemoryStream(); await (cmd | PipeTarget.Merge( PipeTarget.ToStream(mergedStream1), PipeTarget.ToStream(mergedStream2)) @@ -608,7 +608,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_stre // Assert // Run without merging to get the expected byte array (random seed is constant) - await using var unmergedStream = new MemoryStream(); + using var unmergedStream = new MemoryStream(); await (cmd | PipeTarget.ToStream(unmergedStream)).ExecuteAsync(); unmergedStream.Length.Should().Be(1_000_000); @@ -620,7 +620,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_stre public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdout_into_a_stream() { // Arrange - await using var stream = new MemoryStream(); + using var stream = new MemoryStream(); var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(a => a diff --git a/CliWrap/CliWrap.csproj b/CliWrap/CliWrap.csproj index b17064b2..103f07fa 100644 --- a/CliWrap/CliWrap.csproj +++ b/CliWrap/CliWrap.csproj @@ -34,7 +34,7 @@ - + From e36481861b8dc56ce80e9ddc122d726f029bbfc6 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:03:45 +0300 Subject: [PATCH 13/57] Work around strong-naming issues on netfx --- CliWrap.Tests/xunit.runner.json | 1 + 1 file changed, 1 insertion(+) diff --git a/CliWrap.Tests/xunit.runner.json b/CliWrap.Tests/xunit.runner.json index 186540e3..f41def88 100644 --- a/CliWrap.Tests/xunit.runner.json +++ b/CliWrap.Tests/xunit.runner.json @@ -1,5 +1,6 @@ { "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "appDomain": "denied", "methodDisplayOptions": "all", "methodDisplay": "method" } \ No newline at end of file From 26090474ea6ccc122b401ec4d16eecae410c0356 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:32:52 +0300 Subject: [PATCH 14/57] Use nameof when referring to symbols --- CliWrap/Command.Execution.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CliWrap/Command.Execution.cs b/CliWrap/Command.Execution.cs index 91fdb201..6d5e15c0 100644 --- a/CliWrap/Command.Execution.cs +++ b/CliWrap/Command.Execution.cs @@ -264,7 +264,7 @@ Command execution failed because the underlying process ({process.Name}#{process Command: {TargetFilePath} {Arguments} - You can suppress this validation by calling `WithValidation(CommandResultValidation.None)` on the command. + You can suppress this validation by calling `{nameof(WithValidation)}({nameof(CommandResultValidation)}.{nameof(CommandResultValidation.None)})` on the command. """ ); } From dbae19b912ae2662c653095164e4c40457b3f84f Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 12 Aug 2023 20:59:22 +0300 Subject: [PATCH 15/57] Update issue forms --- .github/ISSUE_TEMPLATE/bug-report.yml | 111 ++++++++++++++------- .github/ISSUE_TEMPLATE/config.yml | 8 +- .github/ISSUE_TEMPLATE/feature-request.yml | 45 ++++++--- 3 files changed, 107 insertions(+), 57 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index eb25bf13..a1ac0a99 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,42 +1,77 @@ -name: 🐞 Bug report +name: 🐛 Bug report description: Report broken functionality. labels: [bug] body: -- type: markdown - attributes: - value: | - 🧐 **Guidelines:** - - - Search through [existing issues](https://github.com/Tyrrrz/CliWrap/issues?q=is%3Aissue) first to ensure that this bug has not been reported before. - - Write a descriptive title for your issue. Avoid generic or vague titles such as "Something's not working" or "A couple of problems". - - Keep your issue focused on one single problem. If you have multiple bug reports, please create separate issues for each of them. - - Provide as much context as possible in the details section. Include screenshots, screen recordings, links, references, or anything else you may consider relevant. - - If you want to ask a question instead of reporting a bug, please use [discussions](https://github.com/Tyrrrz/CliWrap/discussions/new) instead. - -- type: input - attributes: - label: Version - description: Which version of CliWrap does this bug affect? - placeholder: ver X.Y.Z - validations: - required: true - -- type: textarea - attributes: - label: Details - description: Clear and thorough explanation of the bug. - placeholder: I was doing X expecting Y to happen, but Z happened instead. - validations: - required: true - -- type: textarea - attributes: - label: Steps to reproduce - description: Minimum steps required to reproduce the bug. - placeholder: | - - Step 1 - - Step 2 - - Step 3 - validations: - required: true \ No newline at end of file + - type: markdown + attributes: + value: | + ### 📖 Important + + - Avoid generic or vague titles such as "Something's not working" or "A couple of problems" — be as descriptive as possible. + - Keep your issue focused on one single problem. If you have multiple bug reports, please create separate issues for each of them. + - Issues should represent **complete and actionable** work items. If you are unsure about something or have a question, please start a [discussion](https://github.com/Tyrrrz/CliWrap/discussions/new) instead. + - Remember that CliWrap is an open-source project funded by the community. If you find it useful, **please consider [donating](https://tyrrrz.me/donate) to support its development**. + + ___ + + - type: input + attributes: + label: Version + description: Which version of the package does this bug affect? + placeholder: v1.0.0 + validations: + required: true + + - type: input + attributes: + label: Platform + description: What platform do you experience this bug on? + placeholder: .NET 7.0 / Windows 11 + validations: + required: true + + - type: textarea + attributes: + label: Steps to reproduce + description: > + Minimum steps required to reproduce the bug, including prerequisites, code snippets, or other relevant items. + The information provided in this field must be readily actionable, meaning that anyone should be able to reproduce the bug by following these steps. + If the reproduction steps are too complex to fit in this field, please provide a link to a repository instead. + placeholder: | + - Step 1 + - Step 2 + - Step 3 + validations: + required: true + + - type: textarea + attributes: + label: Details + description: Clear and thorough explanation of the bug, including any additional information you may find relevant. + placeholder: | + - Expected behavior: ... + - Actual behavior: ... + validations: + required: true + + - type: checkboxes + attributes: + label: Checklist + description: Quick list of checks to ensure that everything is in order. + options: + - label: I have looked through existing open and closed issues to make sure that this bug has not been reported before + required: true + - label: I have provided a descriptive title for this issue + required: true + - label: I have made sure that that this bug is reproducible on the latest version of the package + required: true + - label: I have provided all the information needed to reproduce this bug as efficiently as possible + required: true + - label: I have sponsored this project + required: false + + - type: markdown + attributes: + value: | + If you are struggling to provide actionable reproduction steps, or if something else is preventing you from creating a complete bug report, please start a [discussion](https://github.com/Tyrrrz/CliWrap/discussions/new) instead. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 7c704bed..486365b4 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ blank_issues_enabled: false contact_links: - - name: 💬 Discord server - url: https://discord.gg/2SUWKFnHSm - about: Chat with the project community. - name: 🗨 Discussions url: https://github.com/Tyrrrz/CliWrap/discussions/new - about: Ask and answer questions. \ No newline at end of file + about: Ask and answer questions. + - name: 💬 Discord server + url: https://discord.gg/2SUWKFnHSm + about: Chat with the community. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index b63017ed..18319d33 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -3,20 +3,35 @@ description: Request a new feature. labels: [enhancement] body: -- type: markdown - attributes: - value: | - 🧐 **Guidelines:** + - type: markdown + attributes: + value: | + ### 📖 Important - - Search through [existing issues](https://github.com/Tyrrrz/CliWrap/issues?q=is%3Aissue) first to ensure that this feature has not been requested before. - - Write a descriptive title for your issue. Avoid generic or vague titles such as "Some suggestions" or "Ideas for improvement". - - Keep your issue focused on one single problem. If you have multiple feature requests, please create separate issues for each of them. - - Provide as much context as possible in the details section. Include screenshots, screen recordings, links, references, or anything else you may consider relevant. - - If you want to ask a question instead of requesting a feature, please use [discussions](https://github.com/Tyrrrz/CliWrap/discussions/new) instead. + - Avoid generic or vague titles such as "Something's not working" or "A couple of problems" — be as descriptive as possible. + - Keep your issue focused on one single problem. If you have multiple feature requests, please create separate issues for each of them. + - Issues should represent **complete and actionable** work items. If you are unsure about something or have a question, please start a [discussion](https://github.com/Tyrrrz/CliWrap/discussions/new) instead. + - Remember that CliWrap is an open-source project funded by the community. If you find it useful, **please consider [donating](https://tyrrrz.me/donate) to support its development**. -- type: textarea - attributes: - label: Details - description: Clear and thorough explanation of the feature you have in mind. - validations: - required: true \ No newline at end of file + ___ + + - type: textarea + attributes: + label: Details + description: Clear and thorough explanation of the feature you have in mind. + validations: + required: true + + - type: checkboxes + attributes: + label: Checklist + description: Quick list of checks to ensure that everything is in order. + options: + - label: I have looked through existing open and closed issues to make sure that this feature has not been requested before + required: true + - label: I have provided a descriptive title for this issue + required: true + - label: I am aware that even valid feature requests may be rejected if they do not align with the project's goals + required: true + - label: I have sponsored this project + required: false From a4660ccdce9ba1103552de626a178557751d6f45 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sun, 13 Aug 2023 13:40:47 +0300 Subject: [PATCH 16/57] Update issue forms --- .github/ISSUE_TEMPLATE/bug-report.yml | 6 ++---- .github/ISSUE_TEMPLATE/feature-request.yml | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index a1ac0a99..1ad1e7e5 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -6,12 +6,10 @@ body: - type: markdown attributes: value: | - ### 📖 Important - - Avoid generic or vague titles such as "Something's not working" or "A couple of problems" — be as descriptive as possible. - - Keep your issue focused on one single problem. If you have multiple bug reports, please create separate issues for each of them. + - Keep your issue focused on one single problem. If you have multiple bug reports, please create a separate issue for each of them. - Issues should represent **complete and actionable** work items. If you are unsure about something or have a question, please start a [discussion](https://github.com/Tyrrrz/CliWrap/discussions/new) instead. - - Remember that CliWrap is an open-source project funded by the community. If you find it useful, **please consider [donating](https://tyrrrz.me/donate) to support its development**. + - Remember that **CliWrap** is an open-source project funded by the community. If you find it useful, **please consider [donating](https://tyrrrz.me/donate) to support its development**. ___ diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 18319d33..992eacf0 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -6,12 +6,10 @@ body: - type: markdown attributes: value: | - ### 📖 Important - - Avoid generic or vague titles such as "Something's not working" or "A couple of problems" — be as descriptive as possible. - - Keep your issue focused on one single problem. If you have multiple feature requests, please create separate issues for each of them. + - Keep your issue focused on one single problem. If you have multiple feature requests, please create a separate issue for each of them. - Issues should represent **complete and actionable** work items. If you are unsure about something or have a question, please start a [discussion](https://github.com/Tyrrrz/CliWrap/discussions/new) instead. - - Remember that CliWrap is an open-source project funded by the community. If you find it useful, **please consider [donating](https://tyrrrz.me/donate) to support its development**. + - Remember that **CliWrap** is an open-source project funded by the community. If you find it useful, **please consider [donating](https://tyrrrz.me/donate) to support its development**. ___ From a6497bdbacd94598586fc7d4ca09403e2754ff6e Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Mon, 14 Aug 2023 21:38:17 +0300 Subject: [PATCH 17/57] Update readme --- Readme.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index 4d285dfe..de9d8b53 100644 --- a/Readme.md +++ b/Readme.md @@ -104,7 +104,7 @@ var stdErrBuffer = new StringBuilder(); var result = await Cli.Wrap("path/to/exe") .WithArguments(new[] {"--foo", "bar"}) .WithWorkingDirectory("work/dir/path") - // This can be simplified with ExecuteBufferedAsync() + // This can be simplified with `ExecuteBufferedAsync()` .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer)) .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer)) .ExecuteAsync(); @@ -166,7 +166,7 @@ Sets the command-line arguments passed to the child process. ```csharp var cmd = Cli.Wrap("git") // Each element is formatted as a separate argument. - // Equivalent to: git commit -m "my commit" + // Equivalent to: `git commit -m "my commit"` .WithArguments(new[] {"commit", "-m", "my commit"}); ``` @@ -175,7 +175,7 @@ var cmd = Cli.Wrap("git") ```csharp var cmd = Cli.Wrap("git") // Each Add(...) call takes care of formatting automatically. - // Equivalent to: git clone https://github.com/Tyrrrz/CliWrap --depth 20 + // Equivalent to: `git clone https://github.com/Tyrrrz/CliWrap --depth 20` .WithArguments(args => args .Add("clone") .Add("https://github.com/Tyrrrz/CliWrap") @@ -187,7 +187,7 @@ var cmd = Cli.Wrap("git") ```csharp var cmd = Cli.Wrap("git") // Arguments can also be constructed in an imperative fashion. - // Equivalent to: git push --force + // Equivalent to: `git push --force` .WithArguments(args => { args.Add("push"); @@ -205,7 +205,8 @@ var cmd = Cli.Wrap("git") ```csharp var cmd = Cli.Wrap("git") - // Avoid using this overload + // Avoid using this overload unless you really have to. + // Equivalent to: `git commit -m "my commit"` .WithArguments("commit -m \"my commit\""); ``` @@ -682,7 +683,7 @@ forcefulCts.CancelAfter(TimeSpan.FromSeconds(10)); // Cancel gracefully after a timeout of 7 seconds. // If the process takes too long to respond to graceful // cancellation, it will get killed by forceful cancellation -// 3 seoncds later (configured above). +// 3 seoncds later (as configured above). gracefulCts.CancelAfter(TimeSpan.FromSeconds(7)); var result = await Cli.Wrap("foo").ExecuteAsync(forcefulCts.Token, gracefulCts.Token); From 4d7d0371f8fca8abfc13ccc271423bdb9d58eb53 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Fri, 18 Aug 2023 01:40:30 +0300 Subject: [PATCH 18/57] Update issue forms --- .github/ISSUE_TEMPLATE/bug-report.yml | 6 +++--- .github/ISSUE_TEMPLATE/feature-request.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 1ad1e7e5..0a21d7b9 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -16,7 +16,7 @@ body: - type: input attributes: label: Version - description: Which version of the package does this bug affect? + description: Which version of the package does this bug affect? Make sure you're not using an outdated version. placeholder: v1.0.0 validations: required: true @@ -24,7 +24,7 @@ body: - type: input attributes: label: Platform - description: What platform do you experience this bug on? + description: Which platform do you experience this bug on? placeholder: .NET 7.0 / Windows 11 validations: required: true @@ -58,7 +58,7 @@ body: label: Checklist description: Quick list of checks to ensure that everything is in order. options: - - label: I have looked through existing open and closed issues to make sure that this bug has not been reported before + - label: I have looked through existing issues to make sure that this bug has not been reported before required: true - label: I have provided a descriptive title for this issue required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 992eacf0..9d33afac 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -25,7 +25,7 @@ body: label: Checklist description: Quick list of checks to ensure that everything is in order. options: - - label: I have looked through existing open and closed issues to make sure that this feature has not been requested before + - label: I have looked through existing issues to make sure that this feature has not been requested before required: true - label: I have provided a descriptive title for this issue required: true From 06f92c4276efde3143ab6b8776b0c853a57f00b5 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 22 Aug 2023 21:22:51 +0300 Subject: [PATCH 19/57] Use CSharpier --- CliWrap.Benchmarks/BasicBenchmarks.cs | 2 +- CliWrap.Benchmarks/BufferingBenchmarks.cs | 11 +- CliWrap.Benchmarks/CliWrap.Benchmarks.csproj | 1 + .../PipeFromStreamBenchmarks.cs | 2 +- .../PipeToMultipleStreamsBenchmark.cs | 7 +- CliWrap.Benchmarks/PipeToStreamBenchmarks.cs | 2 +- CliWrap.Benchmarks/Program.cs | 2 +- .../PullEventStreamBenchmarks.cs | 11 +- .../PushEventStreamBenchmarks.cs | 30 +- CliWrap.Signaler/CliWrap.Signaler.csproj | 1 + CliWrap.Signaler/Program.cs | 14 +- CliWrap.Signaler/Utils/NativeMethods.cs | 7 +- .../CliWrap.Tests.Dummy.csproj | 1 + CliWrap.Tests.Dummy/Commands/EchoCommand.cs | 2 +- .../Commands/EchoStdInCommand.cs | 2 +- CliWrap.Tests.Dummy/Commands/ExitCommand.cs | 2 +- .../Commands/GenerateBinaryCommand.cs | 2 +- .../Commands/GenerateTextCommand.cs | 6 +- .../PrintEnvironmentVariablesCommand.cs | 2 +- .../Commands/PrintStdInLengthCommand.cs | 2 +- .../Commands/PrintWorkingDirCommand.cs | 2 +- .../Commands/Shared/OutputTarget.cs | 2 +- CliWrap.Tests.Dummy/Commands/SleepCommand.cs | 2 +- CliWrap.Tests.Dummy/Program.cs | 11 +- CliWrap.Tests/BufferedSpecs.cs | 26 +- CliWrap.Tests/CancellationSpecs.cs | 172 ++++---- CliWrap.Tests/CliWrap.Tests.csproj | 1 + CliWrap.Tests/ConfigurationSpecs.cs | 93 +++-- CliWrap.Tests/CredentialsSpecs.cs | 24 +- CliWrap.Tests/EnvironmentSpecs.cs | 37 +- CliWrap.Tests/EventStreamSpecs.cs | 14 +- CliWrap.Tests/ExecutionSpecs.cs | 20 +- CliWrap.Tests/LineBreakSpecs.cs | 44 +- CliWrap.Tests/PathResolutionSpecs.cs | 2 +- CliWrap.Tests/PipingSpecs.cs | 387 ++++++++---------- CliWrap.Tests/Utils/ProcessEx.cs | 2 +- CliWrap.Tests/Utils/TempDir.cs | 12 +- .../Utils/TempEnvironmentVariable.cs | 6 +- CliWrap.Tests/Utils/TempFile.cs | 17 +- CliWrap.Tests/ValidationSpecs.cs | 25 +- CliWrap/CliWrap.csproj | 1 + 41 files changed, 457 insertions(+), 552 deletions(-) diff --git a/CliWrap.Benchmarks/BasicBenchmarks.cs b/CliWrap.Benchmarks/BasicBenchmarks.cs index 43156f92..64d16735 100644 --- a/CliWrap.Benchmarks/BasicBenchmarks.cs +++ b/CliWrap.Benchmarks/BasicBenchmarks.cs @@ -31,4 +31,4 @@ public async Task MedallionShell() var result = await Medallion.Shell.Command.Run(FilePath, Args).Task; return result.ExitCode; } -} \ No newline at end of file +} diff --git a/CliWrap.Benchmarks/BufferingBenchmarks.cs b/CliWrap.Benchmarks/BufferingBenchmarks.cs index fb3b6710..14e017bc 100644 --- a/CliWrap.Benchmarks/BufferingBenchmarks.cs +++ b/CliWrap.Benchmarks/BufferingBenchmarks.cs @@ -11,7 +11,8 @@ namespace CliWrap.Benchmarks; public class BufferingBenchmarks { private const string FilePath = "dotnet"; - private static readonly string Args = $"{Tests.Dummy.Program.FilePath} generate text --lines 1000"; + private static readonly string Args = + $"{Tests.Dummy.Program.FilePath} generate text --lines 1000"; [Benchmark(Baseline = true)] public async Task<(string, string)> CliWrap() @@ -41,8 +42,10 @@ public class BufferingBenchmarks [Benchmark] public async Task<(string, string)> ProcessX() { - var (_, stdOutStream, stdErrStream) = Cysharp.Diagnostics.ProcessX - .GetDualAsyncEnumerable(FilePath, arguments: Args); + var (_, stdOutStream, stdErrStream) = Cysharp.Diagnostics.ProcessX.GetDualAsyncEnumerable( + FilePath, + arguments: Args + ); var stdOutTask = stdOutStream.ToTask(); var stdErrTask = stdErrStream.ToTask(); @@ -54,4 +57,4 @@ public class BufferingBenchmarks string.Join(Environment.NewLine, stdErrTask.Result) ); } -} \ No newline at end of file +} diff --git a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj index 9ab808bd..afaf8f47 100644 --- a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj +++ b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj @@ -7,6 +7,7 @@ + diff --git a/CliWrap.Benchmarks/PipeFromStreamBenchmarks.cs b/CliWrap.Benchmarks/PipeFromStreamBenchmarks.cs index 54ed54c7..b3584e40 100644 --- a/CliWrap.Benchmarks/PipeFromStreamBenchmarks.cs +++ b/CliWrap.Benchmarks/PipeFromStreamBenchmarks.cs @@ -32,4 +32,4 @@ public async Task MedallionShell() return stream; } -} \ No newline at end of file +} diff --git a/CliWrap.Benchmarks/PipeToMultipleStreamsBenchmark.cs b/CliWrap.Benchmarks/PipeToMultipleStreamsBenchmark.cs index 140be1a9..4301b022 100644 --- a/CliWrap.Benchmarks/PipeToMultipleStreamsBenchmark.cs +++ b/CliWrap.Benchmarks/PipeToMultipleStreamsBenchmark.cs @@ -17,14 +17,11 @@ public class PipeToMultipleStreamsBenchmark await using var stream1 = new MemoryStream(); await using var stream2 = new MemoryStream(); - var target = PipeTarget.Merge( - PipeTarget.ToStream(stream1), - PipeTarget.ToStream(stream2) - ); + var target = PipeTarget.Merge(PipeTarget.ToStream(stream1), PipeTarget.ToStream(stream2)); var command = Cli.Wrap(FilePath).WithArguments(Args) | target; await command.ExecuteAsync(); return (stream1, stream2); } -} \ No newline at end of file +} diff --git a/CliWrap.Benchmarks/PipeToStreamBenchmarks.cs b/CliWrap.Benchmarks/PipeToStreamBenchmarks.cs index aa9d4b98..d4ea787c 100644 --- a/CliWrap.Benchmarks/PipeToStreamBenchmarks.cs +++ b/CliWrap.Benchmarks/PipeToStreamBenchmarks.cs @@ -32,4 +32,4 @@ public async Task MedallionShell() return stream; } -} \ No newline at end of file +} diff --git a/CliWrap.Benchmarks/Program.cs b/CliWrap.Benchmarks/Program.cs index 28d81d2f..985ae71e 100644 --- a/CliWrap.Benchmarks/Program.cs +++ b/CliWrap.Benchmarks/Program.cs @@ -6,4 +6,4 @@ namespace CliWrap.Benchmarks; public static class Program { public static void Main() => BenchmarkRunner.Run(Assembly.GetExecutingAssembly()); -} \ No newline at end of file +} diff --git a/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs b/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs index 47409f67..52b16eec 100644 --- a/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs +++ b/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs @@ -9,7 +9,8 @@ namespace CliWrap.Benchmarks; public class PullEventStreamBenchmarks { private const string FilePath = "dotnet"; - private static readonly string Args = $"{Tests.Dummy.Program.FilePath} generate text --lines 1000"; + private static readonly string Args = + $"{Tests.Dummy.Program.FilePath} generate text --lines 1000"; [Benchmark(Baseline = true)] public async Task CliWrap() @@ -37,8 +38,10 @@ public async Task ProcessX() { var counter = 0; - var (_, stdOutStream, stdErrStream) = Cysharp.Diagnostics.ProcessX - .GetDualAsyncEnumerable(FilePath, arguments: Args); + var (_, stdOutStream, stdErrStream) = Cysharp.Diagnostics.ProcessX.GetDualAsyncEnumerable( + FilePath, + arguments: Args + ); var consumeStdOutTask = Task.Run(async () => { @@ -60,4 +63,4 @@ public async Task ProcessX() return counter; } -} \ No newline at end of file +} diff --git a/CliWrap.Benchmarks/PushEventStreamBenchmarks.cs b/CliWrap.Benchmarks/PushEventStreamBenchmarks.cs index f2f2cb64..9c5fe641 100644 --- a/CliWrap.Benchmarks/PushEventStreamBenchmarks.cs +++ b/CliWrap.Benchmarks/PushEventStreamBenchmarks.cs @@ -10,26 +10,30 @@ namespace CliWrap.Benchmarks; public class PushEventStreamBenchmarks { private const string FilePath = "dotnet"; - private static readonly string Args = $"{Tests.Dummy.Program.FilePath} generate text --lines 1000"; + private static readonly string Args = + $"{Tests.Dummy.Program.FilePath} generate text --lines 1000"; [Benchmark(Baseline = true)] public async Task CliWrap() { var counter = 0; - await Cli.Wrap(FilePath).WithArguments(Args).Observe().ForEachAsync(cmdEvent => - { - switch (cmdEvent) + await Cli.Wrap(FilePath) + .WithArguments(Args) + .Observe() + .ForEachAsync(cmdEvent => { - case StandardOutputCommandEvent: - counter++; - break; - case StandardErrorCommandEvent: - counter++; - break; - } - }); + switch (cmdEvent) + { + case StandardOutputCommandEvent: + counter++; + break; + case StandardErrorCommandEvent: + counter++; + break; + } + }); return counter; } -} \ No newline at end of file +} diff --git a/CliWrap.Signaler/CliWrap.Signaler.csproj b/CliWrap.Signaler/CliWrap.Signaler.csproj index dcc00361..f42fa349 100644 --- a/CliWrap.Signaler/CliWrap.Signaler.csproj +++ b/CliWrap.Signaler/CliWrap.Signaler.csproj @@ -6,6 +6,7 @@ + diff --git a/CliWrap.Signaler/Program.cs b/CliWrap.Signaler/Program.cs index c7b78d72..e1abba1c 100644 --- a/CliWrap.Signaler/Program.cs +++ b/CliWrap.Signaler/Program.cs @@ -21,14 +21,14 @@ public static int Main(string[] args) var isSuccess = // Attach to the target process's console - NativeMethods.Windows.AttachConsole((uint) processId) && + NativeMethods.Windows.AttachConsole((uint)processId) + && // Ignore signals on ourselves so we can return a proper exit code - NativeMethods.Windows.SetConsoleCtrlHandler(null, true) && + NativeMethods.Windows.SetConsoleCtrlHandler(null, true) + && // Send the signal to the console - NativeMethods.Windows.GenerateConsoleCtrlEvent((uint) signalId, 0); + NativeMethods.Windows.GenerateConsoleCtrlEvent((uint)signalId, 0); - return isSuccess - ? 0 - : Marshal.GetLastWin32Error(); + return isSuccess ? 0 : Marshal.GetLastWin32Error(); } -} \ No newline at end of file +} diff --git a/CliWrap.Signaler/Utils/NativeMethods.cs b/CliWrap.Signaler/Utils/NativeMethods.cs index 479b1fcf..c3699636 100644 --- a/CliWrap.Signaler/Utils/NativeMethods.cs +++ b/CliWrap.Signaler/Utils/NativeMethods.cs @@ -15,9 +15,12 @@ public static class Windows public delegate bool ConsoleCtrlDelegate(uint dwCtrlEvent); [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate? handlerRoutine, bool add); + public static extern bool SetConsoleCtrlHandler( + ConsoleCtrlDelegate? handlerRoutine, + bool add + ); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj index f618a61d..c3a83576 100644 --- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj @@ -8,6 +8,7 @@ + diff --git a/CliWrap.Tests.Dummy/Commands/EchoCommand.cs b/CliWrap.Tests.Dummy/Commands/EchoCommand.cs index 13286cd3..97aaced1 100644 --- a/CliWrap.Tests.Dummy/Commands/EchoCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/EchoCommand.cs @@ -24,4 +24,4 @@ public async ValueTask ExecuteAsync(IConsole console) foreach (var writer in console.GetWriters(Target)) await writer.WriteLineAsync(string.Join(Separator, Items)); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Commands/EchoStdInCommand.cs b/CliWrap.Tests.Dummy/Commands/EchoStdInCommand.cs index 464307d8..54459b6f 100644 --- a/CliWrap.Tests.Dummy/Commands/EchoStdInCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/EchoStdInCommand.cs @@ -36,4 +36,4 @@ public async ValueTask ExecuteAsync(IConsole console) totalBytesRead += bytesRead; } } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Commands/ExitCommand.cs b/CliWrap.Tests.Dummy/Commands/ExitCommand.cs index d3db1cfb..9651661a 100644 --- a/CliWrap.Tests.Dummy/Commands/ExitCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/ExitCommand.cs @@ -19,4 +19,4 @@ public ValueTask ExecuteAsync(IConsole console) return default; } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Commands/GenerateBinaryCommand.cs b/CliWrap.Tests.Dummy/Commands/GenerateBinaryCommand.cs index e4ff4828..20c62004 100644 --- a/CliWrap.Tests.Dummy/Commands/GenerateBinaryCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/GenerateBinaryCommand.cs @@ -40,4 +40,4 @@ public async ValueTask ExecuteAsync(IConsole console) totalBytesGenerated += bytesWanted; } } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Commands/GenerateTextCommand.cs b/CliWrap.Tests.Dummy/Commands/GenerateTextCommand.cs index e51019c7..603c2110 100644 --- a/CliWrap.Tests.Dummy/Commands/GenerateTextCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/GenerateTextCommand.cs @@ -33,13 +33,11 @@ public async ValueTask ExecuteAsync(IConsole console) for (var i = 0; i < Length; i++) { - buffer.Append( - _allowedChars[_random.Next(0, _allowedChars.Length)] - ); + buffer.Append(_allowedChars[_random.Next(0, _allowedChars.Length)]); } foreach (var writer in console.GetWriters(Target)) await writer.WriteLineAsync(buffer.ToString()); } } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Commands/PrintEnvironmentVariablesCommand.cs b/CliWrap.Tests.Dummy/Commands/PrintEnvironmentVariablesCommand.cs index e717fb48..37da600e 100644 --- a/CliWrap.Tests.Dummy/Commands/PrintEnvironmentVariablesCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/PrintEnvironmentVariablesCommand.cs @@ -16,4 +16,4 @@ public async ValueTask ExecuteAsync(IConsole console) foreach (var (name, value) in Environment.GetEnvironmentVariables().Cast()) await console.Output.WriteLineAsync($"[{name}] = {value}"); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Commands/PrintStdInLengthCommand.cs b/CliWrap.Tests.Dummy/Commands/PrintStdInLengthCommand.cs index 6458f797..28e13b8c 100644 --- a/CliWrap.Tests.Dummy/Commands/PrintStdInLengthCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/PrintStdInLengthCommand.cs @@ -31,4 +31,4 @@ public async ValueTask ExecuteAsync(IConsole console) foreach (var writer in console.GetWriters(Target)) await writer.WriteLineAsync(totalBytesRead.ToString(CultureInfo.InvariantCulture)); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Commands/PrintWorkingDirCommand.cs b/CliWrap.Tests.Dummy/Commands/PrintWorkingDirCommand.cs index f23b36be..487b9e74 100644 --- a/CliWrap.Tests.Dummy/Commands/PrintWorkingDirCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/PrintWorkingDirCommand.cs @@ -13,4 +13,4 @@ public async ValueTask ExecuteAsync(IConsole console) { await console.Output.WriteAsync(Directory.GetCurrentDirectory()); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Commands/Shared/OutputTarget.cs b/CliWrap.Tests.Dummy/Commands/Shared/OutputTarget.cs index 64e43d3d..47dfe65b 100644 --- a/CliWrap.Tests.Dummy/Commands/Shared/OutputTarget.cs +++ b/CliWrap.Tests.Dummy/Commands/Shared/OutputTarget.cs @@ -22,4 +22,4 @@ public static IEnumerable GetWriters(this IConsole console, Outpu if (target.HasFlag(OutputTarget.StdErr)) yield return console.Error; } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Commands/SleepCommand.cs b/CliWrap.Tests.Dummy/Commands/SleepCommand.cs index c441ec3e..5c22cf43 100644 --- a/CliWrap.Tests.Dummy/Commands/SleepCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/SleepCommand.cs @@ -29,4 +29,4 @@ public async ValueTask ExecuteAsync(IConsole console) await console.Output.WriteLineAsync("Done."); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests.Dummy/Program.cs b/CliWrap.Tests.Dummy/Program.cs index 5b2db914..76faa28d 100644 --- a/CliWrap.Tests.Dummy/Program.cs +++ b/CliWrap.Tests.Dummy/Program.cs @@ -10,10 +10,11 @@ namespace CliWrap.Tests.Dummy; public static class Program { // Path to the apphost - public static string FilePath { get; } = Path.ChangeExtension( - Assembly.GetExecutingAssembly().Location, - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "exe" : null - ); + public static string FilePath { get; } = + Path.ChangeExtension( + Assembly.GetExecutingAssembly().Location, + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "exe" : null + ); public static async Task Main(string[] args) { @@ -28,4 +29,4 @@ public static async Task Main(string[] args) .Build() .RunAsync(args); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/BufferedSpecs.cs b/CliWrap.Tests/BufferedSpecs.cs index 44f7ca20..5b8baaeb 100644 --- a/CliWrap.Tests/BufferedSpecs.cs +++ b/CliWrap.Tests/BufferedSpecs.cs @@ -12,11 +12,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("echo") - .Add("Hello stdout") - .Add("--target").Add("stdout") - ); + .WithArguments(a => a.Add("echo").Add("Hello stdout").Add("--target").Add("stdout")); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -31,11 +27,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stderr() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("echo") - .Add("Hello stderr") - .Add("--target").Add("stderr") - ); + .WithArguments(a => a.Add("echo").Add("Hello stderr").Add("--target").Add("stderr")); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -50,10 +42,8 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout_and_ { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("echo") - .Add("Hello stdout and stderr") - .Add("--target").Add("all") + .WithArguments( + a => a.Add("echo").Add("Hello stdout and stderr").Add("--target").Add("all") ); // Act @@ -69,10 +59,8 @@ public async Task I_can_execute_a_command_with_buffering_and_not_hang_on_large_s { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--target").Add("all") - .Add("--length").Add(100_000) + .WithArguments( + a => a.Add("generate text").Add("--target").Add("all").Add("--length").Add(100_000) ); // Act @@ -82,4 +70,4 @@ public async Task I_can_execute_a_command_with_buffering_and_not_hang_on_large_s result.StandardOutput.Should().NotBeNullOrWhiteSpace(); result.StandardError.Should().NotBeNullOrWhiteSpace(); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/CancellationSpecs.cs b/CliWrap.Tests/CancellationSpecs.cs index 3b649294..19c78ff9 100644 --- a/CliWrap.Tests/CancellationSpecs.cs +++ b/CliWrap.Tests/CancellationSpecs.cs @@ -22,11 +22,10 @@ public async Task I_can_execute_a_command_and_cancel_it_immediately() var stdOutBuffer = new StringBuilder(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ) | stdOutBuffer; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")) + | stdOutBuffer; // Act var task = cmd.ExecuteAsync(cts.Token); @@ -48,11 +47,10 @@ public async Task I_can_execute_a_command_and_cancel_it_after_a_delay() var stdOutBuffer = new StringBuilder(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ) | stdOutBuffer; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")) + | stdOutBuffer; // Act var task = cmd.ExecuteAsync(cts.Token); @@ -87,11 +85,9 @@ void HandleStdOut(string line) PipeTarget.ToStringBuilder(stdOutBuffer) ); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ) | target; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")) | target; // Act var task = cmd.ExecuteAsync(CancellationToken.None, cts.Token); @@ -112,14 +108,11 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_immediate cts.Cancel(); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ); + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); // Act & assert - var ex = await Assert.ThrowsAnyAsync(async () => - await cmd.ExecuteBufferedAsync(cts.Token) + var ex = await Assert.ThrowsAnyAsync( + async () => await cmd.ExecuteBufferedAsync(cts.Token) ); ex.CancellationToken.Should().Be(cts.Token); @@ -133,14 +126,11 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_after_a_d cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ); + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); // Act & assert - var ex = await Assert.ThrowsAnyAsync(async () => - await cmd.ExecuteBufferedAsync(cts.Token) + var ex = await Assert.ThrowsAnyAsync( + async () => await cmd.ExecuteBufferedAsync(cts.Token) ); ex.CancellationToken.Should().Be(cts.Token); @@ -154,19 +144,17 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_gracefull cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ); + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); // Act & assert - var ex = await Assert.ThrowsAnyAsync(async () => - await cmd.ExecuteBufferedAsync( - Console.OutputEncoding, - Console.OutputEncoding, - CancellationToken.None, - cts.Token - ) + var ex = await Assert.ThrowsAnyAsync( + async () => + await cmd.ExecuteBufferedAsync( + Console.OutputEncoding, + Console.OutputEncoding, + CancellationToken.None, + cts.Token + ) ); ex.CancellationToken.Should().Be(cts.Token); @@ -180,10 +168,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance cts.Cancel(); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ); + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -206,10 +191,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ); + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -232,19 +214,19 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ); + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => { - await foreach (var cmdEvent in cmd.ListenAsync( - Console.OutputEncoding, - Console.OutputEncoding, - CancellationToken.None, - cts.Token)) + await foreach ( + var cmdEvent in cmd.ListenAsync( + Console.OutputEncoding, + Console.OutputEncoding, + CancellationToken.None, + cts.Token + ) + ) { if (cmdEvent is StandardOutputCommandEvent stdOutEvent) stdOutEvent.Text.Should().NotContain("Done."); @@ -262,18 +244,20 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance cts.Cancel(); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ); + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); // Act & assert - var ex = await Assert.ThrowsAnyAsync(async () => - await cmd.Observe(cts.Token).ForEachAsync(cmdEvent => - { - if (cmdEvent is StandardOutputCommandEvent stdOutEvent) - stdOutEvent.Text.Should().NotContain("Done."); - }, CancellationToken.None) + var ex = await Assert.ThrowsAnyAsync( + async () => + await cmd.Observe(cts.Token) + .ForEachAsync( + cmdEvent => + { + if (cmdEvent is StandardOutputCommandEvent stdOutEvent) + stdOutEvent.Text.Should().NotContain("Done."); + }, + CancellationToken.None + ) ); ex.CancellationToken.Should().Be(cts.Token); @@ -287,18 +271,20 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ); + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); // Act & assert - var ex = await Assert.ThrowsAnyAsync(async () => - await cmd.Observe(cts.Token).ForEachAsync(cmdEvent => - { - if (cmdEvent is StandardOutputCommandEvent stdOutEvent) - stdOutEvent.Text.Should().NotContain("Done."); - }, CancellationToken.None) + var ex = await Assert.ThrowsAnyAsync( + async () => + await cmd.Observe(cts.Token) + .ForEachAsync( + cmdEvent => + { + if (cmdEvent is StandardOutputCommandEvent stdOutEvent) + stdOutEvent.Text.Should().NotContain("Done."); + }, + CancellationToken.None + ) ); ex.CancellationToken.Should().Be(cts.Token); @@ -312,27 +298,27 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("sleep") - .Add("--duration").Add("00:00:20") - ); + .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); // Act & assert - var ex = await Assert.ThrowsAnyAsync(async () => - await cmd - .Observe( - Console.OutputEncoding, - Console.OutputEncoding, - CancellationToken.None, - cts.Token - ) - .ForEachAsync(cmdEvent => - { - if (cmdEvent is StandardOutputCommandEvent stdOutEvent) - stdOutEvent.Text.Should().NotContain("Done."); - }, CancellationToken.None) + var ex = await Assert.ThrowsAnyAsync( + async () => + await cmd.Observe( + Console.OutputEncoding, + Console.OutputEncoding, + CancellationToken.None, + cts.Token + ) + .ForEachAsync( + cmdEvent => + { + if (cmdEvent is StandardOutputCommandEvent stdOutEvent) + stdOutEvent.Text.Should().NotContain("Done."); + }, + CancellationToken.None + ) ); ex.CancellationToken.Should().Be(cts.Token); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index 476b26ef..ed0d04e1 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -11,6 +11,7 @@ + diff --git a/CliWrap.Tests/ConfigurationSpecs.cs b/CliWrap.Tests/ConfigurationSpecs.cs index 7fa242bc..38438959 100644 --- a/CliWrap.Tests/ConfigurationSpecs.cs +++ b/CliWrap.Tests/ConfigurationSpecs.cs @@ -78,19 +78,22 @@ public void I_can_configure_the_command_line_arguments_using_a_builder() var original = Cli.Wrap("foo").WithArguments("xxx"); // Act - var modified = original.WithArguments(b => b - .Add("-a") - .Add("foo bar") - .Add("\"foo\\\\bar\"") - .Add(3.14) - .Add(new[] { "foo", "bar" }) - .Add(new IFormattable[] { -5, 89.13 }) + var modified = original.WithArguments( + b => + b.Add("-a") + .Add("foo bar") + .Add("\"foo\\\\bar\"") + .Add(3.14) + .Add(new[] { "foo", "bar" }) + .Add(new IFormattable[] { -5, 89.13 }) ); // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Arguments)); original.Arguments.Should().NotBe(modified.Arguments); - modified.Arguments.Should().Be("-a \"foo bar\" \"\\\"foo\\\\bar\\\"\" 3.14 foo bar -5 89.13"); + modified.Arguments + .Should() + .Be("-a \"foo bar\" \"\\\"foo\\\\bar\\\"\" 3.14 foo bar -5 89.13"); } [Fact(Timeout = 15000)] @@ -115,12 +118,16 @@ public void I_can_configure_the_user_credentials() var original = Cli.Wrap("foo").WithCredentials(new Credentials("xxx", "xxx", "xxx")); // Act - var modified = original.WithCredentials(new Credentials("domain", "username", "password", true)); + var modified = original.WithCredentials( + new Credentials("domain", "username", "password", true) + ); // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Credentials)); original.Credentials.Should().NotBe(modified.Credentials); - modified.Credentials.Should().BeEquivalentTo(new Credentials("domain", "username", "password", true)); + modified.Credentials + .Should() + .BeEquivalentTo(new Credentials("domain", "username", "password", true)); } [Fact(Timeout = 15000)] @@ -130,17 +137,20 @@ public void I_can_configure_the_user_credentials_using_a_builder() var original = Cli.Wrap("foo").WithCredentials(new Credentials("xxx", "xxx", "xxx")); // Act - var modified = original.WithCredentials(c => c - .SetDomain("domain") - .SetUserName("username") - .SetPassword("password") - .LoadUserProfile() + var modified = original.WithCredentials( + c => + c.SetDomain("domain") + .SetUserName("username") + .SetPassword("password") + .LoadUserProfile() ); // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Credentials)); original.Credentials.Should().NotBe(modified.Credentials); - modified.Credentials.Should().BeEquivalentTo(new Credentials("domain", "username", "password", true)); + modified.Credentials + .Should() + .BeEquivalentTo(new Credentials("domain", "username", "password", true)); } [Fact(Timeout = 15000)] @@ -150,20 +160,18 @@ public void I_can_configure_the_environment_variables() var original = Cli.Wrap("foo").WithEnvironmentVariables(e => e.Set("xxx", "xxx")); // Act - var modified = original.WithEnvironmentVariables(new Dictionary - { - ["name"] = "value", - ["key"] = "door" - }); + var modified = original.WithEnvironmentVariables( + new Dictionary { ["name"] = "value", ["key"] = "door" } + ); // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.EnvironmentVariables)); original.EnvironmentVariables.Should().NotBeEquivalentTo(modified.EnvironmentVariables); - modified.EnvironmentVariables.Should().BeEquivalentTo(new Dictionary - { - ["name"] = "value", - ["key"] = "door" - }); + modified.EnvironmentVariables + .Should() + .BeEquivalentTo( + new Dictionary { ["name"] = "value", ["key"] = "door" } + ); } [Fact(Timeout = 15000)] @@ -173,26 +181,27 @@ public void I_can_configure_the_environment_variables_using_a_builder() var original = Cli.Wrap("foo").WithEnvironmentVariables(e => e.Set("xxx", "xxx")); // Act - var modified = original.WithEnvironmentVariables(b => b - .Set("name", "value") - .Set("key", "door") - .Set(new Dictionary - { - ["zzz"] = "yyy", - ["aaa"] = "bbb" - }) + var modified = original.WithEnvironmentVariables( + b => + b.Set("name", "value") + .Set("key", "door") + .Set(new Dictionary { ["zzz"] = "yyy", ["aaa"] = "bbb" }) ); // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.EnvironmentVariables)); original.EnvironmentVariables.Should().NotBeEquivalentTo(modified.EnvironmentVariables); - modified.EnvironmentVariables.Should().BeEquivalentTo(new Dictionary - { - ["name"] = "value", - ["key"] = "door", - ["zzz"] = "yyy", - ["aaa"] = "bbb" - }); + modified.EnvironmentVariables + .Should() + .BeEquivalentTo( + new Dictionary + { + ["name"] = "value", + ["key"] = "door", + ["zzz"] = "yyy", + ["aaa"] = "bbb" + } + ); } [Fact(Timeout = 15000)] @@ -251,4 +260,4 @@ public void I_can_configure_the_stderr_pipe() original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.StandardErrorPipe)); original.StandardErrorPipe.Should().NotBeSameAs(modified.StandardErrorPipe); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/CredentialsSpecs.cs b/CliWrap.Tests/CredentialsSpecs.cs index 41835eca..ea595c0d 100644 --- a/CliWrap.Tests/CredentialsSpecs.cs +++ b/CliWrap.Tests/CredentialsSpecs.cs @@ -21,10 +21,8 @@ public async Task I_can_execute_a_command_as_a_different_user() // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithCredentials(c => c - .SetUserName("user123") - .SetPassword("pass123") - .LoadUserProfile() + .WithCredentials( + c => c.SetUserName("user123").SetPassword("pass123").LoadUserProfile() ); // Act & assert @@ -44,11 +42,12 @@ public async Task I_can_execute_a_command_as_a_different_user_under_the_specifie // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithCredentials(c => c - .SetDomain("domain123") - .SetUserName("user123") - .SetPassword("pass123") - .LoadUserProfile() + .WithCredentials( + c => + c.SetDomain("domain123") + .SetUserName("user123") + .SetPassword("pass123") + .LoadUserProfile() ); // Act & assert @@ -65,12 +64,9 @@ public async Task I_cannot_execute_a_command_as_a_different_user_on_a_system_tha // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithCredentials(c => c - .SetUserName("user123") - .SetPassword("pass123") - ); + .WithCredentials(c => c.SetUserName("user123").SetPassword("pass123")); // Act & assert await Assert.ThrowsAsync(() => cmd.ExecuteAsync()); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/EnvironmentSpecs.cs b/CliWrap.Tests/EnvironmentSpecs.cs index b40e778f..549d7a60 100644 --- a/CliWrap.Tests/EnvironmentSpecs.cs +++ b/CliWrap.Tests/EnvironmentSpecs.cs @@ -31,11 +31,7 @@ public async Task I_can_execute_a_command_with_a_custom_working_directory() public async Task I_can_execute_a_command_with_additional_environment_variables() { // Arrange - var env = new Dictionary - { - ["foo"] = "bar", - ["hello"] = "world" - }; + var env = new Dictionary { ["foo"] = "bar", ["hello"] = "world" }; var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments("print env") @@ -45,10 +41,7 @@ public async Task I_can_execute_a_command_with_additional_environment_variables( var result = await cmd.ExecuteBufferedAsync(); // Assert - result.StandardOutput.Trim().Should().ContainAll( - "[foo] = bar", - "[hello] = world" - ); + result.StandardOutput.Trim().Should().ContainAll("[foo] = bar", "[hello] = world"); } [Fact(Timeout = 15000)] @@ -66,24 +59,26 @@ public async Task I_can_execute_a_command_with_some_environment_variables_overwr { var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments("print env") - .WithEnvironmentVariables(e => e - .Set(variableToOverwrite, "overwritten") - .Set(variableToUnset, null) + .WithEnvironmentVariables( + e => e.Set(variableToOverwrite, "overwritten").Set(variableToUnset, null) ); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert - result.StandardOutput.Trim().Should().ContainAll( - $"[{variableToKeep}] = keep", - $"[{variableToOverwrite}] = overwritten" - ); + result.StandardOutput + .Trim() + .Should() + .ContainAll($"[{variableToKeep}] = keep", $"[{variableToOverwrite}] = overwritten"); - result.StandardOutput.Trim().Should().NotContainAny( - $"[{variableToOverwrite}] = overwrite", - $"[{variableToUnset}] = unset" - ); + result.StandardOutput + .Trim() + .Should() + .NotContainAny( + $"[{variableToOverwrite}] = overwrite", + $"[{variableToUnset}] = unset" + ); } } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/EventStreamSpecs.cs b/CliWrap.Tests/EventStreamSpecs.cs index d5370180..ca365f5e 100644 --- a/CliWrap.Tests/EventStreamSpecs.cs +++ b/CliWrap.Tests/EventStreamSpecs.cs @@ -15,10 +15,8 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--target").Add("all") - .Add("--lines").Add(100) + .WithArguments( + a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) ); // Act @@ -40,10 +38,8 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--target").Add("all") - .Add("--lines").Add(100) + .WithArguments( + a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) ); // Act @@ -57,4 +53,4 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream() events.OfType().Should().ContainSingle(); events.OfType().Single().ExitCode.Should().Be(0); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/ExecutionSpecs.cs b/CliWrap.Tests/ExecutionSpecs.cs index 0afa302e..37800710 100644 --- a/CliWrap.Tests/ExecutionSpecs.cs +++ b/CliWrap.Tests/ExecutionSpecs.cs @@ -52,10 +52,9 @@ public async Task I_can_execute_a_command_and_not_hang_on_large_stdout_and_stder { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--target").Add("all") - .Add("--length").Add(100_000) + .WithArguments( + a => + a.Add("generate binary").Add("--target").Add("all").Add("--length").Add(100_000) ); // Act & assert @@ -72,11 +71,12 @@ public void I_cannot_execute_a_command_on_a_file_that_does_not_exist() // Should throw synchronously // https://github.com/Tyrrrz/CliWrap/issues/139 - Assert.ThrowsAny(() => - // xUnit tells us to use ThrowsAnyAsync(...) instead for async methods, - // but we're actually interested in the sync portion of this method. - // So cast the result to object to avoid the warning. - (object)cmd.ExecuteAsync() + Assert.ThrowsAny( + () => + // xUnit tells us to use ThrowsAnyAsync(...) instead for async methods, + // but we're actually interested in the sync portion of this method. + // So cast the result to object to avoid the warning. + (object)cmd.ExecuteAsync() ); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/LineBreakSpecs.cs b/CliWrap.Tests/LineBreakSpecs.cs index f96fb43b..7950e65d 100644 --- a/CliWrap.Tests/LineBreakSpecs.cs +++ b/CliWrap.Tests/LineBreakSpecs.cs @@ -16,19 +16,13 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_newline() var stdOutLines = new List(); var cmd = - data | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | - stdOutLines.Add; + data | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | stdOutLines.Add; // Act await cmd.ExecuteAsync(); // Assert - stdOutLines.Should().Equal( - "Foo", - "Bar", - "Baz" - ); + stdOutLines.Should().Equal("Foo", "Bar", "Baz"); } [Fact(Timeout = 15000)] @@ -40,19 +34,13 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_caret_return() var stdOutLines = new List(); var cmd = - data | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | - stdOutLines.Add; + data | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | stdOutLines.Add; // Act await cmd.ExecuteAsync(); // Assert - stdOutLines.Should().Equal( - "Foo", - "Bar", - "Baz" - ); + stdOutLines.Should().Equal("Foo", "Bar", "Baz"); } [Fact(Timeout = 15000)] @@ -64,19 +52,13 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_caret_return_f var stdOutLines = new List(); var cmd = - data | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | - stdOutLines.Add; + data | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | stdOutLines.Add; // Act await cmd.ExecuteAsync(); // Assert - stdOutLines.Should().Equal( - "Foo", - "Bar", - "Baz" - ); + stdOutLines.Should().Equal("Foo", "Bar", "Baz"); } [Fact(Timeout = 15000)] @@ -88,20 +70,12 @@ public async Task I_can_execute_a_command_and_split_the_stdout_by_newline_while_ var stdOutLines = new List(); var cmd = - data | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | - stdOutLines.Add; + data | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | stdOutLines.Add; // Act await cmd.ExecuteAsync(); // Assert - stdOutLines.Should().Equal( - "Foo", - "", - "Bar", - "", - "Baz" - ); + stdOutLines.Should().Equal("Foo", "", "Bar", "", "Baz"); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/PathResolutionSpecs.cs b/CliWrap.Tests/PathResolutionSpecs.cs index 64769341..0dd511d1 100644 --- a/CliWrap.Tests/PathResolutionSpecs.cs +++ b/CliWrap.Tests/PathResolutionSpecs.cs @@ -48,4 +48,4 @@ public async Task I_can_execute_a_command_on_a_script_using_its_short_name() result.StandardOutput.Trim().Should().Be("hello"); } } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index b086d6c3..7a495d8f 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -17,13 +17,12 @@ public class PipingSpecs 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 source = PipeSource.Create( + async (destination, cancellationToken) => + await destination.WriteAsync("Hello world!"u8.ToArray(), cancellationToken) ); - var cmd = - source | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); + var cmd = source | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -36,13 +35,9 @@ await destination.WriteAsync("Hello world!"u8.ToArray(), cancellationToken) 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) - ); + var source = PipeSource.Create(destination => destination.Write("Hello world!"u8)); - var cmd = - source | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); + var cmd = source | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -57,9 +52,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_stream() // Arrange using var stream = new MemoryStream("Hello world!"u8.ToArray()); - var cmd = - stream | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); + var cmd = stream | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -76,8 +69,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_file() File.WriteAllText(file.Path, "Hello world!"); var cmd = - PipeSource.FromFile(file.Path) | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); + PipeSource.FromFile(file.Path) + | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -92,9 +85,7 @@ 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 | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); + var cmd = data | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -109,9 +100,7 @@ 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 | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); + var cmd = data | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -124,9 +113,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_byte_array() public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_string() { // Arrange - var cmd = - "Hello world!" | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); + var cmd = "Hello world!" | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -140,13 +127,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_another_comman { // Arrange var cmd = - Cli.Wrap(Dummy.Program.FilePath).WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(100_000) - ) | - Cli.Wrap(Dummy.Program.FilePath).WithArguments( - "print length stdin" - ); + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) + | Cli.Wrap(Dummy.Program.FilePath).WithArguments("print length stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -160,17 +143,11 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_chain_of_com { // Arrange var cmd = - "Hello world" | - Cli.Wrap(Dummy.Program.FilePath).WithArguments( - "echo stdin" - ) | - Cli.Wrap(Dummy.Program.FilePath).WithArguments(a => a - .Add("echo stdin") - .Add("--length").Add(5) - ) | - Cli.Wrap(Dummy.Program.FilePath).WithArguments( - "print length stdin" - ); + "Hello world" + | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") + | Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("echo stdin").Add("--length").Add(5)) + | Cli.Wrap(Dummy.Program.FilePath).WithArguments("print length stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -185,16 +162,15 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_an_async_anon // Arrange using var stream = new MemoryStream(); - var target = PipeTarget.Create(async (origin, cancellationToken) => - // ReSharper disable once AccessToDisposedClosure - await origin.CopyToAsync(stream, cancellationToken) + var target = PipeTarget.Create( + async (origin, cancellationToken) => + // ReSharper disable once AccessToDisposedClosure + await origin.CopyToAsync(stream, cancellationToken) ); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(100_000) - ) | target; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; // Act await cmd.ExecuteAsync(); @@ -209,16 +185,15 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_anonym // Arrange using var stream = new MemoryStream(); - var target = PipeTarget.Create(origin => - // ReSharper disable once AccessToDisposedClosure - origin.CopyTo(stream) + var target = PipeTarget.Create( + origin => + // ReSharper disable once AccessToDisposedClosure + origin.CopyTo(stream) ); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(100_000) - ) | target; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; // Act await cmd.ExecuteAsync(); @@ -233,11 +208,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_stream() // Arrange using var stream = new MemoryStream(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(100_000) - ) | stream; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | stream; // Act await cmd.ExecuteAsync(); @@ -252,11 +225,10 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_file() // Arrange using var file = TempFile.Create(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(100_000) - ) | PipeTarget.ToFile(file.Path); + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) + | PipeTarget.ToFile(file.Path); // Act await cmd.ExecuteAsync(); @@ -272,11 +244,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_string_buil // Arrange var buffer = new StringBuilder(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("echo") - .Add("Hello world!") - ) | buffer; + var cmd = + Cli.Wrap(Dummy.Program.FilePath).WithArguments(a => a.Add("echo").Add("Hello world!")) + | buffer; // Act await cmd.ExecuteAsync(); @@ -297,11 +267,10 @@ async Task HandleStdOutAsync(string line) stdOutLinesCount++; } - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--lines").Add(100) - ) | HandleStdOutAsync; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate text").Add("--lines").Add(100)) + | HandleStdOutAsync; // Act await cmd.ExecuteAsync(); @@ -322,11 +291,10 @@ async Task HandleStdOutAsync(string line, CancellationToken cancellationToken = stdOutLinesCount++; } - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--lines").Add(100) - ) | HandleStdOutAsync; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate text").Add("--lines").Add(100)) + | HandleStdOutAsync; // Act await cmd.ExecuteAsync(); @@ -343,11 +311,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_delega void HandleStdOut(string line) => stdOutLinesCount++; - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--lines").Add(100) - ) | HandleStdOut; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate text").Add("--lines").Add(100)) | HandleStdOut; // Act await cmd.ExecuteAsync(); @@ -363,12 +329,16 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se using var stdOut = new MemoryStream(); using var stdErr = new MemoryStream(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--target").Add("all") - .Add("--length").Add(100_000) - ) | (stdOut, stdErr); + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments( + a => + a.Add("generate binary") + .Add("--target") + .Add("all") + .Add("--length") + .Add(100_000) + ) | (stdOut, stdErr); // Act await cmd.ExecuteAsync(); @@ -385,11 +355,10 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_st var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("echo").Add("Hello world!") - .Add("--target").Add("all") - ) | (stdOutBuffer, stdErrBuffer); + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("echo").Add("Hello world!").Add("--target").Add("all")) + | (stdOutBuffer, stdErrBuffer); // Act await cmd.ExecuteAsync(); @@ -418,12 +387,11 @@ async Task HandleStdErrAsync(string line) stdErrLinesCount++; } - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--target").Add("all") - .Add("--lines").Add(100) - ) | (HandleStdOutAsync, HandleStdErrAsync); + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments( + a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) + ) | (HandleStdOutAsync, HandleStdErrAsync); // Act await cmd.ExecuteAsync(); @@ -452,12 +420,11 @@ async Task HandleStdErrAsync(string line, CancellationToken cancellationToken = stdErrLinesCount++; } - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--target").Add("all") - .Add("--lines").Add(100) - ) | (HandleStdOutAsync, HandleStdErrAsync); + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments( + a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) + ) | (HandleStdOutAsync, HandleStdErrAsync); // Act await cmd.ExecuteAsync(); @@ -477,12 +444,11 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se void HandleStdOut(string line) => stdOutLinesCount++; void HandleStdErr(string line) => stdErrLinesCount++; - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--target").Add("all") - .Add("--lines").Add(100) - ) | (HandleStdOut, HandleStdErr); + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments( + a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) + ) | (HandleStdOut, HandleStdErr); // Act await cmd.ExecuteAsync(); @@ -506,11 +472,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ PipeTarget.ToStream(stream3) ); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(100_000) - ) | target; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; // Act await cmd.ExecuteAsync(); @@ -534,11 +498,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ PipeTarget.ToDelegate(_ => throw new Exception("Expected exception.")) ); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(100_000) - ) | target; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => await cmd.ExecuteAsync()); @@ -558,17 +520,13 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_hier PipeTarget.ToStream(stream1), PipeTarget.Merge( PipeTarget.ToStream(stream2), - PipeTarget.Merge( - PipeTarget.ToStream(stream3), - PipeTarget.ToStream(stream4) - ) + PipeTarget.Merge(PipeTarget.ToStream(stream3), PipeTarget.ToStream(stream4)) ) ); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(100_000)) | target; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; // Act await cmd.ExecuteAsync(); @@ -590,20 +548,26 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_stre // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(1_000_000) - // Buffer needs to be >= BufferSizes.Stream to fail - .Add("--buffer").Add(100_000) + .WithArguments( + a => + a.Add("generate binary") + .Add("--length") + .Add(1_000_000) + // Buffer needs to be >= BufferSizes.Stream to fail + .Add("--buffer") + .Add(100_000) ); // Act using var mergedStream1 = new MemoryStream(); using var mergedStream2 = new MemoryStream(); - await (cmd | PipeTarget.Merge( + await ( + cmd + | PipeTarget.Merge( PipeTarget.ToStream(mergedStream1), - PipeTarget.ToStream(mergedStream2)) - ).ExecuteAsync(); + PipeTarget.ToStream(mergedStream2) + ) + ).ExecuteAsync(); // Assert @@ -622,11 +586,9 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou // Arrange using var stream = new MemoryStream(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--length").Add(100_000) - ) | stream; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate text").Add("--length").Add(100_000)) | stream; // Act var result = await cmd.ExecuteBufferedAsync(); @@ -648,21 +610,22 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou var delegateLines = new List(); void HandleStdOut(string line) => delegateLines.Add(line); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate text") - .Add("--lines").Add(100) - ) | HandleStdOut; + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate text").Add("--lines").Add(100)) | HandleStdOut; // Act var result = await cmd.ExecuteBufferedAsync(); // Assert - delegateLines.Should().Equal( - result - .StandardOutput - .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries) - ); + delegateLines + .Should() + .Equal( + result.StandardOutput.Split( + Environment.NewLine, + StringSplitOptions.RemoveEmptyEntries + ) + ); } [Fact(Timeout = 15000)] @@ -670,8 +633,8 @@ public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_source_th { // Arrange var cmd = - PipeSource.FromFile("non-existing-file.txt") | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); + PipeSource.FromFile("non-existing-file.txt") + | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act & assert await Assert.ThrowsAnyAsync(async () => await cmd.ExecuteAsync()); @@ -681,11 +644,10 @@ public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_source_th public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_target_throws_an_exception() { // Arrange - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("generate binary") - .Add("--length").Add(100_000) - ) | PipeTarget.ToFile("non-existing-directory/file.txt"); + var cmd = + Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) + | PipeTarget.ToFile("non-existing-directory/file.txt"); // Act & assert await Assert.ThrowsAnyAsync(async () => await cmd.ExecuteAsync()); @@ -706,8 +668,7 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_expects_st { // Arrange var cmd = - Array.Empty() | - Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); + Array.Empty() | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act await cmd.ExecuteAsync(); @@ -721,23 +682,24 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_only_parti // Arrange var random = new Random(1234567); - var source = PipeSource.Create(async (destination, cancellationToken) => - { - var buffer = new byte[256]; - while (true) + var source = PipeSource.Create( + async (destination, cancellationToken) => { - random.NextBytes(buffer); - await destination.WriteAsync(buffer, cancellationToken); + var buffer = new byte[256]; + while (true) + { + random.NextBytes(buffer); + await destination.WriteAsync(buffer, cancellationToken); + } + + // ReSharper disable once FunctionNeverReturns } + ); - // ReSharper disable once FunctionNeverReturns - }); - - var cmd = source | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("echo stdin") - .Add("--length").Add(100_000) - ); + var cmd = + source + | Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("echo stdin").Add("--length").Add(100_000)); // Act & assert await cmd.ExecuteAsync(); @@ -749,17 +711,18 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_does_not_c // https://github.com/Tyrrrz/CliWrap/issues/74 // Arrange - var source = PipeSource.Create(async (_, cancellationToken) => - { - // Not infinite, but long enough - await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken); - }); - - var cmd = source | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("echo stdin") - .Add("--length").Add(0) - ); + var source = PipeSource.Create( + async (_, cancellationToken) => + { + // Not infinite, but long enough + await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken); + } + ); + + var cmd = + source + | Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("echo stdin").Add("--length").Add(0)); // Act & assert await cmd.ExecuteAsync(); @@ -771,16 +734,16 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_does_not_c // https://github.com/Tyrrrz/CliWrap/issues/74 // Arrange - var source = PipeSource.Create(async (_, _) => - // Not infinite, but long enough - await Task.Delay(TimeSpan.FromSeconds(20), CancellationToken.None) + var source = PipeSource.Create( + async (_, _) => + // Not infinite, but long enough + await Task.Delay(TimeSpan.FromSeconds(20), CancellationToken.None) ); - var cmd = source | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("echo stdin") - .Add("--length").Add(0) - ); + var cmd = + source + | Cli.Wrap(Dummy.Program.FilePath) + .WithArguments(a => a.Add("echo stdin").Add("--length").Add(0)); // Act & assert await cmd.ExecuteAsync(); @@ -795,23 +758,25 @@ public async Task I_can_execute_a_command_and_not_hang_on_large_stdin_while_also var random = new Random(1234567); var bytesRemaining = 100_000; - var source = PipeSource.Create(async (destination, cancellationToken) => - { - var buffer = new byte[256]; - while (bytesRemaining > 0) + var source = PipeSource.Create( + async (destination, cancellationToken) => { - random.NextBytes(buffer); + var buffer = new byte[256]; + while (bytesRemaining > 0) + { + random.NextBytes(buffer); - var count = Math.Min(bytesRemaining, buffer.Length); - await destination.WriteAsync(buffer.AsMemory()[..count], cancellationToken); + var count = Math.Min(bytesRemaining, buffer.Length); + await destination.WriteAsync(buffer.AsMemory()[..count], cancellationToken); - bytesRemaining -= count; + bytesRemaining -= count; + } } - }); + ); var cmd = source | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin"); // Act & assert await cmd.ExecuteAsync(); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/Utils/ProcessEx.cs b/CliWrap.Tests/Utils/ProcessEx.cs index bac83c4f..e9a34300 100644 --- a/CliWrap.Tests/Utils/ProcessEx.cs +++ b/CliWrap.Tests/Utils/ProcessEx.cs @@ -16,4 +16,4 @@ public static bool IsRunning(int processId) return false; } } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/Utils/TempDir.cs b/CliWrap.Tests/Utils/TempDir.cs index f79d9b6f..c7339cdc 100644 --- a/CliWrap.Tests/Utils/TempDir.cs +++ b/CliWrap.Tests/Utils/TempDir.cs @@ -9,8 +9,7 @@ internal partial class TempDir : IDisposable { public string Path { get; } - public TempDir(string path) => - Path = path; + public TempDir(string path) => Path = path; public void Dispose() { @@ -18,9 +17,7 @@ public void Dispose() { Directory.Delete(Path, true); } - catch (DirectoryNotFoundException) - { - } + catch (DirectoryNotFoundException) { } } } @@ -29,7 +26,8 @@ internal partial class TempDir public static TempDir Create() { var dirPath = PathEx.Combine( - PathEx.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Directory.GetCurrentDirectory(), + PathEx.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + ?? Directory.GetCurrentDirectory(), "Temp", Guid.NewGuid().ToString() ); @@ -38,4 +36,4 @@ public static TempDir Create() return new TempDir(dirPath); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/Utils/TempEnvironmentVariable.cs b/CliWrap.Tests/Utils/TempEnvironmentVariable.cs index 9ac38ee3..edf8c29f 100644 --- a/CliWrap.Tests/Utils/TempEnvironmentVariable.cs +++ b/CliWrap.Tests/Utils/TempEnvironmentVariable.cs @@ -11,11 +11,9 @@ public static IDisposable Set(string name, string? value) var lastValue = Environment.GetEnvironmentVariable(name); Environment.SetEnvironmentVariable(name, value); - return Disposable.Create(() => - Environment.SetEnvironmentVariable(name, lastValue) - ); + return Disposable.Create(() => Environment.SetEnvironmentVariable(name, lastValue)); } public static IDisposable ExtendPath(string path) => Set("PATH", Environment.GetEnvironmentVariable("PATH") + Path.PathSeparator + path); -} \ No newline at end of file +} diff --git a/CliWrap.Tests/Utils/TempFile.cs b/CliWrap.Tests/Utils/TempFile.cs index 87aabf92..5e85e510 100644 --- a/CliWrap.Tests/Utils/TempFile.cs +++ b/CliWrap.Tests/Utils/TempFile.cs @@ -9,8 +9,7 @@ internal partial class TempFile : IDisposable { public string Path { get; } - public TempFile(string path) => - Path = path; + public TempFile(string path) => Path = path; public void Dispose() { @@ -18,9 +17,7 @@ public void Dispose() { File.Delete(Path); } - catch (FileNotFoundException) - { - } + catch (FileNotFoundException) { } } } @@ -29,17 +26,15 @@ internal partial class TempFile public static TempFile Create() { var dirPath = PathEx.Combine( - PathEx.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Directory.GetCurrentDirectory(), + PathEx.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + ?? Directory.GetCurrentDirectory(), "Temp" ); Directory.CreateDirectory(dirPath); - var filePath = PathEx.Combine( - dirPath, - Guid.NewGuid() + ".tmp" - ); + var filePath = PathEx.Combine(dirPath, Guid.NewGuid() + ".tmp"); return new TempFile(filePath); } -} \ No newline at end of file +} diff --git a/CliWrap.Tests/ValidationSpecs.cs b/CliWrap.Tests/ValidationSpecs.cs index c39b47d7..e5fc623e 100644 --- a/CliWrap.Tests/ValidationSpecs.cs +++ b/CliWrap.Tests/ValidationSpecs.cs @@ -18,14 +18,11 @@ public async Task I_can_execute_a_command_and_get_an_error_if_it_returns_a_non_z { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("exit") - .Add("--code").Add(1) - ); + .WithArguments(a => a.Add("exit").Add("--code").Add(1)); // Act & assert - var ex = await Assert.ThrowsAsync(async () => - await cmd.ExecuteAsync() + var ex = await Assert.ThrowsAsync( + async () => await cmd.ExecuteAsync() ); ex.ExitCode.Should().Be(1); @@ -39,14 +36,11 @@ public async Task I_can_execute_a_command_with_buffering_and_get_a_detailed_exce { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("exit") - .Add("--code").Add(1) - ); + .WithArguments(a => a.Add("exit").Add("--code").Add(1)); // Act & assert - var ex = await Assert.ThrowsAsync(async () => - await cmd.ExecuteBufferedAsync() + var ex = await Assert.ThrowsAsync( + async () => await cmd.ExecuteBufferedAsync() ); ex.Message.Should().Contain("Exit code set to 1"); // expected stderr @@ -61,10 +55,7 @@ public async Task I_can_execute_a_command_without_validating_the_exit_code() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a - .Add("exit") - .Add("--code").Add(1) - ) + .WithArguments(a => a.Add("exit").Add("--code").Add(1)) .WithValidation(CommandResultValidation.None); // Act @@ -73,4 +64,4 @@ public async Task I_can_execute_a_command_without_validating_the_exit_code() // Assert result.ExitCode.Should().Be(1); } -} \ No newline at end of file +} diff --git a/CliWrap/CliWrap.csproj b/CliWrap/CliWrap.csproj index 103f07fa..7e80ad93 100644 --- a/CliWrap/CliWrap.csproj +++ b/CliWrap/CliWrap.csproj @@ -31,6 +31,7 @@ + From f720091f042331a83e1c5853a17178e6d201d01b Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 22 Aug 2023 21:35:12 +0300 Subject: [PATCH 20/57] Prettify --- CliWrap.Tests/BufferedSpecs.cs | 12 ++--- CliWrap.Tests/CancellationSpecs.cs | 26 +++++----- CliWrap.Tests/EventStreamSpecs.cs | 8 +-- CliWrap.Tests/ExecutionSpecs.cs | 5 +- CliWrap.Tests/PipingSpecs.cs | 83 +++++++++++++----------------- CliWrap.Tests/ValidationSpecs.cs | 8 ++- 6 files changed, 59 insertions(+), 83 deletions(-) diff --git a/CliWrap.Tests/BufferedSpecs.cs b/CliWrap.Tests/BufferedSpecs.cs index 5b8baaeb..3059bb42 100644 --- a/CliWrap.Tests/BufferedSpecs.cs +++ b/CliWrap.Tests/BufferedSpecs.cs @@ -12,7 +12,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("echo").Add("Hello stdout").Add("--target").Add("stdout")); + .WithArguments(new[] { "echo", "Hello stdout", "--target", "stdout" }); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -27,7 +27,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stderr() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("echo").Add("Hello stderr").Add("--target").Add("stderr")); + .WithArguments(new[] { "echo", "Hello stderr", "--target", "stderr" }); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -42,9 +42,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout_and_ { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - a => a.Add("echo").Add("Hello stdout and stderr").Add("--target").Add("all") - ); + .WithArguments(new[] { "echo", "Hello stdout and stderr", "--target", "all" }); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -59,9 +57,7 @@ public async Task I_can_execute_a_command_with_buffering_and_not_hang_on_large_s { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - a => a.Add("generate text").Add("--target").Add("all").Add("--length").Add(100_000) - ); + .WithArguments(new[] { "generate text", "--target", "all", "--length", "100000" }); // Act var result = await cmd.ExecuteBufferedAsync(); diff --git a/CliWrap.Tests/CancellationSpecs.cs b/CliWrap.Tests/CancellationSpecs.cs index 19c78ff9..f74c835b 100644 --- a/CliWrap.Tests/CancellationSpecs.cs +++ b/CliWrap.Tests/CancellationSpecs.cs @@ -24,8 +24,7 @@ public async Task I_can_execute_a_command_and_cancel_it_immediately() var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")) - | stdOutBuffer; + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }) | stdOutBuffer; // Act var task = cmd.ExecuteAsync(cts.Token); @@ -49,8 +48,7 @@ public async Task I_can_execute_a_command_and_cancel_it_after_a_delay() var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")) - | stdOutBuffer; + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }) | stdOutBuffer; // Act var task = cmd.ExecuteAsync(cts.Token); @@ -87,7 +85,7 @@ void HandleStdOut(string line) var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")) | target; + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }) | target; // Act var task = cmd.ExecuteAsync(CancellationToken.None, cts.Token); @@ -108,7 +106,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_immediate cts.Cancel(); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -126,7 +124,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_after_a_d cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -144,7 +142,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_gracefull cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -168,7 +166,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance cts.Cancel(); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -191,7 +189,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -214,7 +212,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -244,7 +242,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance cts.Cancel(); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -271,7 +269,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -298,7 +296,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance cts.CancelAfter(TimeSpan.FromSeconds(0.2)); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("sleep").Add("--duration").Add("00:00:20")); + .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( diff --git a/CliWrap.Tests/EventStreamSpecs.cs b/CliWrap.Tests/EventStreamSpecs.cs index ca365f5e..ae37e995 100644 --- a/CliWrap.Tests/EventStreamSpecs.cs +++ b/CliWrap.Tests/EventStreamSpecs.cs @@ -15,9 +15,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) - ); + .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }); // Act var events = new List(); @@ -38,9 +36,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) - ); + .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }); // Act var events = await cmd.Observe().ToArray(); diff --git a/CliWrap.Tests/ExecutionSpecs.cs b/CliWrap.Tests/ExecutionSpecs.cs index 37800710..00585feb 100644 --- a/CliWrap.Tests/ExecutionSpecs.cs +++ b/CliWrap.Tests/ExecutionSpecs.cs @@ -52,10 +52,7 @@ public async Task I_can_execute_a_command_and_not_hang_on_large_stdout_and_stder { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - a => - a.Add("generate binary").Add("--target").Add("all").Add("--length").Add(100_000) - ); + .WithArguments(new[] { "generate binary", "--target", "all", "--length", "100000" }); // Act & assert await cmd.ExecuteAsync(); diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index 7a495d8f..2f1c9f1d 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -128,7 +128,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_another_comman // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) + .WithArguments(new[] { "generate binary", "--length", "100000" }) | Cli.Wrap(Dummy.Program.FilePath).WithArguments("print length stdin"); // Act @@ -146,7 +146,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_chain_of_com "Hello world" | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("echo stdin").Add("--length").Add(5)) + .WithArguments(new[] { "echo stdin", "--length", "5" }) | Cli.Wrap(Dummy.Program.FilePath).WithArguments("print length stdin"); // Act @@ -170,7 +170,7 @@ await origin.CopyToAsync(stream, cancellationToken) var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; + .WithArguments(new[] { "generate binary", "--length", "100000" }) | target; // Act await cmd.ExecuteAsync(); @@ -193,7 +193,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_anonym var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; + .WithArguments(new[] { "generate binary", "--length", "100000" }) | target; // Act await cmd.ExecuteAsync(); @@ -210,7 +210,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_stream() var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | stream; + .WithArguments(new[] { "generate binary", "--length", "100000" }) | stream; // Act await cmd.ExecuteAsync(); @@ -227,7 +227,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_file() var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) + .WithArguments(new[] { "generate binary", "--length", "100000" }) | PipeTarget.ToFile(file.Path); // Act @@ -245,7 +245,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_string_buil var buffer = new StringBuilder(); var cmd = - Cli.Wrap(Dummy.Program.FilePath).WithArguments(a => a.Add("echo").Add("Hello world!")) + Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "echo", "Hello world!" }) | buffer; // Act @@ -269,8 +269,7 @@ async Task HandleStdOutAsync(string line) var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate text").Add("--lines").Add(100)) - | HandleStdOutAsync; + .WithArguments(new[] { "generate text", "--lines", "100" }) | HandleStdOutAsync; // Act await cmd.ExecuteAsync(); @@ -293,8 +292,7 @@ async Task HandleStdOutAsync(string line, CancellationToken cancellationToken = var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate text").Add("--lines").Add(100)) - | HandleStdOutAsync; + .WithArguments(new[] { "generate text", "--lines", "100" }) | HandleStdOutAsync; // Act await cmd.ExecuteAsync(); @@ -313,7 +311,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_delega var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate text").Add("--lines").Add(100)) | HandleStdOut; + .WithArguments(new[] { "generate text", "--lines", "100" }) | HandleStdOut; // Act await cmd.ExecuteAsync(); @@ -331,14 +329,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - a => - a.Add("generate binary") - .Add("--target") - .Add("all") - .Add("--length") - .Add(100_000) - ) | (stdOut, stdErr); + .WithArguments(new[] { "generate binary", "--target", "all", "--length", "100000" }) + | (stdOut, stdErr); // Act await cmd.ExecuteAsync(); @@ -357,7 +349,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_st var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("echo").Add("Hello world!").Add("--target").Add("all")) + .WithArguments(new[] { "echo", "Hello world!", "--target", "all" }) | (stdOutBuffer, stdErrBuffer); // Act @@ -389,9 +381,8 @@ async Task HandleStdErrAsync(string line) var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) - ) | (HandleStdOutAsync, HandleStdErrAsync); + .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }) + | (HandleStdOutAsync, HandleStdErrAsync); // Act await cmd.ExecuteAsync(); @@ -422,9 +413,8 @@ async Task HandleStdErrAsync(string line, CancellationToken cancellationToken = var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) - ) | (HandleStdOutAsync, HandleStdErrAsync); + .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }) + | (HandleStdOutAsync, HandleStdErrAsync); // Act await cmd.ExecuteAsync(); @@ -446,9 +436,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - a => a.Add("generate text").Add("--target").Add("all").Add("--lines").Add(100) - ) | (HandleStdOut, HandleStdErr); + .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }) + | (HandleStdOut, HandleStdErr); // Act await cmd.ExecuteAsync(); @@ -474,7 +463,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; + .WithArguments(new[] { "generate binary", "--length", "100000" }) | target; // Act await cmd.ExecuteAsync(); @@ -500,7 +489,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; + .WithArguments(new[] { "generate binary", "--length", "100_000" }) | target; // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => await cmd.ExecuteAsync()); @@ -526,7 +515,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_hier var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) | target; + .WithArguments(new[] { "generate binary", "--length", "100000" }) | target; // Act await cmd.ExecuteAsync(); @@ -549,13 +538,15 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_stre // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments( - a => - a.Add("generate binary") - .Add("--length") - .Add(1_000_000) - // Buffer needs to be >= BufferSizes.Stream to fail - .Add("--buffer") - .Add(100_000) + new[] + { + "generate binary", + "--length", + "1000000", + // Buffer needs to be >= BufferSizes.Stream to fail + "--buffer", + "100000" + } ); // Act @@ -588,7 +579,7 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate text").Add("--length").Add(100_000)) | stream; + .WithArguments(new[] { "generate text", "--length", "100000" }) | stream; // Act var result = await cmd.ExecuteBufferedAsync(); @@ -612,7 +603,7 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate text").Add("--lines").Add(100)) | HandleStdOut; + .WithArguments(new[] { "generate text", "--lines", "100" }) | HandleStdOut; // Act var result = await cmd.ExecuteBufferedAsync(); @@ -646,7 +637,7 @@ public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_target_th // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("generate binary").Add("--length").Add(100_000)) + .WithArguments(new[] { "generate binary", "--length", "100_000" }) | PipeTarget.ToFile("non-existing-directory/file.txt"); // Act & assert @@ -699,7 +690,7 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_only_parti var cmd = source | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("echo stdin").Add("--length").Add(100_000)); + .WithArguments(new[] { "echo stdin", "--length", "100000" }); // Act & assert await cmd.ExecuteAsync(); @@ -722,7 +713,7 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_does_not_c var cmd = source | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("echo stdin").Add("--length").Add(0)); + .WithArguments(new[] { "echo stdin", "--length", "0" }); // Act & assert await cmd.ExecuteAsync(); @@ -743,7 +734,7 @@ await Task.Delay(TimeSpan.FromSeconds(20), CancellationToken.None) var cmd = source | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("echo stdin").Add("--length").Add(0)); + .WithArguments(new[] { "echo stdin", "--length", "0" }); // Act & assert await cmd.ExecuteAsync(); diff --git a/CliWrap.Tests/ValidationSpecs.cs b/CliWrap.Tests/ValidationSpecs.cs index e5fc623e..159ad97b 100644 --- a/CliWrap.Tests/ValidationSpecs.cs +++ b/CliWrap.Tests/ValidationSpecs.cs @@ -17,8 +17,7 @@ public class ValidationSpecs public async Task I_can_execute_a_command_and_get_an_error_if_it_returns_a_non_zero_exit_code() { // Arrange - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("exit").Add("--code").Add(1)); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "--code", "1" }); // Act & assert var ex = await Assert.ThrowsAsync( @@ -35,8 +34,7 @@ public async Task I_can_execute_a_command_and_get_an_error_if_it_returns_a_non_z public async Task I_can_execute_a_command_with_buffering_and_get_a_detailed_exception_if_it_returns_a_non_zero_exit_code() { // Arrange - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("exit").Add("--code").Add(1)); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "--code", "1" }); // Act & assert var ex = await Assert.ThrowsAsync( @@ -55,7 +53,7 @@ public async Task I_can_execute_a_command_without_validating_the_exit_code() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(a => a.Add("exit").Add("--code").Add(1)) + .WithArguments(new[] { "exit", "--code", "1" }) .WithValidation(CommandResultValidation.None); // Act From f8daff13c63c76f63dc3a61bd86d4ab6549a04a8 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 22 Aug 2023 21:41:44 +0300 Subject: [PATCH 21/57] Reformat --- CliWrap/Buffered/BufferedCommandExtensions.cs | 25 +- CliWrap/Buffered/BufferedCommandResult.cs | 5 +- CliWrap/Builders/ArgumentsBuilder.cs | 42 +-- CliWrap/Builders/CredentialsBuilder.cs | 9 +- .../Builders/EnvironmentVariablesBuilder.cs | 2 +- CliWrap/Cli.cs | 2 +- CliWrap/Command.Execution.cs | 98 +++---- CliWrap/Command.PipeOperators.cs | 65 +++-- CliWrap/Command.cs | 241 +++++++++--------- CliWrap/CommandResult.cs | 2 +- CliWrap/CommandResultValidation.cs | 2 +- CliWrap/CommandTask.cs | 5 +- CliWrap/Credentials.cs | 14 +- CliWrap/EventStream/CommandEvent.cs | 6 +- .../PullEventStreamCommandExtensions.cs | 77 +++--- .../PushEventStreamCommandExtensions.cs | 39 +-- CliWrap/Exceptions/CliWrapException.cs | 10 +- .../Exceptions/CommandExecutionException.cs | 14 +- CliWrap/ICommandConfiguration.cs | 2 +- CliWrap/PipeSource.cs | 85 +++--- CliWrap/PipeTarget.cs | 190 ++++++++------ CliWrap/Utils/BufferSizes.cs | 2 +- CliWrap/Utils/Channel.cs | 5 +- CliWrap/Utils/Disposable.cs | 2 +- CliWrap/Utils/EnvironmentEx.cs | 13 +- .../Utils/Extensions/AssemblyExtensions.cs | 11 +- .../Extensions/AsyncDisposableExtensions.cs | 2 +- .../Extensions/CancellationTokenExtensions.cs | 12 +- .../Utils/Extensions/ExceptionExtensions.cs | 6 +- CliWrap/Utils/Extensions/StreamExtensions.cs | 21 +- CliWrap/Utils/Extensions/StringExtensions.cs | 2 +- CliWrap/Utils/Extensions/TaskExtensions.cs | 5 +- CliWrap/Utils/NativeMethods.cs | 2 +- CliWrap/Utils/Observable.cs | 2 +- CliWrap/Utils/ProcessEx.cs | 25 +- CliWrap/Utils/SimplexStream.cs | 11 +- CliWrap/Utils/WindowsSignaler.cs | 11 +- 37 files changed, 584 insertions(+), 483 deletions(-) diff --git a/CliWrap/Buffered/BufferedCommandExtensions.cs b/CliWrap/Buffered/BufferedCommandExtensions.cs index 122e8bd0..4cf9ff8c 100644 --- a/CliWrap/Buffered/BufferedCommandExtensions.cs +++ b/CliWrap/Buffered/BufferedCommandExtensions.cs @@ -24,7 +24,8 @@ public static CommandTask ExecuteBufferedAsync( Encoding standardOutputEncoding, Encoding standardErrorEncoding, CancellationToken forcefulCancellationToken, - CancellationToken gracefulCancellationToken) + CancellationToken gracefulCancellationToken + ) { var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); @@ -88,7 +89,8 @@ public static CommandTask ExecuteBufferedAsync( this Command command, Encoding standardOutputEncoding, Encoding standardErrorEncoding, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { return command.ExecuteBufferedAsync( standardOutputEncoding, @@ -109,13 +111,10 @@ public static CommandTask ExecuteBufferedAsync( public static CommandTask ExecuteBufferedAsync( this Command command, Encoding encoding, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { - return command.ExecuteBufferedAsync( - encoding, - encoding, - cancellationToken - ); + return command.ExecuteBufferedAsync(encoding, encoding, cancellationToken); } ///

@@ -129,11 +128,9 @@ public static CommandTask ExecuteBufferedAsync( /// public static CommandTask ExecuteBufferedAsync( this Command command, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { - return command.ExecuteBufferedAsync( - Console.OutputEncoding, - cancellationToken - ); + return command.ExecuteBufferedAsync(Console.OutputEncoding, cancellationToken); } -} \ No newline at end of file +} diff --git a/CliWrap/Buffered/BufferedCommandResult.cs b/CliWrap/Buffered/BufferedCommandResult.cs index 5f3b0d05..1dbafa14 100644 --- a/CliWrap/Buffered/BufferedCommandResult.cs +++ b/CliWrap/Buffered/BufferedCommandResult.cs @@ -25,10 +25,11 @@ public BufferedCommandResult( DateTimeOffset startTime, DateTimeOffset exitTime, string standardOutput, - string standardError) + string standardError + ) : base(exitCode, startTime, exitTime) { StandardOutput = standardOutput; StandardError = standardError; } -} \ No newline at end of file +} diff --git a/CliWrap/Builders/ArgumentsBuilder.cs b/CliWrap/Builders/ArgumentsBuilder.cs index 97733cab..361c5dd7 100644 --- a/CliWrap/Builders/ArgumentsBuilder.cs +++ b/CliWrap/Builders/ArgumentsBuilder.cs @@ -23,10 +23,7 @@ public ArgumentsBuilder Add(string value, bool escape) if (_buffer.Length > 0) _buffer.Append(' '); - _buffer.Append(escape - ? Escape(value) - : value - ); + _buffer.Append(escape ? Escape(value) : value); return this; } @@ -52,14 +49,16 @@ public ArgumentsBuilder Add(IEnumerable values, bool escape) /// Adds the specified values to the list of arguments. /// // TODO: (breaking change) remove in favor of optional parameter - public ArgumentsBuilder Add(IEnumerable values) => - Add(values, true); + public ArgumentsBuilder Add(IEnumerable values) => Add(values, true); /// /// Adds the specified value to the list of arguments. /// - public ArgumentsBuilder Add(IFormattable value, IFormatProvider formatProvider, bool escape = true) => - Add(value.ToString(null, formatProvider), escape); + public ArgumentsBuilder Add( + IFormattable value, + IFormatProvider formatProvider, + bool escape = true + ) => Add(value.ToString(null, formatProvider), escape); /// /// Adds the specified value to the list of arguments. @@ -87,13 +86,16 @@ public ArgumentsBuilder Add(IFormattable value, bool escape) => /// The value is converted to string using invariant culture. /// // TODO: (breaking change) remove in favor of optional parameter - public ArgumentsBuilder Add(IFormattable value) => - Add(value, true); + public ArgumentsBuilder Add(IFormattable value) => Add(value, true); /// /// Adds the specified values to the list of arguments. /// - public ArgumentsBuilder Add(IEnumerable values, IFormatProvider formatProvider, bool escape = true) + public ArgumentsBuilder Add( + IEnumerable values, + IFormatProvider formatProvider, + bool escape = true + ) { foreach (var value in values) Add(value, formatProvider, escape); @@ -105,8 +107,11 @@ public ArgumentsBuilder Add(IEnumerable values, IFormatProvider fo /// Adds the specified values to the list of arguments. /// // TODO: (breaking change) remove in favor of the other overloads - public ArgumentsBuilder Add(IEnumerable values, CultureInfo cultureInfo, bool escape) => - Add(values, (IFormatProvider)cultureInfo, escape); + public ArgumentsBuilder Add( + IEnumerable values, + CultureInfo cultureInfo, + bool escape + ) => Add(values, (IFormatProvider)cultureInfo, escape); /// /// Adds the specified values to the list of arguments. @@ -127,8 +132,7 @@ public ArgumentsBuilder Add(IEnumerable values, bool escape) => /// The values are converted to string using invariant culture. /// // TODO: (breaking change) remove in favor of optional parameter - public ArgumentsBuilder Add(IEnumerable values) => - Add(values, true); + public ArgumentsBuilder Add(IEnumerable values) => Add(values, true); /// /// Builds the resulting arguments string. @@ -152,7 +156,7 @@ private static string Escape(string argument) buffer.Append('"'); - for (var i = 0; i < argument.Length;) + for (var i = 0; i < argument.Length; ) { var c = argument[i++]; @@ -171,9 +175,7 @@ private static string Escape(string argument) } else if (argument[i] == '"') { - buffer - .Append('\\', backslashCount * 2 + 1) - .Append('"'); + buffer.Append('\\', backslashCount * 2 + 1).Append('"'); i++; } @@ -196,4 +198,4 @@ private static string Escape(string argument) return buffer.ToString(); } -} \ No newline at end of file +} diff --git a/CliWrap/Builders/CredentialsBuilder.cs b/CliWrap/Builders/CredentialsBuilder.cs index 7df142b4..d21520a6 100644 --- a/CliWrap/Builders/CredentialsBuilder.cs +++ b/CliWrap/Builders/CredentialsBuilder.cs @@ -58,10 +58,5 @@ public CredentialsBuilder LoadUserProfile(bool loadUserProfile = true) /// /// Builds the resulting credentials. /// - public Credentials Build() => new( - _domain, - _userName, - _password, - _loadUserProfile - ); -} \ No newline at end of file + public Credentials Build() => new(_domain, _userName, _password, _loadUserProfile); +} diff --git a/CliWrap/Builders/EnvironmentVariablesBuilder.cs b/CliWrap/Builders/EnvironmentVariablesBuilder.cs index 0c692309..514025b0 100644 --- a/CliWrap/Builders/EnvironmentVariablesBuilder.cs +++ b/CliWrap/Builders/EnvironmentVariablesBuilder.cs @@ -42,4 +42,4 @@ public EnvironmentVariablesBuilder Set(IReadOnlyDictionary vari public IReadOnlyDictionary Build() => // Create a new dictionary instance to prevent the builder from modifying it new Dictionary(_envVars, _envVars.Comparer); -} \ No newline at end of file +} diff --git a/CliWrap/Cli.cs b/CliWrap/Cli.cs index 4ac85356..79526bdf 100644 --- a/CliWrap/Cli.cs +++ b/CliWrap/Cli.cs @@ -12,4 +12,4 @@ public static class Cli /// Creates a new command that targets the specified command-line executable, batch file, or script. /// public static Command Wrap(string targetFilePath) => new(targetFilePath); -} \ No newline at end of file +} diff --git a/CliWrap/Command.Execution.cs b/CliWrap/Command.Execution.cs index 6d5e15c0..861d6d76 100644 --- a/CliWrap/Command.Execution.cs +++ b/CliWrap/Command.Execution.cs @@ -30,7 +30,10 @@ private string GetOptimallyQualifiedTargetFilePath() // Note that IsPathRooted(...) doesn't check if the path is absolute, as it also returns true for // strings like 'c:foo.txt' (which is relative to the current directory on drive C), but it's good // enough for our purposes and the alternative is only available on .NET Standard 2.1+. - if (Path.IsPathRooted(TargetFilePath) || !string.IsNullOrWhiteSpace(Path.GetExtension(TargetFilePath))) + if ( + Path.IsPathRooted(TargetFilePath) + || !string.IsNullOrWhiteSpace(Path.GetExtension(TargetFilePath)) + ) return TargetFilePath; static IEnumerable GetProbeDirectoryPaths() @@ -59,13 +62,12 @@ static IEnumerable GetProbeDirectoryPaths() } return ( - from probeDirPath in GetProbeDirectoryPaths() - where Directory.Exists(probeDirPath) - select Path.Combine(probeDirPath, TargetFilePath) - into baseFilePath - from extension in new[] {"exe", "cmd", "bat"} - select Path.ChangeExtension(baseFilePath, extension) - ).FirstOrDefault(File.Exists) ?? TargetFilePath; + from probeDirPath in GetProbeDirectoryPaths() + where Directory.Exists(probeDirPath) + select Path.Combine(probeDirPath, TargetFilePath) into baseFilePath + from extension in new[] { "exe", "cmd", "bat" } + select Path.ChangeExtension(baseFilePath, extension) + ).FirstOrDefault(File.Exists) ?? TargetFilePath; } private ProcessStartInfo CreateStartInfo() @@ -105,8 +107,8 @@ private ProcessStartInfo CreateStartInfo() catch (NotSupportedException ex) { throw new NotSupportedException( - "Cannot start a process using the provided credentials. " + - "Setting custom domain, password, or loading user profile is only supported on Windows.", + "Cannot start a process using the provided credentials. " + + "Setting custom domain, password, or loading user profile is only supported on Windows.", ex ); } @@ -132,13 +134,15 @@ private ProcessStartInfo CreateStartInfo() private async Task PipeStandardInputAsync( ProcessEx process, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { await using (process.StandardInput.ToAsyncDisposable()) { try { - await StandardInputPipe.CopyToAsync(process.StandardInput, cancellationToken) + await StandardInputPipe + .CopyToAsync(process.StandardInput, cancellationToken) // Some streams do not support cancellation, so we add a fallback that // drops the task and returns early. // This is important with stdin because the process might finish before @@ -152,30 +156,32 @@ await StandardInputPipe.CopyToAsync(process.StandardInput, cancellationToken) // stdin to complete successfully. // Don't catch derived exceptions, such as FileNotFoundException, to avoid false positives. // We also can't rely on process.HasExited here because of potential race conditions. - catch (IOException ex) when (ex.GetType() == typeof(IOException)) - { - } + catch (IOException ex) when (ex.GetType() == typeof(IOException)) { } } } private async Task PipeStandardOutputAsync( ProcessEx process, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { await using (process.StandardOutput.ToAsyncDisposable()) { - await StandardOutputPipe.CopyFromAsync(process.StandardOutput, cancellationToken) + await StandardOutputPipe + .CopyFromAsync(process.StandardOutput, cancellationToken) .ConfigureAwait(false); } } private async Task PipeStandardErrorAsync( ProcessEx process, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { await using (process.StandardError.ToAsyncDisposable()) { - await StandardErrorPipe.CopyFromAsync(process.StandardError, cancellationToken) + await StandardErrorPipe + .CopyFromAsync(process.StandardError, cancellationToken) .ConfigureAwait(false); } } @@ -183,7 +189,8 @@ await StandardErrorPipe.CopyFromAsync(process.StandardError, cancellationToken) private async Task ExecuteAsync( ProcessEx process, CancellationToken forcefulCancellationToken = default, - CancellationToken gracefulCancellationToken = default) + CancellationToken gracefulCancellationToken = default + ) { using var _ = process; @@ -193,17 +200,24 @@ private async Task ExecuteAsync( // exits, but it's theoretically possible that an attempt to kill the process may fail, // so we need a fallback. using var waitTimeoutCts = new CancellationTokenSource(); - await using var _1 = forcefulCancellationToken.Register(() => - // ReSharper disable once AccessToDisposedClosure - waitTimeoutCts.CancelAfter(TimeSpan.FromSeconds(3)) - ).ToAsyncDisposable(); + await using var _1 = forcefulCancellationToken + .Register( + () => + // ReSharper disable once AccessToDisposedClosure + waitTimeoutCts.CancelAfter(TimeSpan.FromSeconds(3)) + ) + .ToAsyncDisposable(); // Additional cancellation for the stdin pipe in case the process exits without fully exhausting it - using var stdInCts = CancellationTokenSource.CreateLinkedTokenSource(forcefulCancellationToken); + using var stdInCts = CancellationTokenSource.CreateLinkedTokenSource( + forcefulCancellationToken + ); // Bind user-provided cancellation tokens to the process await using var _2 = forcefulCancellationToken.Register(process.Kill).ToAsyncDisposable(); - await using var _3 = gracefulCancellationToken.Register(process.Interrupt).ToAsyncDisposable(); + await using var _3 = gracefulCancellationToken + .Register(process.Interrupt) + .ToAsyncDisposable(); // Start piping streams in the background var pipingTask = Task.WhenAll( @@ -230,26 +244,25 @@ private async Task ExecuteAsync( } // Swallow exceptions caused by internal and user-provided cancellations, // because we have a separate mechanism for handling them below. - catch (OperationCanceledException ex) when ( - ex.CancellationToken == forcefulCancellationToken || - ex.CancellationToken == gracefulCancellationToken || - ex.CancellationToken == waitTimeoutCts.Token || - ex.CancellationToken == stdInCts.Token) - { - } + catch (OperationCanceledException ex) + when (ex.CancellationToken == forcefulCancellationToken + || ex.CancellationToken == gracefulCancellationToken + || ex.CancellationToken == waitTimeoutCts.Token + || ex.CancellationToken == stdInCts.Token + ) { } // Throw if forceful cancellation was requested. // This needs to be checked first because it effectively overrides graceful cancellation // by outright killing the process, even if graceful cancellation was requested earlier. forcefulCancellationToken.ThrowIfCancellationRequested( - "Command execution canceled. " + - $"Underlying process ({process.Name}#{process.Id}) was forcefully terminated." + "Command execution canceled. " + + $"Underlying process ({process.Name}#{process.Id}) was forcefully terminated." ); // Throw if graceful cancellation was requested gracefulCancellationToken.ThrowIfCancellationRequested( - "Command execution canceled. " + - $"Underlying process ({process.Name}#{process.Id}) was gracefully terminated." + "Command execution canceled. " + + $"Underlying process ({process.Name}#{process.Id}) was gracefully terminated." ); // Validate the exit code if required @@ -269,11 +282,7 @@ Command execution failed because the underlying process ({process.Name}#{process ); } - return new CommandResult( - process.ExitCode, - process.StartTime, - process.ExitTime - ); + return new CommandResult(process.ExitCode, process.StartTime, process.ExitTime); } /// @@ -285,7 +294,8 @@ Command execution failed because the underlying process ({process.Name}#{process // TODO: (breaking change) use optional parameters and remove the other overload public CommandTask ExecuteAsync( CancellationToken forcefulCancellationToken, - CancellationToken gracefulCancellationToken) + CancellationToken gracefulCancellationToken + ) { var process = new ProcessEx(CreateStartInfo()); @@ -312,4 +322,4 @@ public CommandTask ExecuteAsync( /// public CommandTask ExecuteAsync(CancellationToken cancellationToken = default) => ExecuteAsync(cancellationToken, CancellationToken.None); -} \ No newline at end of file +} diff --git a/CliWrap/Command.PipeOperators.cs b/CliWrap/Command.PipeOperators.cs index 80679672..54fa75e0 100644 --- a/CliWrap/Command.PipeOperators.cs +++ b/CliWrap/Command.PipeOperators.cs @@ -37,8 +37,10 @@ public partial class Command /// Uses for decoding. /// [Pure] - public static Command operator |(Command source, Func target) => - source | PipeTarget.ToDelegate(target); + public static Command operator |( + Command source, + Func target + ) => source | PipeTarget.ToDelegate(target); /// /// Creates a new command that pipes its standard output line-by-line to the specified @@ -63,23 +65,17 @@ public partial class Command /// specified targets. /// [Pure] - public static Command operator |(Command source, ( - PipeTarget stdOut, - PipeTarget stdErr - ) targets) => - source - .WithStandardOutputPipe(targets.stdOut) - .WithStandardErrorPipe(targets.stdErr); + public static Command operator |( + Command source, + (PipeTarget stdOut, PipeTarget stdErr) targets + ) => source.WithStandardOutputPipe(targets.stdOut).WithStandardErrorPipe(targets.stdErr); /// /// Creates a new command that pipes its standard output and standard error to the /// specified streams. /// [Pure] - public static Command operator |(Command source, ( - Stream stdOut, - Stream stdErr - ) targets) => + public static Command operator |(Command source, (Stream stdOut, Stream stdErr) targets) => source | (PipeTarget.ToStream(targets.stdOut), PipeTarget.ToStream(targets.stdErr)); /// @@ -88,11 +84,12 @@ Stream stdErr /// Uses for decoding. /// [Pure] - public static Command operator |(Command source, ( - StringBuilder stdOut, - StringBuilder stdErr - ) targets) => - source | (PipeTarget.ToStringBuilder(targets.stdOut), PipeTarget.ToStringBuilder(targets.stdErr)); + public static Command operator |( + Command source, + (StringBuilder stdOut, StringBuilder stdErr) targets + ) => + source + | (PipeTarget.ToStringBuilder(targets.stdOut), PipeTarget.ToStringBuilder(targets.stdErr)); /// /// Creates a new command that pipes its standard output and standard error line-by-line @@ -100,11 +97,13 @@ StringBuilder stdErr /// Uses for decoding. /// [Pure] - public static Command operator |(Command source, ( - Func stdOut, - Func stdErr - ) targets) => - source | (PipeTarget.ToDelegate(targets.stdOut), PipeTarget.ToDelegate(targets.stdErr)); + public static Command operator |( + Command source, + ( + Func stdOut, + Func stdErr + ) targets + ) => source | (PipeTarget.ToDelegate(targets.stdOut), PipeTarget.ToDelegate(targets.stdErr)); /// /// Creates a new command that pipes its standard output and standard error line-by-line @@ -112,11 +111,10 @@ Func stdErr /// Uses for decoding. /// [Pure] - public static Command operator |(Command source, ( - Func stdOut, - Func stdErr - ) targets) => - source | (PipeTarget.ToDelegate(targets.stdOut), PipeTarget.ToDelegate(targets.stdErr)); + public static Command operator |( + Command source, + (Func stdOut, Func stdErr) targets + ) => source | (PipeTarget.ToDelegate(targets.stdOut), PipeTarget.ToDelegate(targets.stdErr)); /// /// Creates a new command that pipes its standard output and standard error line-by-line @@ -124,11 +122,10 @@ Func stdErr /// Uses for decoding. /// [Pure] - public static Command operator |(Command source, ( - Action stdOut, - Action stdErr - ) targets) => - source | (PipeTarget.ToDelegate(targets.stdOut), PipeTarget.ToDelegate(targets.stdErr)); + public static Command operator |( + Command source, + (Action stdOut, Action stdErr) targets + ) => source | (PipeTarget.ToDelegate(targets.stdOut), PipeTarget.ToDelegate(targets.stdErr)); /// /// Creates a new command that pipes its standard input from the specified source. @@ -173,4 +170,4 @@ Action stdErr [Pure] public static Command operator |(Command source, Command target) => PipeSource.FromCommand(source) | target; -} \ No newline at end of file +} diff --git a/CliWrap/Command.cs b/CliWrap/Command.cs index 3ec04553..c4d5b5ec 100644 --- a/CliWrap/Command.cs +++ b/CliWrap/Command.cs @@ -51,7 +51,8 @@ public Command( CommandResultValidation validation, PipeSource standardInputPipe, PipeTarget standardOutputPipe, - PipeTarget standardErrorPipe) + PipeTarget standardErrorPipe + ) { TargetFilePath = targetFilePath; Arguments = arguments; @@ -67,34 +68,35 @@ public Command( /// /// Initializes an instance of . /// - public Command(string targetFilePath) : this( - targetFilePath, - string.Empty, - Directory.GetCurrentDirectory(), - Credentials.Default, - new Dictionary(), - CommandResultValidation.ZeroExitCode, - PipeSource.Null, - PipeTarget.Null, - PipeTarget.Null) - { - } + public Command(string targetFilePath) + : this( + targetFilePath, + string.Empty, + Directory.GetCurrentDirectory(), + Credentials.Default, + new Dictionary(), + CommandResultValidation.ZeroExitCode, + PipeSource.Null, + PipeTarget.Null, + PipeTarget.Null + ) { } /// /// Creates a copy of this command, setting the target file path to the specified value. /// [Pure] - public Command WithTargetFile(string targetFilePath) => new( - targetFilePath, - Arguments, - WorkingDirPath, - Credentials, - EnvironmentVariables, - Validation, - StandardInputPipe, - StandardOutputPipe, - StandardErrorPipe - ); + public Command WithTargetFile(string targetFilePath) => + new( + targetFilePath, + Arguments, + WorkingDirPath, + Credentials, + EnvironmentVariables, + Validation, + StandardInputPipe, + StandardOutputPipe, + StandardErrorPipe + ); /// /// Creates a copy of this command, setting the arguments to the specified value. @@ -104,17 +106,18 @@ public Command(string targetFilePath) : this( /// Formatting errors may lead to unexpected bugs and security vulnerabilities. /// [Pure] - public Command WithArguments(string arguments) => new( - TargetFilePath, - arguments, - WorkingDirPath, - Credentials, - EnvironmentVariables, - Validation, - StandardInputPipe, - StandardOutputPipe, - StandardErrorPipe - ); + public Command WithArguments(string arguments) => + new( + TargetFilePath, + arguments, + WorkingDirPath, + Credentials, + EnvironmentVariables, + Validation, + StandardInputPipe, + StandardOutputPipe, + StandardErrorPipe + ); /// /// Creates a copy of this command, setting the arguments to the value @@ -130,8 +133,7 @@ public Command WithArguments(IEnumerable arguments, bool escape) => /// // TODO: (breaking change) remove in favor of optional parameter [Pure] - public Command WithArguments(IEnumerable arguments) => - WithArguments(arguments, true); + public Command WithArguments(IEnumerable arguments) => WithArguments(arguments, true); /// /// Creates a copy of this command, setting the arguments to the value @@ -150,33 +152,35 @@ public Command WithArguments(Action configure) /// Creates a copy of this command, setting the working directory path to the specified value. /// [Pure] - public Command WithWorkingDirectory(string workingDirPath) => new( - TargetFilePath, - Arguments, - workingDirPath, - Credentials, - EnvironmentVariables, - Validation, - StandardInputPipe, - StandardOutputPipe, - StandardErrorPipe - ); + public Command WithWorkingDirectory(string workingDirPath) => + new( + TargetFilePath, + Arguments, + workingDirPath, + Credentials, + EnvironmentVariables, + Validation, + StandardInputPipe, + StandardOutputPipe, + StandardErrorPipe + ); /// /// Creates a copy of this command, setting the user credentials to the specified value. /// [Pure] - public Command WithCredentials(Credentials credentials) => new( - TargetFilePath, - Arguments, - WorkingDirPath, - credentials, - EnvironmentVariables, - Validation, - StandardInputPipe, - StandardOutputPipe, - StandardErrorPipe - ); + public Command WithCredentials(Credentials credentials) => + new( + TargetFilePath, + Arguments, + WorkingDirPath, + credentials, + EnvironmentVariables, + Validation, + StandardInputPipe, + StandardOutputPipe, + StandardErrorPipe + ); /// /// Creates a copy of this command, setting the user credentials to the value @@ -195,17 +199,20 @@ public Command WithCredentials(Action configure) /// Creates a copy of this command, setting the environment variables to the specified value. /// [Pure] - public Command WithEnvironmentVariables(IReadOnlyDictionary environmentVariables) => new( - TargetFilePath, - Arguments, - WorkingDirPath, - Credentials, - environmentVariables, - Validation, - StandardInputPipe, - StandardOutputPipe, - StandardErrorPipe - ); + public Command WithEnvironmentVariables( + IReadOnlyDictionary environmentVariables + ) => + new( + TargetFilePath, + Arguments, + WorkingDirPath, + Credentials, + environmentVariables, + Validation, + StandardInputPipe, + StandardOutputPipe, + StandardErrorPipe + ); /// /// Creates a copy of this command, setting the environment variables to the value @@ -224,67 +231,71 @@ public Command WithEnvironmentVariables(Action conf /// Creates a copy of this command, setting the validation options to the specified value. /// [Pure] - public Command WithValidation(CommandResultValidation validation) => new( - TargetFilePath, - Arguments, - WorkingDirPath, - Credentials, - EnvironmentVariables, - validation, - StandardInputPipe, - StandardOutputPipe, - StandardErrorPipe - ); + public Command WithValidation(CommandResultValidation validation) => + new( + TargetFilePath, + Arguments, + WorkingDirPath, + Credentials, + EnvironmentVariables, + validation, + StandardInputPipe, + StandardOutputPipe, + StandardErrorPipe + ); /// /// Creates a copy of this command, setting the standard input pipe to the specified source. /// [Pure] - public Command WithStandardInputPipe(PipeSource source) => new( - TargetFilePath, - Arguments, - WorkingDirPath, - Credentials, - EnvironmentVariables, - Validation, - source, - StandardOutputPipe, - StandardErrorPipe - ); + public Command WithStandardInputPipe(PipeSource source) => + new( + TargetFilePath, + Arguments, + WorkingDirPath, + Credentials, + EnvironmentVariables, + Validation, + source, + StandardOutputPipe, + StandardErrorPipe + ); /// /// Creates a copy of this command, setting the standard output pipe to the specified target. /// [Pure] - public Command WithStandardOutputPipe(PipeTarget target) => new( - TargetFilePath, - Arguments, - WorkingDirPath, - Credentials, - EnvironmentVariables, - Validation, - StandardInputPipe, - target, - StandardErrorPipe - ); + public Command WithStandardOutputPipe(PipeTarget target) => + new( + TargetFilePath, + Arguments, + WorkingDirPath, + Credentials, + EnvironmentVariables, + Validation, + StandardInputPipe, + target, + StandardErrorPipe + ); /// /// Creates a copy of this command, setting the standard error pipe to the specified target. /// [Pure] - public Command WithStandardErrorPipe(PipeTarget target) => new( - TargetFilePath, - Arguments, - WorkingDirPath, - Credentials, - EnvironmentVariables, - Validation, - StandardInputPipe, - StandardOutputPipe, - target - ); + public Command WithStandardErrorPipe(PipeTarget target) => + new( + TargetFilePath, + Arguments, + WorkingDirPath, + Credentials, + EnvironmentVariables, + Validation, + StandardInputPipe, + StandardOutputPipe, + target + ); /// [ExcludeFromCodeCoverage] public override string ToString() => $"{TargetFilePath} {Arguments}"; -} \ No newline at end of file +} diff --git a/CliWrap/CommandResult.cs b/CliWrap/CommandResult.cs index a4b0fdaa..20994f89 100644 --- a/CliWrap/CommandResult.cs +++ b/CliWrap/CommandResult.cs @@ -36,4 +36,4 @@ public CommandResult(int exitCode, DateTimeOffset startTime, DateTimeOffset exit StartTime = startTime; ExitTime = exitTime; } -} \ No newline at end of file +} diff --git a/CliWrap/CommandResultValidation.cs b/CliWrap/CommandResultValidation.cs index 9dc9b486..4034166b 100644 --- a/CliWrap/CommandResultValidation.cs +++ b/CliWrap/CommandResultValidation.cs @@ -23,4 +23,4 @@ internal static class CommandResultValidationExtensions { public static bool IsZeroExitCodeValidationEnabled(this CommandResultValidation validation) => (validation & CommandResultValidation.ZeroExitCode) != 0; -} \ No newline at end of file +} diff --git a/CliWrap/CommandTask.cs b/CliWrap/CommandTask.cs index 5d32994c..0fced180 100644 --- a/CliWrap/CommandTask.cs +++ b/CliWrap/CommandTask.cs @@ -60,5 +60,6 @@ public partial class CommandTask /// /// Casts the command task into a regular task. /// - public static implicit operator Task(CommandTask commandTask) => commandTask.Task; -} \ No newline at end of file + public static implicit operator Task(CommandTask commandTask) => + commandTask.Task; +} diff --git a/CliWrap/Credentials.cs b/CliWrap/Credentials.cs index 6b95e4a7..c10a576c 100644 --- a/CliWrap/Credentials.cs +++ b/CliWrap/Credentials.cs @@ -43,7 +43,8 @@ public Credentials( string? domain = null, string? userName = null, string? password = null, - bool loadUserProfile = false) + bool loadUserProfile = false + ) { Domain = domain; UserName = userName; @@ -56,13 +57,8 @@ public Credentials( /// // TODO: (breaking change) remove in favor of the other overload [ExcludeFromCodeCoverage] - public Credentials( - string? domain, - string? username, - string? password) - : this(domain, username, password, false) - { - } + public Credentials(string? domain, string? username, string? password) + : this(domain, username, password, false) { } } public partial class Credentials @@ -71,4 +67,4 @@ public partial class Credentials /// Empty credentials. /// public static Credentials Default { get; } = new(); -} \ No newline at end of file +} diff --git a/CliWrap/EventStream/CommandEvent.cs b/CliWrap/EventStream/CommandEvent.cs index c217bfe4..db1f3e6d 100644 --- a/CliWrap/EventStream/CommandEvent.cs +++ b/CliWrap/EventStream/CommandEvent.cs @@ -17,9 +17,7 @@ namespace CliWrap.EventStream; /// /// /// -public abstract class CommandEvent -{ -} +public abstract class CommandEvent { } /// /// Event triggered when the command starts executing. @@ -101,4 +99,4 @@ public class ExitedCommandEvent : CommandEvent /// [ExcludeFromCodeCoverage] public override string ToString() => $"Exit code: {ExitCode}"; -} \ No newline at end of file +} diff --git a/CliWrap/EventStream/PullEventStreamCommandExtensions.cs b/CliWrap/EventStream/PullEventStreamCommandExtensions.cs index 1b551d1c..6d4fd15f 100644 --- a/CliWrap/EventStream/PullEventStreamCommandExtensions.cs +++ b/CliWrap/EventStream/PullEventStreamCommandExtensions.cs @@ -26,48 +26,63 @@ public static async IAsyncEnumerable ListenAsync( Encoding standardOutputEncoding, Encoding standardErrorEncoding, [EnumeratorCancellation] CancellationToken forcefulCancellationToken, - CancellationToken gracefulCancellationToken) + CancellationToken gracefulCancellationToken + ) { using var channel = new Channel(); var stdOutPipe = PipeTarget.Merge( command.StandardOutputPipe, - PipeTarget.ToDelegate(async (line, innerCancellationToken) => - { - // ReSharper disable once AccessToDisposedClosure - await channel - .PublishAsync(new StandardOutputCommandEvent(line), innerCancellationToken) - .ConfigureAwait(false); - }, standardOutputEncoding) + PipeTarget.ToDelegate( + async (line, innerCancellationToken) => + { + // ReSharper disable once AccessToDisposedClosure + await channel + .PublishAsync(new StandardOutputCommandEvent(line), innerCancellationToken) + .ConfigureAwait(false); + }, + standardOutputEncoding + ) ); var stdErrPipe = PipeTarget.Merge( command.StandardErrorPipe, - PipeTarget.ToDelegate(async (line, innerCancellationToken) => - { - // ReSharper disable once AccessToDisposedClosure - await channel - .PublishAsync(new StandardErrorCommandEvent(line), innerCancellationToken) - .ConfigureAwait(false); - }, standardErrorEncoding) + PipeTarget.ToDelegate( + async (line, innerCancellationToken) => + { + // ReSharper disable once AccessToDisposedClosure + await channel + .PublishAsync(new StandardErrorCommandEvent(line), innerCancellationToken) + .ConfigureAwait(false); + }, + standardErrorEncoding + ) ); var commandWithPipes = command .WithStandardOutputPipe(stdOutPipe) .WithStandardErrorPipe(stdErrPipe); - var commandTask = commandWithPipes.ExecuteAsync(forcefulCancellationToken, gracefulCancellationToken); + var commandTask = commandWithPipes.ExecuteAsync( + forcefulCancellationToken, + gracefulCancellationToken + ); yield return new StartedCommandEvent(commandTask.ProcessId); // Close the channel once the command completes, so that ReceiveAsync() can finish - _ = commandTask.Task.ContinueWith(async _ => - // ReSharper disable once AccessToDisposedClosure - await channel.ReportCompletionAsync(forcefulCancellationToken).ConfigureAwait(false), + _ = commandTask.Task.ContinueWith( + async _ => + // ReSharper disable once AccessToDisposedClosure + await channel + .ReportCompletionAsync(forcefulCancellationToken) + .ConfigureAwait(false), // Run the continuation even if the parent task failed TaskContinuationOptions.None ); - await foreach (var cmdEvent in channel.ReceiveAsync(forcefulCancellationToken).ConfigureAwait(false)) + await foreach ( + var cmdEvent in channel.ReceiveAsync(forcefulCancellationToken).ConfigureAwait(false) + ) yield return cmdEvent; var exitCode = await commandTask.Select(r => r.ExitCode).ConfigureAwait(false); @@ -84,7 +99,8 @@ public static IAsyncEnumerable ListenAsync( this Command command, Encoding standardOutputEncoding, Encoding standardErrorEncoding, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { return command.ListenAsync( standardOutputEncoding, @@ -103,13 +119,10 @@ public static IAsyncEnumerable ListenAsync( public static IAsyncEnumerable ListenAsync( this Command command, Encoding encoding, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { - return command.ListenAsync( - encoding, - encoding, - cancellationToken - ); + return command.ListenAsync(encoding, encoding, cancellationToken); } /// @@ -121,11 +134,9 @@ public static IAsyncEnumerable ListenAsync( /// public static IAsyncEnumerable ListenAsync( this Command command, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { - return command.ListenAsync( - Console.OutputEncoding, - cancellationToken - ); + return command.ListenAsync(Console.OutputEncoding, cancellationToken); } -} \ No newline at end of file +} diff --git a/CliWrap/EventStream/PushEventStreamCommandExtensions.cs b/CliWrap/EventStream/PushEventStreamCommandExtensions.cs index d390d8c7..eb97feae 100644 --- a/CliWrap/EventStream/PushEventStreamCommandExtensions.cs +++ b/CliWrap/EventStream/PushEventStreamCommandExtensions.cs @@ -25,7 +25,8 @@ public static IObservable Observe( Encoding standardOutputEncoding, Encoding standardErrorEncoding, CancellationToken forcefulCancellationToken, - CancellationToken gracefulCancellationToken) + CancellationToken gracefulCancellationToken + ) { return Observable.Create(observer => { @@ -49,14 +50,16 @@ public static IObservable Observe( .WithStandardOutputPipe(stdOutPipe) .WithStandardErrorPipe(stdErrPipe); - var commandTask = commandWithPipes.ExecuteAsync(forcefulCancellationToken, gracefulCancellationToken); + var commandTask = commandWithPipes.ExecuteAsync( + forcefulCancellationToken, + gracefulCancellationToken + ); observer.OnNext(new StartedCommandEvent(commandTask.ProcessId)); // Don't pass cancellation token to the continuation because we need it to // trigger regardless of how the task completed. - _ = commandTask - .Task - .ContinueWith(t => + _ = commandTask.Task.ContinueWith( + t => { // Canceled tasks don't have exceptions if (t.IsCanceled) @@ -72,7 +75,9 @@ public static IObservable Observe( observer.OnNext(new ExitedCommandEvent(t.Result.ExitCode)); observer.OnCompleted(); } - }, TaskContinuationOptions.None); + }, + TaskContinuationOptions.None + ); return Disposable.Null; }); @@ -88,7 +93,8 @@ public static IObservable Observe( this Command command, Encoding standardOutputEncoding, Encoding standardErrorEncoding, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { return command.Observe( standardOutputEncoding, @@ -107,13 +113,10 @@ public static IObservable Observe( public static IObservable Observe( this Command command, Encoding encoding, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { - return command.Observe( - encoding, - encoding, - cancellationToken - ); + return command.Observe(encoding, encoding, cancellationToken); } /// @@ -125,11 +128,9 @@ public static IObservable Observe( /// public static IObservable Observe( this Command command, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { - return command.Observe( - Console.OutputEncoding, - cancellationToken - ); + return command.Observe(Console.OutputEncoding, cancellationToken); } -} \ No newline at end of file +} diff --git a/CliWrap/Exceptions/CliWrapException.cs b/CliWrap/Exceptions/CliWrapException.cs index 97de1398..d7b88bca 100644 --- a/CliWrap/Exceptions/CliWrapException.cs +++ b/CliWrap/Exceptions/CliWrapException.cs @@ -12,9 +12,7 @@ public abstract class CliWrapException : Exception /// Initializes an instance of . /// protected CliWrapException(string message, Exception? innerException) - : base(message, innerException) - { - } + : base(message, innerException) { } /// /// Initializes an instance of . @@ -22,7 +20,5 @@ protected CliWrapException(string message, Exception? innerException) // TODO: (breaking change) remove in favor of an optional parameter in the constructor above [ExcludeFromCodeCoverage] protected CliWrapException(string message) - : this(message, null) - { - } -} \ No newline at end of file + : this(message, null) { } +} diff --git a/CliWrap/Exceptions/CommandExecutionException.cs b/CliWrap/Exceptions/CommandExecutionException.cs index 0821f213..b9eb74f5 100644 --- a/CliWrap/Exceptions/CommandExecutionException.cs +++ b/CliWrap/Exceptions/CommandExecutionException.cs @@ -25,7 +25,8 @@ public CommandExecutionException( ICommandConfiguration command, int exitCode, string message, - Exception? innerException) + Exception? innerException + ) : base(message, innerException) { Command = command; @@ -37,11 +38,6 @@ public CommandExecutionException( /// // TODO: (breaking change) remove in favor of an optional parameter in the constructor above [ExcludeFromCodeCoverage] - public CommandExecutionException( - ICommandConfiguration command, - int exitCode, - string message) - : this(command, exitCode, message, null) - { - } -} \ No newline at end of file + public CommandExecutionException(ICommandConfiguration command, int exitCode, string message) + : this(command, exitCode, message, null) { } +} diff --git a/CliWrap/ICommandConfiguration.cs b/CliWrap/ICommandConfiguration.cs index 787d68d5..5a101d0b 100644 --- a/CliWrap/ICommandConfiguration.cs +++ b/CliWrap/ICommandConfiguration.cs @@ -51,4 +51,4 @@ public interface ICommandConfiguration /// Pipe target for the standard error stream of the underlying process. /// PipeTarget StandardErrorPipe { get; } -} \ No newline at end of file +} diff --git a/CliWrap/PipeSource.cs b/CliWrap/PipeSource.cs index e11fb87d..fa70e96e 100644 --- a/CliWrap/PipeSource.cs +++ b/CliWrap/PipeSource.cs @@ -17,7 +17,10 @@ public abstract partial class PipeSource /// Reads the binary content pushed into the pipe and writes it to the destination stream. /// Destination stream represents the process's standard input stream. /// - public abstract Task CopyToAsync(Stream destination, CancellationToken cancellationToken = default); + public abstract Task CopyToAsync( + Stream destination, + CancellationToken cancellationToken = default + ); } file class AnonymousPipeSource : PipeSource @@ -27,8 +30,10 @@ public abstract partial class PipeSource public AnonymousPipeSource(Func copyToAsync) => _copyToAsync = copyToAsync; - public override async Task CopyToAsync(Stream destination, CancellationToken cancellationToken = default) => - await _copyToAsync(destination, cancellationToken).ConfigureAwait(false); + public override async Task CopyToAsync( + Stream destination, + CancellationToken cancellationToken = default + ) => await _copyToAsync(destination, cancellationToken).ConfigureAwait(false); } public partial class PipeSource @@ -37,11 +42,13 @@ public partial class PipeSource /// Pipe source that does not provide any data. /// Functionally equivalent to a null device. /// - public static PipeSource Null { get; } = Create((_, cancellationToken) => - !cancellationToken.IsCancellationRequested - ? Task.CompletedTask - : Task.FromCanceled(cancellationToken) - ); + public static PipeSource Null { get; } = + Create( + (_, cancellationToken) => + !cancellationToken.IsCancellationRequested + ? Task.CompletedTask + : Task.FromCanceled(cancellationToken) + ); /// /// Creates an anonymous pipe source with the method @@ -54,18 +61,24 @@ public static PipeSource Create(Func handlePipe /// Creates an anonymous pipe source with the method /// implemented by the specified synchronous delegate. /// - public static PipeSource Create(Action handlePipe) => Create((destination, _) => - { - handlePipe(destination); - return Task.CompletedTask; - }); + public static PipeSource Create(Action handlePipe) => + Create( + (destination, _) => + { + handlePipe(destination); + return Task.CompletedTask; + } + ); /// /// Creates a pipe source that reads from the specified stream. /// public static PipeSource FromStream(Stream stream, bool autoFlush) => - Create(async (destination, cancellationToken) => - await stream.CopyToAsync(destination, autoFlush, cancellationToken).ConfigureAwait(false) + Create( + async (destination, cancellationToken) => + await stream + .CopyToAsync(destination, autoFlush, cancellationToken) + .ConfigureAwait(false) ); /// @@ -77,19 +90,24 @@ await stream.CopyToAsync(destination, autoFlush, cancellationToken).ConfigureAwa /// /// Creates a pipe source that reads from the specified file. /// - public static PipeSource FromFile(string filePath) => Create(async (destination, cancellationToken) => - { - var source = File.OpenRead(filePath); - await using (source.ToAsyncDisposable()) - await source.CopyToAsync(destination, cancellationToken).ConfigureAwait(false); - }); + public static PipeSource FromFile(string filePath) => + Create( + async (destination, cancellationToken) => + { + var source = File.OpenRead(filePath); + await using (source.ToAsyncDisposable()) + await source.CopyToAsync(destination, cancellationToken).ConfigureAwait(false); + } + ); /// /// Creates a pipe source that reads from the specified memory buffer. /// - public static PipeSource FromBytes(ReadOnlyMemory data) => Create(async (destination, cancellationToken) => - await destination.WriteAsync(data, cancellationToken).ConfigureAwait(false) - ); + public static PipeSource FromBytes(ReadOnlyMemory data) => + Create( + async (destination, cancellationToken) => + await destination.WriteAsync(data, cancellationToken).ConfigureAwait(false) + ); /// /// Creates a pipe source that reads from the specified byte array. @@ -103,7 +121,8 @@ await destination.WriteAsync(data, cancellationToken).ConfigureAwait(false) /// /// Creates a pipe source that reads from the specified string. /// - public static PipeSource FromString(string str, Encoding encoding) => FromBytes(encoding.GetBytes(str)); + public static PipeSource FromString(string str, Encoding encoding) => + FromBytes(encoding.GetBytes(str)); /// /// Creates a pipe source that reads from the specified string. @@ -114,10 +133,12 @@ await destination.WriteAsync(data, cancellationToken).ConfigureAwait(false) /// /// Creates a pipe source that reads from the standard output of the specified command. /// - public static PipeSource FromCommand(Command command) => Create(async (destination, cancellationToken) => - await command - .WithStandardOutputPipe(PipeTarget.ToStream(destination)) - .ExecuteAsync(cancellationToken) - .ConfigureAwait(false) - ); -} \ No newline at end of file + public static PipeSource FromCommand(Command command) => + Create( + async (destination, cancellationToken) => + await command + .WithStandardOutputPipe(PipeTarget.ToStream(destination)) + .ExecuteAsync(cancellationToken) + .ConfigureAwait(false) + ); +} diff --git a/CliWrap/PipeTarget.cs b/CliWrap/PipeTarget.cs index 0e4115fe..69b32559 100644 --- a/CliWrap/PipeTarget.cs +++ b/CliWrap/PipeTarget.cs @@ -20,7 +20,10 @@ public abstract partial class PipeTarget /// Reads the binary content from the origin stream and pushes it into the pipe. /// Origin stream represents the process's standard output or standard error stream. /// - public abstract Task CopyFromAsync(Stream origin, CancellationToken cancellationToken = default); + public abstract Task CopyFromAsync( + Stream origin, + CancellationToken cancellationToken = default + ); } file class AnonymousPipeTarget : PipeTarget @@ -30,8 +33,10 @@ public abstract partial class PipeTarget public AnonymousPipeTarget(Func copyFromAsync) => _copyFromAsync = copyFromAsync; - public override async Task CopyFromAsync(Stream origin, CancellationToken cancellationToken = default) => - await _copyFromAsync(origin, cancellationToken).ConfigureAwait(false); + public override async Task CopyFromAsync( + Stream origin, + CancellationToken cancellationToken = default + ) => await _copyFromAsync(origin, cancellationToken).ConfigureAwait(false); } file class AggregatePipeTarget : PipeTarget @@ -40,7 +45,10 @@ public override async Task CopyFromAsync(Stream origin, CancellationToken cancel public AggregatePipeTarget(IReadOnlyList targets) => Targets = targets; - public override async Task CopyFromAsync(Stream origin, CancellationToken cancellationToken = default) + public override async Task CopyFromAsync( + Stream origin, + CancellationToken cancellationToken = default + ) { // Cancellation to abort the pipe if any of the underlying targets fail using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); @@ -53,24 +61,26 @@ public override async Task CopyFromAsync(Stream origin, CancellationToken cancel try { // Start piping in the background - var readingTask = Task.WhenAll(targetSubStreams.Select(async targetSubStream => - { - var (target, subStream) = targetSubStream; - - try - { - // ReSharper disable once AccessToDisposedClosure - await target.CopyFromAsync(subStream, cts.Token).ConfigureAwait(false); - } - catch + var readingTask = Task.WhenAll( + targetSubStreams.Select(async targetSubStream => { - // Abort the operation if any of the targets fail - // ReSharper disable once AccessToDisposedClosure - cts.Cancel(); - - throw; - } - })); + var (target, subStream) = targetSubStream; + + try + { + // ReSharper disable once AccessToDisposedClosure + await target.CopyFromAsync(subStream, cts.Token).ConfigureAwait(false); + } + catch + { + // Abort the operation if any of the targets fail + // ReSharper disable once AccessToDisposedClosure + cts.Cancel(); + + throw; + } + }) + ); try { @@ -78,12 +88,16 @@ public override async Task CopyFromAsync(Stream origin, CancellationToken cancel using var buffer = MemoryPool.Shared.Rent(BufferSizes.Stream); while (true) { - var bytesRead = await origin.ReadAsync(buffer.Memory, cts.Token).ConfigureAwait(false); + var bytesRead = await origin + .ReadAsync(buffer.Memory, cts.Token) + .ConfigureAwait(false); if (bytesRead <= 0) break; foreach (var (_, subStream) in targetSubStreams) - await subStream.WriteAsync(buffer.Memory[..bytesRead], cts.Token).ConfigureAwait(false); + await subStream + .WriteAsync(buffer.Memory[..bytesRead], cts.Token) + .ConfigureAwait(false); } // Report that transmission is complete @@ -118,11 +132,13 @@ public partial class PipeTarget /// This may be undesirable in certain situations, in which case it's recommended to pipe to a /// null stream explicitly using with . /// - public static PipeTarget Null { get; } = Create((_, cancellationToken) => - !cancellationToken.IsCancellationRequested - ? Task.CompletedTask - : Task.FromCanceled(cancellationToken) - ); + public static PipeTarget Null { get; } = + Create( + (_, cancellationToken) => + !cancellationToken.IsCancellationRequested + ? Task.CompletedTask + : Task.FromCanceled(cancellationToken) + ); /// /// Creates an anonymous pipe target with the method @@ -135,18 +151,23 @@ public static PipeTarget Create(Func handlePipe /// Creates an anonymous pipe target with the method /// implemented by the specified synchronous delegate. /// - public static PipeTarget Create(Action handlePipe) => Create((origin, _) => - { - handlePipe(origin); - return Task.CompletedTask; - }); + public static PipeTarget Create(Action handlePipe) => + Create( + (origin, _) => + { + handlePipe(origin); + return Task.CompletedTask; + } + ); /// /// Creates a pipe target that writes to the specified stream. /// - public static PipeTarget ToStream(Stream stream, bool autoFlush) => Create(async (origin, cancellationToken) => - await origin.CopyToAsync(stream, autoFlush, cancellationToken).ConfigureAwait(false) - ); + public static PipeTarget ToStream(Stream stream, bool autoFlush) => + Create( + async (origin, cancellationToken) => + await origin.CopyToAsync(stream, autoFlush, cancellationToken).ConfigureAwait(false) + ); /// /// Creates a pipe target that writes to the specified stream. @@ -157,31 +178,44 @@ await origin.CopyToAsync(stream, autoFlush, cancellationToken).ConfigureAwait(fa /// /// Creates a pipe target that writes to the specified file. /// - public static PipeTarget ToFile(string filePath) => Create(async (origin, cancellationToken) => - { - var target = File.Create(filePath); - await using (target.ToAsyncDisposable()) - await origin.CopyToAsync(target, cancellationToken).ConfigureAwait(false); - }); + public static PipeTarget ToFile(string filePath) => + Create( + async (origin, cancellationToken) => + { + var target = File.Create(filePath); + await using (target.ToAsyncDisposable()) + await origin.CopyToAsync(target, cancellationToken).ConfigureAwait(false); + } + ); /// /// Creates a pipe target that writes to the specified string builder. /// public static PipeTarget ToStringBuilder(StringBuilder stringBuilder, Encoding encoding) => - Create(async (origin, cancellationToken) => - { - using var reader = new StreamReader(origin, encoding, false, BufferSizes.StreamReader, true); - using var buffer = MemoryPool.Shared.Rent(BufferSizes.StreamReader); - - while (true) + Create( + async (origin, cancellationToken) => { - var charsRead = await reader.ReadAsync(buffer.Memory, cancellationToken).ConfigureAwait(false); - if (charsRead <= 0) - break; + using var reader = new StreamReader( + origin, + encoding, + false, + BufferSizes.StreamReader, + true + ); + using var buffer = MemoryPool.Shared.Rent(BufferSizes.StreamReader); + + while (true) + { + var charsRead = await reader + .ReadAsync(buffer.Memory, cancellationToken) + .ConfigureAwait(false); + if (charsRead <= 0) + break; - stringBuilder.Append(buffer.Memory[..charsRead]); + stringBuilder.Append(buffer.Memory[..charsRead]); + } } - }); + ); /// /// Creates a pipe target that writes to the specified string builder. @@ -193,13 +227,26 @@ public static PipeTarget ToStringBuilder(StringBuilder stringBuilder) => /// /// Creates a pipe target that invokes the specified asynchronous delegate on every line written to the stream. /// - public static PipeTarget ToDelegate(Func handleLineAsync, Encoding encoding) => - Create(async (origin, cancellationToken) => - { - using var reader = new StreamReader(origin, encoding, false, BufferSizes.StreamReader, true); - await foreach (var line in reader.ReadAllLinesAsync(cancellationToken).ConfigureAwait(false)) - await handleLineAsync(line, cancellationToken).ConfigureAwait(false); - }); + public static PipeTarget ToDelegate( + Func handleLineAsync, + Encoding encoding + ) => + Create( + async (origin, cancellationToken) => + { + using var reader = new StreamReader( + origin, + encoding, + false, + BufferSizes.StreamReader, + true + ); + await foreach ( + var line in reader.ReadAllLinesAsync(cancellationToken).ConfigureAwait(false) + ) + await handleLineAsync(line, cancellationToken).ConfigureAwait(false); + } + ); /// /// Creates a pipe target that invokes the specified asynchronous delegate on every line written to the stream. @@ -212,10 +259,7 @@ public static PipeTarget ToDelegate(Func handle /// Creates a pipe target that invokes the specified asynchronous delegate on every line written to the stream. /// public static PipeTarget ToDelegate(Func handleLineAsync, Encoding encoding) => - ToDelegate( - async (line, _) => await handleLineAsync(line).ConfigureAwait(false), - encoding - ); + ToDelegate(async (line, _) => await handleLineAsync(line).ConfigureAwait(false), encoding); /// /// Creates a pipe target that invokes the specified asynchronous delegate on every line written to the stream. @@ -228,11 +272,14 @@ public static PipeTarget ToDelegate(Func handleLineAsync) => /// Creates a pipe target that invokes the specified synchronous delegate on every line written to the stream. /// public static PipeTarget ToDelegate(Action handleLine, Encoding encoding) => - ToDelegate(line => - { - handleLine(line); - return Task.CompletedTask; - }, encoding); + ToDelegate( + line => + { + handleLine(line); + return Task.CompletedTask; + }, + encoding + ); /// /// Creates a pipe target that invokes the specified synchronous delegate on every line written to the stream. @@ -292,5 +339,6 @@ static IReadOnlyList OptimizeTargets(IEnumerable targets /// /// Creates a pipe target that replicates data over multiple inner targets. /// - public static PipeTarget Merge(params PipeTarget[] targets) => Merge((IEnumerable)targets); -} \ No newline at end of file + public static PipeTarget Merge(params PipeTarget[] targets) => + Merge((IEnumerable)targets); +} diff --git a/CliWrap/Utils/BufferSizes.cs b/CliWrap/Utils/BufferSizes.cs index c445843b..65503b1b 100644 --- a/CliWrap/Utils/BufferSizes.cs +++ b/CliWrap/Utils/BufferSizes.cs @@ -4,4 +4,4 @@ internal static class BufferSizes { public const int Stream = 81920; public const int StreamReader = 1024; -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Channel.cs b/CliWrap/Utils/Channel.cs index 9e217859..fe96601e 100644 --- a/CliWrap/Utils/Channel.cs +++ b/CliWrap/Utils/Channel.cs @@ -25,7 +25,8 @@ public async Task PublishAsync(T item, CancellationToken cancellationToken = def } public async IAsyncEnumerable ReceiveAsync( - [EnumeratorCancellation] CancellationToken cancellationToken = default) + [EnumeratorCancellation] CancellationToken cancellationToken = default + ) { while (true) { @@ -62,4 +63,4 @@ public void Dispose() _writeLock.Dispose(); _readLock.Dispose(); } -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Disposable.cs b/CliWrap/Utils/Disposable.cs index dc0cf2b2..ba50b3ae 100644 --- a/CliWrap/Utils/Disposable.cs +++ b/CliWrap/Utils/Disposable.cs @@ -16,4 +16,4 @@ internal partial class Disposable public static IDisposable Null { get; } = Create(() => { }); public static IDisposable Create(Action dispose) => new Disposable(dispose); -} \ No newline at end of file +} diff --git a/CliWrap/Utils/EnvironmentEx.cs b/CliWrap/Utils/EnvironmentEx.cs index 01f4ed17..79b72e82 100644 --- a/CliWrap/Utils/EnvironmentEx.cs +++ b/CliWrap/Utils/EnvironmentEx.cs @@ -5,11 +5,12 @@ namespace CliWrap.Utils; internal static class EnvironmentEx { - private static readonly Lazy ProcessPathLazy = new(() => - { - using var process = Process.GetCurrentProcess(); - return process.MainModule?.FileName; - }); + private static readonly Lazy ProcessPathLazy = + new(() => + { + using var process = Process.GetCurrentProcess(); + return process.MainModule?.FileName; + }); public static string? ProcessPath => ProcessPathLazy.Value; -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Extensions/AssemblyExtensions.cs b/CliWrap/Utils/Extensions/AssemblyExtensions.cs index 9c7a4000..ed249185 100644 --- a/CliWrap/Utils/Extensions/AssemblyExtensions.cs +++ b/CliWrap/Utils/Extensions/AssemblyExtensions.cs @@ -9,13 +9,16 @@ internal static class AssemblyExtensions public static void ExtractManifestResource( this Assembly assembly, string resourceName, - string destFilePath) + string destFilePath + ) { var input = - assembly.GetManifestResourceStream(resourceName) ?? - throw new MissingManifestResourceException($"Could not find resource '{resourceName}'."); + assembly.GetManifestResourceStream(resourceName) + ?? throw new MissingManifestResourceException( + $"Could not find resource '{resourceName}'." + ); using var output = File.Create(destFilePath); input.CopyTo(output); } -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Extensions/AsyncDisposableExtensions.cs b/CliWrap/Utils/Extensions/AsyncDisposableExtensions.cs index 8470ba97..37b3f284 100644 --- a/CliWrap/Utils/Extensions/AsyncDisposableExtensions.cs +++ b/CliWrap/Utils/Extensions/AsyncDisposableExtensions.cs @@ -34,4 +34,4 @@ public async ValueTask DisposeAsync() } } } -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Extensions/CancellationTokenExtensions.cs b/CliWrap/Utils/Extensions/CancellationTokenExtensions.cs index fad0f676..3f32142f 100644 --- a/CliWrap/Utils/Extensions/CancellationTokenExtensions.cs +++ b/CliWrap/Utils/Extensions/CancellationTokenExtensions.cs @@ -5,14 +5,14 @@ namespace CliWrap.Utils.Extensions; internal static class CancellationTokenExtensions { - public static void ThrowIfCancellationRequested(this CancellationToken cancellationToken, string message) + public static void ThrowIfCancellationRequested( + this CancellationToken cancellationToken, + string message + ) { if (!cancellationToken.IsCancellationRequested) return; - throw new OperationCanceledException( - message, - cancellationToken - ); + throw new OperationCanceledException(message, cancellationToken); } -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Extensions/ExceptionExtensions.cs b/CliWrap/Utils/Extensions/ExceptionExtensions.cs index 1736e5a4..007eeadd 100644 --- a/CliWrap/Utils/Extensions/ExceptionExtensions.cs +++ b/CliWrap/Utils/Extensions/ExceptionExtensions.cs @@ -9,8 +9,6 @@ internal static class ExceptionExtensions { var exceptions = exception.Flatten().InnerExceptions; - return exceptions.Count == 1 - ? exceptions.Single() - : null; + return exceptions.Count == 1 ? exceptions.Single() : null; } -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Extensions/StreamExtensions.cs b/CliWrap/Utils/Extensions/StreamExtensions.cs index 9919b3dd..cbbdcb22 100644 --- a/CliWrap/Utils/Extensions/StreamExtensions.cs +++ b/CliWrap/Utils/Extensions/StreamExtensions.cs @@ -14,16 +14,22 @@ public static async Task CopyToAsync( this Stream source, Stream destination, bool autoFlush, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default + ) { using var buffer = MemoryPool.Shared.Rent(BufferSizes.Stream); while (true) { - var bytesRead = await source.ReadAsync(buffer.Memory, cancellationToken).ConfigureAwait(false); + var bytesRead = await source + .ReadAsync(buffer.Memory, cancellationToken) + .ConfigureAwait(false); + if (bytesRead <= 0) break; - await destination.WriteAsync(buffer.Memory[..bytesRead], cancellationToken).ConfigureAwait(false); + await destination + .WriteAsync(buffer.Memory[..bytesRead], cancellationToken) + .ConfigureAwait(false); if (autoFlush) await destination.FlushAsync(cancellationToken).ConfigureAwait(false); @@ -32,7 +38,8 @@ public static async Task CopyToAsync( public static async IAsyncEnumerable ReadAllLinesAsync( this StreamReader reader, - [EnumeratorCancellation] CancellationToken cancellationToken = default) + [EnumeratorCancellation] CancellationToken cancellationToken = default + ) { // We could use reader.ReadLineAsync() and loop on it, but that method // only supports cancellation on .NET 7+ and it's impossible to polyfill @@ -50,7 +57,9 @@ public static async IAsyncEnumerable ReadAllLinesAsync( var isLastCaretReturn = false; while (true) { - var charsRead = await reader.ReadAsync(buffer.Memory, cancellationToken).ConfigureAwait(false); + var charsRead = await reader + .ReadAsync(buffer.Memory, cancellationToken) + .ConfigureAwait(false); if (charsRead <= 0) break; @@ -88,4 +97,4 @@ public static async IAsyncEnumerable ReadAllLinesAsync( if (lineBuffer.Length > 0) yield return lineBuffer.ToString(); } -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Extensions/StringExtensions.cs b/CliWrap/Utils/Extensions/StringExtensions.cs index 0f11670e..a3d9510d 100644 --- a/CliWrap/Utils/Extensions/StringExtensions.cs +++ b/CliWrap/Utils/Extensions/StringExtensions.cs @@ -15,4 +15,4 @@ public static SecureString ToSecureString(this string str) return secureString; } -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Extensions/TaskExtensions.cs b/CliWrap/Utils/Extensions/TaskExtensions.cs index 04366dd1..e01dfb9b 100644 --- a/CliWrap/Utils/Extensions/TaskExtensions.cs +++ b/CliWrap/Utils/Extensions/TaskExtensions.cs @@ -7,9 +7,10 @@ internal static class TaskExtensions { public static async Task Select( this Task task, - Func transform) + Func transform + ) { var result = await task.ConfigureAwait(false); return transform(result); } -} \ No newline at end of file +} diff --git a/CliWrap/Utils/NativeMethods.cs b/CliWrap/Utils/NativeMethods.cs index 9c3a871f..481b8744 100644 --- a/CliWrap/Utils/NativeMethods.cs +++ b/CliWrap/Utils/NativeMethods.cs @@ -9,4 +9,4 @@ public static class Unix [DllImport("libc", EntryPoint = "kill", SetLastError = true)] public static extern int Kill(int pid, int sig); } -} \ No newline at end of file +} diff --git a/CliWrap/Utils/Observable.cs b/CliWrap/Utils/Observable.cs index 4ac5acf7..87e5929a 100644 --- a/CliWrap/Utils/Observable.cs +++ b/CliWrap/Utils/Observable.cs @@ -52,4 +52,4 @@ internal static class Observable { public static IObservable Create(Func, IDisposable> subscribe) => new Observable(subscribe); -} \ No newline at end of file +} diff --git a/CliWrap/Utils/ProcessEx.cs b/CliWrap/Utils/ProcessEx.cs index 3a19cb49..4659188b 100644 --- a/CliWrap/Utils/ProcessEx.cs +++ b/CliWrap/Utils/ProcessEx.cs @@ -12,7 +12,8 @@ namespace CliWrap.Utils; internal class ProcessEx : IDisposable { private readonly Process _nativeProcess; - private readonly TaskCompletionSource _exitTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); + private readonly TaskCompletionSource _exitTcs = + new(TaskCreationOptions.RunContinuationsAsynchronously); public int Id => _nativeProcess.Id; @@ -58,8 +59,8 @@ public void Start() if (!_nativeProcess.Start()) { throw new InvalidOperationException( - $"Failed to start a process with file path '{_nativeProcess.StartInfo.FileName}'. " + - "Target file is not an executable or lacks execute permissions." + $"Failed to start a process with file path '{_nativeProcess.StartInfo.FileName}'. " + + "Target file is not an executable or lacks execute permissions." ); } @@ -68,8 +69,8 @@ public void Start() catch (Win32Exception ex) { throw new Win32Exception( - $"Failed to start a process with file path '{_nativeProcess.StartInfo.FileName}'. " + - "Target file or working directory doesn't exist, or the provided credentials are invalid.", + $"Failed to start a process with file path '{_nativeProcess.StartInfo.FileName}'. " + + "Target file or working directory doesn't exist, or the provided credentials are invalid.", ex ); } @@ -92,8 +93,10 @@ bool TryInterrupt() } // On Unix, we can just send the signal to the process directly - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || - RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + if ( + RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + || RuntimeInformation.IsOSPlatform(OSPlatform.OSX) + ) { return NativeMethods.Unix.Kill(_nativeProcess.Id, 2) == 0; } @@ -138,9 +141,13 @@ public void Kill() public async Task WaitUntilExitAsync(CancellationToken cancellationToken = default) { - await using (cancellationToken.Register(() => _exitTcs.TrySetCanceled(cancellationToken)).ToAsyncDisposable()) + await using ( + cancellationToken + .Register(() => _exitTcs.TrySetCanceled(cancellationToken)) + .ToAsyncDisposable() + ) await _exitTcs.Task.ConfigureAwait(false); } public void Dispose() => _nativeProcess.Dispose(); -} \ No newline at end of file +} diff --git a/CliWrap/Utils/SimplexStream.cs b/CliWrap/Utils/SimplexStream.cs index 96bc0296..b374fbb8 100644 --- a/CliWrap/Utils/SimplexStream.cs +++ b/CliWrap/Utils/SimplexStream.cs @@ -35,7 +35,8 @@ public override async Task WriteAsync( byte[] buffer, int offset, int count, - CancellationToken cancellationToken) + CancellationToken cancellationToken + ) { await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -58,14 +59,14 @@ public override async Task ReadAsync( byte[] buffer, int offset, int count, - CancellationToken cancellationToken) + CancellationToken cancellationToken + ) { await _readLock.WaitAsync(cancellationToken).ConfigureAwait(false); var length = Math.Min(count, _sharedBufferBytes - _sharedBufferBytesRead); - _sharedBuffer - .Memory + _sharedBuffer.Memory .Slice(_sharedBufferBytesRead, length) .CopyTo(buffer.AsMemory(offset, length)); @@ -119,4 +120,4 @@ public override void Write(byte[] buffer, int offset, int count) => [ExcludeFromCodeCoverage] public override void SetLength(long value) => throw new NotSupportedException(); -} \ No newline at end of file +} diff --git a/CliWrap/Utils/WindowsSignaler.cs b/CliWrap/Utils/WindowsSignaler.cs index 0485721e..98417a37 100644 --- a/CliWrap/Utils/WindowsSignaler.cs +++ b/CliWrap/Utils/WindowsSignaler.cs @@ -11,8 +11,7 @@ internal partial class WindowsSignaler : IDisposable { private readonly string _filePath; - public WindowsSignaler(string filePath) => - _filePath = filePath; + public WindowsSignaler(string filePath) => _filePath = filePath; public bool TrySend(int processId, int signalId) { @@ -22,9 +21,9 @@ public bool TrySend(int processId, int signalId) { FileName = _filePath, Arguments = - processId.ToString(CultureInfo.InvariantCulture) + - ' ' + - signalId.ToString(CultureInfo.InvariantCulture), + processId.ToString(CultureInfo.InvariantCulture) + + ' ' + + signalId.ToString(CultureInfo.InvariantCulture), CreateNoWindow = true, UseShellExecute = false, Environment = @@ -69,4 +68,4 @@ public static WindowsSignaler Deploy() return new WindowsSignaler(filePath); } -} \ No newline at end of file +} From c19e50b0bc6e141040383e128903c942d6b0caa3 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 31 Aug 2023 17:09:37 +0300 Subject: [PATCH 22/57] `Microsoft.NETFramework.ReferenceAssemblies` is no longer necessary --- CliWrap.Signaler/CliWrap.Signaler.csproj | 1 - CliWrap/CliWrap.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/CliWrap.Signaler/CliWrap.Signaler.csproj b/CliWrap.Signaler/CliWrap.Signaler.csproj index f42fa349..61f24e9b 100644 --- a/CliWrap.Signaler/CliWrap.Signaler.csproj +++ b/CliWrap.Signaler/CliWrap.Signaler.csproj @@ -7,7 +7,6 @@ - \ No newline at end of file diff --git a/CliWrap/CliWrap.csproj b/CliWrap/CliWrap.csproj index 7e80ad93..eed352ce 100644 --- a/CliWrap/CliWrap.csproj +++ b/CliWrap/CliWrap.csproj @@ -33,7 +33,6 @@ - From ce94b241841594dac3c6d8e836d7f1f29ba5b30a Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Wed, 20 Sep 2023 21:32:11 +0300 Subject: [PATCH 23/57] Treat all warnings as errors --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 27f87381..eb2a590e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ Copyright (C) Oleksii Holub latest enable - nullable + true false false From 42822f14db4044864774a8fb924f771219ffd8b0 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sun, 29 Oct 2023 00:49:37 +0300 Subject: [PATCH 24/57] Simplify workflows --- .github/workflows/main.yml | 18 +++++++++++++++++- .github/workflows/prerelease.yml | 25 ------------------------- 2 files changed, 17 insertions(+), 26 deletions(-) delete mode 100644 .github/workflows/prerelease.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8fe71abc..44bea580 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,12 +1,28 @@ name: main -on: [push, pull_request] +on: + workflow_dispatch: + inputs: + package-version: + type: string + description: Package version + required: false + deploy: + type: boolean + description: Deploy package + required: false + default: false + push: + pull_request: jobs: main: uses: Tyrrrz/.github/.github/workflows/nuget.yml@master with: dotnet-version: 7.0.x + package-version: ${{ inputs.package-version }} + # Deploy only on tags by default, unless deploy is explicitly requested + deploy-on-tags-only: ${{ !(github.event_name == 'workflow_dispatch' && inputs.deploy) }} secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml deleted file mode 100644 index 364b2535..00000000 --- a/.github/workflows/prerelease.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: prerelease - -on: - workflow_dispatch: - inputs: - package-version-base: - type: string - description: Package version base - required: true - package-version-suffix: - type: string - description: Package version suffix - required: true - -jobs: - prerelease: - uses: Tyrrrz/.github/.github/workflows/nuget.yml@master - with: - dotnet-version: 7.0.x - package-version: ${{ format('{0}-{1}', inputs.package-version-base, inputs.package-version-suffix) }} - deploy-on-tags-only: false - secrets: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} - DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} From 0c83a483ed785d9ba1283a13a337cb08c8f292d9 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 9 Nov 2023 13:41:53 +0200 Subject: [PATCH 25/57] Update gitignore file --- .gitignore | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index dab02c42..ef8150c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,12 @@ # User-specific files +.vs/ +.idea/ *.suo *.user -*.userosscache -*.sln.docstates -.idea/ # Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -[Xx]64/ -[Xx]86/ -[Bb]uild/ -bld/ -[Bb]in/ -[Oo]bj/ +bin/ +obj/ -# Coverage -*.opencover.xml \ No newline at end of file +# Test results +TestResults/ \ No newline at end of file From 888246e4894b535d771b1dd5bf046747ed8ffe3e Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 11 Nov 2023 16:18:22 +0200 Subject: [PATCH 26/57] Remove some shared project properties --- CliWrap/CliWrap.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CliWrap/CliWrap.csproj b/CliWrap/CliWrap.csproj index eed352ce..1b66ae75 100644 --- a/CliWrap/CliWrap.csproj +++ b/CliWrap/CliWrap.csproj @@ -13,11 +13,7 @@ https://github.com/Tyrrrz/CliWrap/blob/master/Changelog.md favicon.png MIT - false true - true - true - embedded From bf5a3a889c342692a6bc1448462424fb3f959456 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 11 Nov 2023 20:38:13 +0200 Subject: [PATCH 27/57] Update readme --- Readme.md | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/Readme.md b/Readme.md index de9d8b53..4d0b0061 100644 --- a/Readme.md +++ b/Readme.md @@ -185,6 +185,8 @@ var cmd = Cli.Wrap("git") ``` ```csharp +var forcePush = true; + var cmd = Cli.Wrap("git") // Arguments can also be constructed in an imperative fashion. // Equivalent to: `git push --force` @@ -236,6 +238,16 @@ Sets additional environment variables exposed to the child process. **Examples**: +- Set environment variables using a builder: + +```csharp +var cmd = Cli.Wrap("git") + .WithEnvironmentVariables(env => env + .Set("GIT_AUTHOR_NAME", "John") + .Set("GIT_AUTHOR_EMAIL", "john@email.com") + ); +``` + - Set environment variables directly: ```csharp @@ -247,16 +259,6 @@ var cmd = Cli.Wrap("git") }); ``` -- Set environment variables using a builder: - -```csharp -var cmd = Cli.Wrap("git") - .WithEnvironmentVariables(env => env - .Set("GIT_AUTHOR_NAME", "John") - .Set("GIT_AUTHOR_EMAIL", "john@email.com") - ); -``` - > **Note**: > Environment variables configured using `WithEnvironmentVariables(...)` are applied on top of those inherited from the parent process. > If you need to remove an inherited variable, set the corresponding value to `null`. @@ -269,18 +271,6 @@ Sets domain, name and password of the user, under whom the child process should **Examples**: -- Set credentials directly: - -```csharp -var cmd = Cli.Wrap("git") - .WithCredentials(new Credentials( - domain: "some_workspace", - userName: "johndoe", - password: "securepassword123", - loadUserProfile: true - )); -``` - - Set credentials using a builder: ```csharp @@ -293,6 +283,18 @@ var cmd = Cli.Wrap("git") ); ``` +- Set credentials directly: + +```csharp +var cmd = Cli.Wrap("git") + .WithCredentials(new Credentials( + domain: "some_workspace", + userName: "johndoe", + password: "securepassword123", + loadUserProfile: true + )); +``` + > **Warning**: > Running a process under a different username is supported across all platforms, but other options are only available on Windows. @@ -683,7 +685,7 @@ forcefulCts.CancelAfter(TimeSpan.FromSeconds(10)); // Cancel gracefully after a timeout of 7 seconds. // If the process takes too long to respond to graceful // cancellation, it will get killed by forceful cancellation -// 3 seoncds later (as configured above). +// 3 seconds later (as configured above). gracefulCts.CancelAfter(TimeSpan.FromSeconds(7)); var result = await Cli.Wrap("foo").ExecuteAsync(forcefulCts.Token, gracefulCts.Token); @@ -695,7 +697,7 @@ The underlying process may handle this signal to perform last-minute critical wo Graceful cancellation is inherently cooperative, so it's possible that the process may take too long to fulfill the request or choose to ignore it altogether. In the above example, this risk is mitigated by additionally scheduling a delayed forceful cancellation that prevents the command from hanging. -If you are executing a command inside a method where you don't want to expose those implementation details to the caller, you can rely on the following pattern to use the provided token for graceful cancellation and extend it with a forceful fallback: +If you are executing a command inside a method and don't want to expose those implementation details to the caller, you can rely on the following pattern to use the provided token for graceful cancellation and extend it with a forceful fallback: ```csharp public async Task GitPushAsync(CancellationToken cancellationToken = default) From 900639281fb7916aaaa21b265abddb587d5ff128 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 11 Nov 2023 22:13:55 +0200 Subject: [PATCH 28/57] Update readme --- Readme.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index 4d0b0061..248f7e81 100644 --- a/Readme.md +++ b/Readme.md @@ -325,13 +325,29 @@ var cmd = Cli.Wrap("git") .WithValidation(CommandResultValidation.None); ``` +If you want to throw a custom exception when the process exits with a non-zero exit code, don't disable the result validation, but instead catch the `CommandExecutionException` and rethrow it as part of your own exception. +This way you can preserve additional information provided by the original exception, while extending it with your own context: + +```csharp +try +{ + await Cli.Wrap("git").ExecuteAsync(); +} +catch (CommandExecutionException ex) +{ + // Re-throw the original exception to preserve additional information + // about the command that failed (exit code, arguments, etc.). + throw new MyException("Failed to run the git command-line tool.", ex); +} +``` + #### `WithStandardInputPipe(...)` Sets the pipe source that will be used for the standard _input_ stream of the process. **Default**: `PipeSource.Null`. -_Read more about this method in the [piping section](#piping)._ +Read more about this method in the [piping section](#piping). #### `WithStandardOutputPipe(...)` @@ -339,7 +355,7 @@ Sets the pipe target that will be used for the standard _output_ stream of the p **Default**: `PipeTarget.Null`. -_Read more about this method in the [piping section](#piping)._ +Read more about this method in the [piping section](#piping). #### `WithStandardErrorPipe(...)` @@ -347,7 +363,7 @@ Sets the pipe target that will be used for the standard _error_ stream of the pr **Default**: `PipeTarget.Null`. -_Read more about this method in the [piping section](#piping)._ +Read more about this method in the [piping section](#piping). ### Piping From 8027e012c0caf6cabf5fa752504f25ab84a24fa8 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 11 Nov 2023 22:31:28 +0200 Subject: [PATCH 29/57] Log full exceptions in tests --- CliWrap.Tests/PipingSpecs.cs | 4 ++-- CliWrap.Tests/ValidationSpecs.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index 2f1c9f1d..3824ea70 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -620,7 +620,7 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou } [Fact(Timeout = 15000)] - public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_source_throws_an_exception() + public async Task I_can_try_to_execute_a_command_and_get_an_error_if_the_pipe_source_throws_an_exception() { // Arrange var cmd = @@ -632,7 +632,7 @@ public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_source_th } [Fact(Timeout = 15000)] - public async Task I_can_execute_a_command_and_get_an_error_if_the_pipe_target_throws_an_exception() + public async Task I_can_try_to_execute_a_command_and_get_an_error_if_the_pipe_target_throws_an_exception() { // Arrange var cmd = diff --git a/CliWrap.Tests/ValidationSpecs.cs b/CliWrap.Tests/ValidationSpecs.cs index 159ad97b..f3a29974 100644 --- a/CliWrap.Tests/ValidationSpecs.cs +++ b/CliWrap.Tests/ValidationSpecs.cs @@ -14,7 +14,7 @@ public class ValidationSpecs public ValidationSpecs(ITestOutputHelper testOutput) => _testOutput = testOutput; [Fact(Timeout = 15000)] - public async Task I_can_execute_a_command_and_get_an_error_if_it_returns_a_non_zero_exit_code() + 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 = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "--code", "1" }); @@ -27,11 +27,11 @@ public async Task I_can_execute_a_command_and_get_an_error_if_it_returns_a_non_z ex.ExitCode.Should().Be(1); ex.Command.Should().BeEquivalentTo(cmd); - _testOutput.WriteLine(ex.Message); + _testOutput.WriteLine(ex.ToString()); } [Fact(Timeout = 15000)] - public async Task I_can_execute_a_command_with_buffering_and_get_a_detailed_exception_if_it_returns_a_non_zero_exit_code() + 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 = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "--code", "1" }); @@ -45,7 +45,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_a_detailed_exce ex.ExitCode.Should().Be(1); ex.Command.Should().BeEquivalentTo(cmd); - _testOutput.WriteLine(ex.Message); + _testOutput.WriteLine(ex.ToString()); } [Fact(Timeout = 15000)] From d3ad902fead01f3d7882fa7f86f1cbabab9fc136 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 11 Nov 2023 22:54:20 +0200 Subject: [PATCH 30/57] Standardize more test names --- CliWrap.Tests/CredentialsSpecs.cs | 2 +- CliWrap.Tests/ExecutionSpecs.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CliWrap.Tests/CredentialsSpecs.cs b/CliWrap.Tests/CredentialsSpecs.cs index ea595c0d..8e6be53d 100644 --- a/CliWrap.Tests/CredentialsSpecs.cs +++ b/CliWrap.Tests/CredentialsSpecs.cs @@ -55,7 +55,7 @@ public async Task I_can_execute_a_command_as_a_different_user_under_the_specifie } [SkippableFact(Timeout = 15000)] - public async Task I_cannot_execute_a_command_as_a_different_user_on_a_system_that_does_not_support_it() + public async Task I_can_try_to_execute_a_command_as_a_different_user_and_get_an_error_if_the_operating_system_does_not_support_it() { Skip.If( RuntimeInformation.IsOSPlatform(OSPlatform.Windows), diff --git a/CliWrap.Tests/ExecutionSpecs.cs b/CliWrap.Tests/ExecutionSpecs.cs index 00585feb..7efbe083 100644 --- a/CliWrap.Tests/ExecutionSpecs.cs +++ b/CliWrap.Tests/ExecutionSpecs.cs @@ -59,7 +59,7 @@ public async Task I_can_execute_a_command_and_not_hang_on_large_stdout_and_stder } [Fact(Timeout = 15000)] - public void I_cannot_execute_a_command_on_a_file_that_does_not_exist() + public void I_can_try_to_execute_a_command_and_get_an_error_if_the_target_file_does_not_exist() { // Arrange var cmd = Cli.Wrap("I_do_not_exist.exe"); From 1768589a05e5e8605ccc8783968295b828b27503 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 14 Nov 2023 20:26:01 +0200 Subject: [PATCH 31/57] Update to .NET 8 --- .github/workflows/main.yml | 2 +- CliWrap.Benchmarks/CliWrap.Benchmarks.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 44bea580..8c8fe05f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: main: uses: Tyrrrz/.github/.github/workflows/nuget.yml@master with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x package-version: ${{ inputs.package-version }} # Deploy only on tags by default, unless deploy is explicitly requested deploy-on-tags-only: ${{ !(github.event_name == 'workflow_dispatch' && inputs.deploy) }} diff --git a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj index afaf8f47..c4c1c68c 100644 --- a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj +++ b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 From bbf178ea07da59900aedfe72597e0347203d1b9e Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:34:23 +0200 Subject: [PATCH 32/57] Update workflows --- .github/workflows/main.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8c8fe05f..5334cccc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,10 +19,9 @@ jobs: main: uses: Tyrrrz/.github/.github/workflows/nuget.yml@master with: - dotnet-version: 8.0.x + deploy: ${{ inputs.deploy || github.ref_type == 'tag' }} package-version: ${{ inputs.package-version }} - # Deploy only on tags by default, unless deploy is explicitly requested - deploy-on-tags-only: ${{ !(github.event_name == 'workflow_dispatch' && inputs.deploy) }} + dotnet-version: 8.0.x secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} From df6f08d8471ceb8bdbe6d7bec4fe73f0cf87d578 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sun, 19 Nov 2023 00:28:06 +0200 Subject: [PATCH 33/57] Update favicon --- favicon.png | Bin 21776 -> 22820 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/favicon.png b/favicon.png index fb21e8300b7d83947e4ce55b60e9eb94c7944c31..5178e38c7b88015379bdc6a15df5fbb3f2ed1ba1 100644 GIT binary patch literal 22820 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 Date: Tue, 21 Nov 2023 22:22:31 +0200 Subject: [PATCH 34/57] Add `CommandResult.IsSuccess` --- CliWrap.Tests/ExecutionSpecs.cs | 1 + CliWrap.Tests/PathResolutionSpecs.cs | 4 ++-- CliWrap.Tests/ValidationSpecs.cs | 1 + CliWrap/CommandResult.cs | 5 +++++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CliWrap.Tests/ExecutionSpecs.cs b/CliWrap.Tests/ExecutionSpecs.cs index 7efbe083..1d5b79c5 100644 --- a/CliWrap.Tests/ExecutionSpecs.cs +++ b/CliWrap.Tests/ExecutionSpecs.cs @@ -19,6 +19,7 @@ public async Task I_can_execute_a_command_and_get_the_exit_code_and_execution_ti // Assert result.ExitCode.Should().Be(0); + result.IsSuccess.Should().BeTrue(); result.RunTime.Should().BeGreaterThan(TimeSpan.Zero); } diff --git a/CliWrap.Tests/PathResolutionSpecs.cs b/CliWrap.Tests/PathResolutionSpecs.cs index 0dd511d1..11f09777 100644 --- a/CliWrap.Tests/PathResolutionSpecs.cs +++ b/CliWrap.Tests/PathResolutionSpecs.cs @@ -20,7 +20,7 @@ public async Task I_can_execute_a_command_on_an_executable_using_its_short_name( var result = await cmd.ExecuteBufferedAsync(); // Assert - result.ExitCode.Should().Be(0); + result.IsSuccess.Should().BeTrue(); result.StandardOutput.Trim().Should().MatchRegex(@"^\d+\.\d+\.\d+$"); } @@ -44,7 +44,7 @@ public async Task I_can_execute_a_command_on_a_script_using_its_short_name() var result = await cmd.ExecuteBufferedAsync(); // Assert - result.ExitCode.Should().Be(0); + result.IsSuccess.Should().BeTrue(); result.StandardOutput.Trim().Should().Be("hello"); } } diff --git a/CliWrap.Tests/ValidationSpecs.cs b/CliWrap.Tests/ValidationSpecs.cs index f3a29974..44279f33 100644 --- a/CliWrap.Tests/ValidationSpecs.cs +++ b/CliWrap.Tests/ValidationSpecs.cs @@ -61,5 +61,6 @@ public async Task I_can_execute_a_command_without_validating_the_exit_code() // Assert result.ExitCode.Should().Be(1); + result.IsSuccess.Should().BeFalse(); } } diff --git a/CliWrap/CommandResult.cs b/CliWrap/CommandResult.cs index 20994f89..5d9d36d2 100644 --- a/CliWrap/CommandResult.cs +++ b/CliWrap/CommandResult.cs @@ -12,6 +12,11 @@ public class CommandResult /// public int ExitCode { get; } + /// + /// Whether the command execution was successful (i.e. exit code is zero). + /// + public bool IsSuccess => ExitCode == 0; + /// /// Time at which the command started executing. /// From 1638a68a26808c97cbd0c117f113d15ebd575946 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:22:45 +0200 Subject: [PATCH 35/57] Use `net8.0` in test projects --- CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj | 2 +- CliWrap.Tests/CliWrap.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj index c3a83576..6d12b4e5 100644 --- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 $(TargetFrameworks);net48 diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index ed0d04e1..1b6c7b6f 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 $(TargetFrameworks);net48 From ed0093f905817a861f294812d3c9f02a3c028e82 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:55:39 +0200 Subject: [PATCH 36/57] Update readme --- Readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Readme.md b/Readme.md index 248f7e81..34ee2c6c 100644 --- a/Readme.md +++ b/Readme.md @@ -79,6 +79,7 @@ var result = await Cli.Wrap("path/to/exe") .ExecuteAsync(); // Result contains: +// -- result.IsSuccess (bool) // -- result.ExitCode (int) // -- result.StartTime (DateTimeOffset) // -- result.ExitTime (DateTimeOffset) @@ -132,6 +133,7 @@ var result = await Cli.Wrap("path/to/exe") .ExecuteBufferedAsync(); // Result contains: +// -- result.IsSuccess (bool) // -- result.StandardOutput (string) // -- result.StandardError (string) // -- result.ExitCode (int) From 0e7d4ee01506fcfef7ddec9362555d615ba77d62 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 21 Nov 2023 23:02:23 +0200 Subject: [PATCH 37/57] Update NuGet packages --- CliWrap.Benchmarks/BufferingBenchmarks.cs | 8 ++-- CliWrap.Benchmarks/CliWrap.Benchmarks.csproj | 4 +- .../PullEventStreamBenchmarks.cs | 8 ++-- CliWrap.Signaler/CliWrap.Signaler.csproj | 2 +- .../CliWrap.Tests.Dummy.csproj | 6 +-- CliWrap.Tests/CliWrap.Tests.csproj | 14 +++---- CliWrap.Tests/ConfigurationSpecs.cs | 15 ++++--- CliWrap.Tests/EnvironmentSpecs.cs | 6 ++- CliWrap.Tests/PipingSpecs.cs | 7 ++-- CliWrap/CliWrap.csproj | 16 +++----- CliWrap/CommandTask.cs | 2 +- .../PullEventStreamCommandExtensions.cs | 20 +++++----- .../PushEventStreamCommandExtensions.cs | 40 ++++++++++--------- CliWrap/Utils/SimplexStream.cs | 3 +- Directory.Build.props | 9 +++++ 15 files changed, 87 insertions(+), 73 deletions(-) diff --git a/CliWrap.Benchmarks/BufferingBenchmarks.cs b/CliWrap.Benchmarks/BufferingBenchmarks.cs index 14e017bc..e9a5c3cb 100644 --- a/CliWrap.Benchmarks/BufferingBenchmarks.cs +++ b/CliWrap.Benchmarks/BufferingBenchmarks.cs @@ -42,10 +42,10 @@ public class BufferingBenchmarks [Benchmark] public async Task<(string, string)> ProcessX() { - var (_, stdOutStream, stdErrStream) = Cysharp.Diagnostics.ProcessX.GetDualAsyncEnumerable( - FilePath, - arguments: Args - ); + var (_, stdOutStream, stdErrStream) = Cysharp + .Diagnostics + .ProcessX + .GetDualAsyncEnumerable(FilePath, arguments: Args); var stdOutTask = stdOutStream.ToTask(); var stdErrTask = stdErrStream.ToTask(); diff --git a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj index c4c1c68c..983d1a52 100644 --- a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj +++ b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs b/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs index 52b16eec..862f6451 100644 --- a/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs +++ b/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs @@ -38,10 +38,10 @@ public async Task ProcessX() { var counter = 0; - var (_, stdOutStream, stdErrStream) = Cysharp.Diagnostics.ProcessX.GetDualAsyncEnumerable( - FilePath, - arguments: Args - ); + var (_, stdOutStream, stdErrStream) = Cysharp + .Diagnostics + .ProcessX + .GetDualAsyncEnumerable(FilePath, arguments: Args); var consumeStdOutTask = Task.Run(async () => { diff --git a/CliWrap.Signaler/CliWrap.Signaler.csproj b/CliWrap.Signaler/CliWrap.Signaler.csproj index 61f24e9b..e82cb696 100644 --- a/CliWrap.Signaler/CliWrap.Signaler.csproj +++ b/CliWrap.Signaler/CliWrap.Signaler.csproj @@ -6,7 +6,7 @@ - + \ No newline at end of file diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj index 6d12b4e5..2d9a4787 100644 --- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index 1b6c7b6f..69ad1d2f 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -11,14 +11,14 @@ - - - - - + + + + + - - + + diff --git a/CliWrap.Tests/ConfigurationSpecs.cs b/CliWrap.Tests/ConfigurationSpecs.cs index 38438959..34ee43e4 100644 --- a/CliWrap.Tests/ConfigurationSpecs.cs +++ b/CliWrap.Tests/ConfigurationSpecs.cs @@ -91,7 +91,8 @@ public void I_can_configure_the_command_line_arguments_using_a_builder() // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Arguments)); original.Arguments.Should().NotBe(modified.Arguments); - modified.Arguments + modified + .Arguments .Should() .Be("-a \"foo bar\" \"\\\"foo\\\\bar\\\"\" 3.14 foo bar -5 89.13"); } @@ -125,7 +126,8 @@ public void I_can_configure_the_user_credentials() // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Credentials)); original.Credentials.Should().NotBe(modified.Credentials); - modified.Credentials + modified + .Credentials .Should() .BeEquivalentTo(new Credentials("domain", "username", "password", true)); } @@ -148,7 +150,8 @@ public void I_can_configure_the_user_credentials_using_a_builder() // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Credentials)); original.Credentials.Should().NotBe(modified.Credentials); - modified.Credentials + modified + .Credentials .Should() .BeEquivalentTo(new Credentials("domain", "username", "password", true)); } @@ -167,7 +170,8 @@ public void I_can_configure_the_environment_variables() // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.EnvironmentVariables)); original.EnvironmentVariables.Should().NotBeEquivalentTo(modified.EnvironmentVariables); - modified.EnvironmentVariables + modified + .EnvironmentVariables .Should() .BeEquivalentTo( new Dictionary { ["name"] = "value", ["key"] = "door" } @@ -191,7 +195,8 @@ public void I_can_configure_the_environment_variables_using_a_builder() // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.EnvironmentVariables)); original.EnvironmentVariables.Should().NotBeEquivalentTo(modified.EnvironmentVariables); - modified.EnvironmentVariables + modified + .EnvironmentVariables .Should() .BeEquivalentTo( new Dictionary diff --git a/CliWrap.Tests/EnvironmentSpecs.cs b/CliWrap.Tests/EnvironmentSpecs.cs index 549d7a60..84b08c2c 100644 --- a/CliWrap.Tests/EnvironmentSpecs.cs +++ b/CliWrap.Tests/EnvironmentSpecs.cs @@ -67,12 +67,14 @@ public async Task I_can_execute_a_command_with_some_environment_variables_overwr var result = await cmd.ExecuteBufferedAsync(); // Assert - result.StandardOutput + result + .StandardOutput .Trim() .Should() .ContainAll($"[{variableToKeep}] = keep", $"[{variableToOverwrite}] = overwritten"); - result.StandardOutput + result + .StandardOutput .Trim() .Should() .NotContainAny( diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index 3824ea70..f7e08a63 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -612,10 +612,9 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou delegateLines .Should() .Equal( - result.StandardOutput.Split( - Environment.NewLine, - StringSplitOptions.RemoveEmptyEntries - ) + result + .StandardOutput + .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries) ); } diff --git a/CliWrap/CliWrap.csproj b/CliWrap/CliWrap.csproj index 1b66ae75..cee936c8 100644 --- a/CliWrap/CliWrap.csproj +++ b/CliWrap/CliWrap.csproj @@ -6,13 +6,7 @@ - $(Company) - Library for interacting with external command-line interfaces - shell pipe command line executable interface wrapper standard input output error arguments net core - https://github.com/Tyrrrz/CliWrap - https://github.com/Tyrrrz/CliWrap/blob/master/Changelog.md favicon.png - MIT true @@ -27,12 +21,12 @@ - - - - + + + + - + diff --git a/CliWrap/CommandTask.cs b/CliWrap/CommandTask.cs index 0fced180..8515e5eb 100644 --- a/CliWrap/CommandTask.cs +++ b/CliWrap/CommandTask.cs @@ -58,7 +58,7 @@ public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedCo public partial class CommandTask { /// - /// Casts the command task into a regular task. + /// Converts the command task into a regular task. /// public static implicit operator Task(CommandTask commandTask) => commandTask.Task; diff --git a/CliWrap/EventStream/PullEventStreamCommandExtensions.cs b/CliWrap/EventStream/PullEventStreamCommandExtensions.cs index 6d4fd15f..57c2886a 100644 --- a/CliWrap/EventStream/PullEventStreamCommandExtensions.cs +++ b/CliWrap/EventStream/PullEventStreamCommandExtensions.cs @@ -70,15 +70,17 @@ await channel yield return new StartedCommandEvent(commandTask.ProcessId); // Close the channel once the command completes, so that ReceiveAsync() can finish - _ = commandTask.Task.ContinueWith( - async _ => - // ReSharper disable once AccessToDisposedClosure - await channel - .ReportCompletionAsync(forcefulCancellationToken) - .ConfigureAwait(false), - // Run the continuation even if the parent task failed - TaskContinuationOptions.None - ); + _ = commandTask + .Task + .ContinueWith( + async _ => + // ReSharper disable once AccessToDisposedClosure + await channel + .ReportCompletionAsync(forcefulCancellationToken) + .ConfigureAwait(false), + // Run the continuation even if the parent task failed + TaskContinuationOptions.None + ); await foreach ( var cmdEvent in channel.ReceiveAsync(forcefulCancellationToken).ConfigureAwait(false) diff --git a/CliWrap/EventStream/PushEventStreamCommandExtensions.cs b/CliWrap/EventStream/PushEventStreamCommandExtensions.cs index eb97feae..762777c3 100644 --- a/CliWrap/EventStream/PushEventStreamCommandExtensions.cs +++ b/CliWrap/EventStream/PushEventStreamCommandExtensions.cs @@ -58,26 +58,28 @@ CancellationToken gracefulCancellationToken // Don't pass cancellation token to the continuation because we need it to // trigger regardless of how the task completed. - _ = commandTask.Task.ContinueWith( - t => - { - // Canceled tasks don't have exceptions - if (t.IsCanceled) + _ = commandTask + .Task + .ContinueWith( + t => { - observer.OnError(new TaskCanceledException(t)); - } - else if (t.Exception is not null) - { - observer.OnError(t.Exception.TryGetSingle() ?? t.Exception); - } - else - { - observer.OnNext(new ExitedCommandEvent(t.Result.ExitCode)); - observer.OnCompleted(); - } - }, - TaskContinuationOptions.None - ); + // Canceled tasks don't have exceptions + if (t.IsCanceled) + { + observer.OnError(new TaskCanceledException(t)); + } + else if (t.Exception is not null) + { + observer.OnError(t.Exception.TryGetSingle() ?? t.Exception); + } + else + { + observer.OnNext(new ExitedCommandEvent(t.Result.ExitCode)); + observer.OnCompleted(); + } + }, + TaskContinuationOptions.None + ); return Disposable.Null; }); diff --git a/CliWrap/Utils/SimplexStream.cs b/CliWrap/Utils/SimplexStream.cs index b374fbb8..e4d2e228 100644 --- a/CliWrap/Utils/SimplexStream.cs +++ b/CliWrap/Utils/SimplexStream.cs @@ -66,7 +66,8 @@ CancellationToken cancellationToken var length = Math.Min(count, _sharedBufferBytes - _sharedBufferBytesRead); - _sharedBuffer.Memory + _sharedBuffer + .Memory .Slice(_sharedBufferBytesRead, length) .CopyTo(buffer.AsMemory(offset, length)); diff --git a/Directory.Build.props b/Directory.Build.props index eb2a590e..bd521679 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,4 +16,13 @@ annotations + + $(Company) + Library for interacting with external command-line interfaces + yshell pipe command line executable interface wrapper standard input output error arguments net core + https://github.com/Tyrrrz/CliWrap + https://github.com/Tyrrrz/CliWrap/blob/master/Changelog.md + MIT + + \ No newline at end of file From 119ddb165f224545b17ec763ee8ca751eb825e35 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 23 Nov 2023 18:42:12 +0200 Subject: [PATCH 38/57] Switch to a new versioning strategy --- .github/workflows/main.yml | 2 +- Changelog.md | 6 +++++- Directory.Build.props | 3 +-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5334cccc..aac9796b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ jobs: uses: Tyrrrz/.github/.github/workflows/nuget.yml@master with: deploy: ${{ inputs.deploy || github.ref_type == 'tag' }} - package-version: ${{ inputs.package-version }} + package-version: ${{ inputs.package-version || (github.ref_type == 'tag' && github.ref_name) || '0.0.0' }} dotnet-version: 8.0.x secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/Changelog.md b/Changelog.md index ad30bdb4..8fe47c71 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ # Changelog +> **Important**: +> This changelog is no longer maintained and will be removed in the future. +> Going forward, new versions of this package will have the corresponding release notes published on [GitHub Releases](https://githu.com/Tyrrrz/CliWrap/releases). + ## v3.6.4 (23-Jun-2023) - Renamed `PipeSource.FromMemory(...)` to `PipeSource.FromBytes(...)` for consistency with similar methods in the wider .NET framework. The old method is still available but marked as obsolete. @@ -234,4 +238,4 @@ Check out the new readme to see the whole list of new features. - Execution start and exit times are now calculated separately, without accessing `Process` instance. - Execution start and exit times are now `DateTimeOffset` instead of `DateTime`. -- Fixed an issue that prevented CliWrap from properly working on Linux. \ No newline at end of file +- Fixed an issue that prevented CliWrap from properly working on Linux. diff --git a/Directory.Build.props b/Directory.Build.props index bd521679..9d394d39 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,6 @@ - 3.6.4 Tyrrrz Copyright (C) Oleksii Holub latest @@ -21,7 +20,7 @@ Library for interacting with external command-line interfaces yshell pipe command line executable interface wrapper standard input output error arguments net core https://github.com/Tyrrrz/CliWrap - https://github.com/Tyrrrz/CliWrap/blob/master/Changelog.md + https://github.com/Tyrrrz/CliWrap/releases/tag/$(Version) MIT From 4978a6e2f5a466465c94e08c2a281214cebe31d2 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 23 Nov 2023 18:44:58 +0200 Subject: [PATCH 39/57] Fix typo --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 8fe47c71..80c498a9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ > **Important**: > This changelog is no longer maintained and will be removed in the future. -> Going forward, new versions of this package will have the corresponding release notes published on [GitHub Releases](https://githu.com/Tyrrrz/CliWrap/releases). +> Going forward, new versions of this package will have the corresponding release notes published on [GitHub Releases](https://github.com/Tyrrrz/CliWrap/releases). ## v3.6.4 (23-Jun-2023) From 1f57973d615b8859a1b403ec0521763283ca7af0 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:48:36 +0200 Subject: [PATCH 40/57] Improve wording in readme --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 34ee2c6c..826e68f0 100644 --- a/Readme.md +++ b/Readme.md @@ -327,8 +327,8 @@ var cmd = Cli.Wrap("git") .WithValidation(CommandResultValidation.None); ``` -If you want to throw a custom exception when the process exits with a non-zero exit code, don't disable the result validation, but instead catch the `CommandExecutionException` and rethrow it as part of your own exception. -This way you can preserve additional information provided by the original exception, while extending it with your own context: +If you want to throw a custom exception when the process exits with a non-zero exit code, don't disable result validation, but instead catch the default `CommandExecutionException` and re-throw it inside your own exception. +This way you can preserve the information provided by the original exception, while extending it with additional context: ```csharp try From 2a1a4a0f108750b7dc33320a16fa238ee1689bf3 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:22:06 +0200 Subject: [PATCH 41/57] Clean up --- .../Commands/EnvironmentCommand.cs | 21 ++++++++++ CliWrap.Tests.Dummy/Commands/ExitCommand.cs | 2 +- ...LengthCommand.cs => LengthStdInCommand.cs} | 11 ++---- .../PrintEnvironmentVariablesCommand.cs | 19 ---------- .../Commands/PrintWorkingDirCommand.cs | 16 -------- CliWrap.Tests.Dummy/Commands/SleepCommand.cs | 2 +- .../Commands/WorkingDirectoryCommand.cs | 14 +++++++ CliWrap.Tests/CancellationSpecs.cs | 38 +++++++------------ CliWrap.Tests/EnvironmentSpecs.cs | 26 ++++--------- CliWrap.Tests/PipingSpecs.cs | 4 +- .../Utils/Extensions/AssertionExtensions.cs | 22 +++++++++++ CliWrap.Tests/ValidationSpecs.cs | 6 +-- 12 files changed, 89 insertions(+), 92 deletions(-) create mode 100644 CliWrap.Tests.Dummy/Commands/EnvironmentCommand.cs rename CliWrap.Tests.Dummy/Commands/{PrintStdInLengthCommand.cs => LengthStdInCommand.cs} (60%) delete mode 100644 CliWrap.Tests.Dummy/Commands/PrintEnvironmentVariablesCommand.cs delete mode 100644 CliWrap.Tests.Dummy/Commands/PrintWorkingDirCommand.cs create mode 100644 CliWrap.Tests.Dummy/Commands/WorkingDirectoryCommand.cs create mode 100644 CliWrap.Tests/Utils/Extensions/AssertionExtensions.cs diff --git a/CliWrap.Tests.Dummy/Commands/EnvironmentCommand.cs b/CliWrap.Tests.Dummy/Commands/EnvironmentCommand.cs new file mode 100644 index 00000000..5fef363c --- /dev/null +++ b/CliWrap.Tests.Dummy/Commands/EnvironmentCommand.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using CliFx; +using CliFx.Attributes; +using CliFx.Infrastructure; + +namespace CliWrap.Tests.Dummy.Commands; + +[Command("env")] +public class EnvironmentCommand : ICommand +{ + [CommandParameter(0)] + public IReadOnlyList Names { get; init; } = Array.Empty(); + + public async ValueTask ExecuteAsync(IConsole console) + { + foreach (var name in Names) + await console.Output.WriteLineAsync(Environment.GetEnvironmentVariable(name)); + } +} diff --git a/CliWrap.Tests.Dummy/Commands/ExitCommand.cs b/CliWrap.Tests.Dummy/Commands/ExitCommand.cs index 9651661a..a2a5076f 100644 --- a/CliWrap.Tests.Dummy/Commands/ExitCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/ExitCommand.cs @@ -9,7 +9,7 @@ namespace CliWrap.Tests.Dummy.Commands; [Command("exit")] public class ExitCommand : ICommand { - [CommandOption("code")] + [CommandParameter(0)] public int ExitCode { get; init; } public ValueTask ExecuteAsync(IConsole console) diff --git a/CliWrap.Tests.Dummy/Commands/PrintStdInLengthCommand.cs b/CliWrap.Tests.Dummy/Commands/LengthStdInCommand.cs similarity index 60% rename from CliWrap.Tests.Dummy/Commands/PrintStdInLengthCommand.cs rename to CliWrap.Tests.Dummy/Commands/LengthStdInCommand.cs index 28e13b8c..22eb14bc 100644 --- a/CliWrap.Tests.Dummy/Commands/PrintStdInLengthCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/LengthStdInCommand.cs @@ -4,16 +4,12 @@ using CliFx; using CliFx.Attributes; using CliFx.Infrastructure; -using CliWrap.Tests.Dummy.Commands.Shared; namespace CliWrap.Tests.Dummy.Commands; -[Command("print length stdin")] -public class PrintStdInLengthCommand : ICommand +[Command("length stdin")] +public class LengthStdInCommand : ICommand { - [CommandOption("target")] - public OutputTarget Target { get; init; } = OutputTarget.StdOut; - public async ValueTask ExecuteAsync(IConsole console) { using var buffer = MemoryPool.Shared.Rent(81920); @@ -28,7 +24,6 @@ public async ValueTask ExecuteAsync(IConsole console) totalBytesRead += bytesRead; } - foreach (var writer in console.GetWriters(Target)) - await writer.WriteLineAsync(totalBytesRead.ToString(CultureInfo.InvariantCulture)); + await console.Output.WriteLineAsync(totalBytesRead.ToString(CultureInfo.InvariantCulture)); } } diff --git a/CliWrap.Tests.Dummy/Commands/PrintEnvironmentVariablesCommand.cs b/CliWrap.Tests.Dummy/Commands/PrintEnvironmentVariablesCommand.cs deleted file mode 100644 index 37da600e..00000000 --- a/CliWrap.Tests.Dummy/Commands/PrintEnvironmentVariablesCommand.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections; -using System.Linq; -using System.Threading.Tasks; -using CliFx; -using CliFx.Attributes; -using CliFx.Infrastructure; - -namespace CliWrap.Tests.Dummy.Commands; - -[Command("print env")] -public class PrintEnvironmentVariablesCommand : ICommand -{ - public async ValueTask ExecuteAsync(IConsole console) - { - foreach (var (name, value) in Environment.GetEnvironmentVariables().Cast()) - await console.Output.WriteLineAsync($"[{name}] = {value}"); - } -} diff --git a/CliWrap.Tests.Dummy/Commands/PrintWorkingDirCommand.cs b/CliWrap.Tests.Dummy/Commands/PrintWorkingDirCommand.cs deleted file mode 100644 index 487b9e74..00000000 --- a/CliWrap.Tests.Dummy/Commands/PrintWorkingDirCommand.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.IO; -using System.Threading.Tasks; -using CliFx; -using CliFx.Attributes; -using CliFx.Infrastructure; - -namespace CliWrap.Tests.Dummy.Commands; - -[Command("print cwd")] -public class PrintWorkingDirCommand : ICommand -{ - public async ValueTask ExecuteAsync(IConsole console) - { - await console.Output.WriteAsync(Directory.GetCurrentDirectory()); - } -} diff --git a/CliWrap.Tests.Dummy/Commands/SleepCommand.cs b/CliWrap.Tests.Dummy/Commands/SleepCommand.cs index 5c22cf43..64bdc3d8 100644 --- a/CliWrap.Tests.Dummy/Commands/SleepCommand.cs +++ b/CliWrap.Tests.Dummy/Commands/SleepCommand.cs @@ -9,7 +9,7 @@ namespace CliWrap.Tests.Dummy.Commands; [Command("sleep")] public class SleepCommand : ICommand { - [CommandOption("duration")] + [CommandParameter(0)] public TimeSpan Duration { get; init; } = TimeSpan.FromSeconds(1); public async ValueTask ExecuteAsync(IConsole console) diff --git a/CliWrap.Tests.Dummy/Commands/WorkingDirectoryCommand.cs b/CliWrap.Tests.Dummy/Commands/WorkingDirectoryCommand.cs new file mode 100644 index 00000000..8bf9524b --- /dev/null +++ b/CliWrap.Tests.Dummy/Commands/WorkingDirectoryCommand.cs @@ -0,0 +1,14 @@ +using System.IO; +using System.Threading.Tasks; +using CliFx; +using CliFx.Attributes; +using CliFx.Infrastructure; + +namespace CliWrap.Tests.Dummy.Commands; + +[Command("cwd")] +public class WorkingDirectoryCommand : ICommand +{ + public async ValueTask ExecuteAsync(IConsole console) => + await console.Output.WriteLineAsync(Directory.GetCurrentDirectory()); +} diff --git a/CliWrap.Tests/CancellationSpecs.cs b/CliWrap.Tests/CancellationSpecs.cs index f74c835b..efde61c0 100644 --- a/CliWrap.Tests/CancellationSpecs.cs +++ b/CliWrap.Tests/CancellationSpecs.cs @@ -23,8 +23,8 @@ public async Task I_can_execute_a_command_and_cancel_it_immediately() var stdOutBuffer = new StringBuilder(); var cmd = - Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }) | stdOutBuffer; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }) + | stdOutBuffer; // Act var task = cmd.ExecuteAsync(cts.Token); @@ -47,8 +47,8 @@ public async Task I_can_execute_a_command_and_cancel_it_after_a_delay() var stdOutBuffer = new StringBuilder(); var cmd = - Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }) | stdOutBuffer; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }) + | stdOutBuffer; // Act var task = cmd.ExecuteAsync(cts.Token); @@ -84,8 +84,7 @@ void HandleStdOut(string line) ); var cmd = - Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }) | target; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }) | target; // Act var task = cmd.ExecuteAsync(CancellationToken.None, cts.Token); @@ -105,8 +104,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_immediate using var cts = new CancellationTokenSource(); cts.Cancel(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -123,8 +121,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_after_a_d using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -141,8 +138,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_gracefull using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -165,8 +161,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.Cancel(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -188,8 +183,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -211,8 +205,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -241,8 +234,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.Cancel(); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -268,8 +260,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -295,8 +286,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "sleep", "--duration", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); // Act & assert var ex = await Assert.ThrowsAnyAsync( diff --git a/CliWrap.Tests/EnvironmentSpecs.cs b/CliWrap.Tests/EnvironmentSpecs.cs index 84b08c2c..5483998a 100644 --- a/CliWrap.Tests/EnvironmentSpecs.cs +++ b/CliWrap.Tests/EnvironmentSpecs.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using CliWrap.Buffered; using CliWrap.Tests.Utils; +using CliWrap.Tests.Utils.Extensions; using FluentAssertions; using Xunit; @@ -17,7 +18,7 @@ public async Task I_can_execute_a_command_with_a_custom_working_directory() using var dir = TempDir.Create(); var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments("print cwd") + .WithArguments("cwd") .WithWorkingDirectory(dir.Path); // Act @@ -34,14 +35,14 @@ public async Task I_can_execute_a_command_with_additional_environment_variables( var env = new Dictionary { ["foo"] = "bar", ["hello"] = "world" }; var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments("print env") + .WithArguments(new[] { "env", "foo", "hello" }) .WithEnvironmentVariables(env); // Act var result = await cmd.ExecuteBufferedAsync(); // Assert - result.StandardOutput.Trim().Should().ContainAll("[foo] = bar", "[hello] = world"); + result.StandardOutput.Should().ConsistOfLines("bar", "world"); } [Fact(Timeout = 15000)] @@ -58,7 +59,9 @@ public async Task I_can_execute_a_command_with_some_environment_variables_overwr using (TempEnvironmentVariable.Set(variableToUnset, "unset")) // will be unset { var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments("print env") + .WithArguments( + new[] { "env", variableToKeep, variableToOverwrite, variableToUnset } + ) .WithEnvironmentVariables( e => e.Set(variableToOverwrite, "overwritten").Set(variableToUnset, null) ); @@ -67,20 +70,7 @@ public async Task I_can_execute_a_command_with_some_environment_variables_overwr var result = await cmd.ExecuteBufferedAsync(); // Assert - result - .StandardOutput - .Trim() - .Should() - .ContainAll($"[{variableToKeep}] = keep", $"[{variableToOverwrite}] = overwritten"); - - result - .StandardOutput - .Trim() - .Should() - .NotContainAny( - $"[{variableToOverwrite}] = overwrite", - $"[{variableToUnset}] = unset" - ); + result.StandardOutput.Should().ConsistOfLines("keep", "overwritten"); } } } diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index f7e08a63..0381d0e8 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -129,7 +129,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_another_comman var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(new[] { "generate binary", "--length", "100000" }) - | Cli.Wrap(Dummy.Program.FilePath).WithArguments("print length stdin"); + | Cli.Wrap(Dummy.Program.FilePath).WithArguments("length stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -147,7 +147,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_chain_of_com | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") | Cli.Wrap(Dummy.Program.FilePath) .WithArguments(new[] { "echo stdin", "--length", "5" }) - | Cli.Wrap(Dummy.Program.FilePath).WithArguments("print length stdin"); + | Cli.Wrap(Dummy.Program.FilePath).WithArguments("length stdin"); // Act var result = await cmd.ExecuteBufferedAsync(); diff --git a/CliWrap.Tests/Utils/Extensions/AssertionExtensions.cs b/CliWrap.Tests/Utils/Extensions/AssertionExtensions.cs new file mode 100644 index 00000000..caf90d28 --- /dev/null +++ b/CliWrap.Tests/Utils/Extensions/AssertionExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using FluentAssertions; +using FluentAssertions.Primitives; + +namespace CliWrap.Tests.Utils.Extensions; + +internal static class AssertionExtensions +{ + public static void ConsistOfLines( + this StringAssertions assertions, + IEnumerable lines + ) => + assertions + .Subject + .Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) + .Should() + .Equal(lines); + + public static void ConsistOfLines(this StringAssertions assertions, params string[] lines) => + assertions.ConsistOfLines((IEnumerable)lines); +} diff --git a/CliWrap.Tests/ValidationSpecs.cs b/CliWrap.Tests/ValidationSpecs.cs index 44279f33..09549fac 100644 --- a/CliWrap.Tests/ValidationSpecs.cs +++ b/CliWrap.Tests/ValidationSpecs.cs @@ -17,7 +17,7 @@ public class ValidationSpecs 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 = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "--code", "1" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "1" }); // Act & assert var ex = await Assert.ThrowsAsync( @@ -34,7 +34,7 @@ public async Task I_can_try_to_execute_a_command_and_get_an_error_if_it_returns_ 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 = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "--code", "1" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "1" }); // Act & assert var ex = await Assert.ThrowsAsync( @@ -53,7 +53,7 @@ public async Task I_can_execute_a_command_without_validating_the_exit_code() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "exit", "--code", "1" }) + .WithArguments(new[] { "exit", "1" }) .WithValidation(CommandResultValidation.None); // Act From 76d2fbae6385995e219aedd5b8c88940184de094 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Fri, 24 Nov 2023 01:30:20 +0200 Subject: [PATCH 42/57] Rename `BufferedSpecs` to `BufferingSpecs` --- CliWrap.Tests/{BufferedSpecs.cs => BufferingSpecs.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename CliWrap.Tests/{BufferedSpecs.cs => BufferingSpecs.cs} (98%) diff --git a/CliWrap.Tests/BufferedSpecs.cs b/CliWrap.Tests/BufferingSpecs.cs similarity index 98% rename from CliWrap.Tests/BufferedSpecs.cs rename to CliWrap.Tests/BufferingSpecs.cs index 3059bb42..e6a692d3 100644 --- a/CliWrap.Tests/BufferedSpecs.cs +++ b/CliWrap.Tests/BufferingSpecs.cs @@ -5,7 +5,7 @@ namespace CliWrap.Tests; -public class BufferedSpecs +public class BufferingSpecs { [Fact(Timeout = 15000)] public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout() From 797b8290cace3312a53e061a1ea08eadb84fd786 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Fri, 24 Nov 2023 20:58:44 +0200 Subject: [PATCH 43/57] Suffix unstable packages with git hash --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aac9796b..90a18d1b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ jobs: uses: Tyrrrz/.github/.github/workflows/nuget.yml@master with: deploy: ${{ inputs.deploy || github.ref_type == 'tag' }} - package-version: ${{ inputs.package-version || (github.ref_type == 'tag' && github.ref_name) || '0.0.0' }} + package-version: ${{ inputs.package-version || (github.ref_type == 'tag' && github.ref_name) || format('0.0.0-ci-{0}', github.sha) }} dotnet-version: 8.0.x secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 051c026f8f715f63543af4641b246cfebbc08007 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 25 Nov 2023 18:45:57 +0200 Subject: [PATCH 44/57] Refactor --- .../PushEventStreamCommandExtensions.cs | 3 +- CliWrap/PipeSource.cs | 23 +-- CliWrap/PipeTarget.cs | 140 +++++++++--------- CliWrap/Utils/Observable.cs | 46 +----- CliWrap/Utils/SynchronizedObserver.cs | 39 +++++ 5 files changed, 133 insertions(+), 118 deletions(-) create mode 100644 CliWrap/Utils/SynchronizedObserver.cs diff --git a/CliWrap/EventStream/PushEventStreamCommandExtensions.cs b/CliWrap/EventStream/PushEventStreamCommandExtensions.cs index 762777c3..52adffa7 100644 --- a/CliWrap/EventStream/PushEventStreamCommandExtensions.cs +++ b/CliWrap/EventStream/PushEventStreamCommandExtensions.cs @@ -28,7 +28,7 @@ public static IObservable Observe( CancellationToken gracefulCancellationToken ) { - return Observable.Create(observer => + return Observable.CreateSynchronized(observer => { var stdOutPipe = PipeTarget.Merge( command.StandardOutputPipe, @@ -54,6 +54,7 @@ CancellationToken gracefulCancellationToken forcefulCancellationToken, gracefulCancellationToken ); + observer.OnNext(new StartedCommandEvent(commandTask.ProcessId)); // Don't pass cancellation token to the continuation because we need it to diff --git a/CliWrap/PipeSource.cs b/CliWrap/PipeSource.cs index fa70e96e..75d9943e 100644 --- a/CliWrap/PipeSource.cs +++ b/CliWrap/PipeSource.cs @@ -23,17 +23,20 @@ public abstract Task CopyToAsync( ); } -file class AnonymousPipeSource : PipeSource +public partial class PipeSource { - private readonly Func _copyToAsync; - - public AnonymousPipeSource(Func copyToAsync) => - _copyToAsync = copyToAsync; - - public override async Task CopyToAsync( - Stream destination, - CancellationToken cancellationToken = default - ) => await _copyToAsync(destination, cancellationToken).ConfigureAwait(false); + private class AnonymousPipeSource : PipeSource + { + private readonly Func _copyToAsync; + + public AnonymousPipeSource(Func copyToAsync) => + _copyToAsync = copyToAsync; + + public override async Task CopyToAsync( + Stream destination, + CancellationToken cancellationToken = default + ) => await _copyToAsync(destination, cancellationToken).ConfigureAwait(false); + } } public partial class PipeSource diff --git a/CliWrap/PipeTarget.cs b/CliWrap/PipeTarget.cs index 69b32559..35f71504 100644 --- a/CliWrap/PipeTarget.cs +++ b/CliWrap/PipeTarget.cs @@ -26,95 +26,99 @@ public abstract Task CopyFromAsync( ); } -file class AnonymousPipeTarget : PipeTarget +public partial class PipeTarget { - private readonly Func _copyFromAsync; - - public AnonymousPipeTarget(Func copyFromAsync) => - _copyFromAsync = copyFromAsync; - - public override async Task CopyFromAsync( - Stream origin, - CancellationToken cancellationToken = default - ) => await _copyFromAsync(origin, cancellationToken).ConfigureAwait(false); -} + private class AnonymousPipeTarget : PipeTarget + { + private readonly Func _copyFromAsync; -file class AggregatePipeTarget : PipeTarget -{ - public IReadOnlyList Targets { get; } + public AnonymousPipeTarget(Func copyFromAsync) => + _copyFromAsync = copyFromAsync; - public AggregatePipeTarget(IReadOnlyList targets) => Targets = targets; + public override async Task CopyFromAsync( + Stream origin, + CancellationToken cancellationToken = default + ) => await _copyFromAsync(origin, cancellationToken).ConfigureAwait(false); + } - public override async Task CopyFromAsync( - Stream origin, - CancellationToken cancellationToken = default - ) + private class AggregatePipeTarget : PipeTarget { - // Cancellation to abort the pipe if any of the underlying targets fail - using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + public IReadOnlyList Targets { get; } - // Create a separate sub-stream for each target - var targetSubStreams = new Dictionary(); - foreach (var target in Targets) - targetSubStreams[target] = new SimplexStream(); + public AggregatePipeTarget(IReadOnlyList targets) => Targets = targets; - try + public override async Task CopyFromAsync( + Stream origin, + CancellationToken cancellationToken = default + ) { - // Start piping in the background - var readingTask = Task.WhenAll( - targetSubStreams.Select(async targetSubStream => - { - var (target, subStream) = targetSubStream; + // Cancellation to abort the pipe if any of the underlying targets fail + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - try - { - // ReSharper disable once AccessToDisposedClosure - await target.CopyFromAsync(subStream, cts.Token).ConfigureAwait(false); - } - catch - { - // Abort the operation if any of the targets fail - // ReSharper disable once AccessToDisposedClosure - cts.Cancel(); - - throw; - } - }) - ); + // Create a separate sub-stream for each target + var targetSubStreams = new Dictionary(); + foreach (var target in Targets) + targetSubStreams[target] = new SimplexStream(); try { - // Read from the master stream and replicate the data to each sub-stream - using var buffer = MemoryPool.Shared.Rent(BufferSizes.Stream); - while (true) + // Start piping in the background + var readingTask = Task.WhenAll( + targetSubStreams.Select(async targetSubStream => + { + var (target, subStream) = targetSubStream; + + try + { + // ReSharper disable once AccessToDisposedClosure + await target.CopyFromAsync(subStream, cts.Token).ConfigureAwait(false); + } + catch + { + // Abort the operation if any of the targets fail + // ReSharper disable once AccessToDisposedClosure + cts.Cancel(); + + throw; + } + }) + ); + + try { - var bytesRead = await origin - .ReadAsync(buffer.Memory, cts.Token) - .ConfigureAwait(false); - if (bytesRead <= 0) - break; + // Read from the master stream and replicate the data to each sub-stream + using var buffer = MemoryPool.Shared.Rent(BufferSizes.Stream); + while (true) + { + var bytesRead = await origin + .ReadAsync(buffer.Memory, cts.Token) + .ConfigureAwait(false); + + if (bytesRead <= 0) + break; + + foreach (var (_, subStream) in targetSubStreams) + await subStream + .WriteAsync(buffer.Memory[..bytesRead], cts.Token) + .ConfigureAwait(false); + } + // Report that transmission is complete foreach (var (_, subStream) in targetSubStreams) - await subStream - .WriteAsync(buffer.Memory[..bytesRead], cts.Token) - .ConfigureAwait(false); + await subStream.ReportCompletionAsync(cts.Token).ConfigureAwait(false); + } + finally + { + // Wait for all targets to finish and propagate potential exceptions + await readingTask.ConfigureAwait(false); } - - // Report that transmission is complete - foreach (var (_, subStream) in targetSubStreams) - await subStream.ReportCompletionAsync(cts.Token).ConfigureAwait(false); } finally { - // Wait for all targets to finish and propagate potential exceptions - await readingTask.ConfigureAwait(false); + foreach (var (_, subStream) in targetSubStreams) + await subStream.ToAsyncDisposable().DisposeAsync().ConfigureAwait(false); } } - finally - { - foreach (var (_, subStream) in targetSubStreams) - await subStream.ToAsyncDisposable().DisposeAsync().ConfigureAwait(false); - } } } diff --git a/CliWrap/Utils/Observable.cs b/CliWrap/Utils/Observable.cs index 87e5929a..237ed6ea 100644 --- a/CliWrap/Utils/Observable.cs +++ b/CliWrap/Utils/Observable.cs @@ -2,54 +2,22 @@ namespace CliWrap.Utils; -file class SynchronizedObserver : IObserver -{ - private readonly IObserver _observer; - private readonly object _syncRoot; - - public SynchronizedObserver(IObserver observer, object? syncRoot = null) - { - _observer = observer; - _syncRoot = syncRoot ?? new object(); - } - - public void OnCompleted() - { - lock (_syncRoot) - { - _observer.OnCompleted(); - } - } - - public void OnError(Exception error) - { - lock (_syncRoot) - { - _observer.OnError(error); - } - } - - public void OnNext(T value) - { - lock (_syncRoot) - { - _observer.OnNext(value); - } - } -} - -file class Observable : IObservable +internal class Observable : IObservable { private readonly Func, IDisposable> _subscribe; public Observable(Func, IDisposable> subscribe) => _subscribe = subscribe; - public IDisposable Subscribe(IObserver observer) => - _subscribe(new SynchronizedObserver(observer)); + public IDisposable Subscribe(IObserver observer) => _subscribe(observer); } internal static class Observable { public static IObservable Create(Func, IDisposable> subscribe) => new Observable(subscribe); + + public static IObservable CreateSynchronized( + Func, IDisposable> subscribe, + object? syncRoot = null + ) => Create(observer => subscribe(new SynchronizedObserver(observer, syncRoot))); } diff --git a/CliWrap/Utils/SynchronizedObserver.cs b/CliWrap/Utils/SynchronizedObserver.cs new file mode 100644 index 00000000..fb154b6e --- /dev/null +++ b/CliWrap/Utils/SynchronizedObserver.cs @@ -0,0 +1,39 @@ +using System; + +namespace CliWrap.Utils; + +internal class SynchronizedObserver : IObserver +{ + private readonly IObserver _observer; + private readonly object _syncRoot; + + public SynchronizedObserver(IObserver observer, object? syncRoot = null) + { + _observer = observer; + _syncRoot = syncRoot ?? new object(); + } + + public void OnCompleted() + { + lock (_syncRoot) + { + _observer.OnCompleted(); + } + } + + public void OnError(Exception error) + { + lock (_syncRoot) + { + _observer.OnError(error); + } + } + + public void OnNext(T value) + { + lock (_syncRoot) + { + _observer.OnNext(value); + } + } +} From eddd37d94e124a4d3a1a81ef814bcb3b762d5ea1 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 25 Nov 2023 18:58:18 +0200 Subject: [PATCH 45/57] Don't run the same job for both `push` and `pull_request` --- .github/workflows/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 90a18d1b..1e08a045 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,7 +13,11 @@ on: required: false default: false push: + branches: + - master pull_request: + branches: + - master jobs: main: From 7ca0ec040dda3ffaa2bae41e0def80cfbcfd4166 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 30 Nov 2023 00:31:46 +0200 Subject: [PATCH 46/57] Fix workflow --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1e08a045..6843bd6c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,8 @@ on: push: branches: - master + tags: + - "*" pull_request: branches: - master From 2d2cb35b23266a99440ee32da0fde878e59ada80 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sun, 10 Dec 2023 22:59:43 +0200 Subject: [PATCH 47/57] Update NuGet packages --- CliWrap.Benchmarks/CliWrap.Benchmarks.csproj | 4 ++-- CliWrap.Signaler/CliWrap.Signaler.csproj | 2 +- CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj | 2 +- CliWrap.Tests/CliWrap.Tests.csproj | 6 +++--- CliWrap/CliWrap.csproj | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj index 983d1a52..6bb0d933 100644 --- a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj +++ b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/CliWrap.Signaler/CliWrap.Signaler.csproj b/CliWrap.Signaler/CliWrap.Signaler.csproj index e82cb696..4b82089b 100644 --- a/CliWrap.Signaler/CliWrap.Signaler.csproj +++ b/CliWrap.Signaler/CliWrap.Signaler.csproj @@ -6,7 +6,7 @@ - + \ No newline at end of file diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj index 2d9a4787..cc9f30dc 100644 --- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj @@ -8,7 +8,7 @@ - + diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index 69ad1d2f..16ae7039 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -11,14 +11,14 @@ - + - - + + diff --git a/CliWrap/CliWrap.csproj b/CliWrap/CliWrap.csproj index cee936c8..0749568f 100644 --- a/CliWrap/CliWrap.csproj +++ b/CliWrap/CliWrap.csproj @@ -21,7 +21,7 @@ - + From da19cc6ed6ad9680d67637034f2677f548b0cf59 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sun, 10 Dec 2023 23:17:25 +0200 Subject: [PATCH 48/57] Refactor with C# 12 features --- CliWrap.Tests/Utils/TempDir.cs | 6 ++-- CliWrap.Tests/Utils/TempFile.cs | 6 ++-- CliWrap.Tests/ValidationSpecs.cs | 10 ++----- CliWrap/Buffered/BufferedCommandResult.cs | 28 ++++++------------- CliWrap/CommandResult.cs | 18 +++--------- CliWrap/PipeSource.cs | 10 ++----- CliWrap/PipeTarget.cs | 16 ++++------- CliWrap/Utils/Disposable.cs | 13 ++------- .../Extensions/AsyncDisposableExtensions.cs | 10 ++----- CliWrap/Utils/Observable.cs | 8 ++---- CliWrap/Utils/ProcessEx.cs | 7 ++--- CliWrap/Utils/SynchronizedObserver.cs | 18 ++++-------- CliWrap/Utils/WindowsSignaler.cs | 10 ++----- 13 files changed, 47 insertions(+), 113 deletions(-) diff --git a/CliWrap.Tests/Utils/TempDir.cs b/CliWrap.Tests/Utils/TempDir.cs index c7339cdc..7707bb8b 100644 --- a/CliWrap.Tests/Utils/TempDir.cs +++ b/CliWrap.Tests/Utils/TempDir.cs @@ -5,11 +5,9 @@ namespace CliWrap.Tests.Utils; -internal partial class TempDir : IDisposable +internal partial class TempDir(string path) : IDisposable { - public string Path { get; } - - public TempDir(string path) => Path = path; + public string Path { get; } = path; public void Dispose() { diff --git a/CliWrap.Tests/Utils/TempFile.cs b/CliWrap.Tests/Utils/TempFile.cs index 5e85e510..cbfc4afe 100644 --- a/CliWrap.Tests/Utils/TempFile.cs +++ b/CliWrap.Tests/Utils/TempFile.cs @@ -5,11 +5,9 @@ namespace CliWrap.Tests.Utils; -internal partial class TempFile : IDisposable +internal partial class TempFile(string path) : IDisposable { - public string Path { get; } - - public TempFile(string path) => Path = path; + public string Path { get; } = path; public void Dispose() { diff --git a/CliWrap.Tests/ValidationSpecs.cs b/CliWrap.Tests/ValidationSpecs.cs index 09549fac..bcc2cd7c 100644 --- a/CliWrap.Tests/ValidationSpecs.cs +++ b/CliWrap.Tests/ValidationSpecs.cs @@ -7,12 +7,8 @@ namespace CliWrap.Tests; -public class ValidationSpecs +public class ValidationSpecs(ITestOutputHelper testOutput) { - private readonly ITestOutputHelper _testOutput; - - public ValidationSpecs(ITestOutputHelper testOutput) => _testOutput = testOutput; - [Fact(Timeout = 15000)] public async Task I_can_try_to_execute_a_command_and_get_an_error_if_it_returns_a_non_zero_exit_code() { @@ -27,7 +23,7 @@ public async Task I_can_try_to_execute_a_command_and_get_an_error_if_it_returns_ ex.ExitCode.Should().Be(1); ex.Command.Should().BeEquivalentTo(cmd); - _testOutput.WriteLine(ex.ToString()); + testOutput.WriteLine(ex.ToString()); } [Fact(Timeout = 15000)] @@ -45,7 +41,7 @@ public async Task I_can_try_to_execute_a_command_with_buffering_and_get_a_detail ex.ExitCode.Should().Be(1); ex.Command.Should().BeEquivalentTo(cmd); - _testOutput.WriteLine(ex.ToString()); + testOutput.WriteLine(ex.ToString()); } [Fact(Timeout = 15000)] diff --git a/CliWrap/Buffered/BufferedCommandResult.cs b/CliWrap/Buffered/BufferedCommandResult.cs index 1dbafa14..6acaf871 100644 --- a/CliWrap/Buffered/BufferedCommandResult.cs +++ b/CliWrap/Buffered/BufferedCommandResult.cs @@ -5,31 +5,21 @@ namespace CliWrap.Buffered; /// /// Result of a command execution, with buffered text data from standard output and standard error streams. /// -public class BufferedCommandResult : CommandResult +public class BufferedCommandResult( + int exitCode, + DateTimeOffset startTime, + DateTimeOffset exitTime, + string standardOutput, + string standardError +) : CommandResult(exitCode, startTime, exitTime) { /// /// Standard output data produced by the underlying process. /// - public string StandardOutput { get; } + public string StandardOutput { get; } = standardOutput; /// /// Standard error data produced by the underlying process. /// - public string StandardError { get; } - - /// - /// Initializes an instance of . - /// - public BufferedCommandResult( - int exitCode, - DateTimeOffset startTime, - DateTimeOffset exitTime, - string standardOutput, - string standardError - ) - : base(exitCode, startTime, exitTime) - { - StandardOutput = standardOutput; - StandardError = standardError; - } + public string StandardError { get; } = standardError; } diff --git a/CliWrap/CommandResult.cs b/CliWrap/CommandResult.cs index 5d9d36d2..2043bd9a 100644 --- a/CliWrap/CommandResult.cs +++ b/CliWrap/CommandResult.cs @@ -5,12 +5,12 @@ namespace CliWrap; /// /// Result of a command execution. /// -public class CommandResult +public class CommandResult(int exitCode, DateTimeOffset startTime, DateTimeOffset exitTime) { /// /// Exit code set by the underlying process. /// - public int ExitCode { get; } + public int ExitCode { get; } = exitCode; /// /// Whether the command execution was successful (i.e. exit code is zero). @@ -20,25 +20,15 @@ public class CommandResult /// /// Time at which the command started executing. /// - public DateTimeOffset StartTime { get; } + public DateTimeOffset StartTime { get; } = startTime; /// /// Time at which the command finished executing. /// - public DateTimeOffset ExitTime { get; } + public DateTimeOffset ExitTime { get; } = exitTime; /// /// Total duration of the command execution. /// public TimeSpan RunTime => ExitTime - StartTime; - - /// - /// Initializes an instance of . - /// - public CommandResult(int exitCode, DateTimeOffset startTime, DateTimeOffset exitTime) - { - ExitCode = exitCode; - StartTime = startTime; - ExitTime = exitTime; - } } diff --git a/CliWrap/PipeSource.cs b/CliWrap/PipeSource.cs index 75d9943e..7a8faca4 100644 --- a/CliWrap/PipeSource.cs +++ b/CliWrap/PipeSource.cs @@ -25,17 +25,13 @@ public abstract Task CopyToAsync( public partial class PipeSource { - private class AnonymousPipeSource : PipeSource + private class AnonymousPipeSource(Func copyToAsync) + : PipeSource { - private readonly Func _copyToAsync; - - public AnonymousPipeSource(Func copyToAsync) => - _copyToAsync = copyToAsync; - public override async Task CopyToAsync( Stream destination, CancellationToken cancellationToken = default - ) => await _copyToAsync(destination, cancellationToken).ConfigureAwait(false); + ) => await copyToAsync(destination, cancellationToken).ConfigureAwait(false); } } diff --git a/CliWrap/PipeTarget.cs b/CliWrap/PipeTarget.cs index 35f71504..949bd3af 100644 --- a/CliWrap/PipeTarget.cs +++ b/CliWrap/PipeTarget.cs @@ -28,24 +28,18 @@ public abstract Task CopyFromAsync( public partial class PipeTarget { - private class AnonymousPipeTarget : PipeTarget + private class AnonymousPipeTarget(Func copyFromAsync) + : PipeTarget { - private readonly Func _copyFromAsync; - - public AnonymousPipeTarget(Func copyFromAsync) => - _copyFromAsync = copyFromAsync; - public override async Task CopyFromAsync( Stream origin, CancellationToken cancellationToken = default - ) => await _copyFromAsync(origin, cancellationToken).ConfigureAwait(false); + ) => await copyFromAsync(origin, cancellationToken).ConfigureAwait(false); } - private class AggregatePipeTarget : PipeTarget + private class AggregatePipeTarget(IReadOnlyList targets) : PipeTarget { - public IReadOnlyList Targets { get; } - - public AggregatePipeTarget(IReadOnlyList targets) => Targets = targets; + public IReadOnlyList Targets { get; } = targets; public override async Task CopyFromAsync( Stream origin, diff --git a/CliWrap/Utils/Disposable.cs b/CliWrap/Utils/Disposable.cs index ba50b3ae..1aeef3fb 100644 --- a/CliWrap/Utils/Disposable.cs +++ b/CliWrap/Utils/Disposable.cs @@ -2,18 +2,11 @@ namespace CliWrap.Utils; -internal partial class Disposable : IDisposable -{ - private readonly Action _dispose; - - public Disposable(Action dispose) => _dispose = dispose; - - public void Dispose() => _dispose(); -} - -internal partial class Disposable +internal class Disposable(Action dispose) : IDisposable { public static IDisposable Null { get; } = Create(() => { }); public static IDisposable Create(Action dispose) => new Disposable(dispose); + + public void Dispose() => dispose(); } diff --git a/CliWrap/Utils/Extensions/AsyncDisposableExtensions.cs b/CliWrap/Utils/Extensions/AsyncDisposableExtensions.cs index 37b3f284..d4010e8f 100644 --- a/CliWrap/Utils/Extensions/AsyncDisposableExtensions.cs +++ b/CliWrap/Utils/Extensions/AsyncDisposableExtensions.cs @@ -16,21 +16,17 @@ internal static partial class AsyncDisposableExtensions // - Stream class on .NET Framework 4.6.1 -> calls Dispose() // - Stream class on .NET Core 3.0 -> calls DisposeAsync() // - Stream class on .NET Standard 2.0 -> calls DisposeAsync() or Dispose(), depending on the runtime - private readonly struct AsyncDisposableAdapter : IAsyncDisposable + private readonly struct AsyncDisposableAdapter(IDisposable target) : IAsyncDisposable { - private readonly IDisposable _target; - - public AsyncDisposableAdapter(IDisposable target) => _target = target; - public async ValueTask DisposeAsync() { - if (_target is IAsyncDisposable asyncDisposable) + if (target is IAsyncDisposable asyncDisposable) { await asyncDisposable.DisposeAsync().ConfigureAwait(false); } else { - _target.Dispose(); + target.Dispose(); } } } diff --git a/CliWrap/Utils/Observable.cs b/CliWrap/Utils/Observable.cs index 237ed6ea..947c329a 100644 --- a/CliWrap/Utils/Observable.cs +++ b/CliWrap/Utils/Observable.cs @@ -2,13 +2,9 @@ namespace CliWrap.Utils; -internal class Observable : IObservable +internal class Observable(Func, IDisposable> subscribe) : IObservable { - private readonly Func, IDisposable> _subscribe; - - public Observable(Func, IDisposable> subscribe) => _subscribe = subscribe; - - public IDisposable Subscribe(IObserver observer) => _subscribe(observer); + public IDisposable Subscribe(IObserver observer) => subscribe(observer); } internal static class Observable diff --git a/CliWrap/Utils/ProcessEx.cs b/CliWrap/Utils/ProcessEx.cs index 4659188b..41495a7a 100644 --- a/CliWrap/Utils/ProcessEx.cs +++ b/CliWrap/Utils/ProcessEx.cs @@ -9,9 +9,9 @@ namespace CliWrap.Utils; -internal class ProcessEx : IDisposable +internal class ProcessEx(ProcessStartInfo startInfo) : IDisposable { - private readonly Process _nativeProcess; + private readonly Process _nativeProcess = new() { StartInfo = startInfo }; private readonly TaskCompletionSource _exitTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -40,9 +40,6 @@ internal class ProcessEx : IDisposable public int ExitCode => _nativeProcess.ExitCode; - public ProcessEx(ProcessStartInfo startInfo) => - _nativeProcess = new Process { StartInfo = startInfo }; - public void Start() { // Hook up events diff --git a/CliWrap/Utils/SynchronizedObserver.cs b/CliWrap/Utils/SynchronizedObserver.cs index fb154b6e..822e1318 100644 --- a/CliWrap/Utils/SynchronizedObserver.cs +++ b/CliWrap/Utils/SynchronizedObserver.cs @@ -2,22 +2,16 @@ namespace CliWrap.Utils; -internal class SynchronizedObserver : IObserver +internal class SynchronizedObserver(IObserver observer, object? syncRoot = null) + : IObserver { - private readonly IObserver _observer; - private readonly object _syncRoot; - - public SynchronizedObserver(IObserver observer, object? syncRoot = null) - { - _observer = observer; - _syncRoot = syncRoot ?? new object(); - } + private readonly object _syncRoot = syncRoot ?? new object(); public void OnCompleted() { lock (_syncRoot) { - _observer.OnCompleted(); + observer.OnCompleted(); } } @@ -25,7 +19,7 @@ public void OnError(Exception error) { lock (_syncRoot) { - _observer.OnError(error); + observer.OnError(error); } } @@ -33,7 +27,7 @@ public void OnNext(T value) { lock (_syncRoot) { - _observer.OnNext(value); + observer.OnNext(value); } } } diff --git a/CliWrap/Utils/WindowsSignaler.cs b/CliWrap/Utils/WindowsSignaler.cs index 98417a37..887c623a 100644 --- a/CliWrap/Utils/WindowsSignaler.cs +++ b/CliWrap/Utils/WindowsSignaler.cs @@ -7,19 +7,15 @@ namespace CliWrap.Utils; -internal partial class WindowsSignaler : IDisposable +internal partial class WindowsSignaler(string filePath) : IDisposable { - private readonly string _filePath; - - public WindowsSignaler(string filePath) => _filePath = filePath; - public bool TrySend(int processId, int signalId) { using var process = new Process { StartInfo = new ProcessStartInfo { - FileName = _filePath, + FileName = filePath, Arguments = processId.ToString(CultureInfo.InvariantCulture) + ' ' @@ -49,7 +45,7 @@ public void Dispose() { try { - File.Delete(_filePath); + File.Delete(filePath); } catch { From 7117a2069a1d9a38084854dd2da30085af0f9464 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Mon, 25 Dec 2023 18:59:40 +0200 Subject: [PATCH 49/57] Standardize exception messages --- CliWrap/Utils/Extensions/AssemblyExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CliWrap/Utils/Extensions/AssemblyExtensions.cs b/CliWrap/Utils/Extensions/AssemblyExtensions.cs index ed249185..e7dc6486 100644 --- a/CliWrap/Utils/Extensions/AssemblyExtensions.cs +++ b/CliWrap/Utils/Extensions/AssemblyExtensions.cs @@ -15,7 +15,7 @@ string destFilePath var input = assembly.GetManifestResourceStream(resourceName) ?? throw new MissingManifestResourceException( - $"Could not find resource '{resourceName}'." + $"Failed to find resource '{resourceName}'." ); using var output = File.Create(destFilePath); From 03b29de89609ca121e6909bb8bd365a354786cca Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:24:37 +0200 Subject: [PATCH 50/57] Set default version for use during local development --- Directory.Build.props | 1 + 1 file changed, 1 insertion(+) diff --git a/Directory.Build.props b/Directory.Build.props index 9d394d39..a2ac3bee 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,7 @@ + 0.0.0-dev Tyrrrz Copyright (C) Oleksii Holub latest From 3e638af24fd54496302a119cdbab868c25b0456c Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:24:55 +0200 Subject: [PATCH 51/57] Fix typo --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 0a21d7b9..eec81f68 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -62,7 +62,7 @@ body: required: true - label: I have provided a descriptive title for this issue required: true - - label: I have made sure that that this bug is reproducible on the latest version of the package + - label: I have made sure that this bug is reproducible on the latest version of the package required: true - label: I have provided all the information needed to reproduce this bug as efficiently as possible required: true From 7cf500b98ba66d8bd6e1f6ca1bb92374ff256564 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sun, 31 Dec 2023 16:29:19 +0200 Subject: [PATCH 52/57] Update package release notes link --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index a2ac3bee..437bb523 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -21,7 +21,7 @@ Library for interacting with external command-line interfaces yshell pipe command line executable interface wrapper standard input output error arguments net core https://github.com/Tyrrrz/CliWrap - https://github.com/Tyrrrz/CliWrap/releases/tag/$(Version) + https://github.com/Tyrrrz/CliWrap/releases MIT From c4d5f948c57d08f5e3ec17edcbda91b6b943fdd3 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Mon, 1 Jan 2024 03:09:24 +0200 Subject: [PATCH 53/57] Update license --- License.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/License.txt b/License.txt index 5bb9ba1d..d3290a87 100644 --- a/License.txt +++ b/License.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017-2023 Oleksii Holub +Copyright (c) 2017-2024 Oleksii Holub Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 7c5feee0d6c5d601c8faabeab8737e44492096bc Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 11 Jan 2024 23:22:15 +0200 Subject: [PATCH 54/57] Clean up with latest C# features --- CliWrap.Tests/BufferingSpecs.cs | 8 ++-- CliWrap.Tests/CancellationSpecs.cs | 35 +++++++-------- CliWrap.Tests/ConfigurationSpecs.cs | 9 ++-- CliWrap.Tests/EnvironmentSpecs.cs | 6 +-- CliWrap.Tests/EventStreamSpecs.cs | 4 +- CliWrap.Tests/ExecutionSpecs.cs | 2 +- CliWrap.Tests/PipingSpecs.cs | 69 +++++++++++++---------------- CliWrap.Tests/ValidationSpecs.cs | 6 +-- CliWrap/EventStream/CommandEvent.cs | 2 +- Readme.md | 26 +++++------ 10 files changed, 77 insertions(+), 90 deletions(-) diff --git a/CliWrap.Tests/BufferingSpecs.cs b/CliWrap.Tests/BufferingSpecs.cs index e6a692d3..94ed9cff 100644 --- a/CliWrap.Tests/BufferingSpecs.cs +++ b/CliWrap.Tests/BufferingSpecs.cs @@ -12,7 +12,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "echo", "Hello stdout", "--target", "stdout" }); + .WithArguments(["echo", "Hello stdout", "--target", "stdout"]); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -27,7 +27,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stderr() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "echo", "Hello stderr", "--target", "stderr" }); + .WithArguments(["echo", "Hello stderr", "--target", "stderr"]); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -42,7 +42,7 @@ public async Task I_can_execute_a_command_with_buffering_and_get_the_stdout_and_ { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "echo", "Hello stdout and stderr", "--target", "all" }); + .WithArguments(["echo", "Hello stdout and stderr", "--target", "all"]); // Act var result = await cmd.ExecuteBufferedAsync(); @@ -57,7 +57,7 @@ public async Task I_can_execute_a_command_with_buffering_and_not_hang_on_large_s { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--target", "all", "--length", "100000" }); + .WithArguments(["generate text", "--target", "all", "--length", "100000"]); // Act var result = await cmd.ExecuteBufferedAsync(); diff --git a/CliWrap.Tests/CancellationSpecs.cs b/CliWrap.Tests/CancellationSpecs.cs index efde61c0..6f4ed0c0 100644 --- a/CliWrap.Tests/CancellationSpecs.cs +++ b/CliWrap.Tests/CancellationSpecs.cs @@ -18,13 +18,12 @@ public async Task I_can_execute_a_command_and_cancel_it_immediately() { // Arrange using var cts = new CancellationTokenSource(); - cts.Cancel(); + await cts.CancelAsync(); var stdOutBuffer = new StringBuilder(); var cmd = - Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }) - | stdOutBuffer; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]) | stdOutBuffer; // Act var task = cmd.ExecuteAsync(cts.Token); @@ -47,8 +46,7 @@ public async Task I_can_execute_a_command_and_cancel_it_after_a_delay() var stdOutBuffer = new StringBuilder(); var cmd = - Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }) - | stdOutBuffer; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]) | stdOutBuffer; // Act var task = cmd.ExecuteAsync(cts.Token); @@ -83,8 +81,7 @@ void HandleStdOut(string line) PipeTarget.ToStringBuilder(stdOutBuffer) ); - var cmd = - Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }) | target; + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]) | target; // Act var task = cmd.ExecuteAsync(CancellationToken.None, cts.Token); @@ -102,9 +99,9 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_immediate { // Arrange using var cts = new CancellationTokenSource(); - cts.Cancel(); + await cts.CancelAsync(); - var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -121,7 +118,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_after_a_d using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -138,7 +135,7 @@ public async Task I_can_execute_a_command_with_buffering_and_cancel_it_gracefull using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -159,9 +156,9 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance { // Arrange using var cts = new CancellationTokenSource(); - cts.Cancel(); + await cts.CancelAsync(); - var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -183,7 +180,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -205,7 +202,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]); // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => @@ -232,9 +229,9 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance { // Arrange using var cts = new CancellationTokenSource(); - cts.Cancel(); + await cts.CancelAsync(); - var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -260,7 +257,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]); // Act & assert var ex = await Assert.ThrowsAnyAsync( @@ -286,7 +283,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream_and_cance using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(0.2)); - var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "sleep", "00:00:20" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["sleep", "00:00:20"]); // Act & assert var ex = await Assert.ThrowsAnyAsync( diff --git a/CliWrap.Tests/ConfigurationSpecs.cs b/CliWrap.Tests/ConfigurationSpecs.cs index 34ee43e4..8ce17f4f 100644 --- a/CliWrap.Tests/ConfigurationSpecs.cs +++ b/CliWrap.Tests/ConfigurationSpecs.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using FluentAssertions; using Xunit; @@ -63,7 +62,7 @@ public void I_can_configure_the_command_line_arguments_by_passing_an_array() var original = Cli.Wrap("foo").WithArguments("xxx"); // Act - var modified = original.WithArguments(new[] { "-a", "foo bar" }); + var modified = original.WithArguments(["-a", "foo bar"]); // Assert original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Arguments)); @@ -84,8 +83,8 @@ public void I_can_configure_the_command_line_arguments_using_a_builder() .Add("foo bar") .Add("\"foo\\\\bar\"") .Add(3.14) - .Add(new[] { "foo", "bar" }) - .Add(new IFormattable[] { -5, 89.13 }) + .Add(["foo", "bar"]) + .Add([-5, 89.13]) ); // Assert diff --git a/CliWrap.Tests/EnvironmentSpecs.cs b/CliWrap.Tests/EnvironmentSpecs.cs index 5483998a..942cef63 100644 --- a/CliWrap.Tests/EnvironmentSpecs.cs +++ b/CliWrap.Tests/EnvironmentSpecs.cs @@ -35,7 +35,7 @@ public async Task I_can_execute_a_command_with_additional_environment_variables( var env = new Dictionary { ["foo"] = "bar", ["hello"] = "world" }; var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "env", "foo", "hello" }) + .WithArguments(["env", "foo", "hello"]) .WithEnvironmentVariables(env); // Act @@ -59,9 +59,7 @@ public async Task I_can_execute_a_command_with_some_environment_variables_overwr using (TempEnvironmentVariable.Set(variableToUnset, "unset")) // will be unset { var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments( - new[] { "env", variableToKeep, variableToOverwrite, variableToUnset } - ) + .WithArguments(["env", variableToKeep, variableToOverwrite, variableToUnset]) .WithEnvironmentVariables( e => e.Set(variableToOverwrite, "overwritten").Set(variableToUnset, null) ); diff --git a/CliWrap.Tests/EventStreamSpecs.cs b/CliWrap.Tests/EventStreamSpecs.cs index ae37e995..8cac5fe9 100644 --- a/CliWrap.Tests/EventStreamSpecs.cs +++ b/CliWrap.Tests/EventStreamSpecs.cs @@ -15,7 +15,7 @@ public async Task I_can_execute_a_command_as_a_pull_based_event_stream() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }); + .WithArguments(["generate text", "--target", "all", "--lines", "100"]); // Act var events = new List(); @@ -36,7 +36,7 @@ public async Task I_can_execute_a_command_as_a_push_based_event_stream() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }); + .WithArguments(["generate text", "--target", "all", "--lines", "100"]); // Act var events = await cmd.Observe().ToArray(); diff --git a/CliWrap.Tests/ExecutionSpecs.cs b/CliWrap.Tests/ExecutionSpecs.cs index 1d5b79c5..d9c298ea 100644 --- a/CliWrap.Tests/ExecutionSpecs.cs +++ b/CliWrap.Tests/ExecutionSpecs.cs @@ -53,7 +53,7 @@ public async Task I_can_execute_a_command_and_not_hang_on_large_stdout_and_stder { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--target", "all", "--length", "100000" }); + .WithArguments(["generate binary", "--target", "all", "--length", "100000"]); // Act & assert await cmd.ExecuteAsync(); diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index 0381d0e8..59fd6867 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -128,7 +128,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_another_comman // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--length", "100000" }) + .WithArguments(["generate binary", "--length", "100000"]) | Cli.Wrap(Dummy.Program.FilePath).WithArguments("length stdin"); // Act @@ -145,8 +145,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdin_from_a_chain_of_com var cmd = "Hello world" | Cli.Wrap(Dummy.Program.FilePath).WithArguments("echo stdin") - | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "echo stdin", "--length", "5" }) + | Cli.Wrap(Dummy.Program.FilePath).WithArguments(["echo stdin", "--length", "5"]) | Cli.Wrap(Dummy.Program.FilePath).WithArguments("length stdin"); // Act @@ -170,7 +169,7 @@ await origin.CopyToAsync(stream, cancellationToken) var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--length", "100000" }) | target; + .WithArguments(["generate binary", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); @@ -193,7 +192,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_anonym var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--length", "100000" }) | target; + .WithArguments(["generate binary", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); @@ -210,7 +209,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_stream() var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--length", "100000" }) | stream; + .WithArguments(["generate binary", "--length", "100000"]) | stream; // Act await cmd.ExecuteAsync(); @@ -227,7 +226,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_file() var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--length", "100000" }) + .WithArguments(["generate binary", "--length", "100000"]) | PipeTarget.ToFile(file.Path); // Act @@ -244,9 +243,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_string_buil // Arrange var buffer = new StringBuilder(); - var cmd = - Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "echo", "Hello world!" }) - | buffer; + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["echo", "Hello world!"]) | buffer; // Act await cmd.ExecuteAsync(); @@ -268,8 +265,8 @@ async Task HandleStdOutAsync(string line) } var cmd = - Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--lines", "100" }) | HandleStdOutAsync; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(["generate text", "--lines", "100"]) + | HandleStdOutAsync; // Act await cmd.ExecuteAsync(); @@ -291,8 +288,8 @@ async Task HandleStdOutAsync(string line, CancellationToken cancellationToken = } var cmd = - Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--lines", "100" }) | HandleStdOutAsync; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(["generate text", "--lines", "100"]) + | HandleStdOutAsync; // Act await cmd.ExecuteAsync(); @@ -310,8 +307,8 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_delega void HandleStdOut(string line) => stdOutLinesCount++; var cmd = - Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--lines", "100" }) | HandleStdOut; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(["generate text", "--lines", "100"]) + | HandleStdOut; // Act await cmd.ExecuteAsync(); @@ -329,7 +326,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--target", "all", "--length", "100000" }) + .WithArguments(["generate binary", "--target", "all", "--length", "100000"]) | (stdOut, stdErr); // Act @@ -349,7 +346,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_st var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "echo", "Hello world!", "--target", "all" }) + .WithArguments(["echo", "Hello world!", "--target", "all"]) | (stdOutBuffer, stdErrBuffer); // Act @@ -381,7 +378,7 @@ async Task HandleStdErrAsync(string line) var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }) + .WithArguments(["generate text", "--target", "all", "--lines", "100"]) | (HandleStdOutAsync, HandleStdErrAsync); // Act @@ -413,7 +410,7 @@ async Task HandleStdErrAsync(string line, CancellationToken cancellationToken = var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }) + .WithArguments(["generate text", "--target", "all", "--lines", "100"]) | (HandleStdOutAsync, HandleStdErrAsync); // Act @@ -436,7 +433,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_and_stderr_into_se var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--target", "all", "--lines", "100" }) + .WithArguments(["generate text", "--target", "all", "--lines", "100"]) | (HandleStdOut, HandleStdErr); // Act @@ -463,7 +460,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--length", "100000" }) | target; + .WithArguments(["generate binary", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); @@ -489,7 +486,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_targ var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--length", "100_000" }) | target; + .WithArguments(["generate binary", "--length", "100_000"]) | target; // Act & assert var ex = await Assert.ThrowsAnyAsync(async () => await cmd.ExecuteAsync()); @@ -515,7 +512,7 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_hier var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--length", "100000" }) | target; + .WithArguments(["generate binary", "--length", "100000"]) | target; // Act await cmd.ExecuteAsync(); @@ -538,15 +535,14 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_multiple_stre // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments( - new[] - { + [ "generate binary", "--length", "1000000", // Buffer needs to be >= BufferSizes.Stream to fail "--buffer", "100000" - } + ] ); // Act @@ -578,8 +574,8 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou using var stream = new MemoryStream(); var cmd = - Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--length", "100000" }) | stream; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(["generate text", "--length", "100000"]) + | stream; // Act var result = await cmd.ExecuteBufferedAsync(); @@ -602,8 +598,8 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou void HandleStdOut(string line) => delegateLines.Add(line); var cmd = - Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate text", "--lines", "100" }) | HandleStdOut; + Cli.Wrap(Dummy.Program.FilePath).WithArguments(["generate text", "--lines", "100"]) + | HandleStdOut; // Act var result = await cmd.ExecuteBufferedAsync(); @@ -636,7 +632,7 @@ public async Task I_can_try_to_execute_a_command_and_get_an_error_if_the_pipe_ta // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "generate binary", "--length", "100_000" }) + .WithArguments(["generate binary", "--length", "100_000"]) | PipeTarget.ToFile("non-existing-directory/file.txt"); // Act & assert @@ -688,8 +684,7 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_only_parti var cmd = source - | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "echo stdin", "--length", "100000" }); + | Cli.Wrap(Dummy.Program.FilePath).WithArguments(["echo stdin", "--length", "100000"]); // Act & assert await cmd.ExecuteAsync(); @@ -711,8 +706,7 @@ public async Task I_can_execute_a_command_and_not_hang_if_the_process_does_not_c var cmd = source - | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "echo stdin", "--length", "0" }); + | Cli.Wrap(Dummy.Program.FilePath).WithArguments(["echo stdin", "--length", "0"]); // Act & assert await cmd.ExecuteAsync(); @@ -732,8 +726,7 @@ await Task.Delay(TimeSpan.FromSeconds(20), CancellationToken.None) var cmd = source - | Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "echo stdin", "--length", "0" }); + | Cli.Wrap(Dummy.Program.FilePath).WithArguments(["echo stdin", "--length", "0"]); // Act & assert await cmd.ExecuteAsync(); diff --git a/CliWrap.Tests/ValidationSpecs.cs b/CliWrap.Tests/ValidationSpecs.cs index bcc2cd7c..edc8a084 100644 --- a/CliWrap.Tests/ValidationSpecs.cs +++ b/CliWrap.Tests/ValidationSpecs.cs @@ -13,7 +13,7 @@ public class ValidationSpecs(ITestOutputHelper testOutput) 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 = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "1" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["exit", "1"]); // Act & assert var ex = await Assert.ThrowsAsync( @@ -30,7 +30,7 @@ public async Task I_can_try_to_execute_a_command_and_get_an_error_if_it_returns_ 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 = Cli.Wrap(Dummy.Program.FilePath).WithArguments(new[] { "exit", "1" }); + var cmd = Cli.Wrap(Dummy.Program.FilePath).WithArguments(["exit", "1"]); // Act & assert var ex = await Assert.ThrowsAsync( @@ -49,7 +49,7 @@ public async Task I_can_execute_a_command_without_validating_the_exit_code() { // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithArguments(new[] { "exit", "1" }) + .WithArguments(["exit", "1"]) .WithValidation(CommandResultValidation.None); // Act diff --git a/CliWrap/EventStream/CommandEvent.cs b/CliWrap/EventStream/CommandEvent.cs index db1f3e6d..1bed8822 100644 --- a/CliWrap/EventStream/CommandEvent.cs +++ b/CliWrap/EventStream/CommandEvent.cs @@ -17,7 +17,7 @@ namespace CliWrap.EventStream; /// /// /// -public abstract class CommandEvent { } +public abstract class CommandEvent; /// /// Event triggered when the command starts executing. diff --git a/Readme.md b/Readme.md index 826e68f0..b2765eee 100644 --- a/Readme.md +++ b/Readme.md @@ -74,7 +74,7 @@ Once the command is configured, you can run it by calling `ExecuteAsync()`: using CliWrap; var result = await Cli.Wrap("path/to/exe") - .WithArguments(new[] {"--foo", "bar"}) + .WithArguments(["--foo", "bar"]) .WithWorkingDirectory("work/dir/path") .ExecuteAsync(); @@ -103,7 +103,7 @@ var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); var result = await Cli.Wrap("path/to/exe") - .WithArguments(new[] {"--foo", "bar"}) + .WithArguments(["--foo", "bar"]) .WithWorkingDirectory("work/dir/path") // This can be simplified with `ExecuteBufferedAsync()` .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer)) @@ -128,7 +128,7 @@ using CliWrap.Buffered; // Calling `ExecuteBufferedAsync()` instead of `ExecuteAsync()` // implicitly configures pipes that write to in-memory buffers. var result = await Cli.Wrap("path/to/exe") - .WithArguments(new[] {"--foo", "bar"}) + .WithArguments(["--foo", "bar"]) .WithWorkingDirectory("work/dir/path") .ExecuteBufferedAsync(); @@ -169,7 +169,7 @@ Sets the command-line arguments passed to the child process. var cmd = Cli.Wrap("git") // Each element is formatted as a separate argument. // Equivalent to: `git commit -m "my commit"` - .WithArguments(new[] {"commit", "-m", "my commit"}); + .WithArguments(["commit", "-m", "my commit"]); ``` - Set arguments using a builder: @@ -499,8 +499,8 @@ await cmd.ExecuteAsync(); ```csharp var cmd = "Hello world" | - Cli.Wrap("foo").WithArguments(new[] {"aaa"}) | - Cli.Wrap("bar").WithArguments(new[] {"bbb"}) | + Cli.Wrap("foo").WithArguments(["aaa"]) | + Cli.Wrap("bar").WithArguments(["bbb"]) | (Console.WriteLine, Console.Error.WriteLine); await cmd.ExecuteAsync(); @@ -523,7 +523,7 @@ using CliWrap; using CliWrap.Buffered; var result = await Cli.Wrap("foo") - .WithArguments(new[] {"bar"}) + .WithArguments(["bar"]) .ExecuteBufferedAsync(); var exitCode = result.ExitCode; @@ -537,12 +537,12 @@ To override this, specify the encoding explicitly by using one of the available ```csharp // Treat both stdout and stderr as UTF8-encoded text streams var result = await Cli.Wrap("foo") - .WithArguments(new[] {"bar"}) + .WithArguments(["bar"]) .ExecuteBufferedAsync(Encoding.UTF8); // Treat stdout as ASCII-encoded and stderr as UTF8-encoded var result = await Cli.Wrap("foo") - .WithArguments(new[] {"bar"}) + .WithArguments(["bar"]) .ExecuteBufferedAsync(Encoding.ASCII, Encoding.UTF8); ``` @@ -567,7 +567,7 @@ To execute a command as a _pull-based_ event stream, use the `ListenAsync()` ext using CliWrap; using CliWrap.EventStream; -var cmd = Cli.Wrap("foo").WithArguments(new[] {"bar"}); +var cmd = Cli.Wrap("foo").WithArguments(["bar"]); await foreach (var cmdEvent in cmd.ListenAsync()) { @@ -604,7 +604,7 @@ using System.Reactive; using CliWrap; using CliWrap.EventStream; -var cmd = Cli.Wrap("foo").WithArguments(new[] {"bar"}); +var cmd = Cli.Wrap("foo").WithArguments(["bar"]); await cmd.Observe().ForEachAsync(cmdEvent => { @@ -729,7 +729,7 @@ public async Task GitPushAsync(CancellationToken cancellationToken = default) ); await Cli.Wrap("git") - .WithArguments(new[] {"push"}) + .WithArguments(["push"]) .ExecuteAsync(forcefulCts.Token, cancellationToken); } ``` @@ -744,7 +744,7 @@ This is a specialized awaitable object that contains additional information abou ```csharp var task = Cli.Wrap("foo") - .WithArguments(new[] {"bar"}) + .WithArguments(["bar"]) .ExecuteAsync(); // Get the process ID From b305b608e4b4153c835960faf8ea7ee64948a62c Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 11 Jan 2024 23:32:18 +0200 Subject: [PATCH 55/57] Update NuGet packages --- CliWrap.Benchmarks/BufferingBenchmarks.cs | 8 ++-- CliWrap.Benchmarks/CliWrap.Benchmarks.csproj | 4 +- .../PullEventStreamBenchmarks.cs | 8 ++-- CliWrap.Signaler/CliWrap.Signaler.csproj | 2 +- .../CliWrap.Tests.Dummy.csproj | 4 +- CliWrap.Tests/CliWrap.Tests.csproj | 8 ++-- CliWrap.Tests/ConfigurationSpecs.cs | 15 +++---- CliWrap.Tests/PipingSpecs.cs | 7 ++-- .../Utils/Extensions/AssertionExtensions.cs | 3 +- CliWrap/CliWrap.csproj | 4 +- CliWrap/Command.Execution.cs | 2 +- .../PullEventStreamCommandExtensions.cs | 20 +++++----- .../PushEventStreamCommandExtensions.cs | 40 +++++++++---------- CliWrap/PipeTarget.cs | 2 +- CliWrap/Utils/SimplexStream.cs | 3 +- 15 files changed, 60 insertions(+), 70 deletions(-) diff --git a/CliWrap.Benchmarks/BufferingBenchmarks.cs b/CliWrap.Benchmarks/BufferingBenchmarks.cs index e9a5c3cb..14e017bc 100644 --- a/CliWrap.Benchmarks/BufferingBenchmarks.cs +++ b/CliWrap.Benchmarks/BufferingBenchmarks.cs @@ -42,10 +42,10 @@ public class BufferingBenchmarks [Benchmark] public async Task<(string, string)> ProcessX() { - var (_, stdOutStream, stdErrStream) = Cysharp - .Diagnostics - .ProcessX - .GetDualAsyncEnumerable(FilePath, arguments: Args); + var (_, stdOutStream, stdErrStream) = Cysharp.Diagnostics.ProcessX.GetDualAsyncEnumerable( + FilePath, + arguments: Args + ); var stdOutTask = stdOutStream.ToTask(); var stdErrTask = stdErrStream.ToTask(); diff --git a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj index 6bb0d933..5423a026 100644 --- a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj +++ b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs b/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs index 862f6451..52b16eec 100644 --- a/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs +++ b/CliWrap.Benchmarks/PullEventStreamBenchmarks.cs @@ -38,10 +38,10 @@ public async Task ProcessX() { var counter = 0; - var (_, stdOutStream, stdErrStream) = Cysharp - .Diagnostics - .ProcessX - .GetDualAsyncEnumerable(FilePath, arguments: Args); + var (_, stdOutStream, stdErrStream) = Cysharp.Diagnostics.ProcessX.GetDualAsyncEnumerable( + FilePath, + arguments: Args + ); var consumeStdOutTask = Task.Run(async () => { diff --git a/CliWrap.Signaler/CliWrap.Signaler.csproj b/CliWrap.Signaler/CliWrap.Signaler.csproj index 4b82089b..36260947 100644 --- a/CliWrap.Signaler/CliWrap.Signaler.csproj +++ b/CliWrap.Signaler/CliWrap.Signaler.csproj @@ -6,7 +6,7 @@ - + \ No newline at end of file diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj index cc9f30dc..558f3f4b 100644 --- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index 16ae7039..d64c27e0 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -11,14 +11,14 @@ - + - + - - + + diff --git a/CliWrap.Tests/ConfigurationSpecs.cs b/CliWrap.Tests/ConfigurationSpecs.cs index 8ce17f4f..98b69e09 100644 --- a/CliWrap.Tests/ConfigurationSpecs.cs +++ b/CliWrap.Tests/ConfigurationSpecs.cs @@ -91,8 +91,7 @@ public void I_can_configure_the_command_line_arguments_using_a_builder() original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Arguments)); original.Arguments.Should().NotBe(modified.Arguments); modified - .Arguments - .Should() + .Arguments.Should() .Be("-a \"foo bar\" \"\\\"foo\\\\bar\\\"\" 3.14 foo bar -5 89.13"); } @@ -126,8 +125,7 @@ public void I_can_configure_the_user_credentials() original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Credentials)); original.Credentials.Should().NotBe(modified.Credentials); modified - .Credentials - .Should() + .Credentials.Should() .BeEquivalentTo(new Credentials("domain", "username", "password", true)); } @@ -150,8 +148,7 @@ public void I_can_configure_the_user_credentials_using_a_builder() original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.Credentials)); original.Credentials.Should().NotBe(modified.Credentials); modified - .Credentials - .Should() + .Credentials.Should() .BeEquivalentTo(new Credentials("domain", "username", "password", true)); } @@ -170,8 +167,7 @@ public void I_can_configure_the_environment_variables() original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.EnvironmentVariables)); original.EnvironmentVariables.Should().NotBeEquivalentTo(modified.EnvironmentVariables); modified - .EnvironmentVariables - .Should() + .EnvironmentVariables.Should() .BeEquivalentTo( new Dictionary { ["name"] = "value", ["key"] = "door" } ); @@ -195,8 +191,7 @@ public void I_can_configure_the_environment_variables_using_a_builder() original.Should().BeEquivalentTo(modified, o => o.Excluding(c => c.EnvironmentVariables)); original.EnvironmentVariables.Should().NotBeEquivalentTo(modified.EnvironmentVariables); modified - .EnvironmentVariables - .Should() + .EnvironmentVariables.Should() .BeEquivalentTo( new Dictionary { diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index 59fd6867..d9d364bf 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -608,9 +608,10 @@ public async Task I_can_execute_a_command_with_buffering_and_also_pipe_the_stdou delegateLines .Should() .Equal( - result - .StandardOutput - .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries) + result.StandardOutput.Split( + Environment.NewLine, + StringSplitOptions.RemoveEmptyEntries + ) ); } diff --git a/CliWrap.Tests/Utils/Extensions/AssertionExtensions.cs b/CliWrap.Tests/Utils/Extensions/AssertionExtensions.cs index caf90d28..0d705d3c 100644 --- a/CliWrap.Tests/Utils/Extensions/AssertionExtensions.cs +++ b/CliWrap.Tests/Utils/Extensions/AssertionExtensions.cs @@ -12,8 +12,7 @@ public static void ConsistOfLines( IEnumerable lines ) => assertions - .Subject - .Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) + .Subject.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) .Should() .Equal(lines); diff --git a/CliWrap/CliWrap.csproj b/CliWrap/CliWrap.csproj index 0749568f..ab6aa2b1 100644 --- a/CliWrap/CliWrap.csproj +++ b/CliWrap/CliWrap.csproj @@ -21,10 +21,10 @@ - + - + diff --git a/CliWrap/Command.Execution.cs b/CliWrap/Command.Execution.cs index 861d6d76..c28f304b 100644 --- a/CliWrap/Command.Execution.cs +++ b/CliWrap/Command.Execution.cs @@ -237,7 +237,7 @@ private async Task ExecuteAsync( // and won't need it anymore. // If the pipe has already been exhausted (most likely), this won't do anything. // If the pipe is still trying to transfer data, this will cause it to abort. - stdInCts.Cancel(); + await stdInCts.CancelAsync(); // Wait until piping is done and propagate exceptions await pipingTask.ConfigureAwait(false); diff --git a/CliWrap/EventStream/PullEventStreamCommandExtensions.cs b/CliWrap/EventStream/PullEventStreamCommandExtensions.cs index 57c2886a..6d4fd15f 100644 --- a/CliWrap/EventStream/PullEventStreamCommandExtensions.cs +++ b/CliWrap/EventStream/PullEventStreamCommandExtensions.cs @@ -70,17 +70,15 @@ await channel yield return new StartedCommandEvent(commandTask.ProcessId); // Close the channel once the command completes, so that ReceiveAsync() can finish - _ = commandTask - .Task - .ContinueWith( - async _ => - // ReSharper disable once AccessToDisposedClosure - await channel - .ReportCompletionAsync(forcefulCancellationToken) - .ConfigureAwait(false), - // Run the continuation even if the parent task failed - TaskContinuationOptions.None - ); + _ = commandTask.Task.ContinueWith( + async _ => + // ReSharper disable once AccessToDisposedClosure + await channel + .ReportCompletionAsync(forcefulCancellationToken) + .ConfigureAwait(false), + // Run the continuation even if the parent task failed + TaskContinuationOptions.None + ); await foreach ( var cmdEvent in channel.ReceiveAsync(forcefulCancellationToken).ConfigureAwait(false) diff --git a/CliWrap/EventStream/PushEventStreamCommandExtensions.cs b/CliWrap/EventStream/PushEventStreamCommandExtensions.cs index 52adffa7..1f95f126 100644 --- a/CliWrap/EventStream/PushEventStreamCommandExtensions.cs +++ b/CliWrap/EventStream/PushEventStreamCommandExtensions.cs @@ -59,28 +59,26 @@ CancellationToken gracefulCancellationToken // Don't pass cancellation token to the continuation because we need it to // trigger regardless of how the task completed. - _ = commandTask - .Task - .ContinueWith( - t => + _ = commandTask.Task.ContinueWith( + t => + { + // Canceled tasks don't have exceptions + if (t.IsCanceled) { - // Canceled tasks don't have exceptions - if (t.IsCanceled) - { - observer.OnError(new TaskCanceledException(t)); - } - else if (t.Exception is not null) - { - observer.OnError(t.Exception.TryGetSingle() ?? t.Exception); - } - else - { - observer.OnNext(new ExitedCommandEvent(t.Result.ExitCode)); - observer.OnCompleted(); - } - }, - TaskContinuationOptions.None - ); + observer.OnError(new TaskCanceledException(t)); + } + else if (t.Exception is not null) + { + observer.OnError(t.Exception.TryGetSingle() ?? t.Exception); + } + else + { + observer.OnNext(new ExitedCommandEvent(t.Result.ExitCode)); + observer.OnCompleted(); + } + }, + TaskContinuationOptions.None + ); return Disposable.Null; }); diff --git a/CliWrap/PipeTarget.cs b/CliWrap/PipeTarget.cs index 949bd3af..4ed7946b 100644 --- a/CliWrap/PipeTarget.cs +++ b/CliWrap/PipeTarget.cs @@ -71,7 +71,7 @@ public override async Task CopyFromAsync( { // Abort the operation if any of the targets fail // ReSharper disable once AccessToDisposedClosure - cts.Cancel(); + await cts.CancelAsync(); throw; } diff --git a/CliWrap/Utils/SimplexStream.cs b/CliWrap/Utils/SimplexStream.cs index e4d2e228..4adb6c0b 100644 --- a/CliWrap/Utils/SimplexStream.cs +++ b/CliWrap/Utils/SimplexStream.cs @@ -67,8 +67,7 @@ CancellationToken cancellationToken var length = Math.Min(count, _sharedBufferBytes - _sharedBufferBytesRead); _sharedBuffer - .Memory - .Slice(_sharedBufferBytesRead, length) + .Memory.Slice(_sharedBufferBytesRead, length) .CopyTo(buffer.AsMemory(offset, length)); _sharedBufferBytesRead += length; From 82adfb793753bddb88d2be35431e162dd98f79ac Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 11 Jan 2024 23:45:01 +0200 Subject: [PATCH 56/57] More cleanup --- CliWrap/Command.cs | 89 ++++++++----------- CliWrap/CommandTask.cs | 15 +--- CliWrap/Credentials.cs | 47 ++++------ CliWrap/EventStream/CommandEvent.cs | 36 ++------ CliWrap/Exceptions/CliWrapException.cs | 9 +- .../Exceptions/CommandExecutionException.cs | 40 ++++----- 6 files changed, 83 insertions(+), 153 deletions(-) diff --git a/CliWrap/Command.cs b/CliWrap/Command.cs index c4d5b5ec..27bed170 100644 --- a/CliWrap/Command.cs +++ b/CliWrap/Command.cs @@ -10,76 +10,61 @@ namespace CliWrap; /// /// Instructions for running a process. /// -public partial class Command : ICommandConfiguration +public partial class Command( + string targetFilePath, + string arguments, + string workingDirPath, + Credentials credentials, + IReadOnlyDictionary environmentVariables, + CommandResultValidation validation, + PipeSource standardInputPipe, + PipeTarget standardOutputPipe, + PipeTarget standardErrorPipe +) : ICommandConfiguration { - /// - public string TargetFilePath { get; } + /// + /// Initializes an instance of . + /// + public Command(string targetFilePath) + : this( + targetFilePath, + string.Empty, + Directory.GetCurrentDirectory(), + Credentials.Default, + new Dictionary(), + CommandResultValidation.ZeroExitCode, + PipeSource.Null, + PipeTarget.Null, + PipeTarget.Null + ) { } /// - public string Arguments { get; } + public string TargetFilePath { get; } = targetFilePath; /// - public string WorkingDirPath { get; } + public string Arguments { get; } = arguments; /// - public Credentials Credentials { get; } + public string WorkingDirPath { get; } = workingDirPath; /// - public IReadOnlyDictionary EnvironmentVariables { get; } + public Credentials Credentials { get; } = credentials; /// - public CommandResultValidation Validation { get; } + public IReadOnlyDictionary EnvironmentVariables { get; } = + environmentVariables; /// - public PipeSource StandardInputPipe { get; } + public CommandResultValidation Validation { get; } = validation; /// - public PipeTarget StandardOutputPipe { get; } + public PipeSource StandardInputPipe { get; } = standardInputPipe; /// - public PipeTarget StandardErrorPipe { get; } - - /// - /// Initializes an instance of . - /// - public Command( - string targetFilePath, - string arguments, - string workingDirPath, - Credentials credentials, - IReadOnlyDictionary environmentVariables, - CommandResultValidation validation, - PipeSource standardInputPipe, - PipeTarget standardOutputPipe, - PipeTarget standardErrorPipe - ) - { - TargetFilePath = targetFilePath; - Arguments = arguments; - WorkingDirPath = workingDirPath; - Credentials = credentials; - EnvironmentVariables = environmentVariables; - Validation = validation; - StandardInputPipe = standardInputPipe; - StandardOutputPipe = standardOutputPipe; - StandardErrorPipe = standardErrorPipe; - } + public PipeTarget StandardOutputPipe { get; } = standardOutputPipe; - /// - /// Initializes an instance of . - /// - public Command(string targetFilePath) - : this( - targetFilePath, - string.Empty, - Directory.GetCurrentDirectory(), - Credentials.Default, - new Dictionary(), - CommandResultValidation.ZeroExitCode, - PipeSource.Null, - PipeTarget.Null, - PipeTarget.Null - ) { } + /// + public PipeTarget StandardErrorPipe { get; } = standardErrorPipe; /// /// Creates a copy of this command, setting the target file path to the specified value. diff --git a/CliWrap/CommandTask.cs b/CliWrap/CommandTask.cs index 8515e5eb..a57ffd1e 100644 --- a/CliWrap/CommandTask.cs +++ b/CliWrap/CommandTask.cs @@ -8,26 +8,17 @@ namespace CliWrap; /// /// Represents an asynchronous execution of a command. /// -public partial class CommandTask : IDisposable +public partial class CommandTask(Task task, int processId) : IDisposable { /// /// Underlying task. /// - public Task Task { get; } + public Task Task { get; } = task; /// /// Underlying process ID. /// - public int ProcessId { get; } - - /// - /// Initializes an instance of . - /// - public CommandTask(Task task, int processId) - { - Task = task; - ProcessId = processId; - } + public int ProcessId { get; } = processId; internal CommandTask Bind(Func, Task> transform) => new(transform(Task), ProcessId); diff --git a/CliWrap/Credentials.cs b/CliWrap/Credentials.cs index c10a576c..b833fa0c 100644 --- a/CliWrap/Credentials.cs +++ b/CliWrap/Credentials.cs @@ -5,20 +5,33 @@ namespace CliWrap; /// /// User credentials used for starting a process. /// -public partial class Credentials +public partial class Credentials( + string? domain = null, + string? userName = null, + string? password = null, + bool loadUserProfile = false +) { + /// + /// Initializes an instance of . + /// + // TODO: (breaking change) remove in favor of the other overload + [ExcludeFromCodeCoverage] + public Credentials(string? domain, string? username, string? password) + : this(domain, username, password, false) { } + /// /// Active Directory domain used for starting the process. /// /// /// Only supported on Windows. /// - public string? Domain { get; } + public string? Domain { get; } = domain; /// /// Username used for starting the process. /// - public string? UserName { get; } + public string? UserName { get; } = userName; /// /// Password used for starting the process. @@ -26,7 +39,7 @@ public partial class Credentials /// /// Only supported on Windows. /// - public string? Password { get; } + public string? Password { get; } = password; /// /// Whether to load the user profile when starting the process. @@ -34,31 +47,7 @@ public partial class Credentials /// /// Only supported on Windows. /// - public bool LoadUserProfile { get; } - - /// - /// Initializes an instance of . - /// - public Credentials( - string? domain = null, - string? userName = null, - string? password = null, - bool loadUserProfile = false - ) - { - Domain = domain; - UserName = userName; - Password = password; - LoadUserProfile = loadUserProfile; - } - - /// - /// Initializes an instance of . - /// - // TODO: (breaking change) remove in favor of the other overload - [ExcludeFromCodeCoverage] - public Credentials(string? domain, string? username, string? password) - : this(domain, username, password, false) { } + public bool LoadUserProfile { get; } = loadUserProfile; } public partial class Credentials diff --git a/CliWrap/EventStream/CommandEvent.cs b/CliWrap/EventStream/CommandEvent.cs index 1bed8822..8d5c4a12 100644 --- a/CliWrap/EventStream/CommandEvent.cs +++ b/CliWrap/EventStream/CommandEvent.cs @@ -23,17 +23,12 @@ public abstract class CommandEvent; /// Event triggered when the command starts executing. /// May only appear once in the event stream. /// -public class StartedCommandEvent : CommandEvent +public class StartedCommandEvent(int processId) : CommandEvent { /// /// Underlying process ID. /// - public int ProcessId { get; } - - /// - /// Initializes an instance of . - /// - public StartedCommandEvent(int processId) => ProcessId = processId; + public int ProcessId { get; } = processId; /// [ExcludeFromCodeCoverage] @@ -43,17 +38,12 @@ public class StartedCommandEvent : CommandEvent /// /// Event triggered when the underlying process writes a line of text to the standard output stream. /// -public class StandardOutputCommandEvent : CommandEvent +public class StandardOutputCommandEvent(string text) : CommandEvent { /// /// Line of text written to the standard output stream. /// - public string Text { get; } - - /// - /// Initializes an instance of . - /// - public StandardOutputCommandEvent(string text) => Text = text; + public string Text { get; } = text; /// [ExcludeFromCodeCoverage] @@ -63,17 +53,12 @@ public class StandardOutputCommandEvent : CommandEvent /// /// Event triggered when the underlying process writes a line of text to the standard error stream. /// -public class StandardErrorCommandEvent : CommandEvent +public class StandardErrorCommandEvent(string text) : CommandEvent { /// /// Line of text written to the standard error stream. /// - public string Text { get; } - - /// - /// Initializes an instance of . - /// - public StandardErrorCommandEvent(string text) => Text = text; + public string Text { get; } = text; /// [ExcludeFromCodeCoverage] @@ -84,17 +69,12 @@ public class StandardErrorCommandEvent : CommandEvent /// Event triggered when the command finishes executing. /// May only appear once in the event stream. /// -public class ExitedCommandEvent : CommandEvent +public class ExitedCommandEvent(int exitCode) : CommandEvent { /// /// Exit code set by the underlying process. /// - public int ExitCode { get; } - - /// - /// Initializes an instance of . - /// - public ExitedCommandEvent(int exitCode) => ExitCode = exitCode; + public int ExitCode { get; } = exitCode; /// [ExcludeFromCodeCoverage] diff --git a/CliWrap/Exceptions/CliWrapException.cs b/CliWrap/Exceptions/CliWrapException.cs index d7b88bca..08199b54 100644 --- a/CliWrap/Exceptions/CliWrapException.cs +++ b/CliWrap/Exceptions/CliWrapException.cs @@ -6,14 +6,9 @@ namespace CliWrap.Exceptions; /// /// Parent class for exceptions thrown by . /// -public abstract class CliWrapException : Exception +public abstract class CliWrapException(string message, Exception? innerException = null) + : Exception(message, innerException) { - /// - /// Initializes an instance of . - /// - protected CliWrapException(string message, Exception? innerException) - : base(message, innerException) { } - /// /// Initializes an instance of . /// diff --git a/CliWrap/Exceptions/CommandExecutionException.cs b/CliWrap/Exceptions/CommandExecutionException.cs index b9eb74f5..dc76a564 100644 --- a/CliWrap/Exceptions/CommandExecutionException.cs +++ b/CliWrap/Exceptions/CommandExecutionException.cs @@ -6,38 +6,28 @@ namespace CliWrap.Exceptions; /// /// Exception thrown when the command fails to execute correctly. /// -public class CommandExecutionException : CliWrapException +public class CommandExecutionException( + ICommandConfiguration command, + int exitCode, + string message, + Exception? innerException = null +) : CliWrapException(message, innerException) { /// - /// Command that triggered the exception. - /// - public ICommandConfiguration Command { get; } - - /// - /// Exit code returned by the process. + /// Initializes an instance of . /// - public int ExitCode { get; } + // TODO: (breaking change) remove in favor of an optional parameter in the constructor above + [ExcludeFromCodeCoverage] + public CommandExecutionException(ICommandConfiguration command, int exitCode, string message) + : this(command, exitCode, message, null) { } /// - /// Initializes an instance of . + /// Command that triggered the exception. /// - public CommandExecutionException( - ICommandConfiguration command, - int exitCode, - string message, - Exception? innerException - ) - : base(message, innerException) - { - Command = command; - ExitCode = exitCode; - } + public ICommandConfiguration Command { get; } = command; /// - /// Initializes an instance of . + /// Exit code returned by the process. /// - // TODO: (breaking change) remove in favor of an optional parameter in the constructor above - [ExcludeFromCodeCoverage] - public CommandExecutionException(ICommandConfiguration command, int exitCode, string message) - : this(command, exitCode, message, null) { } + public int ExitCode { get; } = exitCode; } From ebdc370d3f97183494a7311d41c7fc3e68ba8a8c Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:55:05 +0200 Subject: [PATCH 57/57] Update NuGet packages --- CliWrap.Benchmarks/CliWrap.Benchmarks.csproj | 2 +- CliWrap.Signaler/CliWrap.Signaler.csproj | 2 +- .../CliWrap.Tests.Dummy.csproj | 2 +- CliWrap.Tests/CliWrap.Tests.csproj | 4 +-- CliWrap.Tests/ConfigurationSpecs.cs | 32 ++++++++----------- CliWrap.Tests/CredentialsSpecs.cs | 15 ++++----- CliWrap.Tests/EnvironmentSpecs.cs | 4 +-- CliWrap.Tests/PipingSpecs.cs | 7 ++-- CliWrap/CliWrap.csproj | 2 +- CliWrap/Command.Execution.cs | 4 ++- 10 files changed, 34 insertions(+), 40 deletions(-) diff --git a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj index 5423a026..0531b917 100644 --- a/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj +++ b/CliWrap.Benchmarks/CliWrap.Benchmarks.csproj @@ -7,7 +7,7 @@ - + diff --git a/CliWrap.Signaler/CliWrap.Signaler.csproj b/CliWrap.Signaler/CliWrap.Signaler.csproj index 36260947..ac129019 100644 --- a/CliWrap.Signaler/CliWrap.Signaler.csproj +++ b/CliWrap.Signaler/CliWrap.Signaler.csproj @@ -6,7 +6,7 @@ - + \ No newline at end of file diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj index 558f3f4b..db18fb32 100644 --- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj @@ -8,7 +8,7 @@ - + diff --git a/CliWrap.Tests/CliWrap.Tests.csproj b/CliWrap.Tests/CliWrap.Tests.csproj index d64c27e0..a6e70433 100644 --- a/CliWrap.Tests/CliWrap.Tests.csproj +++ b/CliWrap.Tests/CliWrap.Tests.csproj @@ -11,13 +11,13 @@ - + - + diff --git a/CliWrap.Tests/ConfigurationSpecs.cs b/CliWrap.Tests/ConfigurationSpecs.cs index 98b69e09..74549a87 100644 --- a/CliWrap.Tests/ConfigurationSpecs.cs +++ b/CliWrap.Tests/ConfigurationSpecs.cs @@ -77,14 +77,13 @@ public void I_can_configure_the_command_line_arguments_using_a_builder() var original = Cli.Wrap("foo").WithArguments("xxx"); // Act - var modified = original.WithArguments( - b => - b.Add("-a") - .Add("foo bar") - .Add("\"foo\\\\bar\"") - .Add(3.14) - .Add(["foo", "bar"]) - .Add([-5, 89.13]) + var modified = original.WithArguments(b => + b.Add("-a") + .Add("foo bar") + .Add("\"foo\\\\bar\"") + .Add(3.14) + .Add(["foo", "bar"]) + .Add([-5, 89.13]) ); // Assert @@ -136,12 +135,8 @@ public void I_can_configure_the_user_credentials_using_a_builder() var original = Cli.Wrap("foo").WithCredentials(new Credentials("xxx", "xxx", "xxx")); // Act - var modified = original.WithCredentials( - c => - c.SetDomain("domain") - .SetUserName("username") - .SetPassword("password") - .LoadUserProfile() + var modified = original.WithCredentials(c => + c.SetDomain("domain").SetUserName("username").SetPassword("password").LoadUserProfile() ); // Assert @@ -180,11 +175,10 @@ public void I_can_configure_the_environment_variables_using_a_builder() var original = Cli.Wrap("foo").WithEnvironmentVariables(e => e.Set("xxx", "xxx")); // Act - var modified = original.WithEnvironmentVariables( - b => - b.Set("name", "value") - .Set("key", "door") - .Set(new Dictionary { ["zzz"] = "yyy", ["aaa"] = "bbb" }) + var modified = original.WithEnvironmentVariables(b => + b.Set("name", "value") + .Set("key", "door") + .Set(new Dictionary { ["zzz"] = "yyy", ["aaa"] = "bbb" }) ); // Assert diff --git a/CliWrap.Tests/CredentialsSpecs.cs b/CliWrap.Tests/CredentialsSpecs.cs index 8e6be53d..ebc14d46 100644 --- a/CliWrap.Tests/CredentialsSpecs.cs +++ b/CliWrap.Tests/CredentialsSpecs.cs @@ -21,8 +21,8 @@ public async Task I_can_execute_a_command_as_a_different_user() // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithCredentials( - c => c.SetUserName("user123").SetPassword("pass123").LoadUserProfile() + .WithCredentials(c => + c.SetUserName("user123").SetPassword("pass123").LoadUserProfile() ); // Act & assert @@ -42,12 +42,11 @@ public async Task I_can_execute_a_command_as_a_different_user_under_the_specifie // Arrange var cmd = Cli.Wrap(Dummy.Program.FilePath) - .WithCredentials( - c => - c.SetDomain("domain123") - .SetUserName("user123") - .SetPassword("pass123") - .LoadUserProfile() + .WithCredentials(c => + c.SetDomain("domain123") + .SetUserName("user123") + .SetPassword("pass123") + .LoadUserProfile() ); // Act & assert diff --git a/CliWrap.Tests/EnvironmentSpecs.cs b/CliWrap.Tests/EnvironmentSpecs.cs index 942cef63..bf7dc39e 100644 --- a/CliWrap.Tests/EnvironmentSpecs.cs +++ b/CliWrap.Tests/EnvironmentSpecs.cs @@ -60,8 +60,8 @@ public async Task I_can_execute_a_command_with_some_environment_variables_overwr { var cmd = Cli.Wrap(Dummy.Program.FilePath) .WithArguments(["env", variableToKeep, variableToOverwrite, variableToUnset]) - .WithEnvironmentVariables( - e => e.Set(variableToOverwrite, "overwritten").Set(variableToUnset, null) + .WithEnvironmentVariables(e => + e.Set(variableToOverwrite, "overwritten").Set(variableToUnset, null) ); // Act diff --git a/CliWrap.Tests/PipingSpecs.cs b/CliWrap.Tests/PipingSpecs.cs index d9d364bf..66d84f5e 100644 --- a/CliWrap.Tests/PipingSpecs.cs +++ b/CliWrap.Tests/PipingSpecs.cs @@ -184,10 +184,9 @@ public async Task I_can_execute_a_command_and_pipe_the_stdout_into_a_sync_anonym // Arrange using var stream = new MemoryStream(); - var target = PipeTarget.Create( - origin => - // ReSharper disable once AccessToDisposedClosure - origin.CopyTo(stream) + var target = PipeTarget.Create(origin => + // ReSharper disable once AccessToDisposedClosure + origin.CopyTo(stream) ); var cmd = diff --git a/CliWrap/CliWrap.csproj b/CliWrap/CliWrap.csproj index ab6aa2b1..46a3fa77 100644 --- a/CliWrap/CliWrap.csproj +++ b/CliWrap/CliWrap.csproj @@ -21,7 +21,7 @@ - + diff --git a/CliWrap/Command.Execution.cs b/CliWrap/Command.Execution.cs index c28f304b..d5d74da5 100644 --- a/CliWrap/Command.Execution.cs +++ b/CliWrap/Command.Execution.cs @@ -277,7 +277,9 @@ Command execution failed because the underlying process ({process.Name}#{process Command: {TargetFilePath} {Arguments} - You can suppress this validation by calling `{nameof(WithValidation)}({nameof(CommandResultValidation)}.{nameof(CommandResultValidation.None)})` on the command. + You can suppress this validation by calling `{nameof(WithValidation)}({nameof( + CommandResultValidation + )}.{nameof(CommandResultValidation.None)})` on the command. """ ); }