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