Skip to content

Commit 7536707

Browse files
committed
fix the tests by accounting for partial writes and reads, remove reflection
1 parent ace2855 commit 7536707

File tree

1 file changed

+69
-35
lines changed
  • src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/RandomAccess

1 file changed

+69
-35
lines changed

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/RandomAccess/NonSeekable.cs

+69-35
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.IO.Pipes;
55
using System.Linq;
6+
using System.Runtime.CompilerServices;
67
using System.Security.Cryptography;
78
using System.Threading;
89
using System.Threading.Tasks;
@@ -96,7 +97,9 @@ public async Task ReadToAnEmptyBufferReturnsZeroWhenDataIsAvailable()
9697

9798
Assert.Equal(0, RandomAccess.Read(readHandle, Array.Empty<byte>(), fileOffset: 0)); // what we test
9899
byte[] buffer = new byte[content.Length * 2];
99-
Assert.Equal(content.Length, RandomAccess.Read(readHandle, buffer, fileOffset: 0)); // what is required for the above write to succeed
100+
101+
ReadExactly(readHandle, buffer, content.Length); // what is required for the above write to succeed
102+
100103
Assert.Equal(content, buffer.AsSpan(0, content.Length).ToArray());
101104

102105
await write;
@@ -118,12 +121,11 @@ public async Task ReadToAnEmptyBufferReturnsZeroWhenDataIsAvailableAsync()
118121
Assert.Equal(0, await readToEmpty);
119122

120123
byte[] buffer = new byte[content.Length * 2];
121-
Task<int> readToNonEmpty = RandomAccess.ReadAsync(readHandle, buffer, fileOffset: 0).AsTask(); // what is required for the above write to succeed
124+
Task readToNonEmpty = ReadExactlyAsync(readHandle, buffer, content.Length); // what is required for the above write to succeed
122125

123126
await Task.WhenAll(readToNonEmpty, write);
124127

125-
Assert.Equal(content.Length, readToNonEmpty.Result);
126-
Assert.Equal(content, buffer.AsSpan(0, readToNonEmpty.Result).ToArray());
128+
Assert.Equal(content, buffer.AsSpan(0, content.Length).ToArray());
127129
}
128130
}
129131

@@ -146,7 +148,7 @@ public async Task CanReadToStackAllocatedMemory()
146148
void ReadToStackAllocatedBuffer(SafeFileHandle handle, byte[] array)
147149
{
148150
Span<byte> buffer = stackalloc byte[array.Length * 2];
149-
Assert.Equal(array.Length, RandomAccess.Read(handle, buffer, fileOffset: 0));
151+
ReadExactly(handle, buffer, array.Length);
150152
Assert.Equal(array, buffer.Slice(0, array.Length).ToArray());
151153
}
152154
}
@@ -161,11 +163,11 @@ public async Task CanWriteFromStackAllocatedMemory()
161163
{
162164
byte[] content = RandomNumberGenerator.GetBytes(BufferSize);
163165
byte[] buffer = new byte[content.Length * 2];
164-
Task<int> read = RandomAccess.ReadAsync(readHandle, buffer, fileOffset: 0).AsTask();
166+
Task read = ReadExactlyAsync(readHandle, buffer, content.Length);
165167

166168
WriteFromStackAllocatedBuffer(writeHandle, content);
167169

168-
Assert.Equal(content.Length, await read);
170+
await read;
169171
Assert.Equal(content, buffer.AsSpan(0, content.Length).ToArray());
170172
}
171173

@@ -190,8 +192,8 @@ public async Task FileOffsetsAreIgnored_AsyncWrite_SyncRead()
190192
byte[] buffer = new byte[content.Length * 2];
191193
int readFromOffset456 = RandomAccess.Read(readHandle, buffer, fileOffset: 456);
192194

193-
Assert.Equal(content.Length, readFromOffset456);
194-
Assert.Equal(content, buffer.AsSpan(0, readFromOffset456).ToArray());
195+
Assert.InRange(readFromOffset456, 1, content.Length);
196+
Assert.Equal(content.Take(readFromOffset456), buffer.AsSpan(0, readFromOffset456).ToArray());
195197

196198
await writeToOffset123;
197199
}
@@ -211,8 +213,9 @@ public async Task FileOffsetsAreIgnored_AsyncRead_SyncWrite()
211213

212214
RandomAccess.Write(writeHandle, content, fileOffset: 123);
213215

214-
Assert.Equal(content.Length, readFromOffset456.Result);
215-
Assert.Equal(content, buffer.AsSpan(0, readFromOffset456.Result).ToArray());
216+
int bytesRead = await readFromOffset456;
217+
Assert.InRange(bytesRead, 1, content.Length);
218+
Assert.Equal(content.Take(bytesRead), buffer.AsSpan(0, readFromOffset456.Result).ToArray());
216219
}
217220
}
218221

@@ -231,8 +234,8 @@ public async Task FileOffsetsAreIgnoredAsync()
231234

232235
await Task.WhenAll(readFromOffset456, writeToOffset123);
233236

234-
Assert.Equal(content.Length, readFromOffset456.Result);
235-
Assert.Equal(content, buffer.AsSpan(0, readFromOffset456.Result).ToArray());
237+
Assert.InRange(readFromOffset456.Result, 1, content.Length);
238+
Assert.Equal(content.Take(readFromOffset456.Result), buffer.AsSpan(0, readFromOffset456.Result).ToArray());
236239
}
237240
}
238241

@@ -247,13 +250,13 @@ public async Task PartialReadsAreSupported()
247250
byte[] content = RandomNumberGenerator.GetBytes(BufferSize);
248251
ValueTask write = RandomAccess.WriteAsync(writeHandle, content, fileOffset: 0);
249252

250-
byte[] buffer = new byte[content.Length * 2];
253+
byte[] buffer = new byte[BufferSize];
251254

252-
for (int i = 0; i < content.Length; i++)
255+
for (int i = 0; i < BufferSize; i++)
253256
{
254257
Assert.Equal(1, RandomAccess.Read(readHandle, buffer.AsSpan(i, 1), fileOffset: 0));
255258
}
256-
Assert.Equal(content, buffer.AsSpan(0, content.Length).ToArray());
259+
Assert.Equal(content, buffer);
257260

258261
await write;
259262
}
@@ -270,10 +273,13 @@ public async Task PartialReadsAreSupportedAsync()
270273
byte[] content = RandomNumberGenerator.GetBytes(BufferSize);
271274
ValueTask write = RandomAccess.WriteAsync(writeHandle, content, fileOffset: 0);
272275

273-
byte[] buffer = new byte[content.Length * 2];
274-
Assert.Equal(1, await RandomAccess.ReadAsync(readHandle, buffer.AsMemory(0, 1), fileOffset: 0));
275-
Assert.Equal(2, await RandomAccess.ReadAsync(readHandle, buffer.AsMemory(1), fileOffset: 0));
276-
Assert.Equal(content, buffer.AsSpan(0, 3).ToArray());
276+
byte[] buffer = new byte[BufferSize];
277+
278+
for (int i = 0; i < BufferSize; i++)
279+
{
280+
Assert.Equal(1, await RandomAccess.ReadAsync(readHandle, buffer.AsMemory(i, 1), fileOffset: 0));
281+
}
282+
Assert.Equal(content, buffer);
277283
}
278284
}
279285

@@ -289,14 +295,13 @@ public async Task MultipleBuffersAreSupported_AsyncWrite_SyncReads()
289295
Task write = RandomAccess.WriteAsync(writeHandle, vectors, fileOffset: 123).AsTask();
290296
byte[] buffer = new byte[VectorsByteCount * 2];
291297

292-
int read = 0;
293-
298+
int bytesRead = 0;
294299
do
295300
{
296-
read += RandomAccess.Read(readHandle, buffer.AsSpan(read), fileOffset: 456);
297-
} while (read != VectorsByteCount);
301+
bytesRead += RandomAccess.Read(readHandle, buffer.AsSpan(bytesRead), fileOffset: 456);
302+
} while (bytesRead != VectorsByteCount);
298303

299-
Assert.Equal(vectors.SelectMany(vector => vector.ToArray()), buffer.AsSpan(0, read).ToArray());
304+
Assert.Equal(vectors.SelectMany(vector => vector.ToArray()), buffer.AsSpan(0, bytesRead).ToArray());
300305

301306
await write;
302307
}
@@ -315,10 +320,10 @@ public async Task MultipleBuffersAreSupported_AsyncWrite_SyncRead()
315320
byte[] buffer = new byte[VectorsByteCount * 2];
316321

317322
Memory<byte>[] writableVectors = GenerateVectors(VectorCount, BufferSize);
318-
long read = RandomAccess.Read(readHandle, writableVectors, fileOffset: 456);
323+
int bytesRead = (int)RandomAccess.Read(readHandle, writableVectors, fileOffset: 456);
319324

320-
Assert.Equal(VectorsByteCount, read);
321-
Assert.Equal(readOnlyVectors.SelectMany(vector => vector.ToArray()), writableVectors.SelectMany(vector => vector.ToArray()));
325+
Assert.InRange(bytesRead, 1, VectorsByteCount);
326+
AssertEqual(readOnlyVectors, writableVectors, bytesRead);
322327

323328
await write;
324329
}
@@ -338,8 +343,9 @@ public async Task MultipleBuffersAreSupported_AsyncWrite_AsyncRead()
338343

339344
Memory<byte>[] writableVectors = GenerateVectors(VectorCount, BufferSize);
340345

341-
Assert.Equal(VectorsByteCount, await RandomAccess.ReadAsync(readHandle, writableVectors, fileOffset: 456));
342-
Assert.Equal(readOnlyVectors.SelectMany(vector => vector.ToArray()), writableVectors.SelectMany(vector => vector.ToArray()));
346+
long bytesRead = await RandomAccess.ReadAsync(readHandle, writableVectors, fileOffset: 456);
347+
Assert.InRange(bytesRead, 1, VectorsByteCount);
348+
AssertEqual(readOnlyVectors, writableVectors, (int)bytesRead);
343349

344350
await write;
345351
}
@@ -359,8 +365,9 @@ public async Task MultipleBuffersAreSupported_AsyncRead_SyncWrite()
359365
byte[] content = RandomNumberGenerator.GetBytes(VectorsByteCount);
360366
RandomAccess.Write(writeHandle, content, fileOffset: 123);
361367

362-
Assert.Equal(VectorsByteCount, await read);
363-
Assert.Equal(content, writableVectors.SelectMany(vector => vector.ToArray()));
368+
int bytesRead = (int)await read;
369+
Assert.InRange(bytesRead, 1, VectorsByteCount);
370+
Assert.Equal(content.Take(bytesRead), writableVectors.SelectMany(vector => vector.ToArray()).Take(bytesRead));
364371
}
365372
}
366373

@@ -379,9 +386,7 @@ private static SafeFileHandle GetFileHandle(PipeStream pipeStream, bool isAsync)
379386
if (OperatingSystem.IsWindows() && isAsync)
380387
{
381388
// Currently it's impossible to duplicate an async safe handle that has already been bound to Thread Pool: https://github.com/dotnet/runtime/issues/28585
382-
// I am opened for ideas on how we could solve it without an ugly reflection hack..
383-
ThreadPoolBoundHandle threadPoolBinding = (ThreadPoolBoundHandle)typeof(PipeStream).GetField("_threadPoolBinding", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance).GetValue(pipeStream);
384-
typeof(SafeFileHandle).GetProperty("ThreadPoolBinding", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance).GetSetMethod(true).Invoke(serverHandle, new object[] { threadPoolBinding });
389+
FileThreadPoolBoundHandle(serverHandle) = PipeThreadPoolBoundHandle(pipeStream);
385390
}
386391

387392
return serverHandle;
@@ -390,6 +395,35 @@ private static SafeFileHandle GetFileHandle(PipeStream pipeStream, bool isAsync)
390395
{
391396
pipeStream.SafePipeHandle.SetHandleAsInvalid();
392397
}
398+
399+
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_threadPoolBinding")]
400+
extern static ref ThreadPoolBoundHandle PipeThreadPoolBoundHandle(PipeStream @this);
401+
402+
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "<ThreadPoolBinding>k__BackingField")]
403+
extern static ref ThreadPoolBoundHandle FileThreadPoolBoundHandle(SafeFileHandle @this);
404+
}
405+
406+
private static void ReadExactly(SafeFileHandle readHandle, Span<byte> buffer, int expectedByteCount)
407+
{
408+
int bytesRead = 0;
409+
do
410+
{
411+
bytesRead += RandomAccess.Read(readHandle, buffer.Slice(bytesRead), fileOffset: 0); // fileOffset NOT set to bytesRead on purpose
412+
} while (bytesRead != expectedByteCount);
393413
}
414+
415+
private static async Task ReadExactlyAsync(SafeFileHandle readHandle, byte[] buffer, int expectedByteCount)
416+
{
417+
int bytesRead = 0;
418+
do
419+
{
420+
bytesRead += await RandomAccess.ReadAsync(readHandle, buffer.AsMemory(bytesRead), fileOffset: 0); // fileOffset NOT set to bytesRead on purpose
421+
} while (bytesRead != expectedByteCount);
422+
}
423+
424+
private static void AssertEqual(ReadOnlyMemory<byte>[] readOnlyVectors, Memory<byte>[] writableVectors, int byteCount)
425+
=> Assert.Equal(
426+
readOnlyVectors.SelectMany(vector => vector.ToArray()).Take(byteCount),
427+
writableVectors.SelectMany(vector => vector.ToArray()).Take(byteCount));
394428
}
395429
}

0 commit comments

Comments
 (0)