Skip to content

Commit

Permalink
Merge pull request #279 from adamralph/encoding
Browse files Browse the repository at this point in the history
Support preferred encoding when reading commands
  • Loading branch information
adamralph committed Mar 13, 2021
2 parents 1498157 + 8e83d59 commit 70c4481
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 10 deletions.
17 changes: 10 additions & 7 deletions SimpleExec/Command.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -35,7 +36,7 @@ public static void Run(string name, string args = null, string workingDirectory

using (var process = new Process())
{
process.StartInfo = ProcessStartInfo.Create(name, args, workingDirectory, false, windowsName, windowsArgs, configureEnvironment, createNoWindow);
process.StartInfo = ProcessStartInfo.Create(name, args, workingDirectory, false, windowsName, windowsArgs, configureEnvironment, createNoWindow, null);
process.Run(noEcho, echoPrefix ?? DefaultPrefix.Value);

if (process.ExitCode != 0)
Expand Down Expand Up @@ -71,7 +72,7 @@ public static async Task RunAsync(string name, string args = null, string workin

using (var process = new Process())
{
process.StartInfo = ProcessStartInfo.Create(name, args, workingDirectory, false, windowsName, windowsArgs, configureEnvironment, createNoWindow);
process.StartInfo = ProcessStartInfo.Create(name, args, workingDirectory, false, windowsName, windowsArgs, configureEnvironment, createNoWindow, null);
await process.RunAsync(noEcho, echoPrefix ?? DefaultPrefix.Value, cancellationToken).ConfigureAwait(false);

if (process.ExitCode != 0)
Expand All @@ -94,6 +95,7 @@ public static async Task RunAsync(string name, string args = null, string workin
/// <param name="echoPrefix">The prefix to use when echoing the command line and working directory (if specified) to standard error (stderr).</param>
/// <param name="configureEnvironment">An action which configures environment variables for the command.</param>
/// <param name="createNoWindow">Whether to run the command in a new window.</param>
/// <param name="encoding">The preferred <see cref="Encoding"/> for standard output (stdout).</param>
/// <returns>A <see cref="string"/> representing the contents of standard output (stdout).</returns>
/// <exception cref="NonZeroExitCodeException">The command exited with non-zero exit code.</exception>
/// <remarks>
Expand All @@ -102,15 +104,15 @@ public static async Task RunAsync(string name, string args = null, string workin
///
/// This method uses <see cref="Task.WaitAll(Task[])" /> and <see cref="System.Runtime.CompilerServices.TaskAwaiter.GetResult()"/>.
/// This should be fine in most contexts, such as console apps, but in some contexts, such as a UI or ASP.NET, it may deadlock.
/// In those contexts, <see cref="ReadAsync(string, string, string, bool, string, string, string, Action{IDictionary{string, string}}, bool, CancellationToken)" /> should be used instead.
/// In those contexts, <see cref="ReadAsync(string, string, string, bool, string, string, string, Action{IDictionary{string, string}}, bool, Encoding, CancellationToken)" /> should be used instead.
/// </remarks>
public static string Read(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, Action<IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false)
public static string Read(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, Action<IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false, Encoding encoding = null)
{
Validate(name);

using (var process = new Process())
{
process.StartInfo = ProcessStartInfo.Create(name, args, workingDirectory, true, windowsName, windowsArgs, configureEnvironment, createNoWindow);
process.StartInfo = ProcessStartInfo.Create(name, args, workingDirectory, true, windowsName, windowsArgs, configureEnvironment, createNoWindow, encoding);

var runProcess = process.RunAsync(noEcho, echoPrefix ?? DefaultPrefix.Value, CancellationToken.None);

Expand Down Expand Up @@ -149,6 +151,7 @@ public static string Read(string name, string args = null, string workingDirecto
/// <param name="echoPrefix">The prefix to use when echoing the command line and working directory (if specified) to standard error (stderr).</param>
/// <param name="configureEnvironment">An action which configures environment variables for the command.</param>
/// <param name="createNoWindow">Whether to run the command in a new window.</param>
/// <param name="encoding">The preferred <see cref="Encoding"/> for standard output (stdout).</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the command to exit.</param>
/// <returns>
/// A <see cref="Task{TResult}"/> representing the asynchronous running of the command and reading of standard output (stdout).
Expand All @@ -159,13 +162,13 @@ public static string Read(string name, string args = null, string workingDirecto
/// By default, the resulting command line and the working directory (if specified) are echoed to standard error (stderr).
/// To suppress this behavior, provide the <paramref name="noEcho"/> parameter with a value of <c>true</c>.
/// </remarks>
public static async Task<string> ReadAsync(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, Action<IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false, CancellationToken cancellationToken = default)
public static async Task<string> ReadAsync(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, Action<IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false, Encoding encoding = null, CancellationToken cancellationToken = default)
{
Validate(name);

using (var process = new Process())
{
process.StartInfo = ProcessStartInfo.Create(name, args, workingDirectory, true, windowsName, windowsArgs, configureEnvironment, createNoWindow);
process.StartInfo = ProcessStartInfo.Create(name, args, workingDirectory, true, windowsName, windowsArgs, configureEnvironment, createNoWindow, encoding);

var runProcess = process.RunAsync(noEcho, echoPrefix ?? DefaultPrefix.Value, cancellationToken);

Expand Down
5 changes: 4 additions & 1 deletion SimpleExec/ProcessStartInfo.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace SimpleExec
{
internal static class ProcessStartInfo
{
public static System.Diagnostics.ProcessStartInfo Create(
string name, string args, string workingDirectory, bool captureOutput, string windowsName, string windowsArgs, Action<IDictionary<string, string>> configureEnvironment, bool createNoWindow)
string name, string args, string workingDirectory, bool captureOutput, string windowsName, string windowsArgs, Action<IDictionary<string, string>> configureEnvironment, bool createNoWindow, Encoding encoding)
{
var startInfo = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? new System.Diagnostics.ProcessStartInfo
Expand All @@ -19,6 +20,7 @@ public static System.Diagnostics.ProcessStartInfo Create(
RedirectStandardError = false,
RedirectStandardOutput = captureOutput,
CreateNoWindow = createNoWindow,
StandardOutputEncoding = encoding,
}
: new System.Diagnostics.ProcessStartInfo
{
Expand All @@ -29,6 +31,7 @@ public static System.Diagnostics.ProcessStartInfo Create(
RedirectStandardError = false,
RedirectStandardOutput = captureOutput,
CreateNoWindow = createNoWindow,
StandardOutputEncoding = encoding,
};

configureEnvironment?.Invoke(startInfo.Environment);
Expand Down
4 changes: 2 additions & 2 deletions SimpleExec/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ SimpleExec.Command
SimpleExec.NonZeroExitCodeException
SimpleExec.NonZeroExitCodeException.ExitCode.get -> int
SimpleExec.NonZeroExitCodeException.NonZeroExitCodeException(int exitCode) -> void
static SimpleExec.Command.Read(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, System.Action<System.Collections.Generic.IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false) -> string
static SimpleExec.Command.ReadAsync(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, System.Action<System.Collections.Generic.IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
static SimpleExec.Command.Read(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, System.Action<System.Collections.Generic.IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false, System.Text.Encoding encoding = null) -> string
static SimpleExec.Command.ReadAsync(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, System.Action<System.Collections.Generic.IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false, System.Text.Encoding encoding = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
static SimpleExec.Command.Run(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, System.Action<System.Collections.Generic.IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false) -> void
static SimpleExec.Command.RunAsync(string name, string args = null, string workingDirectory = null, bool noEcho = false, string windowsName = null, string windowsArgs = null, string echoPrefix = null, System.Action<System.Collections.Generic.IDictionary<string, string>> configureEnvironment = null, bool createNoWindow = false, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
8 changes: 8 additions & 0 deletions SimpleExecTester/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace SimpleExecTester
Expand All @@ -8,6 +10,12 @@ internal class Program
{
public static int Main(string[] args)
{
if (args.Contains("unicode"))
{
Console.OutputEncoding = Encoding.Unicode;
args = args.Concat(new[] { "Pi (\u03a0)" }).ToArray();
}

Console.Out.WriteLine($"SimpleExecTester (stdout): {string.Join(" ", args)}");

if (args.Contains("large"))
Expand Down
25 changes: 25 additions & 0 deletions SimpleExecTests/ReadingCommands.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using System.Text;
using SimpleExec;
using SimpleExecTests.Infra;
using Xbehave;
Expand Down Expand Up @@ -33,6 +34,30 @@ public void ReadingACommandAsync(bool largeOutput, string output)
.x(() => Assert.Contains("hello world", output));
}

[Scenario]
[Example(false)]
[Example(true)]
public void ReadingAUnicodeCommand(bool largeOutput, string output)
{
("When I read a Unicode command" + (largeOutput ? " with large output" : ""))
.x(() => output = Command.Read("dotnet", $"exec {Tester.Path} hello world unicode" + (largeOutput ? " large" : ""), encoding: new UnicodeEncoding()));

"Then I see Unicode chars in the output"
.x(() => Assert.Contains("Pi (\u03a0)", output));
}

[Scenario]
[Example(false)]
[Example(true)]
public void ReadingAUnicodeCommandAsync(bool largeOutput, string output)
{
("When I read a Unicode command" + (largeOutput ? " with large output" : ""))
.x(async () => output = await Command.ReadAsync("dotnet", $"exec {Tester.Path} hello world unicode" + (largeOutput ? " large" : ""), encoding: new UnicodeEncoding()));

"Then I see Unicode chars in the output"
.x(() => Assert.Contains("Pi (\u03a0)", output));
}

[Scenario]
public void ReadingAFailingCommand(Exception exception)
{
Expand Down

0 comments on commit 70c4481

Please sign in to comment.