Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit c7ea66e

Browse files
committed
Add more pipe tests
1 parent d22fe16 commit c7ea66e

File tree

7 files changed

+364
-24
lines changed

7 files changed

+364
-24
lines changed

src/System.IO.Pipes/tests/Interop.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Runtime.InteropServices;
5+
using System.Text;
6+
using System.Threading;
7+
using Microsoft.Win32.SafeHandles;
8+
9+
namespace System.IO.Pipes.Tests
10+
{
11+
/// <summary>
12+
/// The class contains interop declarations and helpers methods for them.
13+
/// </summary>
14+
internal static class Interop
15+
{
16+
#region Windows
17+
18+
[DllImport("api-ms-win-core-io-l1-1-0.dll", SetLastError = true)]
19+
private static extern unsafe bool CancelIoEx(SafeHandle handle, NativeOverlapped* lpOverlapped);
20+
21+
internal static unsafe bool CancelIoEx(SafeHandle handle)
22+
{
23+
return CancelIoEx(handle, null);
24+
}
25+
26+
[DllImport("api-ms-win-core-namedpipe-l1-2-1.dll", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false, EntryPoint = "GetNamedPipeHandleStateW")]
27+
[return: MarshalAs(UnmanagedType.Bool)]
28+
private static extern bool GetNamedPipeHandleState(
29+
SafePipeHandle hNamedPipe,
30+
IntPtr lpState,
31+
out int lpCurInstances,
32+
IntPtr lpMaxCollectionCount,
33+
IntPtr lpCollectDataTimeout,
34+
StringBuilder lpUserName,
35+
int nMaxUserNameSize);
36+
37+
internal static bool TryGetImpersonationUserName(SafePipeHandle handle, out string userName)
38+
{
39+
const int CREDUI_MAX_USERNAME_LENGTH = 513;
40+
int serverInstances;
41+
var builder = new StringBuilder(CREDUI_MAX_USERNAME_LENGTH + 1);
42+
43+
if (GetNamedPipeHandleState(handle, IntPtr.Zero, out serverInstances, IntPtr.Zero, IntPtr.Zero, builder, builder.Capacity))
44+
{
45+
userName = builder.ToString();
46+
return true;
47+
}
48+
49+
userName = "";
50+
return false;
51+
}
52+
53+
internal static bool TryGetNumberOfServerInstances(SafePipeHandle handle, out int numberOfServerInstances)
54+
{
55+
int serverInstances;
56+
57+
if (GetNamedPipeHandleState(handle, IntPtr.Zero, out serverInstances, IntPtr.Zero, IntPtr.Zero, null, 0))
58+
{
59+
numberOfServerInstances = serverInstances;
60+
return true;
61+
}
62+
63+
numberOfServerInstances = 0;
64+
return false;
65+
}
66+
67+
#endregion
68+
69+
#region Unix
70+
71+
[DllImport("libc", SetLastError = true)]
72+
private static extern unsafe int gethostname(byte* name, int len);
73+
74+
internal static unsafe bool TryGetHostName(out string hostName)
75+
{
76+
const int HOST_NAME_MAX = 255; // man gethostname
77+
const int ArrLength = HOST_NAME_MAX + 1;
78+
79+
byte* name = stackalloc byte[ArrLength];
80+
int result = gethostname(name, ArrLength);
81+
82+
if (result == 0)
83+
{
84+
hostName = Marshal.PtrToStringAnsi((IntPtr)name);
85+
return true;
86+
}
87+
88+
hostName = "";
89+
return false;
90+
}
91+
92+
#endregion
93+
}
94+
}

src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,33 @@ public static void EmptyStringServerName_Throws_ArgumentException(PipeDirection
5050
Assert.Throws<ArgumentException>(() => new NamedPipeClientStream("", "client1", direction, PipeOptions.None, TokenImpersonationLevel.None));
5151
}
5252

53+
[Theory]
54+
[InlineData(PipeDirection.In)]
55+
[InlineData(PipeDirection.InOut)]
56+
[InlineData(PipeDirection.Out)]
57+
[PlatformSpecific(PlatformID.Windows)]
58+
public static void ReservedPipeName_Throws_ArgumentOutOfRangeException(PipeDirection direction)
59+
{
60+
const string serverName = ".";
61+
const string reservedName = "anonymous";
62+
Assert.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(reservedName));
63+
Assert.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(serverName, reservedName));
64+
Assert.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction));
65+
Assert.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None));
66+
Assert.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None, TokenImpersonationLevel.Impersonation));
67+
}
68+
69+
[Fact]
70+
[PlatformSpecific(PlatformID.AnyUnix)]
71+
public static void NotSupportedPipePath_Throws_PlatformNotSupportedException()
72+
{
73+
string hostName;
74+
Assert.True(Interop.TryGetHostName(out hostName));
75+
76+
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream("foobar" + hostName, "foobar"));
77+
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "foobar" + Path.GetInvalidFileNameChars()[0]));
78+
}
79+
5380
[Theory]
5481
[InlineData((PipeDirection)123)]
5582
public static void InvalidPipeDirection_Throws_ArgumentOutOfRangeException(PipeDirection direction)

src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Simple.cs

Lines changed: 133 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -235,45 +235,33 @@ public async Task CancelTokenOn_ServerWaitForConnectionAsync_Throws_OperationCan
235235
}
236236
}
237237

238-
[DllImport("api-ms-win-core-io-l1-1-0.dll", SetLastError = true)]
239-
private static unsafe extern bool CancelIoEx(SafeHandle handle, NativeOverlapped* lpOverlapped);
240-
241238
[Fact]
242239
[PlatformSpecific(PlatformID.Windows)]
243-
// [ActiveIssue(2640)]
244240
public async Task CancelTokenOff_ServerWaitForConnectionAsyncWithOuterCancellation_Throws_OperationCanceledException()
245241
{
246242
using (NamedPipePair pair = CreateNamedPipePair())
247243
{
248244
NamedPipeServerStream server = pair.serverStream;
249245
Task waitForConnectionTask = server.WaitForConnectionAsync(CancellationToken.None);
250-
unsafe
251-
{
252-
Assert.True(CancelIoEx(server.SafePipeHandle, null), "Outer cancellation failed");
253-
}
254246

247+
Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed");
255248
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => waitForConnectionTask);
256249
Assert.True(waitForConnectionTask.IsCanceled);
257250
}
258251
}
259252

260253
[Fact]
261254
[PlatformSpecific(PlatformID.Windows)]
262-
// [ActiveIssue(2640)]
263255
public async Task CancelTokenOn_ServerWaitForConnectionAsyncWithOuterCancellation_Throws_IOException()
264256
{
265257
using (NamedPipePair pair = CreateNamedPipePair())
266258
{
267259
var cts = new CancellationTokenSource();
268260
NamedPipeServerStream server = pair.serverStream;
269261
Task waitForConnectionTask = server.WaitForConnectionAsync(cts.Token);
270-
unsafe
271-
{
272-
Assert.True(CancelIoEx(server.SafePipeHandle, null), "Outer cancellation failed");
273-
}
274262

263+
Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed");
275264
await Assert.ThrowsAsync<IOException>(() => waitForConnectionTask);
276-
Assert.False(waitForConnectionTask.IsCanceled);
277265
}
278266
}
279267

@@ -310,6 +298,7 @@ public async Task OperationsOnDisconnectedServer()
310298

311299
Assert.Throws<InvalidOperationException>(() => server.Flush());
312300
Assert.Throws<InvalidOperationException>(() => server.IsMessageComplete);
301+
Assert.Throws<InvalidOperationException>(() => server.GetImpersonationUserName());
313302
}
314303
}
315304

@@ -337,12 +326,18 @@ public virtual async Task OperationsOnDisconnectedClient()
337326
Assert.Throws<IOException>(() => client.WriteByte(5));
338327
Assert.Throws<IOException>(() => { client.WriteAsync(buffer, 0, buffer.Length); });
339328
Assert.Throws<IOException>(() => client.Flush());
329+
Assert.Throws<IOException>(() => client.NumberOfServerInstances);
340330
}
341331
else
342332
{
343333
// Nothing for the client to read, but no exception throwing
344334
Assert.Equal(0, client.Read(buffer, 0, buffer.Length));
345335
Assert.Equal(-1, client.ReadByte());
336+
337+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
338+
{
339+
Assert.Throws<PlatformNotSupportedException>(() => client.NumberOfServerInstances);
340+
}
346341
}
347342

348343
Assert.Throws<InvalidOperationException>(() => client.IsMessageComplete);
@@ -361,6 +356,7 @@ public async Task Windows_OperationsOnNamedServerWithDisposedClient()
361356

362357
Assert.Throws<IOException>(() => server.WaitForConnection());
363358
await Assert.ThrowsAsync<IOException>(() => server.WaitForConnectionAsync());
359+
Assert.Throws<IOException>(() => server.GetImpersonationUserName());
364360
}
365361
}
366362

@@ -377,6 +373,7 @@ public async Task Unix_OperationsOnNamedServerWithDisposedClient()
377373
// On Unix, the server still thinks that it is connected after client Disposal.
378374
Assert.Throws<InvalidOperationException>(() => server.WaitForConnection());
379375
await Assert.ThrowsAsync<InvalidOperationException>(() => server.WaitForConnectionAsync());
376+
Assert.Throws<PlatformNotSupportedException>(() => server.GetImpersonationUserName());
380377
}
381378
}
382379

@@ -463,6 +460,7 @@ public async Task DisposedServerPipe_Throws_ObjectDisposedException()
463460
byte[] buffer = new byte[] { 0, 0, 0, 0 };
464461

465462
Assert.Throws<ObjectDisposedException>(() => pipe.Disconnect());
463+
Assert.Throws<ObjectDisposedException>(() => pipe.GetImpersonationUserName());
466464
Assert.Throws<ObjectDisposedException>(() => pipe.WaitForConnection());
467465
await Assert.ThrowsAsync<ObjectDisposedException>(() => pipe.WaitForConnectionAsync());
468466
}
@@ -473,12 +471,14 @@ public async Task DisposedClientPipe_Throws_ObjectDisposedException()
473471
{
474472
using (NamedPipePair pair = CreateNamedPipePair())
475473
{
474+
pair.Connect();
476475
NamedPipeClientStream pipe = pair.clientStream;
477476
pipe.Dispose();
478477
byte[] buffer = new byte[] { 0, 0, 0, 0 };
479478

480479
Assert.Throws<ObjectDisposedException>(() => pipe.Connect());
481480
await Assert.ThrowsAsync<ObjectDisposedException>(() => pipe.ConnectAsync());
481+
Assert.Throws<ObjectDisposedException>(() => pipe.NumberOfServerInstances);
482482
}
483483
}
484484

@@ -512,6 +512,66 @@ public async Task Server_ReadWriteCancelledToken_Throws_OperationCanceledExcepti
512512
}
513513
}
514514

515+
[Fact]
516+
[PlatformSpecific(PlatformID.Windows)]
517+
public async Task CancelTokenOff_Server_ReadWriteCancelledToken_Throws_OperationCanceledException()
518+
{
519+
using (NamedPipePair pair = CreateNamedPipePair())
520+
{
521+
NamedPipeServerStream server = pair.serverStream;
522+
byte[] buffer = new byte[] { 0, 0, 0, 0 };
523+
524+
pair.Connect();
525+
526+
if (server.CanRead)
527+
{
528+
Task serverReadToken = server.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None);
529+
530+
Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed");
531+
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverReadToken);
532+
Assert.True(serverReadToken.IsCanceled);
533+
}
534+
if (server.CanWrite)
535+
{
536+
Task serverWriteToken = server.WriteAsync(buffer, 0, buffer.Length, CancellationToken.None);
537+
538+
Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed");
539+
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverWriteToken);
540+
Assert.True(serverWriteToken.IsCanceled);
541+
}
542+
}
543+
}
544+
545+
[Fact]
546+
[PlatformSpecific(PlatformID.Windows)]
547+
public async Task CancelTokenOn_Server_ReadWriteCancelledToken_Throws_IOException()
548+
{
549+
using (NamedPipePair pair = CreateNamedPipePair())
550+
{
551+
NamedPipeServerStream server = pair.serverStream;
552+
byte[] buffer = new byte[] { 0, 0, 0, 0 };
553+
554+
pair.Connect();
555+
556+
if (server.CanRead)
557+
{
558+
var cts = new CancellationTokenSource();
559+
Task serverReadToken = server.ReadAsync(buffer, 0, buffer.Length, cts.Token);
560+
561+
Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed");
562+
await Assert.ThrowsAsync<IOException>(() => serverReadToken);
563+
}
564+
if (server.CanWrite)
565+
{
566+
var cts = new CancellationTokenSource();
567+
Task serverWriteToken = server.WriteAsync(buffer, 0, buffer.Length, cts.Token);
568+
569+
Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed");
570+
await Assert.ThrowsAsync<IOException>(() => serverWriteToken);
571+
}
572+
}
573+
}
574+
515575
[Fact]
516576
[ActiveIssue(812, PlatformID.AnyUnix)] // the cancellation token is ignored after the operation is initiated, due to base Stream's implementation
517577
public async Task Client_ReadWriteCancelledToken_Throws_OperationCanceledException()
@@ -540,6 +600,64 @@ public async Task Client_ReadWriteCancelledToken_Throws_OperationCanceledExcepti
540600
}
541601
}
542602
}
603+
604+
[Fact]
605+
[PlatformSpecific(PlatformID.Windows)]
606+
public async Task CancelTokenOff_Client_ReadWriteCancelledToken_Throws_OperationCanceledException()
607+
{
608+
using (NamedPipePair pair = CreateNamedPipePair())
609+
{
610+
NamedPipeClientStream client = pair.clientStream;
611+
byte[] buffer = new byte[] { 0, 0, 0, 0 };
612+
613+
pair.Connect();
614+
if (client.CanRead)
615+
{
616+
Task clientReadToken = client.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None);
617+
618+
Assert.True(Interop.CancelIoEx(client.SafePipeHandle), "Outer cancellation failed");
619+
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => clientReadToken);
620+
Assert.True(clientReadToken.IsCanceled);
621+
}
622+
if (client.CanWrite)
623+
{
624+
Task clientWriteToken = client.WriteAsync(buffer, 0, buffer.Length, CancellationToken.None);
625+
626+
Assert.True(Interop.CancelIoEx(client.SafePipeHandle), "Outer cancellation failed");
627+
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => clientWriteToken);
628+
Assert.True(clientWriteToken.IsCanceled);
629+
}
630+
}
631+
}
632+
633+
[Fact]
634+
[PlatformSpecific(PlatformID.Windows)]
635+
public async Task CancelTokenOn_Client_ReadWriteCancelledToken_Throws_IOException()
636+
{
637+
using (NamedPipePair pair = CreateNamedPipePair())
638+
{
639+
NamedPipeClientStream client = pair.clientStream;
640+
byte[] buffer = new byte[] { 0, 0, 0, 0 };
641+
642+
pair.Connect();
643+
if (client.CanRead)
644+
{
645+
var cts = new CancellationTokenSource();
646+
Task clientReadToken = client.ReadAsync(buffer, 0, buffer.Length, cts.Token);
647+
648+
Assert.True(Interop.CancelIoEx(client.SafePipeHandle), "Outer cancellation failed");
649+
await Assert.ThrowsAsync<IOException>(() => clientReadToken);
650+
}
651+
if (client.CanWrite)
652+
{
653+
var cts = new CancellationTokenSource();
654+
Task clientWriteToken = client.WriteAsync(buffer, 0, buffer.Length, cts.Token);
655+
656+
Assert.True(Interop.CancelIoEx(client.SafePipeHandle), "Outer cancellation failed");
657+
await Assert.ThrowsAsync<IOException>(() => clientWriteToken);
658+
}
659+
}
660+
}
543661
}
544662

545663
public class NamedPipeTest_Simple_ServerInOutRead_ClientInOutWrite : NamedPipeTest_Simple
@@ -581,6 +699,7 @@ public override async Task OperationsOnDisconnectedClient()
581699
await client.WriteAsync(buffer, 0, buffer.Length);
582700
client.Flush();
583701
Assert.Throws<InvalidOperationException>(() => client.IsMessageComplete);
702+
Assert.Throws<PlatformNotSupportedException>(() => client.NumberOfServerInstances);
584703
}
585704
}
586705
}

0 commit comments

Comments
 (0)