Skip to content

Commit

Permalink
Handle Stream throwing for Length (#995)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonCropp authored Sep 13, 2023
1 parent 92838ed commit 72a754a
Show file tree
Hide file tree
Showing 11 changed files with 61 additions and 39 deletions.
2 changes: 1 addition & 1 deletion docs/naming.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ public static string NameWithParent(this Type type)
return type.Name;
}
```
<sup><a href='/src/Verify/Extensions.cs#L67-L79' title='Snippet source file'>snippet source</a> | <a href='#snippet-namewithparent' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify/Extensions.cs#L99-L111' title='Snippet source file'>snippet source</a> | <a href='#snippet-namewithparent' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand Down
4 changes: 2 additions & 2 deletions docs/verify-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Verifies the contents of a file.
public Task VerifyFilePath() =>
VerifyFile("sample.txt");
```
<sup><a href='/src/Verify.Tests/StreamTests.cs#L175-L181' title='Snippet source file'>snippet source</a> | <a href='#snippet-verifyfile' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify.Tests/StreamTests.cs#L176-L182' title='Snippet source file'>snippet source</a> | <a href='#snippet-verifyfile' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand All @@ -33,7 +33,7 @@ public Task VerifyFileWithInfo() =>
"sample.txt",
info: "the info");
```
<sup><a href='/src/Verify.Tests/StreamTests.cs#L194-L202' title='Snippet source file'>snippet source</a> | <a href='#snippet-verifyfilewithinfo' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify.Tests/StreamTests.cs#L195-L203' title='Snippet source file'>snippet source</a> | <a href='#snippet-verifyfilewithinfo' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand Down
2 changes: 1 addition & 1 deletion docs/verify-xml.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Verifies Xml:
public Task VerifyFilePath() =>
VerifyFile("sample.txt");
```
<sup><a href='/src/Verify.Tests/StreamTests.cs#L175-L181' title='Snippet source file'>snippet source</a> | <a href='#snippet-verifyfile' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify.Tests/StreamTests.cs#L176-L182' title='Snippet source file'>snippet source</a> | <a href='#snippet-verifyfile' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class UnboundedStream :
MemoryStream
class NoLengthStream(byte[] bytes) :
MemoryStream(bytes)
{
public override long Length => throw new NotImplementedException();
}
1 change: 1 addition & 0 deletions src/Verify.Tests/StreamTests.NoLengthStream.verified.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

4 changes: 0 additions & 4 deletions src/Verify.Tests/StreamTests.UnboundedStream.verified.txt

This file was deleted.

11 changes: 6 additions & 5 deletions src/Verify.Tests/StreamTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,14 @@ public Task StreamNotAtStartAsText()
}

[Fact]
public Task UnboundedStream()
public Task NoLengthStream()
{
var stream = new UnboundedStream();
var stream = new NoLengthStream(new byte[]
{
1
});

return ThrowsTask(() => Verify(stream))
.DisableRequireUniquePrefix()
.IgnoreStackTrace();
return Verify(stream);
}

[Fact]
Expand Down
6 changes: 3 additions & 3 deletions src/Verify/Compare/FileComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static async Task<EqualityResult> DoCompare(VerifySettings settings, File
return await InnerCompare(file, receivedStream, (s1, s2) => compare(s1, s2, settings.Context));
}

if (receivedStream.CanSeek &&
if (receivedStream.CanSeekAndReadLength() &&
IoHelpers.Length(file.VerifiedPath) != receivedStream.Length)
{
await IoHelpers.WriteStream(file.ReceivedPath, receivedStream);
Expand Down Expand Up @@ -66,14 +66,14 @@ async Task<EqualityResult> EqualityResult(Stream receivedStream, Stream verified
return new(Equality.NotEqual, compareResult.Message, null, null);
}

if (receivedStream.CanSeek)
if (receivedStream.CanSeekAndReadLength())
{
receivedStream.MoveToStart();
return await EqualityResult(receivedStream, verifiedStream);
}

using var memoryStream = new MemoryStream();
await receivedStream.CopyToAsync(memoryStream);
await receivedStream.SafeCopy(memoryStream);
memoryStream.MoveToStart();

return await EqualityResult(memoryStream, verifiedStream);
Expand Down
34 changes: 33 additions & 1 deletion src/Verify/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
static class Extensions
// ReSharper disable UnusedVariable
static class Extensions
{
public static string Extension(this FileStream file) =>
FileExtensions.GetExtension(file.Name);
Expand All @@ -17,6 +18,37 @@ public static async Task<List<T>> ToList<T>(this IAsyncEnumerable<T> target)
return list;
}

// Streams can throw for Length. Eg a http stream that the server has not specified the length header
// Specify buffer to avoid an exception in Stream.CopyToAsync where it reads Length
// https://github.com/dotnet/runtime/issues/43448
public static Task SafeCopy(this Stream source, Stream target)
{
if (source.CanReadLength())
{
return source.CopyToAsync(target);
}

return source.CopyToAsync(target, 81920);
}

public static bool CanSeekAndReadLength(this Stream stream) =>
stream.CanSeek &&
CanReadLength(stream);

static bool CanReadLength(this Stream stream)
{
try
{
var streamLength = stream.Length;
}
catch (NotImplementedException)
{
return false;
}

return true;
}

public static string TrimPreamble(this string text) =>
text.TrimStart('\uFEFF');

Expand Down
14 changes: 12 additions & 2 deletions src/Verify/IoHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ public static async Task WriteStream(string path, Stream stream)
if (!TryCopyFileStream(path, stream))
{
await using var targetStream = OpenWrite(path);
await stream.CopyToAsync(targetStream);
await stream.SafeCopy(targetStream);
HandleEmptyFile(path);
}
}

Expand All @@ -236,9 +237,18 @@ public static async Task WriteStream(string path, Stream stream)
if (!TryCopyFileStream(path, stream))
{
using var targetStream = OpenWrite(path);
await stream.CopyToAsync(targetStream);
await stream.SafeCopy(targetStream);
HandleEmptyFile(path);
}
}

#endif

static void HandleEmptyFile(string path)
{
if (new FileInfo(path).Length == 0)
{
throw new("Empty data is not allowed.");
}
}
}
18 changes: 0 additions & 18 deletions src/Verify/Verifier/InnerVerifier_Stream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,24 +84,6 @@ public async Task<VerifyResult> VerifyStream(Stream? stream, string extension, o

using (stream)
{
long GetLength()
{
try
{
return stream.Length;
}
catch (NotImplementedException)
{
throw new("Could not read Length property of target stream. Verify does not support unbounded streams.");
}
}

var length = GetLength();
if (length == 0)
{
throw new("Empty data is not allowed.");
}

if (VerifierSettings.HasExtensionConverter(extension))
{
var (newInfo, converted, cleanup) = await DoExtensionConversion(extension, stream, info);
Expand Down

0 comments on commit 72a754a

Please sign in to comment.