Skip to content

Commit 7fbe917

Browse files
authored
Merge pull request #77 from madelson/1.6.2
Added net471 build in attempt to fix #75
2 parents 801101c + 9b7ee32 commit 7fbe917

File tree

6 files changed

+85
-121
lines changed

6 files changed

+85
-121
lines changed

MedallionShell.Tests/GeneralTest.cs

+36-45
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
namespace Medallion.Shell.Tests
1414
{
1515
using System.Collections;
16+
using System.Text.RegularExpressions;
1617
using static UnitTestHelpers;
1718

1819
public class GeneralTest
@@ -145,45 +146,39 @@ public void TestZeroTimeout()
145146
[Test]
146147
public void TestCancellationAlreadyCanceled()
147148
{
148-
using (var alreadyCanceled = new CancellationTokenSource(millisecondsDelay: 0))
149-
{
150-
var command = TestShell.Run(SampleCommand, new object[] { "sleep", 1000000 }, o => o.CancellationToken(alreadyCanceled.Token));
151-
Assert.Throws<TaskCanceledException>(() => command.Wait());
152-
Assert.Throws<TaskCanceledException>(() => command.Result.ToString());
153-
command.Task.Status.ShouldEqual(TaskStatus.Canceled);
154-
Assert.DoesNotThrow(() => command.ProcessId.ToString(), "still executes a command and gets a process ID");
155-
}
149+
using var alreadyCanceled = new CancellationTokenSource(millisecondsDelay: 0);
150+
var command = TestShell.Run(SampleCommand, new object[] { "sleep", 1000000 }, o => o.CancellationToken(alreadyCanceled.Token));
151+
Assert.Throws<TaskCanceledException>(() => command.Wait());
152+
Assert.Throws<TaskCanceledException>(() => command.Result.ToString());
153+
command.Task.Status.ShouldEqual(TaskStatus.Canceled);
154+
Assert.DoesNotThrow(() => command.ProcessId.ToString(), "still executes a command and gets a process ID");
156155
}
157156

158157
[Test]
159158
public void TestCancellationNotCanceled()
160159
{
161-
using (var notCanceled = new CancellationTokenSource())
162-
{
163-
var command = TestShell.Run(SampleCommand, new object[] { "sleep", 1000000 }, o => o.CancellationToken(notCanceled.Token));
164-
command.Task.Wait(50).ShouldEqual(false);
165-
command.Kill();
166-
command.Task.Wait(1000).ShouldEqual(true);
167-
command.Result.Success.ShouldEqual(false);
168-
}
160+
using var notCanceled = new CancellationTokenSource();
161+
var command = TestShell.Run(SampleCommand, new object[] { "sleep", 1000000 }, o => o.CancellationToken(notCanceled.Token));
162+
command.Task.Wait(50).ShouldEqual(false);
163+
command.Kill();
164+
command.Task.Wait(1000).ShouldEqual(true);
165+
command.Result.Success.ShouldEqual(false);
169166
}
170167

171168
[Test]
172169
public void TestCancellationCanceledPartway()
173170
{
174-
using (var cancellationTokenSource = new CancellationTokenSource())
175-
{
176-
var results = new SyncCollection();
177-
var command = TestShell.Run(SampleCommand, new object[] { "echo", "--per-char" }, o => o.CancellationToken(cancellationTokenSource.Token)) > results;
178-
command.StandardInput.WriteLine("hello");
179-
var timeout = Task.Delay(TimeSpan.FromSeconds(10));
180-
while (results.Count == 0 && !timeout.IsCompleted) { }
181-
results.Count.ShouldEqual(1);
182-
cancellationTokenSource.Cancel();
183-
var aggregateException = Assert.Throws<AggregateException>(() => command.Task.Wait(1000));
184-
Assert.IsInstanceOf<TaskCanceledException>(aggregateException.GetBaseException());
185-
CollectionAssert.AreEqual(results, new[] { "hello" });
186-
}
171+
using var cancellationTokenSource = new CancellationTokenSource();
172+
var results = new SyncCollection();
173+
var command = TestShell.Run(SampleCommand, new object[] { "echo", "--per-char" }, o => o.CancellationToken(cancellationTokenSource.Token)) > results;
174+
command.StandardInput.WriteLine("hello");
175+
var timeout = Task.Delay(TimeSpan.FromSeconds(10));
176+
while (results.Count == 0 && !timeout.IsCompleted) { }
177+
results.Count.ShouldEqual(1);
178+
cancellationTokenSource.Cancel();
179+
var aggregateException = Assert.Throws<AggregateException>(() => command.Task.Wait(1000));
180+
Assert.IsInstanceOf<TaskCanceledException>(aggregateException.GetBaseException());
181+
CollectionAssert.AreEqual(results, new[] { "hello" });
187182
}
188183

189184
private class SyncCollection : ICollection<string>
@@ -207,16 +202,14 @@ private class SyncCollection : ICollection<string>
207202
[Test]
208203
public void TestCancellationCanceledAfterCompletion()
209204
{
210-
using (var cancellationTokenSource = new CancellationTokenSource())
211-
{
212-
var results = new List<string>();
213-
var command = TestShell.Run(SampleCommand, new object[] { "echo" }, o => o.CancellationToken(cancellationTokenSource.Token)) > results;
214-
command.StandardInput.WriteLine("hello");
215-
command.StandardInput.Close();
216-
command.Task.Wait(1000).ShouldEqual(true);
217-
cancellationTokenSource.Cancel();
218-
command.Result.Success.ShouldEqual(true);
219-
}
205+
using var cancellationTokenSource = new CancellationTokenSource();
206+
var results = new List<string>();
207+
var command = TestShell.Run(SampleCommand, new object[] { "echo" }, o => o.CancellationToken(cancellationTokenSource.Token)) > results;
208+
command.StandardInput.WriteLine("hello");
209+
command.StandardInput.Close();
210+
command.Task.Wait(1000).ShouldEqual(true);
211+
cancellationTokenSource.Cancel();
212+
command.Result.Success.ShouldEqual(true);
220213
}
221214

222215
[Test]
@@ -357,7 +350,7 @@ public void TestVersioning()
357350
var version = typeof(Command).GetTypeInfo().Assembly.GetName().Version.ToString();
358351
var informationalVersion = (AssemblyInformationalVersionAttribute)typeof(Command).GetTypeInfo().Assembly.GetCustomAttribute(typeof(AssemblyInformationalVersionAttribute));
359352
Assert.IsNotNull(informationalVersion);
360-
version.ShouldEqual(informationalVersion.InformationalVersion + ".0");
353+
version.ShouldEqual(Regex.Replace(informationalVersion.InformationalVersion, "-.*$", string.Empty) + ".0");
361354
}
362355

363356
[Test]
@@ -495,12 +488,10 @@ void TestHelper(bool disposeOnExit)
495488

496489
#if !NETCOREAPP2_2
497490
// https://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c
498-
string GetCommandLine(int processId)
491+
static string GetCommandLine(int processId)
499492
{
500-
using (var searcher = new System.Management.ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + processId))
501-
{
502-
return searcher.Get().Cast<System.Management.ManagementBaseObject>().Single()["CommandLine"].ToString();
503-
}
493+
using var searcher = new System.Management.ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + processId);
494+
return searcher.Get().Cast<System.Management.ManagementBaseObject>().Single()["CommandLine"].ToString();
504495
}
505496
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
506497
{

MedallionShell.Tests/Streams/PipeTest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ public static Task WriteTextAsync(this Pipe @this, string text)
364364
return new StreamWriter(@this.InputStream) { AutoFlush = true }.WriteAsync(text);
365365
}
366366

367-
public static async Task<string?> ReadTextAsync(this Pipe @this, int count, CancellationToken token = default(CancellationToken))
367+
public static async Task<string?> ReadTextAsync(this Pipe @this, int count, CancellationToken token = default)
368368
{
369369
var bytes = new byte[count];
370370
var bytesRead = 0;

MedallionShell/MedallionShell.csproj

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard1.3;netstandard2.0;net45;net46</TargetFrameworks>
4+
<TargetFrameworks>netstandard1.3;netstandard2.0;net45;net46;net471</TargetFrameworks>
55
</PropertyGroup>
66

77
<PropertyGroup>
88
<RootNamespace>Medallion.Shell</RootNamespace>
9-
<Version>1.6.1</Version>
10-
<AssemblyVersion>1.6.1.0</AssemblyVersion>
11-
<FileVersion>1.6.1.0</FileVersion>
9+
<Version>1.6.2</Version>
10+
<AssemblyVersion>1.6.2.0</AssemblyVersion>
11+
<FileVersion>1.6.2.0</FileVersion>
1212
<Authors>Michael Adelson</Authors>
1313
<Description>A lightweight, cross-platform library that simplifies working with processes in .NET</Description>
1414
<Copyright>Copyright © 2017 Michael Adelson</Copyright>
@@ -42,7 +42,7 @@
4242
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3'">
4343
<PackageReference Include="System.Diagnostics.Process" version="4.3.0" />
4444
</ItemGroup>
45-
<ItemGroup Condition="'$(TargetFramework)' == 'net46'">
45+
<ItemGroup Condition="'$(TargetFramework)' == 'net46' or '$(TargetFramework)' == 'net45'">
4646
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
4747
</ItemGroup>
4848

MedallionShell/PlatformCompatibilityHelper.cs

+1-24
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,7 @@ internal static class PlatformCompatibilityHelper
1313
// see http://www.mono-project.com/docs/faq/technical/
1414
public static readonly bool IsMono = Type.GetType("Mono.Runtime") != null;
1515

16-
public static bool IsWindows
17-
{
18-
get
19-
{
20-
#if !NET45
21-
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
22-
#else
23-
if (!IsMono) { return true; }
24-
#pragma warning disable DE0007, DE0009
25-
switch (Environment.OSVersion.Platform)
26-
{
27-
case PlatformID.Win32S:
28-
case PlatformID.Win32Windows:
29-
case PlatformID.Win32NT:
30-
case PlatformID.WinCE:
31-
case PlatformID.Xbox:
32-
return true;
33-
default:
34-
return false;
35-
}
36-
#pragma warning restore DE0007, DE0009
37-
#endif
38-
}
39-
}
16+
public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
4017

4118
public static CommandLineSyntax GetDefaultCommandLineSyntax()
4219
{

MedallionShell/Signals/WindowsProcessSignaler.cs

+29-31
Original file line numberDiff line numberDiff line change
@@ -69,43 +69,41 @@ private static async Task<bool> SendSignalFromCurrentProcess(int processId, Nati
6969
await SignalFromCurrentProcessLock.WaitAsync().ConfigureAwait(false);
7070
try
7171
{
72-
using (var waitForSignalSemaphore = new SemaphoreSlim(initialCount: 0, maxCount: 1))
72+
using var waitForSignalSemaphore = new SemaphoreSlim(initialCount: 0, maxCount: 1);
73+
NativeMethods.ConsoleCtrlDelegate handler = receivedSignal =>
7374
{
74-
NativeMethods.ConsoleCtrlDelegate handler = receivedSignal =>
75+
if (receivedSignal == signal)
7576
{
76-
if (receivedSignal == signal)
77-
{
78-
waitForSignalSemaphore.Release();
79-
// if we're signaling another process on the same console, we return true
80-
// to prevent the signal from bubbling. If we're signaling ourselves, we
81-
// allow it to bubble since presumably that's what the caller wanted
82-
return processId != ProcessHelper.CurrentProcessId;
83-
}
84-
return false;
85-
};
86-
if (!NativeMethods.SetConsoleCtrlHandler(handler, add: true))
87-
{
88-
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
77+
waitForSignalSemaphore.Release();
78+
// if we're signaling another process on the same console, we return true
79+
// to prevent the signal from bubbling. If we're signaling ourselves, we
80+
// allow it to bubble since presumably that's what the caller wanted
81+
return processId != ProcessHelper.CurrentProcessId;
8982
}
90-
try
83+
return false;
84+
};
85+
if (!NativeMethods.SetConsoleCtrlHandler(handler, add: true))
86+
{
87+
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
88+
}
89+
try
90+
{
91+
if (!NativeMethods.GenerateConsoleCtrlEvent(signal, NativeMethods.AllProcessesWithCurrentConsoleGroup))
9192
{
92-
if (!NativeMethods.GenerateConsoleCtrlEvent(signal, NativeMethods.AllProcessesWithCurrentConsoleGroup))
93-
{
94-
return false;
95-
}
96-
97-
// Wait until the signal has reached our handler and been handled to know that it is safe to
98-
// remove the handler.
99-
// Timeout here just to ensure we don't hang forever if something weird happens (e. g. someone
100-
// else registers a handler concurrently with us).
101-
return await waitForSignalSemaphore.WaitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false);
93+
return false;
10294
}
103-
finally
95+
96+
// Wait until the signal has reached our handler and been handled to know that it is safe to
97+
// remove the handler.
98+
// Timeout here just to ensure we don't hang forever if something weird happens (e. g. someone
99+
// else registers a handler concurrently with us).
100+
return await waitForSignalSemaphore.WaitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false);
101+
}
102+
finally
103+
{
104+
if (!NativeMethods.SetConsoleCtrlHandler(handler, add: false))
104105
{
105-
if (!NativeMethods.SetConsoleCtrlHandler(handler, add: false))
106-
{
107-
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
108-
}
106+
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
109107
}
110108
}
111109
}

MedallionShell/Streams/ProcessStreamWriter.cs

+13-15
Original file line numberDiff line numberDiff line change
@@ -124,23 +124,21 @@ public Task PipeFromAsync(IEnumerable<char> chars, bool leaveWriterOpen = false)
124124
: () => Task.Run(async () =>
125125
{
126126
var buffer = new char[Constants.CharBufferSize];
127-
using (var enumerator = chars.GetEnumerator())
127+
using var enumerator = chars.GetEnumerator();
128+
while (true)
128129
{
129-
while (true)
130+
var i = 0;
131+
while (i < buffer.Length && enumerator.MoveNext())
130132
{
131-
var i = 0;
132-
while (i < buffer.Length && enumerator.MoveNext())
133-
{
134-
buffer[i++] = enumerator.Current;
135-
}
136-
if (i > 0)
137-
{
138-
await this.WriteAsync(buffer, 0, count: i).ConfigureAwait(false);
139-
}
140-
else
141-
{
142-
break;
143-
}
133+
buffer[i++] = enumerator.Current;
134+
}
135+
if (i > 0)
136+
{
137+
await this.WriteAsync(buffer, 0, count: i).ConfigureAwait(false);
138+
}
139+
else
140+
{
141+
break;
144142
}
145143
}
146144
}),

0 commit comments

Comments
 (0)