|
| 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 | + |
| 4 | +using Microsoft.DotNet.XUnitExtensions; |
| 5 | +using Microsoft.Win32.SafeHandles; |
| 6 | +using System.Collections.Generic; |
| 7 | +using System.IO.Pipes; |
| 8 | +using System.Runtime.InteropServices; |
| 9 | +using System.Text; |
| 10 | +using System.Threading.Tasks; |
| 11 | +using Xunit; |
| 12 | + |
| 13 | +namespace System.IO.Tests |
| 14 | +{ |
| 15 | + [PlatformSpecific(TestPlatforms.AnyUnix)] |
| 16 | + public class DevicesPipesAndSockets : FileSystemTest |
| 17 | + { |
| 18 | + [Theory] |
| 19 | + [MemberData(nameof(DevicePath_FileOptions_TestData))] |
| 20 | + public void CharacterDevice_FileStream_Write(string devicePath, FileOptions fileOptions) |
| 21 | + { |
| 22 | + FileStreamOptions options = new() { Options = fileOptions, Access = FileAccess.Write, Share = FileShare.Write }; |
| 23 | + using FileStream fs = new(devicePath, options); |
| 24 | + fs.Write(Encoding.UTF8.GetBytes("foo")); |
| 25 | + } |
| 26 | + |
| 27 | + [Theory] |
| 28 | + [MemberData(nameof(DevicePath_FileOptions_TestData))] |
| 29 | + public async Task CharacterDevice_FileStream_WriteAsync(string devicePath, FileOptions fileOptions) |
| 30 | + { |
| 31 | + FileStreamOptions options = new() { Options = fileOptions, Access = FileAccess.Write, Share = FileShare.Write }; |
| 32 | + using FileStream fs = new(devicePath, options); |
| 33 | + await fs.WriteAsync(Encoding.UTF8.GetBytes("foo")); |
| 34 | + } |
| 35 | + |
| 36 | + [Theory] |
| 37 | + [MemberData(nameof(DevicePath_TestData))] |
| 38 | + public void CharacterDevice_WriteAllBytes(string devicePath) |
| 39 | + { |
| 40 | + File.WriteAllBytes(devicePath, Encoding.UTF8.GetBytes("foo")); |
| 41 | + } |
| 42 | + |
| 43 | + [Theory] |
| 44 | + [MemberData(nameof(DevicePath_TestData))] |
| 45 | + public async Task CharacterDevice_WriteAllBytesAsync(string devicePath) |
| 46 | + { |
| 47 | + await File.WriteAllBytesAsync(devicePath, Encoding.UTF8.GetBytes("foo")); |
| 48 | + } |
| 49 | + |
| 50 | + [Theory] |
| 51 | + [MemberData(nameof(DevicePath_TestData))] |
| 52 | + public void CharacterDevice_WriteAllText(string devicePath) |
| 53 | + { |
| 54 | + File.WriteAllText(devicePath, "foo"); |
| 55 | + } |
| 56 | + |
| 57 | + [Theory] |
| 58 | + [MemberData(nameof(DevicePath_TestData))] |
| 59 | + public async Task CharacterDevice_WriteAllTextAsync(string devicePath) |
| 60 | + { |
| 61 | + await File.WriteAllTextAsync(devicePath, "foo"); |
| 62 | + } |
| 63 | + |
| 64 | + [Fact] |
| 65 | + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] |
| 66 | + public async Task NamedPipe_ReadWrite() |
| 67 | + { |
| 68 | + string fifoPath = GetTestFilePath(); |
| 69 | + Assert.Equal(0, mkfifo(fifoPath, 438 /* 666 in octal */ )); |
| 70 | + |
| 71 | + await Task.WhenAll( |
| 72 | + Task.Run(() => |
| 73 | + { |
| 74 | + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); |
| 75 | + ReadByte(fs, 42); |
| 76 | + }), |
| 77 | + Task.Run(() => |
| 78 | + { |
| 79 | + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Write, FileShare.Read); |
| 80 | + WriteByte(fs, 42); |
| 81 | + })); |
| 82 | + } |
| 83 | + |
| 84 | + [Fact] |
| 85 | + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] |
| 86 | + public async Task NamedPipe_ReadWrite_Async() |
| 87 | + { |
| 88 | + string fifoPath = GetTestFilePath(); |
| 89 | + Assert.Equal(0, mkfifo(fifoPath, 438 /* 666 in octal */ )); |
| 90 | + |
| 91 | + await Task.WhenAll( |
| 92 | + Task.Run(async () => { |
| 93 | + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); |
| 94 | + await ReadByteAsync(fs, 42); |
| 95 | + }), |
| 96 | + Task.Run(async () => |
| 97 | + { |
| 98 | + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Write, FileShare.Read); |
| 99 | + await WriteByteAsync(fs, 42); |
| 100 | + })); |
| 101 | + } |
| 102 | + |
| 103 | + private const int AF_UNIX = 1; |
| 104 | + private const int SOCK_STREAM = 1; |
| 105 | + |
| 106 | + [Fact] |
| 107 | + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] |
| 108 | + public unsafe void SocketPair_ReadWrite() |
| 109 | + { |
| 110 | + int* ptr = stackalloc int[2]; |
| 111 | + Assert.Equal(0, socketpair(AF_UNIX, SOCK_STREAM, 0, ptr)); |
| 112 | + |
| 113 | + using var readFileStream = new FileStream(new SafeFileHandle((IntPtr)ptr[0], ownsHandle: true), FileAccess.Read); |
| 114 | + using var writeFileStream = new FileStream(new SafeFileHandle((IntPtr)ptr[1], ownsHandle: true), FileAccess.Write); |
| 115 | + |
| 116 | + Task.WhenAll( |
| 117 | + Task.Run(() => ReadByte(readFileStream, 42)), |
| 118 | + Task.Run(() => WriteByte(writeFileStream, 42))).GetAwaiter().GetResult(); |
| 119 | + } |
| 120 | + |
| 121 | + [Fact] |
| 122 | + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] |
| 123 | + public void SocketPair_ReadWrite_Async() |
| 124 | + { |
| 125 | + unsafe |
| 126 | + { |
| 127 | + int* ptr = stackalloc int[2]; |
| 128 | + Assert.Equal(0, socketpair(AF_UNIX, SOCK_STREAM, 0, ptr)); |
| 129 | + |
| 130 | + using var readFileStream = new FileStream(new SafeFileHandle((IntPtr)ptr[0], ownsHandle: true), FileAccess.Read); |
| 131 | + using var writeFileStream = new FileStream(new SafeFileHandle((IntPtr)ptr[1], ownsHandle: true), FileAccess.Write); |
| 132 | + |
| 133 | + Task.WhenAll( |
| 134 | + ReadByteAsync(readFileStream, 42), |
| 135 | + WriteByteAsync(writeFileStream, 42)).GetAwaiter().GetResult(); |
| 136 | + } |
| 137 | + } |
| 138 | + |
| 139 | + private static void ReadByte(FileStream fs, byte expected) |
| 140 | + { |
| 141 | + var buffer = new byte[1]; |
| 142 | + Assert.Equal(1, fs.Read(buffer)); |
| 143 | + Assert.Equal(expected, buffer[0]); |
| 144 | + } |
| 145 | + |
| 146 | + private static void WriteByte(FileStream fs, byte value) |
| 147 | + { |
| 148 | + fs.Write(new byte[] { value }); |
| 149 | + fs.Flush(); |
| 150 | + } |
| 151 | + |
| 152 | + private static async Task ReadByteAsync(FileStream fs, byte expected) |
| 153 | + { |
| 154 | + var buffer = new byte[1]; |
| 155 | + Assert.Equal(1, await fs.ReadAsync(buffer)); |
| 156 | + Assert.Equal(expected, buffer[0]); |
| 157 | + } |
| 158 | + |
| 159 | + private static async Task WriteByteAsync(FileStream fs, byte value) |
| 160 | + { |
| 161 | + await fs.WriteAsync(new byte[] { value }); |
| 162 | + await fs.FlushAsync(); |
| 163 | + } |
| 164 | + |
| 165 | + private static Lazy<IEnumerable<string>> AvailableDevicePaths = new Lazy<IEnumerable<string>>(() => |
| 166 | + { |
| 167 | + List<string> paths = new(); |
| 168 | + FileStreamOptions options = new() { Access = FileAccess.Write, Share = FileShare.Write }; |
| 169 | + |
| 170 | + foreach (string devicePath in new[] { "/dev/tty", "/dev/console", "/dev/null", "/dev/zero" }) |
| 171 | + { |
| 172 | + if (!File.Exists(devicePath)) |
| 173 | + { |
| 174 | + continue; |
| 175 | + } |
| 176 | + |
| 177 | + try |
| 178 | + { |
| 179 | + File.Open(devicePath, options).Dispose(); |
| 180 | + } |
| 181 | + catch (Exception ex) |
| 182 | + { |
| 183 | + if (ex is IOException || ex is UnauthorizedAccessException) |
| 184 | + { |
| 185 | + continue; |
| 186 | + } |
| 187 | + |
| 188 | + throw; |
| 189 | + } |
| 190 | + |
| 191 | + paths.Add(devicePath); |
| 192 | + } |
| 193 | + |
| 194 | + return paths; |
| 195 | + }); |
| 196 | + |
| 197 | + public static IEnumerable<object[]> DevicePath_FileOptions_TestData() |
| 198 | + { |
| 199 | + foreach (string devicePath in AvailableDevicePaths.Value) |
| 200 | + { |
| 201 | + foreach (FileOptions options in new[] { FileOptions.None, FileOptions.Asynchronous }) |
| 202 | + { |
| 203 | + yield return new object[] { devicePath, options}; |
| 204 | + } |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + public static IEnumerable<object[]> DevicePath_TestData() |
| 209 | + { |
| 210 | + foreach (string devicePath in AvailableDevicePaths.Value) |
| 211 | + { |
| 212 | + yield return new object[] { devicePath }; |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + [DllImport("libc")] |
| 217 | + private static unsafe extern int socketpair(int domain, int type, int protocol, int* ptr); |
| 218 | + } |
| 219 | +} |
0 commit comments