From 67e449466004accd4626f5df89b7baa216061530 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sat, 16 Mar 2024 16:11:09 -0500 Subject: [PATCH 01/11] WIP refactoring how vendored code works --- CliWrap.FSharp.sln | 19 +++++++++++-------- scripts/update-vendored-code.sh | 5 +++++ .../CliWrap.FSharp.Tests.fsproj | 2 +- src/CliWrap.Tests.Dummy | 1 + 4 files changed, 18 insertions(+), 9 deletions(-) create mode 100755 scripts/update-vendored-code.sh create mode 120000 src/CliWrap.Tests.Dummy diff --git a/CliWrap.FSharp.sln b/CliWrap.FSharp.sln index 882bfbf..fb776a5 100644 --- a/CliWrap.FSharp.sln +++ b/CliWrap.FSharp.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 @@ -40,9 +40,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{BC5059 .gitmodules = .gitmodules EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "vendor", "vendor", "{711E438A-D184-4513-921C-C4D7AE85E2B9}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{85223DF2-CAD6-4978-A180-B19DE2BB9F41}" + ProjectSection(SolutionItems) = preProject + scripts\update-vendored-code.sh = scripts\update-vendored-code.sh + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliWrap.Tests.Dummy", "vendor\CliWrap\CliWrap.Tests.Dummy\CliWrap.Tests.Dummy.csproj", "{C06FC79C-B308-4078-8C36-AB2F42C583E0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliWrap.Tests.Dummy", "src\CliWrap.Tests.Dummy\CliWrap.Tests.Dummy.csproj", "{26F29434-8760-4387-BF01-4FB1FA65426E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -61,14 +64,14 @@ Global {12BFD7AA-776D-4B58-8AC3-241AF39EED84}.Debug|Any CPU.Build.0 = Debug|Any CPU {12BFD7AA-776D-4B58-8AC3-241AF39EED84}.Release|Any CPU.ActiveCfg = Release|Any CPU {12BFD7AA-776D-4B58-8AC3-241AF39EED84}.Release|Any CPU.Build.0 = Release|Any CPU - {C06FC79C-B308-4078-8C36-AB2F42C583E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C06FC79C-B308-4078-8C36-AB2F42C583E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C06FC79C-B308-4078-8C36-AB2F42C583E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C06FC79C-B308-4078-8C36-AB2F42C583E0}.Release|Any CPU.Build.0 = Release|Any CPU + {26F29434-8760-4387-BF01-4FB1FA65426E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {26F29434-8760-4387-BF01-4FB1FA65426E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {26F29434-8760-4387-BF01-4FB1FA65426E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {26F29434-8760-4387-BF01-4FB1FA65426E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {2E2FBB43-30C0-45A1-8DAC-D7A48411EC7A} = {DE85DD40-1128-4648-A2EB-9A1FE73C28A4} {12BFD7AA-776D-4B58-8AC3-241AF39EED84} = {DE85DD40-1128-4648-A2EB-9A1FE73C28A4} - {C06FC79C-B308-4078-8C36-AB2F42C583E0} = {711E438A-D184-4513-921C-C4D7AE85E2B9} + {26F29434-8760-4387-BF01-4FB1FA65426E} = {DE85DD40-1128-4648-A2EB-9A1FE73C28A4} EndGlobalSection EndGlobal diff --git a/scripts/update-vendored-code.sh b/scripts/update-vendored-code.sh new file mode 100755 index 0000000..d578f63 --- /dev/null +++ b/scripts/update-vendored-code.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -eum + +root="$(git rev-parse --show-toplevel)" +ln -s "$root/vendor/CliWrap/CliWrap.Tests.Dummy" "$root/src" diff --git a/src/CliWrap.FSharp.Tests/CliWrap.FSharp.Tests.fsproj b/src/CliWrap.FSharp.Tests/CliWrap.FSharp.Tests.fsproj index 60bf36e..bf019f5 100644 --- a/src/CliWrap.FSharp.Tests/CliWrap.FSharp.Tests.fsproj +++ b/src/CliWrap.FSharp.Tests/CliWrap.FSharp.Tests.fsproj @@ -25,7 +25,7 @@ - + diff --git a/src/CliWrap.Tests.Dummy b/src/CliWrap.Tests.Dummy new file mode 120000 index 0000000..edb7737 --- /dev/null +++ b/src/CliWrap.Tests.Dummy @@ -0,0 +1 @@ +/home/erik/src/github.com/UnstoppableMango/CliWrap.FSharp/vendor/CliWrap/CliWrap.Tests.Dummy \ No newline at end of file From f90e5c5e9aed18a2dc6197055964a3119e72ed13 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sat, 16 Mar 2024 16:20:23 -0500 Subject: [PATCH 02/11] Add vendor patch --- patches/dummy.patch | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 patches/dummy.patch diff --git a/patches/dummy.patch b/patches/dummy.patch new file mode 100644 index 0000000..5110feb --- /dev/null +++ b/patches/dummy.patch @@ -0,0 +1,23 @@ +diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +index db18fb3..f11cddf 100644 +--- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj ++++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +@@ -2,15 +2,14 @@ + + + Exe +- net8.0 ++ net8.0 + $(TargetFrameworks);net48 + + + + +- + + + + +- +\ No newline at end of file ++ From 4ede49415e95ff7dcf2e6e83f8c34f6f64ebf29b Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sat, 16 Mar 2024 16:25:44 -0500 Subject: [PATCH 03/11] =?UTF-8?q?Add=20new=20scripts=20and=20patches,=20up?= =?UTF-8?q?date=20project=20configurations=20=F0=9F=9B=A0=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added new scripts and patches to the project solution. Updated project configurations for better compatibility. --- CliWrap.FSharp.sln | 7 ++----- patches/dummy.patch | 5 +++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CliWrap.FSharp.sln b/CliWrap.FSharp.sln index fb776a5..807e5d2 100644 --- a/CliWrap.FSharp.sln +++ b/CliWrap.FSharp.sln @@ -20,6 +20,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{BE2AB152 global.json = global.json .config\dotnet-tools.json = .config\dotnet-tools.json Directory.Build.props = Directory.Build.props + scripts\update-vendored-code.sh = scripts\update-vendored-code.sh + patches\dummy.patch = patches\dummy.patch EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{FE346152-5716-4224-9B25-C6B3815C70CA}" @@ -40,11 +42,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{BC5059 .gitmodules = .gitmodules EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{85223DF2-CAD6-4978-A180-B19DE2BB9F41}" - ProjectSection(SolutionItems) = preProject - scripts\update-vendored-code.sh = scripts\update-vendored-code.sh - EndProjectSection -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliWrap.Tests.Dummy", "src\CliWrap.Tests.Dummy\CliWrap.Tests.Dummy.csproj", "{26F29434-8760-4387-BF01-4FB1FA65426E}" EndProject Global diff --git a/patches/dummy.patch b/patches/dummy.patch index 5110feb..aff2070 100644 --- a/patches/dummy.patch +++ b/patches/dummy.patch @@ -1,5 +1,5 @@ diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj -index db18fb3..f11cddf 100644 +index db18fb3..99290aa 100644 --- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj +++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj @@ -2,15 +2,14 @@ @@ -7,8 +7,9 @@ index db18fb3..f11cddf 100644 Exe - net8.0 +- $(TargetFrameworks);net48 + net8.0 - $(TargetFrameworks);net48 ++ true From aea82edb7ed38ad1d1dc9cd9f8f5762ebaed0481 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sat, 16 Mar 2024 17:11:22 -0500 Subject: [PATCH 04/11] Use patch instead of git --- patches/0001-CliWrap.Tests.Dummy.csproj.patch | 13 +++++++++++++ scripts/update-vendored-code.sh | 9 ++++++++- src/CliWrap.Tests.Dummy | 1 - 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 patches/0001-CliWrap.Tests.Dummy.csproj.patch delete mode 120000 src/CliWrap.Tests.Dummy diff --git a/patches/0001-CliWrap.Tests.Dummy.csproj.patch b/patches/0001-CliWrap.Tests.Dummy.csproj.patch new file mode 100644 index 0000000..9006bea --- /dev/null +++ b/patches/0001-CliWrap.Tests.Dummy.csproj.patch @@ -0,0 +1,13 @@ +5,6c5,6 +< net8.0 +< $(TargetFrameworks);net48 +--- +> net8.0 +> true +11d10 +< +16c15 +< +\ No newline at end of file +--- +> diff --git a/scripts/update-vendored-code.sh b/scripts/update-vendored-code.sh index d578f63..2fca6c7 100755 --- a/scripts/update-vendored-code.sh +++ b/scripts/update-vendored-code.sh @@ -2,4 +2,11 @@ set -eum root="$(git rev-parse --show-toplevel)" -ln -s "$root/vendor/CliWrap/CliWrap.Tests.Dummy" "$root/src" +patch="$root/patches/0001-CliWrap.Tests.Dummy.csproj.patch" +vendorDir="$root/vendor/CliWrap" +targetDir="$root/src/CliWrap.Tests.Dummy" + +git submodule foreach --recursive git clean -xfd +rm -rf "$targetDir" || echo 'Target is clean' +cp -r "$vendorDir/CliWrap.Tests.Dummy" "$targetDir" +patch "$targetDir/CliWrap.Tests.Dummy.csproj" <"$patch" diff --git a/src/CliWrap.Tests.Dummy b/src/CliWrap.Tests.Dummy deleted file mode 120000 index edb7737..0000000 --- a/src/CliWrap.Tests.Dummy +++ /dev/null @@ -1 +0,0 @@ -/home/erik/src/github.com/UnstoppableMango/CliWrap.FSharp/vendor/CliWrap/CliWrap.Tests.Dummy \ No newline at end of file From b6564747984129470ab85a77cba6e7ad41c4f93b Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sat, 16 Mar 2024 17:14:40 -0500 Subject: [PATCH 05/11] Update vendor script --- scripts/update-vendored-code.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/update-vendored-code.sh b/scripts/update-vendored-code.sh index 2fca6c7..43ee47e 100755 --- a/scripts/update-vendored-code.sh +++ b/scripts/update-vendored-code.sh @@ -8,5 +8,8 @@ targetDir="$root/src/CliWrap.Tests.Dummy" git submodule foreach --recursive git clean -xfd rm -rf "$targetDir" || echo 'Target is clean' + +[ '--clean' == "${1:-}" ] && exit 0 + cp -r "$vendorDir/CliWrap.Tests.Dummy" "$targetDir" patch "$targetDir/CliWrap.Tests.Dummy.csproj" <"$patch" From 912ebfdfcf9c68c1cf6b1250019c29d1c72e2b88 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sat, 16 Mar 2024 17:17:02 -0500 Subject: [PATCH 06/11] Clean up patches --- CliWrap.FSharp.sln | 2 +- patches/0001-CliWrap.Tests.Dummy.csproj.patch | 2 +- patches/dummy.patch | 24 ------------------- 3 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 patches/dummy.patch diff --git a/CliWrap.FSharp.sln b/CliWrap.FSharp.sln index 807e5d2..230b569 100644 --- a/CliWrap.FSharp.sln +++ b/CliWrap.FSharp.sln @@ -21,7 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{BE2AB152 .config\dotnet-tools.json = .config\dotnet-tools.json Directory.Build.props = Directory.Build.props scripts\update-vendored-code.sh = scripts\update-vendored-code.sh - patches\dummy.patch = patches\dummy.patch + patches\0001-CliWrap.Tests.Dummy.csproj.patch = patches\0001-CliWrap.Tests.Dummy.csproj.patch EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{FE346152-5716-4224-9B25-C6B3815C70CA}" diff --git a/patches/0001-CliWrap.Tests.Dummy.csproj.patch b/patches/0001-CliWrap.Tests.Dummy.csproj.patch index 9006bea..c488e50 100644 --- a/patches/0001-CliWrap.Tests.Dummy.csproj.patch +++ b/patches/0001-CliWrap.Tests.Dummy.csproj.patch @@ -3,7 +3,7 @@ < $(TargetFrameworks);net48 --- > net8.0 -> true +> false 11d10 < 16c15 diff --git a/patches/dummy.patch b/patches/dummy.patch deleted file mode 100644 index aff2070..0000000 --- a/patches/dummy.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj -index db18fb3..99290aa 100644 ---- a/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj -+++ b/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj -@@ -2,15 +2,14 @@ - - - Exe -- net8.0 -- $(TargetFrameworks);net48 -+ net8.0 -+ true - - - - -- - - - - -- -\ No newline at end of file -+ From ce83811c4e715fff15ea1cc5ca0230199e0ca58c Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sat, 16 Mar 2024 17:17:32 -0500 Subject: [PATCH 07/11] vendor the dummy project a different way --- .../CliWrap.Tests.Dummy.csproj | 15 +++++++ .../Commands/EchoCommand.cs | 27 ++++++++++++ .../Commands/EchoStdInCommand.cs | 39 +++++++++++++++++ .../Commands/EnvironmentCommand.cs | 21 +++++++++ .../Commands/ExitCommand.cs | 22 ++++++++++ .../Commands/GenerateBinaryCommand.cs | 43 +++++++++++++++++++ .../Commands/GenerateTextCommand.cs | 43 +++++++++++++++++++ .../Commands/LengthStdInCommand.cs | 29 +++++++++++++ .../Commands/Shared/OutputTarget.cs | 25 +++++++++++ .../Commands/SleepCommand.cs | 32 ++++++++++++++ .../Commands/WorkingDirectoryCommand.cs | 14 ++++++ src/CliWrap.Tests.Dummy/Program.cs | 32 ++++++++++++++ 12 files changed, 342 insertions(+) create mode 100644 src/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj create mode 100644 src/CliWrap.Tests.Dummy/Commands/EchoCommand.cs create mode 100644 src/CliWrap.Tests.Dummy/Commands/EchoStdInCommand.cs create mode 100644 src/CliWrap.Tests.Dummy/Commands/EnvironmentCommand.cs create mode 100644 src/CliWrap.Tests.Dummy/Commands/ExitCommand.cs create mode 100644 src/CliWrap.Tests.Dummy/Commands/GenerateBinaryCommand.cs create mode 100644 src/CliWrap.Tests.Dummy/Commands/GenerateTextCommand.cs create mode 100644 src/CliWrap.Tests.Dummy/Commands/LengthStdInCommand.cs create mode 100644 src/CliWrap.Tests.Dummy/Commands/Shared/OutputTarget.cs create mode 100644 src/CliWrap.Tests.Dummy/Commands/SleepCommand.cs create mode 100644 src/CliWrap.Tests.Dummy/Commands/WorkingDirectoryCommand.cs create mode 100644 src/CliWrap.Tests.Dummy/Program.cs diff --git a/src/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj b/src/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj new file mode 100644 index 0000000..f7e2fa7 --- /dev/null +++ b/src/CliWrap.Tests.Dummy/CliWrap.Tests.Dummy.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + false + + + + + + + + + diff --git a/src/CliWrap.Tests.Dummy/Commands/EchoCommand.cs b/src/CliWrap.Tests.Dummy/Commands/EchoCommand.cs new file mode 100644 index 0000000..97aaced --- /dev/null +++ b/src/CliWrap.Tests.Dummy/Commands/EchoCommand.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using CliFx; +using CliFx.Attributes; +using CliFx.Infrastructure; +using CliWrap.Tests.Dummy.Commands.Shared; + +namespace CliWrap.Tests.Dummy.Commands; + +[Command("echo")] +public class EchoCommand : ICommand +{ + [CommandParameter(0)] + public required IReadOnlyList Items { get; init; } + + [CommandOption("target")] + public OutputTarget Target { get; init; } = OutputTarget.StdOut; + + [CommandOption("separator")] + public string Separator { get; init; } = " "; + + public async ValueTask ExecuteAsync(IConsole console) + { + foreach (var writer in console.GetWriters(Target)) + await writer.WriteLineAsync(string.Join(Separator, Items)); + } +} diff --git a/src/CliWrap.Tests.Dummy/Commands/EchoStdInCommand.cs b/src/CliWrap.Tests.Dummy/Commands/EchoStdInCommand.cs new file mode 100644 index 0000000..54459b6 --- /dev/null +++ b/src/CliWrap.Tests.Dummy/Commands/EchoStdInCommand.cs @@ -0,0 +1,39 @@ +using System; +using System.Buffers; +using System.Threading.Tasks; +using CliFx; +using CliFx.Attributes; +using CliFx.Infrastructure; +using CliWrap.Tests.Dummy.Commands.Shared; + +namespace CliWrap.Tests.Dummy.Commands; + +[Command("echo stdin")] +public class EchoStdInCommand : ICommand +{ + [CommandOption("target")] + public OutputTarget Target { get; init; } = OutputTarget.StdOut; + + [CommandOption("length")] + public long Length { get; init; } = long.MaxValue; + + public async ValueTask ExecuteAsync(IConsole console) + { + using var buffer = MemoryPool.Shared.Rent(81920); + + var totalBytesRead = 0L; + while (totalBytesRead < Length) + { + var bytesWanted = (int)Math.Min(buffer.Memory.Length, Length - totalBytesRead); + + var bytesRead = await console.Input.BaseStream.ReadAsync(buffer.Memory[..bytesWanted]); + if (bytesRead <= 0) + break; + + foreach (var writer in console.GetWriters(Target)) + await writer.BaseStream.WriteAsync(buffer.Memory[..bytesRead]); + + totalBytesRead += bytesRead; + } + } +} diff --git a/src/CliWrap.Tests.Dummy/Commands/EnvironmentCommand.cs b/src/CliWrap.Tests.Dummy/Commands/EnvironmentCommand.cs new file mode 100644 index 0000000..5fef363 --- /dev/null +++ b/src/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/src/CliWrap.Tests.Dummy/Commands/ExitCommand.cs b/src/CliWrap.Tests.Dummy/Commands/ExitCommand.cs new file mode 100644 index 0000000..a2a5076 --- /dev/null +++ b/src/CliWrap.Tests.Dummy/Commands/ExitCommand.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using CliFx; +using CliFx.Attributes; +using CliFx.Exceptions; +using CliFx.Infrastructure; + +namespace CliWrap.Tests.Dummy.Commands; + +[Command("exit")] +public class ExitCommand : ICommand +{ + [CommandParameter(0)] + public int ExitCode { get; init; } + + public ValueTask ExecuteAsync(IConsole console) + { + if (ExitCode != 0) + throw new CommandException($"Exit code set to {ExitCode}", ExitCode); + + return default; + } +} diff --git a/src/CliWrap.Tests.Dummy/Commands/GenerateBinaryCommand.cs b/src/CliWrap.Tests.Dummy/Commands/GenerateBinaryCommand.cs new file mode 100644 index 0000000..20c6200 --- /dev/null +++ b/src/CliWrap.Tests.Dummy/Commands/GenerateBinaryCommand.cs @@ -0,0 +1,43 @@ +using System; +using System.Buffers; +using System.Threading.Tasks; +using CliFx; +using CliFx.Attributes; +using CliFx.Infrastructure; +using CliWrap.Tests.Dummy.Commands.Shared; + +namespace CliWrap.Tests.Dummy.Commands; + +[Command("generate binary")] +public class GenerateBinaryCommand : ICommand +{ + // Tests rely on the random seed being fixed + private readonly Random _random = new(1234567); + + [CommandOption("target")] + public OutputTarget Target { get; init; } = OutputTarget.StdOut; + + [CommandOption("length")] + public long Length { get; init; } = 100_000; + + [CommandOption("buffer")] + public int BufferSize { get; init; } = 1024; + + public async ValueTask ExecuteAsync(IConsole console) + { + using var buffer = MemoryPool.Shared.Rent(BufferSize); + + var totalBytesGenerated = 0L; + while (totalBytesGenerated < Length) + { + _random.NextBytes(buffer.Memory.Span); + + var bytesWanted = (int)Math.Min(buffer.Memory.Length, Length - totalBytesGenerated); + + foreach (var writer in console.GetWriters(Target)) + await writer.BaseStream.WriteAsync(buffer.Memory[..bytesWanted]); + + totalBytesGenerated += bytesWanted; + } + } +} diff --git a/src/CliWrap.Tests.Dummy/Commands/GenerateTextCommand.cs b/src/CliWrap.Tests.Dummy/Commands/GenerateTextCommand.cs new file mode 100644 index 0000000..603c211 --- /dev/null +++ b/src/CliWrap.Tests.Dummy/Commands/GenerateTextCommand.cs @@ -0,0 +1,43 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CliFx; +using CliFx.Attributes; +using CliFx.Infrastructure; +using CliWrap.Tests.Dummy.Commands.Shared; + +namespace CliWrap.Tests.Dummy.Commands; + +[Command("generate text")] +public class GenerateTextCommand : ICommand +{ + // Tests rely on the random seed being fixed + private readonly Random _random = new(1234567); + private readonly char[] _allowedChars = Enumerable.Range(32, 94).Select(i => (char)i).ToArray(); + + [CommandOption("target")] + public OutputTarget Target { get; init; } = OutputTarget.StdOut; + + [CommandOption("length")] + public int Length { get; init; } = 100_000; + + [CommandOption("lines")] + public int LinesCount { get; init; } = 1; + + public async ValueTask ExecuteAsync(IConsole console) + { + for (var line = 0; line < LinesCount; line++) + { + var buffer = new StringBuilder(Length); + + for (var i = 0; i < Length; i++) + { + buffer.Append(_allowedChars[_random.Next(0, _allowedChars.Length)]); + } + + foreach (var writer in console.GetWriters(Target)) + await writer.WriteLineAsync(buffer.ToString()); + } + } +} diff --git a/src/CliWrap.Tests.Dummy/Commands/LengthStdInCommand.cs b/src/CliWrap.Tests.Dummy/Commands/LengthStdInCommand.cs new file mode 100644 index 0000000..22eb14b --- /dev/null +++ b/src/CliWrap.Tests.Dummy/Commands/LengthStdInCommand.cs @@ -0,0 +1,29 @@ +using System.Buffers; +using System.Globalization; +using System.Threading.Tasks; +using CliFx; +using CliFx.Attributes; +using CliFx.Infrastructure; + +namespace CliWrap.Tests.Dummy.Commands; + +[Command("length stdin")] +public class LengthStdInCommand : ICommand +{ + public async ValueTask ExecuteAsync(IConsole console) + { + using var buffer = MemoryPool.Shared.Rent(81920); + + var totalBytesRead = 0L; + while (true) + { + var bytesRead = await console.Input.BaseStream.ReadAsync(buffer.Memory); + if (bytesRead <= 0) + break; + + totalBytesRead += bytesRead; + } + + await console.Output.WriteLineAsync(totalBytesRead.ToString(CultureInfo.InvariantCulture)); + } +} diff --git a/src/CliWrap.Tests.Dummy/Commands/Shared/OutputTarget.cs b/src/CliWrap.Tests.Dummy/Commands/Shared/OutputTarget.cs new file mode 100644 index 0000000..47dfe65 --- /dev/null +++ b/src/CliWrap.Tests.Dummy/Commands/Shared/OutputTarget.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using CliFx.Infrastructure; + +namespace CliWrap.Tests.Dummy.Commands.Shared; + +[Flags] +public enum OutputTarget +{ + StdOut = 1, + StdErr = 2, + All = StdOut | StdErr +} + +internal static class OutputTargetExtensions +{ + public static IEnumerable GetWriters(this IConsole console, OutputTarget target) + { + if (target.HasFlag(OutputTarget.StdOut)) + yield return console.Output; + + if (target.HasFlag(OutputTarget.StdErr)) + yield return console.Error; + } +} diff --git a/src/CliWrap.Tests.Dummy/Commands/SleepCommand.cs b/src/CliWrap.Tests.Dummy/Commands/SleepCommand.cs new file mode 100644 index 0000000..64bdc3d --- /dev/null +++ b/src/CliWrap.Tests.Dummy/Commands/SleepCommand.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using CliFx; +using CliFx.Attributes; +using CliFx.Infrastructure; + +namespace CliWrap.Tests.Dummy.Commands; + +[Command("sleep")] +public class SleepCommand : ICommand +{ + [CommandParameter(0)] + public TimeSpan Duration { get; init; } = TimeSpan.FromSeconds(1); + + public async ValueTask ExecuteAsync(IConsole console) + { + var cancellationToken = console.RegisterCancellationHandler(); + + try + { + await console.Output.WriteLineAsync($"Sleeping for {Duration}..."); + await Task.Delay(Duration, cancellationToken); + } + catch (OperationCanceledException) + { + await console.Output.WriteLineAsync("Canceled."); + return; + } + + await console.Output.WriteLineAsync("Done."); + } +} diff --git a/src/CliWrap.Tests.Dummy/Commands/WorkingDirectoryCommand.cs b/src/CliWrap.Tests.Dummy/Commands/WorkingDirectoryCommand.cs new file mode 100644 index 0000000..8bf9524 --- /dev/null +++ b/src/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/src/CliWrap.Tests.Dummy/Program.cs b/src/CliWrap.Tests.Dummy/Program.cs new file mode 100644 index 0000000..76faa28 --- /dev/null +++ b/src/CliWrap.Tests.Dummy/Program.cs @@ -0,0 +1,32 @@ +using System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using CliFx; + +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 async Task Main(string[] args) + { + // Make sure color codes are not produced because we rely on the output in tests + Environment.SetEnvironmentVariable( + "DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", + "false" + ); + + return await new CliApplicationBuilder() + .AddCommandsFromThisAssembly() + .Build() + .RunAsync(args); + } +} From ca21926e728ec2c3086ddd60ef72ee5dd3d78335 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Tue, 23 Apr 2024 22:15:03 -0500 Subject: [PATCH 08/11] UGH this is painful --- CliWrap.FSharp.sln | 7 +++ .../CliWrap.FSharp.Tests.Dummy.fsproj | 17 ++++++ src/CliWrap.FSharp.Tests.Dummy/Program.fs | 22 +++++++ .../packages.lock.json | 19 +++++++ src/CliWrap.FSharp.Tests/CliTests.fs | 37 ++++++------ .../CliWrap.FSharp.Tests.fsproj | 2 +- .../CommandBuilderTests.fs | 57 +++++++++---------- src/CliWrap.FSharp.Tests/packages.lock.json | 17 ++---- 8 files changed, 118 insertions(+), 60 deletions(-) create mode 100644 src/CliWrap.FSharp.Tests.Dummy/CliWrap.FSharp.Tests.Dummy.fsproj create mode 100644 src/CliWrap.FSharp.Tests.Dummy/Program.fs create mode 100644 src/CliWrap.FSharp.Tests.Dummy/packages.lock.json diff --git a/CliWrap.FSharp.sln b/CliWrap.FSharp.sln index 230b569..7b2893e 100644 --- a/CliWrap.FSharp.sln +++ b/CliWrap.FSharp.sln @@ -44,6 +44,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{BC5059 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliWrap.Tests.Dummy", "src\CliWrap.Tests.Dummy\CliWrap.Tests.Dummy.csproj", "{26F29434-8760-4387-BF01-4FB1FA65426E}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "CliWrap.FSharp.Tests.Dummy", "src\CliWrap.FSharp.Tests.Dummy\CliWrap.FSharp.Tests.Dummy.fsproj", "{47479E30-792D-4B63-A1A9-D5981968D21E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -65,10 +67,15 @@ Global {26F29434-8760-4387-BF01-4FB1FA65426E}.Debug|Any CPU.Build.0 = Debug|Any CPU {26F29434-8760-4387-BF01-4FB1FA65426E}.Release|Any CPU.ActiveCfg = Release|Any CPU {26F29434-8760-4387-BF01-4FB1FA65426E}.Release|Any CPU.Build.0 = Release|Any CPU + {47479E30-792D-4B63-A1A9-D5981968D21E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47479E30-792D-4B63-A1A9-D5981968D21E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47479E30-792D-4B63-A1A9-D5981968D21E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47479E30-792D-4B63-A1A9-D5981968D21E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {2E2FBB43-30C0-45A1-8DAC-D7A48411EC7A} = {DE85DD40-1128-4648-A2EB-9A1FE73C28A4} {12BFD7AA-776D-4B58-8AC3-241AF39EED84} = {DE85DD40-1128-4648-A2EB-9A1FE73C28A4} {26F29434-8760-4387-BF01-4FB1FA65426E} = {DE85DD40-1128-4648-A2EB-9A1FE73C28A4} + {47479E30-792D-4B63-A1A9-D5981968D21E} = {DE85DD40-1128-4648-A2EB-9A1FE73C28A4} EndGlobalSection EndGlobal diff --git a/src/CliWrap.FSharp.Tests.Dummy/CliWrap.FSharp.Tests.Dummy.fsproj b/src/CliWrap.FSharp.Tests.Dummy/CliWrap.FSharp.Tests.Dummy.fsproj new file mode 100644 index 0000000..fb60bdd --- /dev/null +++ b/src/CliWrap.FSharp.Tests.Dummy/CliWrap.FSharp.Tests.Dummy.fsproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + UnMango.CliWrap.FSharp.Tests.Dummy + + + + + + + + + + + diff --git a/src/CliWrap.FSharp.Tests.Dummy/Program.fs b/src/CliWrap.FSharp.Tests.Dummy/Program.fs new file mode 100644 index 0000000..3297391 --- /dev/null +++ b/src/CliWrap.FSharp.Tests.Dummy/Program.fs @@ -0,0 +1,22 @@ +namespace UnMango.CliWrap.FSharp.Tests.Dummy + +open System.CommandLine +open System.IO +open System.Reflection +open System.Runtime.InteropServices + +type Program = + static member FilePath = + Path.ChangeExtension( + Assembly.GetExecutingAssembly().Location, + if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then + "exe" + else + null + ) + +module Program = + let command = RootCommand("Dummy program for testing") + + [] + let main args = command.Invoke args diff --git a/src/CliWrap.FSharp.Tests.Dummy/packages.lock.json b/src/CliWrap.FSharp.Tests.Dummy/packages.lock.json new file mode 100644 index 0000000..df7cc1b --- /dev/null +++ b/src/CliWrap.FSharp.Tests.Dummy/packages.lock.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "FSharp.Core": { + "type": "Direct", + "requested": "[8.0.200, )", + "resolved": "8.0.200", + "contentHash": "qnxoF3Fu0HzfOeYdrwmQOsLP1v+OtOMSIYkNVUwf6nGqWzL03Hh4r6VFCvCb54jlsgtt3WADVYkKkrgdeY5kiQ==" + }, + "System.CommandLine": { + "type": "Direct", + "requested": "[2.0.0-beta4.22272.1, )", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" + } + } + } +} \ No newline at end of file diff --git a/src/CliWrap.FSharp.Tests/CliTests.fs b/src/CliWrap.FSharp.Tests/CliTests.fs index 80068d8..5591d20 100644 --- a/src/CliWrap.FSharp.Tests/CliTests.fs +++ b/src/CliWrap.FSharp.Tests/CliTests.fs @@ -5,7 +5,6 @@ open System.Linq open System.Text open System.Threading open CliWrap -open CliWrap.Tests open Xunit open FsCheck open FsCheck.Xunit @@ -40,7 +39,7 @@ let ``Should create command the long way`` [] let ``Should configure a single argument`` (arg: NonNull) = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithArguments(arg.Get) let actual = cmd |> Cli.arg arg.Get @@ -49,7 +48,7 @@ let ``Should configure a single argument`` (arg: NonNull) = [] let ``Should configure arguments`` (arg: NonNull) = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithArguments([ arg.Get ]) let actual = cmd |> Cli.args [ arg.Get ] @@ -58,7 +57,7 @@ let ``Should configure arguments`` (arg: NonNull) = [] let ``Should configure arguments with escape`` (arg: NonNull) escape = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithArguments([ arg.Get ], escape) let actual = cmd |> Cli.argse [ arg.Get ] escape @@ -67,7 +66,7 @@ let ``Should configure arguments with escape`` (arg: NonNull) escape = [] let ``Should configure arguments with builder`` (arg: NonNull) = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithArguments(fun b -> b.Add(arg.Get) |> ignore) let actual = cmd |> Cli.argsf _.Add(arg.Get) @@ -76,7 +75,7 @@ let ``Should configure arguments with builder`` (arg: NonNull) = [] let ``Should configure credentials`` (arg: NonNull) = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithCredentials(Credentials(arg.Get)) let actual = cmd |> Cli.creds (Credentials(arg.Get)) @@ -85,7 +84,7 @@ let ``Should configure credentials`` (arg: NonNull) = [] let ``Should configure credentials with builder`` (arg: NonNull) = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithCredentials(fun b -> b.SetUserName(arg.Get) |> ignore) let actual = cmd |> Cli.credsf _.SetUserName(arg.Get) @@ -94,7 +93,7 @@ let ``Should configure credentials with builder`` (arg: NonNull) = [] let ``Should configure environment variables`` (key: NonNull) (value: NonNull) = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithEnvironmentVariables((dict [ key.Get, value.Get ]).AsReadOnly()) @@ -104,7 +103,7 @@ let ``Should configure environment variables`` (key: NonNull) (value: No [] let ``Should configure environment variables with builder`` (key: NonNull) (value: NonNull) = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithEnvironmentVariables(fun b -> b.Set(key.Get, value.Get) |> ignore) @@ -114,7 +113,7 @@ let ``Should configure environment variables with builder`` (key: NonNull] let ``Should execute asynchronously`` () = task { - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let! expected = cmd.ExecuteAsync() let! actual = cmd |> Cli.exec @@ -125,7 +124,7 @@ let ``Should execute asynchronously`` () = task { [] let ``Should execute asynchronously with cancellation`` () = task { use cts = new CancellationTokenSource() - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let! expected = cmd.ExecuteAsync(cts.Token) let! actual = cmd |> Cli.Task.exec cts.Token @@ -137,7 +136,7 @@ let ``Should execute asynchronously with cancellation`` () = task { let ``Should execute asynchronously with forceful and graceful tokens`` () = task { use forceful = new CancellationTokenSource() use graceful = new CancellationTokenSource() - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let! expected = cmd.ExecuteAsync(forceful.Token, graceful.Token) let! actual = cmd |> Cli.Task.execf forceful.Token graceful.Token @@ -148,7 +147,7 @@ let ``Should execute asynchronously with forceful and graceful tokens`` () = tas [] let ``Should configure stdin`` (value: NonNull) = let input = PipeSource.FromString value.Get - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithStandardInputPipe(input) let actual = cmd |> Cli.stdin input @@ -158,7 +157,7 @@ let ``Should configure stdin`` (value: NonNull) = [] let ``Should configure stdout`` () = let pipe = PipeTarget.ToStringBuilder(StringBuilder()) - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithStandardOutputPipe(pipe) let actual = cmd |> Cli.stdout pipe @@ -168,7 +167,7 @@ let ``Should configure stdout`` () = [] let ``Should configure stderr`` () = let pipe = PipeTarget.ToStringBuilder(StringBuilder()) - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithStandardErrorPipe(pipe) let actual = cmd |> Cli.stderr pipe @@ -177,7 +176,7 @@ let ``Should configure stderr`` () = [] let ``Should configure target file`` (file: NonNull) = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithTargetFile(file.Get) let actual = cmd |> Cli.target file.Get @@ -186,7 +185,7 @@ let ``Should configure target file`` (file: NonNull) = [] let ``Should configure validation`` () = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithValidation(CommandResultValidation.None) let actual = cmd |> Cli.validation CommandResultValidation.None @@ -195,7 +194,7 @@ let ``Should configure validation`` () = [] let ``Should configure working directory`` (dir: NonNull) = - let cmd = Command(Dummy.Program.FilePath) + let cmd = Command(Tests.Dummy.Program.FilePath) let expected = cmd.WithWorkingDirectory(dir.Get) let actual = cmd |> Cli.workDir dir.Get @@ -204,7 +203,7 @@ let ``Should configure working directory`` (dir: NonNull) = [] let ``Should convert to string`` (arg: NonNull) = - let cmd = Command(Dummy.Program.FilePath).WithArguments([ arg.Get ]) + let cmd = Command(Tests.Dummy.Program.FilePath).WithArguments([ arg.Get ]) let expected = cmd.ToString() let actual = cmd |> Cli.toString diff --git a/src/CliWrap.FSharp.Tests/CliWrap.FSharp.Tests.fsproj b/src/CliWrap.FSharp.Tests/CliWrap.FSharp.Tests.fsproj index bf019f5..8bb7c93 100644 --- a/src/CliWrap.FSharp.Tests/CliWrap.FSharp.Tests.fsproj +++ b/src/CliWrap.FSharp.Tests/CliWrap.FSharp.Tests.fsproj @@ -25,7 +25,7 @@ - + diff --git a/src/CliWrap.FSharp.Tests/CommandBuilderTests.fs b/src/CliWrap.FSharp.Tests/CommandBuilderTests.fs index 21aa4af..fd54eeb 100644 --- a/src/CliWrap.FSharp.Tests/CommandBuilderTests.fs +++ b/src/CliWrap.FSharp.Tests/CommandBuilderTests.fs @@ -7,7 +7,6 @@ open System.Text open System.Threading open CliWrap open CliWrap.Buffered -open CliWrap.Tests open Xunit open FsCheck open FsCheck.Xunit @@ -22,22 +21,22 @@ let ``Should configure target file path`` target = [] let ``Should configure args`` (a: NonNull list) = let input = a |> List.map _.Get - let expected = Command(Dummy.Program.FilePath).WithArguments(input) - let actual = command Dummy.Program.FilePath { args input } + let expected = Command(Tests.Dummy.Program.FilePath).WithArguments(input) + let actual = command Tests.Dummy.Program.FilePath { args input } actual.Arguments = expected.Arguments [] let ``Should configure string args`` (a: NonNull) = - let expected = Command(Dummy.Program.FilePath).WithArguments(a.Get) - let actual = command Dummy.Program.FilePath { args a.Get } + let expected = Command(Tests.Dummy.Program.FilePath).WithArguments(a.Get) + let actual = command Tests.Dummy.Program.FilePath { args a.Get } actual.Arguments = expected.Arguments [] let ``Should execute CommandTask asynchronously`` () = task { - let! expected = Command(Dummy.Program.FilePath).ExecuteAsync() + let! expected = Command(Tests.Dummy.Program.FilePath).ExecuteAsync() let! actual = - command Dummy.Program.FilePath { + command Tests.Dummy.Program.FilePath { exec async } @@ -48,38 +47,38 @@ let ``Should execute CommandTask asynchronously`` () = task { [] let ``Should execute asynchronously`` () = task { - let! expected = Command(Dummy.Program.FilePath).ExecuteAsync() - let! actual = command Dummy.Program.FilePath { async } |> Async.StartAsTask + let! expected = Command(Tests.Dummy.Program.FilePath).ExecuteAsync() + let! actual = command Tests.Dummy.Program.FilePath { async } |> Async.StartAsTask Assert.Equal(expected.ExitCode, actual.ExitCode) } [] let ``Should execute asynchronously with cancellation`` () = task { use cts = new CancellationTokenSource() - let! expected = Command(Dummy.Program.FilePath).ExecuteAsync(cts.Token) - let! actual = command Dummy.Program.FilePath { async cts.Token } |> Async.StartAsTask + let! expected = Command(Tests.Dummy.Program.FilePath).ExecuteAsync(cts.Token) + let! actual = command Tests.Dummy.Program.FilePath { async cts.Token } |> Async.StartAsTask Assert.Equal(expected.ExitCode, actual.ExitCode) } [] let ``Should execute asynchronously as task`` () = task { - let! expected = Command(Dummy.Program.FilePath).ExecuteAsync() - let! actual = command Dummy.Program.FilePath { exec } + let! expected = Command(Tests.Dummy.Program.FilePath).ExecuteAsync() + let! actual = command Tests.Dummy.Program.FilePath { exec } Assert.Equal(expected.ExitCode, actual.ExitCode) } [] let ``Should execute buffered`` () = task { - let! expected = Command(Dummy.Program.FilePath).ExecuteBufferedAsync() - let! actual = command Dummy.Program.FilePath { buffered } + let! expected = Command(Tests.Dummy.Program.FilePath).ExecuteBufferedAsync() + let! actual = command Tests.Dummy.Program.FilePath { buffered } Assert.Equal(expected.ExitCode, actual.ExitCode) } [] let ``Should execute buffered with encoding`` () = task { let encoding = Encoding.UTF8 - let! expected = Command(Dummy.Program.FilePath).ExecuteBufferedAsync(encoding) - let! actual = command Dummy.Program.FilePath { buffered encoding } + let! expected = Command(Tests.Dummy.Program.FilePath).ExecuteBufferedAsync(encoding) + let! actual = command Tests.Dummy.Program.FilePath { buffered encoding } Assert.Equal(expected.ExitCode, actual.ExitCode) } @@ -87,26 +86,26 @@ let ``Should execute buffered with encoding`` () = task { let ``Should execute buffered with encoding and cancellation`` () = task { let encoding = Encoding.UTF8 use cts = new CancellationTokenSource() - let! expected = Command(Dummy.Program.FilePath).ExecuteBufferedAsync(encoding, cts.Token) - let! actual = command Dummy.Program.FilePath { buffered encoding cts.Token } + let! expected = Command(Tests.Dummy.Program.FilePath).ExecuteBufferedAsync(encoding, cts.Token) + let! actual = command Tests.Dummy.Program.FilePath { buffered encoding cts.Token } Assert.Equal(expected.ExitCode, actual.ExitCode) } [] let ``Should configure environment variables`` var = let expected = - Command(Dummy.Program.FilePath) + Command(Tests.Dummy.Program.FilePath) .WithEnvironmentVariables((dict [ var ]).AsReadOnly()) - let actual = command Dummy.Program.FilePath { env [ var ] } + let actual = command Tests.Dummy.Program.FilePath { env [ var ] } actual.EnvironmentVariables.SequenceEqual(expected.EnvironmentVariables) [] let ``Should configure stdin`` () = task { let input = PipeSource.FromString "testing" - let expected = Command(Dummy.Program.FilePath).WithStandardInputPipe(input) + let expected = Command(Tests.Dummy.Program.FilePath).WithStandardInputPipe(input) - let actual = command Dummy.Program.FilePath { stdin input } + let actual = command Tests.Dummy.Program.FilePath { stdin input } let a, b = new MemoryStream(), new MemoryStream() do! expected.StandardInputPipe.CopyToAsync(a) @@ -120,12 +119,12 @@ let ``Should configure stdout`` () = task { let a, b = StringBuilder(), StringBuilder() let! _ = - Command(Dummy.Program.FilePath) + Command(Tests.Dummy.Program.FilePath) .WithArguments([ "generate binary"; "--target"; "all" ]) .WithStandardOutputPipe(PipeTarget.ToStringBuilder(a)) .ExecuteAsync() - let! _ = command Dummy.Program.FilePath { + let! _ = command Tests.Dummy.Program.FilePath { args [ "generate binary"; "--target"; "all" ] stdout (PipeTarget.ToStringBuilder(b)) exec @@ -139,12 +138,12 @@ let ``Should configure stderr`` () = task { let a, b = StringBuilder(), StringBuilder() let! _ = - Command(Dummy.Program.FilePath) + Command(Tests.Dummy.Program.FilePath) .WithArguments([ "generate binary"; "--target"; "all" ]) .WithStandardErrorPipe(PipeTarget.ToStringBuilder(a)) .ExecuteAsync() - let! _ = command Dummy.Program.FilePath { + let! _ = command Tests.Dummy.Program.FilePath { args [ "generate binary"; "--target"; "all" ] validation CommandResultValidation.None stderr (PipeTarget.ToStringBuilder(b)) @@ -156,6 +155,6 @@ let ``Should configure stderr`` () = task { [] let ``Should configure working directory`` directory = - let expected = Command(Dummy.Program.FilePath).WithWorkingDirectory(directory) - let actual = command Dummy.Program.FilePath { workingDirectory directory } + let expected = Command(Tests.Dummy.Program.FilePath).WithWorkingDirectory(directory) + let actual = command Tests.Dummy.Program.FilePath { workingDirectory directory } actual.WorkingDirPath = expected.WorkingDirPath diff --git a/src/CliWrap.FSharp.Tests/packages.lock.json b/src/CliWrap.FSharp.Tests/packages.lock.json index f6e1762..217dd6c 100644 --- a/src/CliWrap.FSharp.Tests/packages.lock.json +++ b/src/CliWrap.FSharp.Tests/packages.lock.json @@ -60,11 +60,6 @@ "resolved": "2.5.7", "contentHash": "31Rl7dBJriX0DNwZfDp8gqFOPsiM0c9kqpcH/HvNi9vDp+K7Ydf42H7mVIvYT918Ywzn1ymLg1c4DDC6iU754w==" }, - "CliFx": { - "type": "Transitive", - "resolved": "2.3.5", - "contentHash": "WaZDt7FC1ViEzS5m7w7lOWT/aluZ28fFkPcRdkQ+7DowqjTmr5YZswNIDqSuBDaeGXPe8VzTZKMltKrXhIOt9Q==" - }, "CliWrap": { "type": "Transitive", "resolved": "3.6.6", @@ -97,10 +92,10 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "System.Memory": { + "System.CommandLine": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" }, "System.Reflection.Metadata": { "type": "Transitive", @@ -147,11 +142,11 @@ "xunit.extensibility.core": "[2.7.0]" } }, - "cliwrap.tests.dummy": { + "cliwrap.fsharp.tests.dummy": { "type": "Project", "dependencies": { - "CliFx": "[2.3.5, )", - "System.Memory": "[4.5.5, )" + "FSharp.Core": "[8.0.200, )", + "System.CommandLine": "[2.0.0-beta4.22272.1, )" } }, "UnMango.CliWrap.FSharp": { From 1039f024d2cf766e1d4b5d4db64ddce1ebc42ad0 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sun, 2 Jun 2024 13:09:12 -0500 Subject: [PATCH 09/11] Update lockfiles --- src/CliWrap.FSharp.Tests.Dummy/packages.lock.json | 6 +++--- src/CliWrap.FSharp.Tests/packages.lock.json | 10 +++++----- src/CliWrap.FSharp/packages.lock.json | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/CliWrap.FSharp.Tests.Dummy/packages.lock.json b/src/CliWrap.FSharp.Tests.Dummy/packages.lock.json index df7cc1b..f9c2be0 100644 --- a/src/CliWrap.FSharp.Tests.Dummy/packages.lock.json +++ b/src/CliWrap.FSharp.Tests.Dummy/packages.lock.json @@ -4,9 +4,9 @@ "net8.0": { "FSharp.Core": { "type": "Direct", - "requested": "[8.0.200, )", - "resolved": "8.0.200", - "contentHash": "qnxoF3Fu0HzfOeYdrwmQOsLP1v+OtOMSIYkNVUwf6nGqWzL03Hh4r6VFCvCb54jlsgtt3WADVYkKkrgdeY5kiQ==" + "requested": "[8.0.300, )", + "resolved": "8.0.300", + "contentHash": "Jv44fV7TNglyMku89lQcA4Q6mFKLyHb2bs1Yb72nvSVc+cHplEnoZ4XQUaaTLJGUTx/iMqcrkYGtaLzkkIhpaA==" }, "System.CommandLine": { "type": "Direct", diff --git a/src/CliWrap.FSharp.Tests/packages.lock.json b/src/CliWrap.FSharp.Tests/packages.lock.json index 217dd6c..3e80df5 100644 --- a/src/CliWrap.FSharp.Tests/packages.lock.json +++ b/src/CliWrap.FSharp.Tests/packages.lock.json @@ -29,9 +29,9 @@ }, "FSharp.Core": { "type": "Direct", - "requested": "[8.0.200, )", - "resolved": "8.0.200", - "contentHash": "qnxoF3Fu0HzfOeYdrwmQOsLP1v+OtOMSIYkNVUwf6nGqWzL03Hh4r6VFCvCb54jlsgtt3WADVYkKkrgdeY5kiQ==" + "requested": "[8.0.300, )", + "resolved": "8.0.300", + "contentHash": "Jv44fV7TNglyMku89lQcA4Q6mFKLyHb2bs1Yb72nvSVc+cHplEnoZ4XQUaaTLJGUTx/iMqcrkYGtaLzkkIhpaA==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", @@ -145,7 +145,7 @@ "cliwrap.fsharp.tests.dummy": { "type": "Project", "dependencies": { - "FSharp.Core": "[8.0.200, )", + "FSharp.Core": "[8.0.300, )", "System.CommandLine": "[2.0.0-beta4.22272.1, )" } }, @@ -153,7 +153,7 @@ "type": "Project", "dependencies": { "CliWrap": "[3.6.6, )", - "FSharp.Core": "[8.0.200, )" + "FSharp.Core": "[8.0.300, )" } } } diff --git a/src/CliWrap.FSharp/packages.lock.json b/src/CliWrap.FSharp/packages.lock.json index 3d666e3..78ad91a 100644 --- a/src/CliWrap.FSharp/packages.lock.json +++ b/src/CliWrap.FSharp/packages.lock.json @@ -10,9 +10,9 @@ }, "FSharp.Core": { "type": "Direct", - "requested": "[8.0.200, )", - "resolved": "8.0.200", - "contentHash": "qnxoF3Fu0HzfOeYdrwmQOsLP1v+OtOMSIYkNVUwf6nGqWzL03Hh4r6VFCvCb54jlsgtt3WADVYkKkrgdeY5kiQ==" + "requested": "[8.0.300, )", + "resolved": "8.0.300", + "contentHash": "Jv44fV7TNglyMku89lQcA4Q6mFKLyHb2bs1Yb72nvSVc+cHplEnoZ4XQUaaTLJGUTx/iMqcrkYGtaLzkkIhpaA==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", From 98ef4cde9f0463ae16af577cdf8510ab5abbc833 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sun, 2 Jun 2024 14:42:32 -0500 Subject: [PATCH 10/11] Implement binary generation in the dummy --- src/CliWrap.FSharp.Tests.Dummy/Program.fs | 61 ++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/CliWrap.FSharp.Tests.Dummy/Program.fs b/src/CliWrap.FSharp.Tests.Dummy/Program.fs index 3297391..32c4fa0 100644 --- a/src/CliWrap.FSharp.Tests.Dummy/Program.fs +++ b/src/CliWrap.FSharp.Tests.Dummy/Program.fs @@ -1,9 +1,68 @@ namespace UnMango.CliWrap.FSharp.Tests.Dummy +open System +open System.Buffers open System.CommandLine +open System.CommandLine.Invocation open System.IO open System.Reflection open System.Runtime.InteropServices +open System.Threading.Tasks + +module Opt = + let add o (r: Command) = + r.AddOption(o) + r + +module Cmd = + let add c (r: Command) = + r.AddCommand(c) + r + + let handler (f: InvocationContext -> Task) (c: Command) = + c.SetHandler(f) + c + +module Commands = + let streams = + function + | "stdout" -> [ Console.OpenStandardOutput() ] + | "stderr" -> [ Console.OpenStandardError() ] + | "all" -> [ Console.OpenStandardOutput(); Console.OpenStandardError() ] + | _ -> failwith "unsupported target" + + let rec generateBytes (rand: Random) (buf: IMemoryOwner) (streams: Stream list) len = + function + | total when total >= len -> () + | total -> + rand.NextBytes(buf.Memory.Span) + let wanted = Math.Min(int64 buf.Memory.Length, len - total) + + for stream in streams do + stream.Write(buf.Memory.Slice(0, int wanted).Span) + + generateBytes rand buf streams len (total + wanted) + + let generate = + let targetOpt = Option("--target", (fun () -> "stdout")) + let lengthOpt = Option("--length", (fun () -> 100_000L)) + let bufferOpt = Option("--buffer", (fun () -> 1024)) + let rand = Random(1234567) + + Command("generate") + |> Cmd.add ( + Command("binary") + |> Opt.add targetOpt + |> Opt.add lengthOpt + |> Opt.add bufferOpt + |> Cmd.handler (fun c -> task { + let streams = c.ParseResult.GetValueForOption(targetOpt) |> streams + let len = c.ParseResult.GetValueForOption(lengthOpt) + let size = c.ParseResult.GetValueForOption(bufferOpt) + use buf = MemoryPool.Shared.Rent(size) + generateBytes rand buf streams len 0L + }) + ) type Program = static member FilePath = @@ -16,7 +75,7 @@ type Program = ) module Program = - let command = RootCommand("Dummy program for testing") + let command = RootCommand("Dummy program for testing") |> Cmd.add Commands.generate [] let main args = command.Invoke args From 0dced79ec18d88c7f054b9f2fb11db2a6006ff6c Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Sun, 2 Jun 2024 14:42:51 -0500 Subject: [PATCH 11/11] Test commands as multiple args --- src/CliWrap.FSharp.Tests/CommandBuilderTests.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/CliWrap.FSharp.Tests/CommandBuilderTests.fs b/src/CliWrap.FSharp.Tests/CommandBuilderTests.fs index fd54eeb..7e9289f 100644 --- a/src/CliWrap.FSharp.Tests/CommandBuilderTests.fs +++ b/src/CliWrap.FSharp.Tests/CommandBuilderTests.fs @@ -120,12 +120,12 @@ let ``Should configure stdout`` () = task { let! _ = Command(Tests.Dummy.Program.FilePath) - .WithArguments([ "generate binary"; "--target"; "all" ]) + .WithArguments([ "generate"; "binary"; "--target"; "all" ]) .WithStandardOutputPipe(PipeTarget.ToStringBuilder(a)) .ExecuteAsync() let! _ = command Tests.Dummy.Program.FilePath { - args [ "generate binary"; "--target"; "all" ] + args [ "generate"; "binary"; "--target"; "all" ] stdout (PipeTarget.ToStringBuilder(b)) exec } @@ -139,12 +139,12 @@ let ``Should configure stderr`` () = task { let! _ = Command(Tests.Dummy.Program.FilePath) - .WithArguments([ "generate binary"; "--target"; "all" ]) + .WithArguments([ "generate"; "binary"; "--target"; "all" ]) .WithStandardErrorPipe(PipeTarget.ToStringBuilder(a)) .ExecuteAsync() let! _ = command Tests.Dummy.Program.FilePath { - args [ "generate binary"; "--target"; "all" ] + args [ "generate"; "binary"; "--target"; "all" ] validation CommandResultValidation.None stderr (PipeTarget.ToStringBuilder(b)) exec