Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reproduce/quoted #17

Merged
merged 2 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Proc/BufferedObservableProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,14 @@ private IDisposable KickOff(IObserver<CharactersOut> observer)

if (Process.HasExited)
{
Process.ReadStandardErrBlocking(_observer, BufferSize, () => ContinueReadingFromProcessReaders());
Process.ReadStandardOutBlocking(_observer, BufferSize, () => ContinueReadingFromProcessReaders());
OnExit(observer);
return Disposable.Empty;
}

_observer = observer;

StartAsyncReads();

Process.Exited += (o, s) =>
Expand Down
2 changes: 0 additions & 2 deletions src/Proc/Extensions/ArgumentExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ProcNet.Extensions;

Expand Down
21 changes: 21 additions & 0 deletions src/Proc/Extensions/ObserveOutputExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,26 @@ private static async Task BufferedRead(Process p, StreamReader r, IObserver<Char
token.ThrowIfCancellationRequested();
}

public static void ReadStandardErrBlocking(this Process process, IObserver<CharactersOut> observer, int bufferSize, Func<bool> keepBuffering) =>
BufferedReadBlocking(process, process.StandardError, observer, bufferSize, ConsoleOut.ErrorOut, keepBuffering);

public static void ReadStandardOutBlocking(this Process process, IObserver<CharactersOut> observer, int bufferSize, Func<bool> keepBuffering) =>
BufferedReadBlocking(process, process.StandardOutput, observer, bufferSize, ConsoleOut.Out, keepBuffering);

private static void BufferedReadBlocking(this Process p, StreamReader r, IObserver<CharactersOut> o, int b, Func<char[], CharactersOut> m, Func<bool> keepBuffering)
{
using var sr = new StreamReader(r.BaseStream, Encoding.UTF8, true, b, true);
while (keepBuffering())
{
var buffer = new char[b];
var read = sr.Read(buffer, 0, buffer.Length);

if (read > 0)
o.OnNext(m(buffer));
else
if (sr.EndOfStream) break;
}
}

}
}
2 changes: 1 addition & 1 deletion src/Proc/ObservableProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public virtual IDisposable SubscribeLinesAndCharacters(
Action<LineOut> onNext, Action<Exception> onError,
Action<CharactersOut> onNextCharacters,
Action<Exception> onExceptionCharacters,
Action? onCompleted = null
Action onCompleted = null
) =>
Subscribe(
Observer.Create(onNext, onError, onCompleted ?? delegate { }),
Expand Down
10 changes: 5 additions & 5 deletions src/Proc/Proc.Exec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,13 @@ public static partial class Proc
var info = new ProcessStartInfo(arguments.Binary)
{
UseShellExecute = false
#if !NETSTANDARD2_1
, Arguments = args
#endif
};
#if NETSTANDARD2_1
#if NETSTANDARD2_1
foreach (var arg in arguments.Args)
info.ArgumentList.Add(arg);
#endif
#else
info.Arguments = args;
#endif

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

return exitCode;
}

private static void HardWaitForExit(Process process, TimeSpan timeSpan)
{
using var task = Task.Run(() => process.WaitForExit());
Expand Down
8 changes: 8 additions & 0 deletions tests/Proc.Tests.Binary/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -18,6 +19,7 @@ public static async Task<int> Main(string[] args)

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

if (testCase == nameof(PrintArgs).ToLowerInvariant()) return PrintArgs(args.Skip(1).ToArray());
if (testCase == nameof(SingleLineNoEnter).ToLowerInvariant()) return SingleLineNoEnter();
if (testCase == nameof(TwoWrites).ToLowerInvariant()) return TwoWrites();

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

return 1;
}
private static int PrintArgs(string[] args)
{
foreach (var arg in args)
Console.WriteLine(arg);
return 0;
}
private static int DelayedWriter()
{
Thread.Sleep(3000);
Expand Down
47 changes: 47 additions & 0 deletions tests/Proc.Tests/PrintArgsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using FluentAssertions;
using Xunit;
using Xunit.Abstractions;

namespace ProcNet.Tests;

public class PrintArgsTests(ITestOutputHelper output) : TestsBase
{
[Fact]
public void ProcSendsAllArguments()
{
string[] testArgs = ["hello", "world"];
AssertOutput(testArgs);
}

[Fact]
public void ArgumentsWithSpaceAreNotSplit()
{
string[] testArgs = ["hello", "world", "this argument has spaces"];
AssertOutput(testArgs);
}

[Fact]
public void ArgumentsSeesArgumentsAfterQuoted()
{
string[] testArgs = ["this argument has spaces", "hello", "world"];
AssertOutput(testArgs);
}
[Fact]
public void EscapedQuotes()
{
string[] testArgs = ["\"this argument has spaces\"", "hello", "world"];
AssertOutput(testArgs);
}

private void AssertOutput(string[] testArgs)
{
var args = TestCaseArguments("PrintArgs", testArgs);
var outputWriter = new TestConsoleOutWriter(output);
var result = Proc.Start(args, WaitTimeout, outputWriter);
result.ExitCode.Should().Be(0);
result.ConsoleOut.Should().NotBeEmpty().And.HaveCount(testArgs.Length);
for (var i = 0; i < result.ConsoleOut.Count; i++)
result.ConsoleOut[i].Line.Should().Be(testArgs[i], i.ToString());
}

}
1 change: 1 addition & 0 deletions tests/Proc.Tests/Proc.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<AssemblyName>Proc.Tests</AssemblyName>
<RootNamespace>ProcNet.Tests</RootNamespace>
<IsPackable>false</IsPackable>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Proc\Proc.csproj" />
Expand Down
3 changes: 2 additions & 1 deletion tests/Proc.Tests/TestConsoleOutWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ public class TestConsoleOutWriter(ITestOutputHelper output) : IConsoleOutWriter
private readonly StringBuilder _sb = new();
public string[] Lines => _sb.ToString().Replace("\r\n", "\n").Split(new [] {"\n"}, StringSplitOptions.None);
public string Text => _sb.ToString();
private static char[] NewLineChars = Environment.NewLine.ToCharArray();

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

public void Write(ConsoleOut consoleOut)
{
consoleOut.CharsOrString(c => _sb.Append(new string(c)), s => _sb.AppendLine(s));
consoleOut.CharsOrString(c => output.WriteLine(new string(c)), output.WriteLine);
consoleOut.CharsOrString(c => output.WriteLine(new string(c).TrimEnd(NewLineChars)), s => output.WriteLine(s));
}
}
9 changes: 7 additions & 2 deletions tests/Proc.Tests/TestsBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace ProcNet.Tests
Expand All @@ -25,11 +26,15 @@ private static string GetWorkingDir()
return binaryFolder;
}

protected static StartArguments TestCaseArguments(string testcase) =>
new("dotnet", GetDll(), testcase)
protected static StartArguments TestCaseArguments(string testcase, params string[] args)
{
string[] arguments = [GetDll(), testcase];

return new StartArguments("dotnet", arguments.Concat(args))
{
WorkingDirectory = GetWorkingDir(),
};
}

protected static LongRunningArguments LongRunningTestCaseArguments(string testcase) =>
new("dotnet", GetDll(), testcase)
Expand Down
Loading