Skip to content

Commit 32dca9e

Browse files
committed
Validate arguments of CopyTo
1 parent 15320f7 commit 32dca9e

File tree

2 files changed

+136
-18
lines changed

2 files changed

+136
-18
lines changed

src/TestableIO.System.IO.Abstractions/FileSystemStream.cs

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -103,18 +103,23 @@ public override IAsyncResult BeginWrite(byte[] buffer,
103103

104104
/// <inheritdoc cref="Stream.CopyTo(Stream, int)" />
105105
#if NETSTANDARD2_0 || NET462
106-
public new virtual void CopyTo(Stream destination, int bufferSize)
107-
=> _stream.CopyTo(destination, bufferSize);
106+
public new virtual void CopyTo(Stream destination, int bufferSize)
108107
#else
109108
public override void CopyTo(Stream destination, int bufferSize)
110-
=> _stream.CopyTo(destination, bufferSize);
111109
#endif
110+
{
111+
ValidateCopyToArguments(this, destination, bufferSize);
112+
_stream.CopyTo(destination, bufferSize);
113+
}
112114

113115
/// <inheritdoc cref="Stream.CopyToAsync(Stream, int, CancellationToken)" />
114116
public override Task CopyToAsync(Stream destination,
115117
int bufferSize,
116118
CancellationToken cancellationToken)
117-
=> _stream.CopyToAsync(destination, bufferSize, cancellationToken);
119+
{
120+
ValidateCopyToArguments(this, destination, bufferSize);
121+
return _stream.CopyToAsync(destination, bufferSize, cancellationToken);
122+
}
118123

119124
/// <inheritdoc cref="Stream.EndRead(IAsyncResult)" />
120125
public override int EndRead(IAsyncResult asyncResult)
@@ -141,9 +146,9 @@ public override int Read(byte[] buffer, int offset, int count)
141146
=> _stream.Read(buffer, offset, count);
142147

143148
#if FEATURE_SPAN
144-
/// <inheritdoc cref="Stream.Read(Span{byte})" />
145-
public override int Read(Span<byte> buffer)
146-
=> _stream.Read(buffer);
149+
/// <inheritdoc cref="Stream.Read(Span{byte})" />
150+
public override int Read(Span<byte> buffer)
151+
=> _stream.Read(buffer);
147152
#endif
148153

149154
/// <inheritdoc cref="Stream.ReadAsync(byte[], int, int, CancellationToken)" />
@@ -154,10 +159,10 @@ public override Task<int> ReadAsync(byte[] buffer,
154159
=> _stream.ReadAsync(buffer, offset, count, cancellationToken);
155160

156161
#if FEATURE_SPAN
157-
/// <inheritdoc cref="Stream.ReadAsync(Memory{byte}, CancellationToken)" />
158-
public override ValueTask<int> ReadAsync(Memory<byte> buffer,
159-
CancellationToken cancellationToken = new())
160-
=> _stream.ReadAsync(buffer, cancellationToken);
162+
/// <inheritdoc cref="Stream.ReadAsync(Memory{byte}, CancellationToken)" />
163+
public override ValueTask<int> ReadAsync(Memory<byte> buffer,
164+
CancellationToken cancellationToken = new())
165+
=> _stream.ReadAsync(buffer, cancellationToken);
161166
#endif
162167

163168
/// <inheritdoc cref="Stream.ReadByte()" />
@@ -181,9 +186,9 @@ public override void Write(byte[] buffer, int offset, int count)
181186
=> _stream.Write(buffer, offset, count);
182187

183188
#if FEATURE_SPAN
184-
/// <inheritdoc cref="Stream.Write(ReadOnlySpan{byte})" />
185-
public override void Write(ReadOnlySpan<byte> buffer)
186-
=> _stream.Write(buffer);
189+
/// <inheritdoc cref="Stream.Write(ReadOnlySpan{byte})" />
190+
public override void Write(ReadOnlySpan<byte> buffer)
191+
=> _stream.Write(buffer);
187192
#endif
188193

189194
/// <inheritdoc cref="Stream.WriteAsync(byte[], int, int, CancellationToken)" />
@@ -194,10 +199,10 @@ public override Task WriteAsync(byte[] buffer,
194199
=> _stream.WriteAsync(buffer, offset, count, cancellationToken);
195200

196201
#if FEATURE_SPAN
197-
/// <inheritdoc cref="Stream.WriteAsync(ReadOnlyMemory{byte}, CancellationToken)" />
198-
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer,
199-
CancellationToken cancellationToken = new())
200-
=> _stream.WriteAsync(buffer, cancellationToken);
202+
/// <inheritdoc cref="Stream.WriteAsync(ReadOnlyMemory{byte}, CancellationToken)" />
203+
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer,
204+
CancellationToken cancellationToken = new())
205+
=> _stream.WriteAsync(buffer, cancellationToken);
201206
#endif
202207

203208
/// <inheritdoc cref="Stream.WriteByte(byte)" />
@@ -220,5 +225,38 @@ public static explicit operator FileStream(FileSystemStream fsStream)
220225
{
221226
return (FileStream) fsStream._stream;
222227
}
228+
229+
private static void ValidateCopyToArguments(Stream source, Stream destination, int bufferSize)
230+
{
231+
if (destination == null)
232+
{
233+
throw new ArgumentNullException(nameof(destination), "Destination cannot be null.");
234+
}
235+
236+
if (bufferSize <= 0)
237+
{
238+
throw new ArgumentOutOfRangeException(nameof(bufferSize), "Buffer size must be greater than zero.");
239+
}
240+
241+
if (!destination.CanWrite)
242+
{
243+
if (destination.CanRead)
244+
{
245+
throw new NotSupportedException("Stream does not support writing.");
246+
}
247+
248+
throw new ObjectDisposedException("Cannot access a closed Stream.");
249+
}
250+
251+
if (!source.CanRead)
252+
{
253+
if (source.CanWrite)
254+
{
255+
throw new NotSupportedException("Stream does not support reading.");
256+
}
257+
258+
throw new ObjectDisposedException("Cannot access a closed Stream.");
259+
}
260+
}
223261
}
224262
}

tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,5 +293,85 @@ public void MockFileStream_Null_ShouldHaveExpectedProperties()
293293
Assert.That(result.Length, Is.Zero);
294294
Assert.That(result.IsAsync, Is.True);
295295
}
296+
297+
[Test]
298+
[TestCase(0)]
299+
[TestCase(-1)]
300+
public void MockFileStream_WhenBufferSizeIsNotPositive_ShouldThrowArgumentNullException(int bufferSize)
301+
{
302+
var fileSystem = new MockFileSystem();
303+
fileSystem.File.WriteAllText("foo.txt", "");
304+
fileSystem.File.WriteAllText("bar.txt", "");
305+
using var source = fileSystem.FileInfo.New(@"foo.txt").OpenRead();
306+
using var destination = fileSystem.FileInfo.New(@"bar.txt").OpenWrite();
307+
308+
Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () =>
309+
await source.CopyToAsync(destination, bufferSize));
310+
}
311+
312+
[Test]
313+
public void MockFileStream_WhenDestinationIsClosed_ShouldThrowObjectDisposedException()
314+
{
315+
var fileSystem = new MockFileSystem();
316+
fileSystem.File.WriteAllText("foo.txt", "");
317+
fileSystem.File.WriteAllText("bar.txt", "");
318+
using var source = fileSystem.FileInfo.New(@"foo.txt").OpenRead();
319+
using var destination = fileSystem.FileInfo.New(@"bar.txt").OpenWrite();
320+
destination.Close();
321+
322+
Assert.ThrowsAsync<ObjectDisposedException>(async () =>
323+
await source.CopyToAsync(destination));
324+
}
325+
326+
[Test]
327+
public void MockFileStream_WhenDestinationIsNull_ShouldThrowArgumentNullException()
328+
{
329+
var fileSystem = new MockFileSystem();
330+
fileSystem.File.WriteAllText("foo.txt", "");
331+
using var source = fileSystem.FileInfo.New(@"foo.txt").OpenRead();
332+
333+
Assert.ThrowsAsync<ArgumentNullException>(async () =>
334+
await source.CopyToAsync(null));
335+
}
336+
337+
[Test]
338+
public void MockFileStream_WhenDestinationIsReadOnly_ShouldThrowNotSupportedException()
339+
{
340+
var fileSystem = new MockFileSystem();
341+
fileSystem.File.WriteAllText("foo.txt", "");
342+
fileSystem.File.WriteAllText("bar.txt", "");
343+
using var source = fileSystem.FileInfo.New(@"foo.txt").OpenRead();
344+
using var destination = fileSystem.FileInfo.New(@"bar.txt").OpenRead();
345+
346+
Assert.ThrowsAsync<NotSupportedException>(async () =>
347+
await source.CopyToAsync(destination));
348+
}
349+
350+
[Test]
351+
public void MockFileStream_WhenSourceIsClosed_ShouldThrowObjectDisposedException()
352+
{
353+
var fileSystem = new MockFileSystem();
354+
fileSystem.File.WriteAllText("foo.txt", "");
355+
fileSystem.File.WriteAllText("bar.txt", "");
356+
using var source = fileSystem.FileInfo.New(@"foo.txt").OpenRead();
357+
using var destination = fileSystem.FileInfo.New(@"bar.txt").OpenWrite();
358+
source.Close();
359+
360+
Assert.ThrowsAsync<ObjectDisposedException>(async () =>
361+
await source.CopyToAsync(destination));
362+
}
363+
364+
[Test]
365+
public void MockFileStream_WhenSourceIsWriteOnly_ShouldThrowNotSupportedException()
366+
{
367+
var fileSystem = new MockFileSystem();
368+
fileSystem.File.WriteAllText("foo.txt", "");
369+
fileSystem.File.WriteAllText("bar.txt", "");
370+
using var source = fileSystem.FileInfo.New(@"foo.txt").OpenWrite();
371+
using var destination = fileSystem.FileInfo.New(@"bar.txt").OpenWrite();
372+
373+
Assert.ThrowsAsync<NotSupportedException>(async () =>
374+
await source.CopyToAsync(destination));
375+
}
296376
}
297377
}

0 commit comments

Comments
 (0)