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 Xunit; 6 7 namespace System.IO.Pipes.Tests 8 { 9 /// <summary> 10 /// The class that all Pipes tests will inherit from. 11 /// 12 /// Contains methods and variables with code that is frequently repeated 13 /// </summary> 14 public class PipeTestBase 15 { 16 protected static byte[] sendBytes = new byte[] { 123, 234 }; 17 DoStreamOperations(PipeStream stream)18 protected static void DoStreamOperations(PipeStream stream) 19 { 20 if (stream.CanWrite) 21 { 22 stream.Write(new byte[] { 123, 124 }, 0, 2); 23 } 24 if (stream.CanRead) 25 { 26 Assert.Equal(123, stream.ReadByte()); 27 Assert.Equal(124, stream.ReadByte()); 28 } 29 } 30 SuppressClientHandleFinalizationIfNetFramework(AnonymousPipeServerStream serverStream)31 protected static void SuppressClientHandleFinalizationIfNetFramework(AnonymousPipeServerStream serverStream) 32 { 33 if (PlatformDetection.IsFullFramework) 34 { 35 // See https://github.com/dotnet/corefx/pull/1871. When AnonymousPipeServerStream.GetClientHandleAsString() 36 // is called, the assumption is that this string is going to be passed to another process, rather than wrapped 37 // into a SafeHandle in the same process. If it's wrapped into a SafeHandle in the same process, there are then 38 // two SafeHandles that believe they own the same underlying handle, which leads to use-after-free and recycling 39 // bugs. AnonymousPipeServerStream incorrectly deals with this in desktop: it marks the SafeHandle as having 40 // been exposed, but that then only prevents the disposal of AnonymousPipeServerStream from calling Dispose 41 // on the SafeHandle... it doesn't prevent the SafeHandle itself from getting finalized, which leads to random 42 // "The handle is invalid" or "Pipe is broken" errors at some later point when the handle is recycled and used 43 // for another instance. In core, this was addressed in 1871 by calling GC.SuppressFinalize(_clientHandle) 44 // in GetClientHandleAsString. For desktop, we work around this by suppressing the handle in this explicit call. 45 GC.SuppressFinalize(serverStream.ClientSafePipeHandle); 46 } 47 } 48 49 /// <summary> 50 /// Represents a Server-Client pair where "readablePipe" refers to whichever 51 /// of the two streams is defined with PipeDirection.In and "writeablePipe" is 52 /// defined with PipeDirection.Out. 53 /// </summary> 54 /// <remarks> 55 /// For tests where InOut is used, writeablePipe will refer to whichever pipe 56 /// the tests should be treating as the one with PipeDirection.Out. 57 /// </remarks> 58 protected class ServerClientPair : IDisposable 59 { 60 public PipeStream readablePipe; 61 public PipeStream writeablePipe; 62 Dispose()63 public void Dispose() 64 { 65 try 66 { 67 if (readablePipe != null) 68 readablePipe.Dispose(); 69 } 70 finally 71 { 72 writeablePipe.Dispose(); 73 } 74 } 75 } 76 77 /// <summary> 78 /// Get a unique pipe name very unlikely to be in use elsewhere. 79 /// </summary> 80 /// <returns></returns> GetUniquePipeName()81 protected static string GetUniquePipeName() 82 { 83 if (PlatformDetection.IsInAppContainer) 84 { 85 return @"LOCAL\" + Path.GetRandomFileName(); 86 } 87 else 88 { 89 return Path.GetRandomFileName(); 90 } 91 } 92 93 /// <summary> 94 /// Virtual method to create a Server-Client PipeStream pair 95 /// that the test methods can override and make use of. 96 /// 97 /// The default (in PipeTest) will return a null ServerClientPair. 98 /// </summary> CreateServerClientPair()99 protected virtual ServerClientPair CreateServerClientPair() 100 { 101 return null; 102 } 103 } 104 } 105