1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections.Generic; 6 using System.Linq; 7 using System.Runtime.InteropServices; 8 using System.Threading; 9 using System.Threading.Tasks; 10 using Xunit; 11 12 namespace System.IO.Pipes.Tests 13 { 14 /// <summary> 15 /// The Simple NamedPipe tests cover potentially every-day scenarios that are shared 16 /// by all NamedPipes whether they be Server/Client or In/Out/Inout. 17 /// </summary> 18 public abstract class NamedPipeTest_Simple : NamedPipeTestBase 19 { 20 /// <summary> 21 /// Yields every combination of testing options for the OneWayReadWrites test 22 /// </summary> 23 /// <returns></returns> OneWayReadWritesMemberData()24 public static IEnumerable<object[]> OneWayReadWritesMemberData() 25 { 26 var options = new[] { PipeOptions.None, PipeOptions.Asynchronous }; 27 var bools = new[] { false, true }; 28 foreach (PipeOptions serverOption in options) 29 foreach (PipeOptions clientOption in options) 30 foreach (bool asyncServerOps in bools) 31 foreach (bool asyncClientOps in bools) 32 yield return new object[] { serverOption, clientOption, asyncServerOps, asyncClientOps }; 33 } 34 35 [Theory] 36 [MemberData(nameof(OneWayReadWritesMemberData))] OneWayReadWrites(PipeOptions serverOptions, PipeOptions clientOptions, bool asyncServerOps, bool asyncClientOps)37 public async Task OneWayReadWrites(PipeOptions serverOptions, PipeOptions clientOptions, bool asyncServerOps, bool asyncClientOps) 38 { 39 using (NamedPipePair pair = CreateNamedPipePair(serverOptions, clientOptions)) 40 { 41 NamedPipeClientStream client = pair.clientStream; 42 NamedPipeServerStream server = pair.serverStream; 43 byte[] received = new byte[] { 0 }; 44 Task clientTask = Task.Run(async () => 45 { 46 if (asyncClientOps) 47 { 48 await client.ConnectAsync(); 49 if (pair.writeToServer) 50 { 51 received = await ReadBytesAsync(client, sendBytes.Length); 52 } 53 else 54 { 55 await WriteBytesAsync(client, sendBytes); 56 } 57 } 58 else 59 { 60 client.Connect(); 61 if (pair.writeToServer) 62 { 63 received = ReadBytes(client, sendBytes.Length); 64 } 65 else 66 { 67 WriteBytes(client, sendBytes); 68 } 69 } 70 }); 71 if (asyncServerOps) 72 { 73 await server.WaitForConnectionAsync(); 74 if (pair.writeToServer) 75 { 76 await WriteBytesAsync(server, sendBytes); 77 } 78 else 79 { 80 received = await ReadBytesAsync(server, sendBytes.Length); 81 } 82 } 83 else 84 { 85 server.WaitForConnection(); 86 if (pair.writeToServer) 87 { 88 WriteBytes(server, sendBytes); 89 } 90 else 91 { 92 received = ReadBytes(server, sendBytes.Length); 93 } 94 } 95 96 await clientTask; 97 Assert.Equal(sendBytes, received); 98 99 server.Disconnect(); 100 Assert.False(server.IsConnected); 101 } 102 } 103 104 [Fact] ClonedServer_ActsAsOriginalServer()105 public async Task ClonedServer_ActsAsOriginalServer() 106 { 107 byte[] msg1 = new byte[] { 5, 7, 9, 10 }; 108 byte[] received1 = new byte[] { 0, 0, 0, 0 }; 109 110 using (NamedPipePair pair = CreateNamedPipePair()) 111 { 112 NamedPipeServerStream serverBase = pair.serverStream; 113 NamedPipeClientStream client = pair.clientStream; 114 pair.Connect(); 115 116 if (pair.writeToServer) 117 { 118 Task<int> clientTask = client.ReadAsync(received1, 0, received1.Length); 119 using (NamedPipeServerStream server = new NamedPipeServerStream(PipeDirection.Out, false, true, serverBase.SafePipeHandle)) 120 { 121 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 122 { 123 Assert.Equal(1, client.NumberOfServerInstances); 124 } 125 server.Write(msg1, 0, msg1.Length); 126 int receivedLength = await clientTask; 127 Assert.Equal(msg1.Length, receivedLength); 128 Assert.Equal(msg1, received1); 129 } 130 } 131 else 132 { 133 Task clientTask = client.WriteAsync(msg1, 0, msg1.Length); 134 using (NamedPipeServerStream server = new NamedPipeServerStream(PipeDirection.In, false, true, serverBase.SafePipeHandle)) 135 { 136 int receivedLength = server.Read(received1, 0, msg1.Length); 137 Assert.Equal(msg1.Length, receivedLength); 138 Assert.Equal(msg1, received1); 139 await clientTask; 140 } 141 } 142 } 143 } 144 145 [Fact] ClonedClient_ActsAsOriginalClient()146 public async Task ClonedClient_ActsAsOriginalClient() 147 { 148 byte[] msg1 = new byte[] { 5, 7, 9, 10 }; 149 byte[] received1 = new byte[] { 0, 0, 0, 0 }; 150 151 using (NamedPipePair pair = CreateNamedPipePair()) 152 { 153 pair.Connect(); 154 NamedPipeServerStream server = pair.serverStream; 155 if (pair.writeToServer) 156 { 157 using (NamedPipeClientStream client = new NamedPipeClientStream(PipeDirection.In, false, true, pair.clientStream.SafePipeHandle)) 158 { 159 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 160 { 161 Assert.Equal(1, client.NumberOfServerInstances); 162 } 163 Task<int> clientTask = client.ReadAsync(received1, 0, received1.Length); 164 server.Write(msg1, 0, msg1.Length); 165 int receivedLength = await clientTask; 166 Assert.Equal(msg1.Length, receivedLength); 167 Assert.Equal(msg1, received1); 168 } 169 } 170 else 171 { 172 using (NamedPipeClientStream client = new NamedPipeClientStream(PipeDirection.Out, false, true, pair.clientStream.SafePipeHandle)) 173 { 174 Task clientTask = client.WriteAsync(msg1, 0, msg1.Length); 175 int receivedLength = server.Read(received1, 0, msg1.Length); 176 Assert.Equal(msg1.Length, receivedLength); 177 Assert.Equal(msg1, received1); 178 await clientTask; 179 } 180 } 181 } 182 } 183 184 [Fact] ConnectOnAlreadyConnectedClient_Throws_InvalidOperationException()185 public void ConnectOnAlreadyConnectedClient_Throws_InvalidOperationException() 186 { 187 using (NamedPipePair pair = CreateNamedPipePair()) 188 { 189 NamedPipeServerStream server = pair.serverStream; 190 NamedPipeClientStream client = pair.clientStream; 191 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 192 193 pair.Connect(); 194 195 Assert.True(client.IsConnected); 196 Assert.True(server.IsConnected); 197 198 Assert.Throws<InvalidOperationException>(() => client.Connect()); 199 } 200 } 201 202 [Fact] WaitForConnectionOnAlreadyConnectedServer_Throws_InvalidOperationException()203 public void WaitForConnectionOnAlreadyConnectedServer_Throws_InvalidOperationException() 204 { 205 using (NamedPipePair pair = CreateNamedPipePair()) 206 { 207 NamedPipeServerStream server = pair.serverStream; 208 NamedPipeClientStream client = pair.clientStream; 209 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 210 211 pair.Connect(); 212 213 Assert.True(client.IsConnected); 214 Assert.True(server.IsConnected); 215 216 Assert.Throws<InvalidOperationException>(() => server.WaitForConnection()); 217 } 218 } 219 220 [Fact] CancelTokenOn_ServerWaitForConnectionAsync_Throws_OperationCanceledException()221 public async Task CancelTokenOn_ServerWaitForConnectionAsync_Throws_OperationCanceledException() 222 { 223 using (NamedPipePair pair = CreateNamedPipePair()) 224 { 225 NamedPipeServerStream server = pair.serverStream; 226 var ctx = new CancellationTokenSource(); 227 228 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // cancellation token after the operation has been initiated 229 { 230 Task serverWaitTimeout = server.WaitForConnectionAsync(ctx.Token); 231 ctx.Cancel(); 232 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverWaitTimeout); 233 } 234 235 ctx.Cancel(); 236 Assert.True(server.WaitForConnectionAsync(ctx.Token).IsCanceled); 237 } 238 } 239 240 [Fact] 241 [PlatformSpecific(TestPlatforms.Windows)] // P/Invoking to Win32 functions CancelTokenOff_ServerWaitForConnectionAsyncWithOuterCancellation_Throws_OperationCanceledException()242 public async Task CancelTokenOff_ServerWaitForConnectionAsyncWithOuterCancellation_Throws_OperationCanceledException() 243 { 244 using (NamedPipePair pair = CreateNamedPipePair()) 245 { 246 NamedPipeServerStream server = pair.serverStream; 247 Task waitForConnectionTask = server.WaitForConnectionAsync(CancellationToken.None); 248 249 Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed"); 250 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => waitForConnectionTask); 251 Assert.True(waitForConnectionTask.IsCanceled); 252 } 253 } 254 255 [Fact] 256 [PlatformSpecific(TestPlatforms.Windows)] // P/Invoking to Win32 functions CancelTokenOn_ServerWaitForConnectionAsyncWithOuterCancellation_Throws_IOException()257 public async Task CancelTokenOn_ServerWaitForConnectionAsyncWithOuterCancellation_Throws_IOException() 258 { 259 using (NamedPipePair pair = CreateNamedPipePair()) 260 { 261 var cts = new CancellationTokenSource(); 262 NamedPipeServerStream server = pair.serverStream; 263 Task waitForConnectionTask = server.WaitForConnectionAsync(cts.Token); 264 265 Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed"); 266 await Assert.ThrowsAsync<IOException>(() => waitForConnectionTask); 267 } 268 } 269 270 [Fact] OperationsOnDisconnectedServer()271 public async Task OperationsOnDisconnectedServer() 272 { 273 using (NamedPipePair pair = CreateNamedPipePair()) 274 { 275 NamedPipeServerStream server = pair.serverStream; 276 pair.Connect(); 277 278 Assert.Throws<InvalidOperationException>(() => server.IsMessageComplete); 279 Assert.Throws<InvalidOperationException>(() => server.WaitForConnection()); 280 await Assert.ThrowsAsync<InvalidOperationException>(() => server.WaitForConnectionAsync()); // fails because allowed connections is set to 1 281 282 server.Disconnect(); 283 284 Assert.Throws<InvalidOperationException>(() => server.Disconnect()); // double disconnect 285 286 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 287 288 if (pair.writeToServer) 289 { 290 Assert.Throws<InvalidOperationException>(() => server.Write(buffer, 0, buffer.Length)); 291 Assert.Throws<InvalidOperationException>(() => server.WriteByte(5)); 292 Assert.Throws<InvalidOperationException>(() => { server.WriteAsync(buffer, 0, buffer.Length); }); 293 } 294 else 295 { 296 Assert.Throws<InvalidOperationException>(() => server.Read(buffer, 0, buffer.Length)); 297 Assert.Throws<InvalidOperationException>(() => server.ReadByte()); 298 Assert.Throws<InvalidOperationException>(() => { server.ReadAsync(buffer, 0, buffer.Length); }); 299 } 300 301 Assert.Throws<InvalidOperationException>(() => server.Flush()); 302 Assert.Throws<InvalidOperationException>(() => server.IsMessageComplete); 303 Assert.Throws<InvalidOperationException>(() => server.GetImpersonationUserName()); 304 } 305 } 306 307 [Fact] OperationsOnDisconnectedClient()308 public virtual async Task OperationsOnDisconnectedClient() 309 { 310 using (NamedPipePair pair = CreateNamedPipePair()) 311 { 312 NamedPipeServerStream server = pair.serverStream; 313 NamedPipeClientStream client = pair.clientStream; 314 pair.Connect(); 315 316 Assert.Throws<InvalidOperationException>(() => client.IsMessageComplete); 317 Assert.Throws<InvalidOperationException>(() => client.Connect()); 318 await Assert.ThrowsAsync<InvalidOperationException>(() => client.ConnectAsync()); 319 320 server.Disconnect(); 321 322 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 323 324 if (!pair.writeToServer) 325 { 326 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // writes on Unix may still succeed after other end disconnects, due to socket being used 327 { 328 // Pipe is broken 329 Assert.Throws<IOException>(() => client.Write(buffer, 0, buffer.Length)); 330 Assert.Throws<IOException>(() => client.WriteByte(5)); 331 Assert.Throws<IOException>(() => { client.WriteAsync(buffer, 0, buffer.Length); }); 332 Assert.Throws<IOException>(() => client.Flush()); 333 Assert.Throws<IOException>(() => client.NumberOfServerInstances); 334 } 335 } 336 else 337 { 338 // Nothing for the client to read, but no exception throwing 339 Assert.Equal(0, client.Read(buffer, 0, buffer.Length)); 340 Assert.Equal(-1, client.ReadByte()); 341 342 if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // NumberOfServerInstances not supported on Unix 343 { 344 Assert.Throws<PlatformNotSupportedException>(() => client.NumberOfServerInstances); 345 } 346 } 347 348 Assert.Throws<InvalidOperationException>(() => client.IsMessageComplete); 349 } 350 } 351 352 [Fact] 353 [PlatformSpecific(TestPlatforms.Windows)] // Unix implemented on sockets, where disposal information doesn't propagate Windows_OperationsOnNamedServerWithDisposedClient()354 public async Task Windows_OperationsOnNamedServerWithDisposedClient() 355 { 356 using (NamedPipePair pair = CreateNamedPipePair()) 357 { 358 NamedPipeServerStream server = pair.serverStream; 359 pair.Connect(); 360 pair.clientStream.Dispose(); 361 362 Assert.Throws<IOException>(() => server.WaitForConnection()); 363 await Assert.ThrowsAsync<IOException>(() => server.WaitForConnectionAsync()); 364 Assert.Throws<IOException>(() => server.GetImpersonationUserName()); 365 } 366 } 367 368 [Fact] 369 [PlatformSpecific(TestPlatforms.AnyUnix)] // Unix implemented on sockets, where disposal information doesn't propagate Unix_OperationsOnNamedServerWithDisposedClient()370 public async Task Unix_OperationsOnNamedServerWithDisposedClient() 371 { 372 using (NamedPipePair pair = CreateNamedPipePair()) 373 { 374 NamedPipeServerStream server = pair.serverStream; 375 pair.Connect(); 376 pair.clientStream.Dispose(); 377 378 // On Unix, the server still thinks that it is connected after client Disposal. 379 Assert.Throws<InvalidOperationException>(() => server.WaitForConnection()); 380 await Assert.ThrowsAsync<InvalidOperationException>(() => server.WaitForConnectionAsync()); 381 Assert.NotNull(server.GetImpersonationUserName()); 382 } 383 } 384 385 [Fact] OperationsOnUnconnectedServer()386 public void OperationsOnUnconnectedServer() 387 { 388 using (NamedPipePair pair = CreateNamedPipePair()) 389 { 390 NamedPipeServerStream server = pair.serverStream; 391 392 // doesn't throw exceptions 393 PipeTransmissionMode transmitMode = server.TransmissionMode; 394 Assert.Throws<ArgumentOutOfRangeException>(() => server.ReadMode = (PipeTransmissionMode)999); 395 396 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 397 398 if (pair.writeToServer) 399 { 400 Assert.Equal(0, server.OutBufferSize); 401 Assert.Throws<InvalidOperationException>(() => server.Write(buffer, 0, buffer.Length)); 402 Assert.Throws<InvalidOperationException>(() => server.WriteByte(5)); 403 Assert.Throws<InvalidOperationException>(() => { server.WriteAsync(buffer, 0, buffer.Length); }); 404 } 405 else 406 { 407 Assert.Equal(0, server.InBufferSize); 408 PipeTransmissionMode readMode = server.ReadMode; 409 Assert.Throws<InvalidOperationException>(() => server.Read(buffer, 0, buffer.Length)); 410 Assert.Throws<InvalidOperationException>(() => server.ReadByte()); 411 Assert.Throws<InvalidOperationException>(() => { server.ReadAsync(buffer, 0, buffer.Length); }); 412 } 413 414 Assert.Throws<InvalidOperationException>(() => server.Disconnect()); // disconnect when not connected 415 Assert.Throws<InvalidOperationException>(() => server.IsMessageComplete); 416 } 417 } 418 419 [Fact] OperationsOnUnconnectedClient()420 public void OperationsOnUnconnectedClient() 421 { 422 using (NamedPipePair pair = CreateNamedPipePair()) 423 { 424 NamedPipeClientStream client = pair.clientStream; 425 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 426 427 if (client.CanRead) 428 { 429 Assert.Throws<InvalidOperationException>(() => client.Read(buffer, 0, buffer.Length)); 430 Assert.Throws<InvalidOperationException>(() => client.ReadByte()); 431 Assert.Throws<InvalidOperationException>(() => { client.ReadAsync(buffer, 0, buffer.Length); }); 432 Assert.Throws<InvalidOperationException>(() => client.ReadMode); 433 Assert.Throws<InvalidOperationException>(() => client.ReadMode = PipeTransmissionMode.Byte); 434 } 435 if (client.CanWrite) 436 { 437 Assert.Throws<InvalidOperationException>(() => client.Write(buffer, 0, buffer.Length)); 438 Assert.Throws<InvalidOperationException>(() => client.WriteByte(5)); 439 Assert.Throws<InvalidOperationException>(() => { client.WriteAsync(buffer, 0, buffer.Length); }); 440 } 441 442 Assert.Throws<InvalidOperationException>(() => client.NumberOfServerInstances); 443 Assert.Throws<InvalidOperationException>(() => client.TransmissionMode); 444 Assert.Throws<InvalidOperationException>(() => client.InBufferSize); 445 Assert.Throws<InvalidOperationException>(() => client.OutBufferSize); 446 Assert.Throws<InvalidOperationException>(() => client.SafePipeHandle); 447 } 448 } 449 450 [Fact] DisposedServerPipe_Throws_ObjectDisposedException()451 public async Task DisposedServerPipe_Throws_ObjectDisposedException() 452 { 453 using (NamedPipePair pair = CreateNamedPipePair()) 454 { 455 NamedPipeServerStream pipe = pair.serverStream; 456 pipe.Dispose(); 457 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 458 459 Assert.Throws<ObjectDisposedException>(() => pipe.Disconnect()); 460 Assert.Throws<ObjectDisposedException>(() => pipe.GetImpersonationUserName()); 461 Assert.Throws<ObjectDisposedException>(() => pipe.WaitForConnection()); 462 await Assert.ThrowsAsync<ObjectDisposedException>(() => pipe.WaitForConnectionAsync()); 463 } 464 } 465 466 [Fact] DisposedClientPipe_Throws_ObjectDisposedException()467 public async Task DisposedClientPipe_Throws_ObjectDisposedException() 468 { 469 using (NamedPipePair pair = CreateNamedPipePair()) 470 { 471 pair.Connect(); 472 NamedPipeClientStream pipe = pair.clientStream; 473 pipe.Dispose(); 474 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 475 476 Assert.Throws<ObjectDisposedException>(() => pipe.Connect()); 477 await Assert.ThrowsAsync<ObjectDisposedException>(() => pipe.ConnectAsync()); 478 Assert.Throws<ObjectDisposedException>(() => pipe.NumberOfServerInstances); 479 } 480 } 481 482 [Fact] ReadAsync_DisconnectDuringRead_Returns0()483 public async Task ReadAsync_DisconnectDuringRead_Returns0() 484 { 485 using (NamedPipePair pair = CreateNamedPipePair()) 486 { 487 pair.Connect(); 488 Task<int> readTask; 489 if (pair.clientStream.CanRead) 490 { 491 readTask = pair.clientStream.ReadAsync(new byte[1], 0, 1); 492 pair.serverStream.Dispose(); 493 } 494 else 495 { 496 readTask = pair.serverStream.ReadAsync(new byte[1], 0, 1); 497 pair.clientStream.Dispose(); 498 } 499 Assert.Equal(0, await readTask); 500 } 501 } 502 503 [PlatformSpecific(TestPlatforms.Windows)] // Unix named pipes are on sockets, where small writes with an empty buffer will succeed immediately 504 [Fact] WriteAsync_DisconnectDuringWrite_Throws()505 public async Task WriteAsync_DisconnectDuringWrite_Throws() 506 { 507 using (NamedPipePair pair = CreateNamedPipePair()) 508 { 509 pair.Connect(); 510 Task writeTask; 511 if (pair.clientStream.CanWrite) 512 { 513 writeTask = pair.clientStream.WriteAsync(new byte[1], 0, 1); 514 pair.serverStream.Dispose(); 515 } 516 else 517 { 518 writeTask = pair.serverStream.WriteAsync(new byte[1], 0, 1); 519 pair.clientStream.Dispose(); 520 } 521 await Assert.ThrowsAsync<IOException>(() => writeTask); 522 } 523 } 524 525 [Fact] 526 [ActiveIssue("dotnet/corefx #16934", TargetFrameworkMonikers.NetFramework)] //Hangs forever in desktop as it doesn't have cancellation support Server_ReadWriteCancelledToken_Throws_OperationCanceledException()527 public async Task Server_ReadWriteCancelledToken_Throws_OperationCanceledException() 528 { 529 using (NamedPipePair pair = CreateNamedPipePair()) 530 { 531 NamedPipeServerStream server = pair.serverStream; 532 NamedPipeClientStream client = pair.clientStream; 533 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 534 535 pair.Connect(); 536 537 if (server.CanRead && client.CanWrite) 538 { 539 var ctx1 = new CancellationTokenSource(); 540 541 Task<int> serverReadToken = server.ReadAsync(buffer, 0, buffer.Length, ctx1.Token); 542 ctx1.Cancel(); 543 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverReadToken); 544 545 ctx1.Cancel(); 546 Assert.True(server.ReadAsync(buffer, 0, buffer.Length, ctx1.Token).IsCanceled); 547 } 548 549 if (server.CanWrite) 550 { 551 var ctx1 = new CancellationTokenSource(); 552 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // On Unix WriteAsync's aren't cancelable once initiated 553 { 554 Task serverWriteToken = server.WriteAsync(buffer, 0, buffer.Length, ctx1.Token); 555 ctx1.Cancel(); 556 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverWriteToken); 557 } 558 ctx1.Cancel(); 559 Assert.True(server.WriteAsync(buffer, 0, buffer.Length, ctx1.Token).IsCanceled); 560 } 561 } 562 } 563 564 [Fact] 565 [PlatformSpecific(TestPlatforms.Windows)] // P/Invoking to Win32 functions CancelTokenOff_Server_ReadWriteCancelledToken_Throws_OperationCanceledException()566 public async Task CancelTokenOff_Server_ReadWriteCancelledToken_Throws_OperationCanceledException() 567 { 568 using (NamedPipePair pair = CreateNamedPipePair()) 569 { 570 NamedPipeServerStream server = pair.serverStream; 571 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 572 573 pair.Connect(); 574 575 if (server.CanRead) 576 { 577 Task serverReadToken = server.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None); 578 579 Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed"); 580 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverReadToken); 581 Assert.True(serverReadToken.IsCanceled); 582 } 583 if (server.CanWrite) 584 { 585 Task serverWriteToken = server.WriteAsync(buffer, 0, buffer.Length, CancellationToken.None); 586 587 Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed"); 588 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverWriteToken); 589 Assert.True(serverWriteToken.IsCanceled); 590 } 591 } 592 } 593 594 [Fact] 595 [PlatformSpecific(TestPlatforms.Windows)] // P/Invoking to Win32 functions CancelTokenOn_Server_ReadWriteCancelledToken_Throws_OperationCanceledException()596 public async Task CancelTokenOn_Server_ReadWriteCancelledToken_Throws_OperationCanceledException() 597 { 598 using (NamedPipePair pair = CreateNamedPipePair()) 599 { 600 NamedPipeServerStream server = pair.serverStream; 601 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 602 603 pair.Connect(); 604 605 if (server.CanRead) 606 { 607 var cts = new CancellationTokenSource(); 608 Task serverReadToken = server.ReadAsync(buffer, 0, buffer.Length, cts.Token); 609 610 Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed"); 611 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverReadToken); 612 } 613 if (server.CanWrite) 614 { 615 var cts = new CancellationTokenSource(); 616 Task serverWriteToken = server.WriteAsync(buffer, 0, buffer.Length, cts.Token); 617 618 Assert.True(Interop.CancelIoEx(server.SafePipeHandle), "Outer cancellation failed"); 619 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverWriteToken); 620 } 621 } 622 } 623 624 [Fact] 625 [ActiveIssue("dotnet/corefx #16934", TargetFrameworkMonikers.NetFramework)] //Hangs forever in desktop as it doesn't have cancellation support Client_ReadWriteCancelledToken_Throws_OperationCanceledException()626 public async Task Client_ReadWriteCancelledToken_Throws_OperationCanceledException() 627 { 628 using (NamedPipePair pair = CreateNamedPipePair()) 629 { 630 NamedPipeClientStream client = pair.clientStream; 631 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 632 633 pair.Connect(); 634 635 if (client.CanRead) 636 { 637 var ctx1 = new CancellationTokenSource(); 638 639 Task serverReadToken = client.ReadAsync(buffer, 0, buffer.Length, ctx1.Token); 640 ctx1.Cancel(); 641 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverReadToken); 642 643 Assert.True(client.ReadAsync(buffer, 0, buffer.Length, ctx1.Token).IsCanceled); 644 } 645 646 if (client.CanWrite) 647 { 648 var ctx1 = new CancellationTokenSource(); 649 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // On Unix WriteAsync's aren't cancelable once initiated 650 { 651 Task serverWriteToken = client.WriteAsync(buffer, 0, buffer.Length, ctx1.Token); 652 ctx1.Cancel(); 653 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverWriteToken); 654 } 655 ctx1.Cancel(); 656 Assert.True(client.WriteAsync(buffer, 0, buffer.Length, ctx1.Token).IsCanceled); 657 } 658 } 659 } 660 661 [Fact] 662 [PlatformSpecific(TestPlatforms.Windows)] // P/Invoking to Win32 functions CancelTokenOff_Client_ReadWriteCancelledToken_Throws_OperationCanceledException()663 public async Task CancelTokenOff_Client_ReadWriteCancelledToken_Throws_OperationCanceledException() 664 { 665 using (NamedPipePair pair = CreateNamedPipePair()) 666 { 667 NamedPipeClientStream client = pair.clientStream; 668 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 669 670 pair.Connect(); 671 if (client.CanRead) 672 { 673 Task clientReadToken = client.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None); 674 675 Assert.True(Interop.CancelIoEx(client.SafePipeHandle), "Outer cancellation failed"); 676 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => clientReadToken); 677 Assert.True(clientReadToken.IsCanceled); 678 } 679 if (client.CanWrite) 680 { 681 Task clientWriteToken = client.WriteAsync(buffer, 0, buffer.Length, CancellationToken.None); 682 683 Assert.True(Interop.CancelIoEx(client.SafePipeHandle), "Outer cancellation failed"); 684 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => clientWriteToken); 685 Assert.True(clientWriteToken.IsCanceled); 686 } 687 } 688 } 689 690 [Fact] 691 [PlatformSpecific(TestPlatforms.Windows)] // P/Invoking to Win32 functions CancelTokenOn_Client_ReadWriteCancelledToken_Throws_OperationCanceledException()692 public async Task CancelTokenOn_Client_ReadWriteCancelledToken_Throws_OperationCanceledException() 693 { 694 using (NamedPipePair pair = CreateNamedPipePair()) 695 { 696 NamedPipeClientStream client = pair.clientStream; 697 byte[] buffer = new byte[] { 0, 0, 0, 0 }; 698 699 pair.Connect(); 700 if (client.CanRead) 701 { 702 var cts = new CancellationTokenSource(); 703 Task clientReadToken = client.ReadAsync(buffer, 0, buffer.Length, cts.Token); 704 705 Assert.True(Interop.CancelIoEx(client.SafePipeHandle), "Outer cancellation failed"); 706 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => clientReadToken); 707 } 708 if (client.CanWrite) 709 { 710 var cts = new CancellationTokenSource(); 711 Task clientWriteToken = client.WriteAsync(buffer, 0, buffer.Length, cts.Token); 712 713 Assert.True(Interop.CancelIoEx(client.SafePipeHandle), "Outer cancellation failed"); 714 await Assert.ThrowsAnyAsync<OperationCanceledException>(() => clientWriteToken); 715 } 716 } 717 } 718 719 [Theory] 720 [InlineData(true)] 721 [InlineData(false)] ManyConcurrentOperations(bool cancelable)722 public async Task ManyConcurrentOperations(bool cancelable) 723 { 724 using (NamedPipePair pair = CreateNamedPipePair(PipeOptions.Asynchronous, PipeOptions.Asynchronous)) 725 { 726 await Task.WhenAll(pair.serverStream.WaitForConnectionAsync(), pair.clientStream.ConnectAsync()); 727 728 const int NumOps = 100; 729 const int DataPerOp = 512; 730 byte[] sendingData = new byte[NumOps * DataPerOp]; 731 byte[] readingData = new byte[sendingData.Length]; 732 new Random().NextBytes(sendingData); 733 var cancellationToken = cancelable ? new CancellationTokenSource().Token : CancellationToken.None; 734 735 Stream reader = pair.writeToServer ? (Stream)pair.clientStream : pair.serverStream; 736 Stream writer = pair.writeToServer ? (Stream)pair.serverStream : pair.clientStream; 737 738 var reads = new Task<int>[NumOps]; 739 var writes = new Task[NumOps]; 740 741 for (int i = 0; i < reads.Length; i++) 742 reads[i] = reader.ReadAsync(readingData, i * DataPerOp, DataPerOp, cancellationToken); 743 for (int i = 0; i < reads.Length; i++) 744 writes[i] = writer.WriteAsync(sendingData, i * DataPerOp, DataPerOp, cancellationToken); 745 746 const int WaitTimeout = 30000; 747 Assert.True(Task.WaitAll(writes, WaitTimeout)); 748 Assert.True(Task.WaitAll(reads, WaitTimeout)); 749 750 // The data of each write may not be written atomically, and as such some of the data may be 751 // interleaved rather than entirely in the order written. 752 Assert.Equal(sendingData.OrderBy(b => b), readingData.OrderBy(b => b)); 753 } 754 } 755 } 756 757 [ActiveIssue(22271, TargetFrameworkMonikers.UapNotUapAot)] 758 public class NamedPipeTest_Simple_ServerInOutRead_ClientInOutWrite : NamedPipeTest_Simple 759 { CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions)760 protected override NamedPipePair CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions) 761 { 762 NamedPipePair ret = new NamedPipePair(); 763 string pipeName = GetUniquePipeName(); 764 ret.serverStream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, serverOptions); 765 ret.clientStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, clientOptions); 766 ret.writeToServer = false; 767 return ret; 768 } 769 } 770 771 [ActiveIssue(22271, TargetFrameworkMonikers.UapNotUapAot)] 772 public class NamedPipeTest_Simple_ServerInOutWrite_ClientInOutRead : NamedPipeTest_Simple 773 { CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions)774 protected override NamedPipePair CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions) 775 { 776 NamedPipePair ret = new NamedPipePair(); 777 string pipeName = GetUniquePipeName(); 778 ret.serverStream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, serverOptions); 779 ret.clientStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, clientOptions); 780 ret.writeToServer = true; 781 return ret; 782 } 783 } 784 785 [ActiveIssue(22271, TargetFrameworkMonikers.UapNotUapAot)] 786 public class NamedPipeTest_Simple_ServerInOut_ClientIn : NamedPipeTest_Simple 787 { CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions)788 protected override NamedPipePair CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions) 789 { 790 NamedPipePair ret = new NamedPipePair(); 791 string pipeName = GetUniquePipeName(); 792 ret.serverStream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, serverOptions); 793 ret.clientStream = new NamedPipeClientStream(".", pipeName, PipeDirection.In, clientOptions); 794 ret.writeToServer = true; 795 return ret; 796 } 797 } 798 799 [ActiveIssue(22271, TargetFrameworkMonikers.UapNotUapAot)] 800 public class NamedPipeTest_Simple_ServerInOut_ClientOut : NamedPipeTest_Simple 801 { CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions)802 protected override NamedPipePair CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions) 803 { 804 NamedPipePair ret = new NamedPipePair(); 805 string pipeName = GetUniquePipeName(); 806 ret.serverStream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, serverOptions); 807 ret.clientStream = new NamedPipeClientStream(".", pipeName, PipeDirection.Out, clientOptions); 808 ret.writeToServer = false; 809 return ret; 810 } 811 } 812 813 [ActiveIssue(22271, TargetFrameworkMonikers.UapNotUapAot)] 814 public class NamedPipeTest_Simple_ServerOut_ClientIn : NamedPipeTest_Simple 815 { CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions)816 protected override NamedPipePair CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions) 817 { 818 NamedPipePair ret = new NamedPipePair(); 819 string pipeName = GetUniquePipeName(); 820 ret.serverStream = new NamedPipeServerStream(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, serverOptions); 821 ret.clientStream = new NamedPipeClientStream(".", pipeName, PipeDirection.In, clientOptions); 822 ret.writeToServer = true; 823 return ret; 824 } 825 } 826 827 [ActiveIssue(22271, TargetFrameworkMonikers.UapNotUapAot)] 828 public class NamedPipeTest_Simple_ServerIn_ClientOut : NamedPipeTest_Simple 829 { CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions)830 protected override NamedPipePair CreateNamedPipePair(PipeOptions serverOptions, PipeOptions clientOptions) 831 { 832 NamedPipePair ret = new NamedPipePair(); 833 string pipeName = GetUniquePipeName(); 834 ret.serverStream = new NamedPipeServerStream(pipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, serverOptions); 835 ret.clientStream = new NamedPipeClientStream(".", pipeName, PipeDirection.Out, clientOptions); 836 ret.writeToServer = false; 837 return ret; 838 } 839 } 840 841 } 842