Repository: dotnet/runtime
File: src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenNullHandle.cs (C#)

Code before:
        [Fact]
        public void OpenNullHandle_MultipleWrites_AllSucceed()
        {
            using SafeFileHandle handle = File.OpenNullHandle();
            using FileStream stream = new FileStream(handle, FileAccess.Write);
            
            byte[] buffer = new byte[100];
            for (int i = 0; i < 10; i++)
            {
                // Should not throw
                stream.Write(buffer, 0, buffer.Length);
            }
        }

        [Fact]
        public void OpenNullHandle_ReadWriteAccess_BothWork()
        {
            using SafeFileHandle handle = File.OpenNullHandle();
            using FileStream stream = new FileStream(handle, FileAccess.ReadWrite);
            
            byte[] writeBuffer = new byte[100];
            byte[] readBuffer = new byte[100];
            
            // Write should succeed
            stream.Write(writeBuffer, 0, writeBuffer.Length);
            
            // Read should return 0
            int bytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
            Assert.Equal(0, bytesRead);
        }

        [Fact]
        public async Task OpenNullHandle_UseAsync_True_AsyncOperationsWork()
        {
            using SafeFileHandle handle = File.OpenNullHandle();
            using FileStream stream = new FileStream(handle, FileAccess.ReadWrite, 4096, true);
            
            byte[] writeBuffer = new byte[100];
            byte[] readBuffer = new byte[100];
            
            // Async write should succeed
            await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length);
            
            // Async read should return 0
            int bytesRead = await stream.ReadAsync(readBuffer, 0, readBuffer.Length);
            Assert.Equal(0, bytesRead);
        }

        [Fact]
        public void OpenNullHandle_MultipleHandles_AllWork()
        {

Review comment (suggestion):
To ensure that we don't lock the null device please add one more test that uses `File.OpenNullHandle()`, keeps it opened and then uses `RemoteExecutor` to start a new process that also opens the null device.

Code after:
            int bytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
            Assert.Equal(0, bytesRead);
        }

        [Fact]
        public async Task OpenNullHandle_ReadWriteAccess_BothAsyncOperationsWork()
        {
            using SafeFileHandle handle = File.OpenNullHandle();
            using FileStream stream = new FileStream(handle, FileAccess.ReadWrite);
            
            byte[] writeBuffer = new byte[100];
            byte[] readBuffer = new byte[100];
            
            // Write should succeed
            await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length);
            
            // Read should return 0
            int bytesRead = await stream.ReadAsync(readBuffer, 0, readBuffer.Length);
            Assert.Equal(0, bytesRead);
        }

        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
        public void OpenNullHandle_DoesNotLockNullDevice()
        {
            // Keep a handle open in the current process
            using SafeFileHandle handle1 = File.OpenNullHandle();
            using FileStream stream1 = new FileStream(handle1, FileAccess.ReadWrite);
            
            // Start a new process that also opens the null device
            RemoteExecutor.Invoke(() =>
            {
                using SafeFileHandle handle2 = File.OpenNullHandle();
                using FileStream stream2 = new FileStream(handle2, FileAccess.ReadWrite);
                
                byte[] buffer = new byte[100];
                
                // Both read and write should work in the child process
                stream2.Write(buffer, 0, buffer.Length);
                int bytesRead = stream2.Read(buffer, 0, buffer.Length);
                Assert.Equal(0, bytesRead);
            }).Dispose();
            
            // Original handle should still work after the child process exits
            byte[] buffer = new byte[100];
            stream1.Write(buffer, 0, buffer.Length);
            int bytesRead = stream1.Read(buffer, 0, buffer.Length);
            Assert.Equal(0, bytesRead);
        }
    }
}