Skip to content

Commit 63574fe

Browse files
authored
reproduce/quoted (#17)
* Attempt to reproduce #15, handling of quoted strings * move back to just testing net8.0
1 parent 69ed5c2 commit 63574fe

File tree

10 files changed

+95
-11
lines changed

10 files changed

+95
-11
lines changed

src/Proc/BufferedObservableProcess.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,14 @@ private IDisposable KickOff(IObserver<CharactersOut> observer)
7474

7575
if (Process.HasExited)
7676
{
77+
Process.ReadStandardErrBlocking(_observer, BufferSize, () => ContinueReadingFromProcessReaders());
78+
Process.ReadStandardOutBlocking(_observer, BufferSize, () => ContinueReadingFromProcessReaders());
7779
OnExit(observer);
7880
return Disposable.Empty;
7981
}
8082

8183
_observer = observer;
84+
8285
StartAsyncReads();
8386

8487
Process.Exited += (o, s) =>

src/Proc/Extensions/ArgumentExtensions.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
using System.Collections;
21
using System.Collections.Generic;
32
using System.Linq;
4-
using System.Text;
53

64
namespace ProcNet.Extensions;
75

src/Proc/Extensions/ObserveOutputExtensions.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,26 @@ private static async Task BufferedRead(Process p, StreamReader r, IObserver<Char
7070
token.ThrowIfCancellationRequested();
7171
}
7272

73+
public static void ReadStandardErrBlocking(this Process process, IObserver<CharactersOut> observer, int bufferSize, Func<bool> keepBuffering) =>
74+
BufferedReadBlocking(process, process.StandardError, observer, bufferSize, ConsoleOut.ErrorOut, keepBuffering);
75+
76+
public static void ReadStandardOutBlocking(this Process process, IObserver<CharactersOut> observer, int bufferSize, Func<bool> keepBuffering) =>
77+
BufferedReadBlocking(process, process.StandardOutput, observer, bufferSize, ConsoleOut.Out, keepBuffering);
78+
79+
private static void BufferedReadBlocking(this Process p, StreamReader r, IObserver<CharactersOut> o, int b, Func<char[], CharactersOut> m, Func<bool> keepBuffering)
80+
{
81+
using var sr = new StreamReader(r.BaseStream, Encoding.UTF8, true, b, true);
82+
while (keepBuffering())
83+
{
84+
var buffer = new char[b];
85+
var read = sr.Read(buffer, 0, buffer.Length);
86+
87+
if (read > 0)
88+
o.OnNext(m(buffer));
89+
else
90+
if (sr.EndOfStream) break;
91+
}
92+
}
93+
7394
}
7495
}

src/Proc/ObservableProcess.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public virtual IDisposable SubscribeLinesAndCharacters(
144144
Action<LineOut> onNext, Action<Exception> onError,
145145
Action<CharactersOut> onNextCharacters,
146146
Action<Exception> onExceptionCharacters,
147-
Action? onCompleted = null
147+
Action onCompleted = null
148148
) =>
149149
Subscribe(
150150
Observer.Create(onNext, onError, onCompleted ?? delegate { }),

src/Proc/Proc.Exec.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,13 @@ public static partial class Proc
5151
var info = new ProcessStartInfo(arguments.Binary)
5252
{
5353
UseShellExecute = false
54-
#if !NETSTANDARD2_1
55-
, Arguments = args
56-
#endif
5754
};
58-
#if NETSTANDARD2_1
55+
#if NETSTANDARD2_1
5956
foreach (var arg in arguments.Args)
6057
info.ArgumentList.Add(arg);
61-
#endif
58+
#else
59+
info.Arguments = args;
60+
#endif
6261

6362
var pwd = arguments.WorkingDirectory;
6463
if (!string.IsNullOrWhiteSpace(pwd)) info.WorkingDirectory = pwd;
@@ -93,6 +92,7 @@ public static partial class Proc
9392

9493
return exitCode;
9594
}
95+
9696
private static void HardWaitForExit(Process process, TimeSpan timeSpan)
9797
{
9898
using var task = Task.Run(() => process.WaitForExit());

tests/Proc.Tests.Binary/Program.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Globalization;
3+
using System.Linq;
34
using System.Net.Sockets;
45
using System.Threading;
56
using System.Threading.Tasks;
@@ -18,6 +19,7 @@ public static async Task<int> Main(string[] args)
1819

1920
var testCase = args[0].ToLowerInvariant();
2021

22+
if (testCase == nameof(PrintArgs).ToLowerInvariant()) return PrintArgs(args.Skip(1).ToArray());
2123
if (testCase == nameof(SingleLineNoEnter).ToLowerInvariant()) return SingleLineNoEnter();
2224
if (testCase == nameof(TwoWrites).ToLowerInvariant()) return TwoWrites();
2325

@@ -41,6 +43,12 @@ public static async Task<int> Main(string[] args)
4143

4244
return 1;
4345
}
46+
private static int PrintArgs(string[] args)
47+
{
48+
foreach (var arg in args)
49+
Console.WriteLine(arg);
50+
return 0;
51+
}
4452
private static int DelayedWriter()
4553
{
4654
Thread.Sleep(3000);

tests/Proc.Tests/PrintArgsTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using FluentAssertions;
2+
using Xunit;
3+
using Xunit.Abstractions;
4+
5+
namespace ProcNet.Tests;
6+
7+
public class PrintArgsTests(ITestOutputHelper output) : TestsBase
8+
{
9+
[Fact]
10+
public void ProcSendsAllArguments()
11+
{
12+
string[] testArgs = ["hello", "world"];
13+
AssertOutput(testArgs);
14+
}
15+
16+
[Fact]
17+
public void ArgumentsWithSpaceAreNotSplit()
18+
{
19+
string[] testArgs = ["hello", "world", "this argument has spaces"];
20+
AssertOutput(testArgs);
21+
}
22+
23+
[Fact]
24+
public void ArgumentsSeesArgumentsAfterQuoted()
25+
{
26+
string[] testArgs = ["this argument has spaces", "hello", "world"];
27+
AssertOutput(testArgs);
28+
}
29+
[Fact]
30+
public void EscapedQuotes()
31+
{
32+
string[] testArgs = ["\"this argument has spaces\"", "hello", "world"];
33+
AssertOutput(testArgs);
34+
}
35+
36+
private void AssertOutput(string[] testArgs)
37+
{
38+
var args = TestCaseArguments("PrintArgs", testArgs);
39+
var outputWriter = new TestConsoleOutWriter(output);
40+
var result = Proc.Start(args, WaitTimeout, outputWriter);
41+
result.ExitCode.Should().Be(0);
42+
result.ConsoleOut.Should().NotBeEmpty().And.HaveCount(testArgs.Length);
43+
for (var i = 0; i < result.ConsoleOut.Count; i++)
44+
result.ConsoleOut[i].Line.Should().Be(testArgs[i], i.ToString());
45+
}
46+
47+
}

tests/Proc.Tests/Proc.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<AssemblyName>Proc.Tests</AssemblyName>
55
<RootNamespace>ProcNet.Tests</RootNamespace>
66
<IsPackable>false</IsPackable>
7+
<LangVersion>preview</LangVersion>
78
</PropertyGroup>
89
<ItemGroup>
910
<ProjectReference Include="..\..\src\Proc\Proc.csproj" />

tests/Proc.Tests/TestConsoleOutWriter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ public class TestConsoleOutWriter(ITestOutputHelper output) : IConsoleOutWriter
88
private readonly StringBuilder _sb = new();
99
public string[] Lines => _sb.ToString().Replace("\r\n", "\n").Split(new [] {"\n"}, StringSplitOptions.None);
1010
public string Text => _sb.ToString();
11+
private static char[] NewLineChars = Environment.NewLine.ToCharArray();
1112

1213
public void Write(Exception e) => throw e;
1314

1415
public void Write(ConsoleOut consoleOut)
1516
{
1617
consoleOut.CharsOrString(c => _sb.Append(new string(c)), s => _sb.AppendLine(s));
17-
consoleOut.CharsOrString(c => output.WriteLine(new string(c)), output.WriteLine);
18+
consoleOut.CharsOrString(c => output.WriteLine(new string(c).TrimEnd(NewLineChars)), s => output.WriteLine(s));
1819
}
1920
}

tests/Proc.Tests/TestsBase.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using System.Linq;
34
using System.Reflection;
45

56
namespace ProcNet.Tests
@@ -25,11 +26,15 @@ private static string GetWorkingDir()
2526
return binaryFolder;
2627
}
2728

28-
protected static StartArguments TestCaseArguments(string testcase) =>
29-
new("dotnet", GetDll(), testcase)
29+
protected static StartArguments TestCaseArguments(string testcase, params string[] args)
30+
{
31+
string[] arguments = [GetDll(), testcase];
32+
33+
return new StartArguments("dotnet", arguments.Concat(args))
3034
{
3135
WorkingDirectory = GetWorkingDir(),
3236
};
37+
}
3338

3439
protected static LongRunningArguments LongRunningTestCaseArguments(string testcase) =>
3540
new("dotnet", GetDll(), testcase)

0 commit comments

Comments
 (0)