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

Added net471 build in attempt to fix #75 #77

Merged
merged 2 commits into from
Nov 15, 2020
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
81 changes: 36 additions & 45 deletions MedallionShell.Tests/GeneralTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
namespace Medallion.Shell.Tests
{
using System.Collections;
using System.Text.RegularExpressions;
using static UnitTestHelpers;

public class GeneralTest
Expand Down Expand Up @@ -145,45 +146,39 @@ public void TestZeroTimeout()
[Test]
public void TestCancellationAlreadyCanceled()
{
using (var alreadyCanceled = new CancellationTokenSource(millisecondsDelay: 0))
{
var command = TestShell.Run(SampleCommand, new object[] { "sleep", 1000000 }, o => o.CancellationToken(alreadyCanceled.Token));
Assert.Throws<TaskCanceledException>(() => command.Wait());
Assert.Throws<TaskCanceledException>(() => command.Result.ToString());
command.Task.Status.ShouldEqual(TaskStatus.Canceled);
Assert.DoesNotThrow(() => command.ProcessId.ToString(), "still executes a command and gets a process ID");
}
using var alreadyCanceled = new CancellationTokenSource(millisecondsDelay: 0);
var command = TestShell.Run(SampleCommand, new object[] { "sleep", 1000000 }, o => o.CancellationToken(alreadyCanceled.Token));
Assert.Throws<TaskCanceledException>(() => command.Wait());
Assert.Throws<TaskCanceledException>(() => command.Result.ToString());
command.Task.Status.ShouldEqual(TaskStatus.Canceled);
Assert.DoesNotThrow(() => command.ProcessId.ToString(), "still executes a command and gets a process ID");
}

[Test]
public void TestCancellationNotCanceled()
{
using (var notCanceled = new CancellationTokenSource())
{
var command = TestShell.Run(SampleCommand, new object[] { "sleep", 1000000 }, o => o.CancellationToken(notCanceled.Token));
command.Task.Wait(50).ShouldEqual(false);
command.Kill();
command.Task.Wait(1000).ShouldEqual(true);
command.Result.Success.ShouldEqual(false);
}
using var notCanceled = new CancellationTokenSource();
var command = TestShell.Run(SampleCommand, new object[] { "sleep", 1000000 }, o => o.CancellationToken(notCanceled.Token));
command.Task.Wait(50).ShouldEqual(false);
command.Kill();
command.Task.Wait(1000).ShouldEqual(true);
command.Result.Success.ShouldEqual(false);
}

[Test]
public void TestCancellationCanceledPartway()
{
using (var cancellationTokenSource = new CancellationTokenSource())
{
var results = new SyncCollection();
var command = TestShell.Run(SampleCommand, new object[] { "echo", "--per-char" }, o => o.CancellationToken(cancellationTokenSource.Token)) > results;
command.StandardInput.WriteLine("hello");
var timeout = Task.Delay(TimeSpan.FromSeconds(10));
while (results.Count == 0 && !timeout.IsCompleted) { }
results.Count.ShouldEqual(1);
cancellationTokenSource.Cancel();
var aggregateException = Assert.Throws<AggregateException>(() => command.Task.Wait(1000));
Assert.IsInstanceOf<TaskCanceledException>(aggregateException.GetBaseException());
CollectionAssert.AreEqual(results, new[] { "hello" });
}
using var cancellationTokenSource = new CancellationTokenSource();
var results = new SyncCollection();
var command = TestShell.Run(SampleCommand, new object[] { "echo", "--per-char" }, o => o.CancellationToken(cancellationTokenSource.Token)) > results;
command.StandardInput.WriteLine("hello");
var timeout = Task.Delay(TimeSpan.FromSeconds(10));
while (results.Count == 0 && !timeout.IsCompleted) { }
results.Count.ShouldEqual(1);
cancellationTokenSource.Cancel();
var aggregateException = Assert.Throws<AggregateException>(() => command.Task.Wait(1000));
Assert.IsInstanceOf<TaskCanceledException>(aggregateException.GetBaseException());
CollectionAssert.AreEqual(results, new[] { "hello" });
}

private class SyncCollection : ICollection<string>
Expand All @@ -207,16 +202,14 @@ private class SyncCollection : ICollection<string>
[Test]
public void TestCancellationCanceledAfterCompletion()
{
using (var cancellationTokenSource = new CancellationTokenSource())
{
var results = new List<string>();
var command = TestShell.Run(SampleCommand, new object[] { "echo" }, o => o.CancellationToken(cancellationTokenSource.Token)) > results;
command.StandardInput.WriteLine("hello");
command.StandardInput.Close();
command.Task.Wait(1000).ShouldEqual(true);
cancellationTokenSource.Cancel();
command.Result.Success.ShouldEqual(true);
}
using var cancellationTokenSource = new CancellationTokenSource();
var results = new List<string>();
var command = TestShell.Run(SampleCommand, new object[] { "echo" }, o => o.CancellationToken(cancellationTokenSource.Token)) > results;
command.StandardInput.WriteLine("hello");
command.StandardInput.Close();
command.Task.Wait(1000).ShouldEqual(true);
cancellationTokenSource.Cancel();
command.Result.Success.ShouldEqual(true);
}

[Test]
Expand Down Expand Up @@ -357,7 +350,7 @@ public void TestVersioning()
var version = typeof(Command).GetTypeInfo().Assembly.GetName().Version.ToString();
var informationalVersion = (AssemblyInformationalVersionAttribute)typeof(Command).GetTypeInfo().Assembly.GetCustomAttribute(typeof(AssemblyInformationalVersionAttribute));
Assert.IsNotNull(informationalVersion);
version.ShouldEqual(informationalVersion.InformationalVersion + ".0");
version.ShouldEqual(Regex.Replace(informationalVersion.InformationalVersion, "-.*$", string.Empty) + ".0");
}

[Test]
Expand Down Expand Up @@ -495,12 +488,10 @@ void TestHelper(bool disposeOnExit)

#if !NETCOREAPP2_2
// https://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c
string GetCommandLine(int processId)
static string GetCommandLine(int processId)
{
using (var searcher = new System.Management.ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + processId))
{
return searcher.Get().Cast<System.Management.ManagementBaseObject>().Single()["CommandLine"].ToString();
}
using var searcher = new System.Management.ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + processId);
return searcher.Get().Cast<System.Management.ManagementBaseObject>().Single()["CommandLine"].ToString();
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Expand Down
2 changes: 1 addition & 1 deletion MedallionShell.Tests/Streams/PipeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ public static Task WriteTextAsync(this Pipe @this, string text)
return new StreamWriter(@this.InputStream) { AutoFlush = true }.WriteAsync(text);
}

public static async Task<string?> ReadTextAsync(this Pipe @this, int count, CancellationToken token = default(CancellationToken))
public static async Task<string?> ReadTextAsync(this Pipe @this, int count, CancellationToken token = default)
{
var bytes = new byte[count];
var bytesRead = 0;
Expand Down
10 changes: 5 additions & 5 deletions MedallionShell/MedallionShell.csproj
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;net45;net46</TargetFrameworks>
<TargetFrameworks>netstandard1.3;netstandard2.0;net45;net46;net471</TargetFrameworks>
</PropertyGroup>

<PropertyGroup>
<RootNamespace>Medallion.Shell</RootNamespace>
<Version>1.6.1</Version>
<AssemblyVersion>1.6.1.0</AssemblyVersion>
<FileVersion>1.6.1.0</FileVersion>
<Version>1.6.2</Version>
<AssemblyVersion>1.6.2.0</AssemblyVersion>
<FileVersion>1.6.2.0</FileVersion>
<Authors>Michael Adelson</Authors>
<Description>A lightweight, cross-platform library that simplifies working with processes in .NET</Description>
<Copyright>Copyright © 2017 Michael Adelson</Copyright>
Expand Down Expand Up @@ -42,7 +42,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.Diagnostics.Process" version="4.3.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net46'">
<ItemGroup Condition="'$(TargetFramework)' == 'net46' or '$(TargetFramework)' == 'net45'">
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
</ItemGroup>

Expand Down
25 changes: 1 addition & 24 deletions MedallionShell/PlatformCompatibilityHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,7 @@ internal static class PlatformCompatibilityHelper
// see http://www.mono-project.com/docs/faq/technical/
public static readonly bool IsMono = Type.GetType("Mono.Runtime") != null;

public static bool IsWindows
{
get
{
#if !NET45
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
#else
if (!IsMono) { return true; }
#pragma warning disable DE0007, DE0009
switch (Environment.OSVersion.Platform)
{
case PlatformID.Win32S:
case PlatformID.Win32Windows:
case PlatformID.Win32NT:
case PlatformID.WinCE:
case PlatformID.Xbox:
return true;
default:
return false;
}
#pragma warning restore DE0007, DE0009
#endif
}
}
public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

public static CommandLineSyntax GetDefaultCommandLineSyntax()
{
Expand Down
60 changes: 29 additions & 31 deletions MedallionShell/Signals/WindowsProcessSignaler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,43 +69,41 @@ private static async Task<bool> SendSignalFromCurrentProcess(int processId, Nati
await SignalFromCurrentProcessLock.WaitAsync().ConfigureAwait(false);
try
{
using (var waitForSignalSemaphore = new SemaphoreSlim(initialCount: 0, maxCount: 1))
using var waitForSignalSemaphore = new SemaphoreSlim(initialCount: 0, maxCount: 1);
NativeMethods.ConsoleCtrlDelegate handler = receivedSignal =>
{
NativeMethods.ConsoleCtrlDelegate handler = receivedSignal =>
if (receivedSignal == signal)
{
if (receivedSignal == signal)
{
waitForSignalSemaphore.Release();
// if we're signaling another process on the same console, we return true
// to prevent the signal from bubbling. If we're signaling ourselves, we
// allow it to bubble since presumably that's what the caller wanted
return processId != ProcessHelper.CurrentProcessId;
}
return false;
};
if (!NativeMethods.SetConsoleCtrlHandler(handler, add: true))
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
waitForSignalSemaphore.Release();
// if we're signaling another process on the same console, we return true
// to prevent the signal from bubbling. If we're signaling ourselves, we
// allow it to bubble since presumably that's what the caller wanted
return processId != ProcessHelper.CurrentProcessId;
}
try
return false;
};
if (!NativeMethods.SetConsoleCtrlHandler(handler, add: true))
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
try
{
if (!NativeMethods.GenerateConsoleCtrlEvent(signal, NativeMethods.AllProcessesWithCurrentConsoleGroup))
{
if (!NativeMethods.GenerateConsoleCtrlEvent(signal, NativeMethods.AllProcessesWithCurrentConsoleGroup))
{
return false;
}

// Wait until the signal has reached our handler and been handled to know that it is safe to
// remove the handler.
// Timeout here just to ensure we don't hang forever if something weird happens (e. g. someone
// else registers a handler concurrently with us).
return await waitForSignalSemaphore.WaitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false);
return false;
}
finally

// Wait until the signal has reached our handler and been handled to know that it is safe to
// remove the handler.
// Timeout here just to ensure we don't hang forever if something weird happens (e. g. someone
// else registers a handler concurrently with us).
return await waitForSignalSemaphore.WaitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false);
}
finally
{
if (!NativeMethods.SetConsoleCtrlHandler(handler, add: false))
{
if (!NativeMethods.SetConsoleCtrlHandler(handler, add: false))
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
}
}
Expand Down
28 changes: 13 additions & 15 deletions MedallionShell/Streams/ProcessStreamWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,23 +124,21 @@ public Task PipeFromAsync(IEnumerable<char> chars, bool leaveWriterOpen = false)
: () => Task.Run(async () =>
{
var buffer = new char[Constants.CharBufferSize];
using (var enumerator = chars.GetEnumerator())
using var enumerator = chars.GetEnumerator();
while (true)
{
while (true)
var i = 0;
while (i < buffer.Length && enumerator.MoveNext())
{
var i = 0;
while (i < buffer.Length && enumerator.MoveNext())
{
buffer[i++] = enumerator.Current;
}
if (i > 0)
{
await this.WriteAsync(buffer, 0, count: i).ConfigureAwait(false);
}
else
{
break;
}
buffer[i++] = enumerator.Current;
}
if (i > 0)
{
await this.WriteAsync(buffer, 0, count: i).ConfigureAwait(false);
}
else
{
break;
}
}
}),
Expand Down