diff --git a/docs/naming.md b/docs/naming.md
index d1a6d2789e..2d855a4b19 100644
--- a/docs/naming.md
+++ b/docs/naming.md
@@ -617,7 +617,7 @@ public static string NameWithParent(this Type type)
return type.Name;
}
```
-snippet source | anchor
+snippet source | anchor
diff --git a/docs/verify-file.md b/docs/verify-file.md
index 60cad1203f..57116bb5bc 100644
--- a/docs/verify-file.md
+++ b/docs/verify-file.md
@@ -16,7 +16,7 @@ Verifies the contents of a file.
public Task VerifyFilePath() =>
VerifyFile("sample.txt");
```
-snippet source | anchor
+snippet source | anchor
@@ -33,7 +33,7 @@ public Task VerifyFileWithInfo() =>
"sample.txt",
info: "the info");
```
-snippet source | anchor
+snippet source | anchor
diff --git a/docs/verify-xml.md b/docs/verify-xml.md
index 8338b4116e..60f9314bf7 100644
--- a/docs/verify-xml.md
+++ b/docs/verify-xml.md
@@ -19,7 +19,7 @@ Verifies Xml:
public Task VerifyFilePath() =>
VerifyFile("sample.txt");
```
-snippet source | anchor
+snippet source | anchor
diff --git a/src/Verify.Tests/UnboundedStream.cs b/src/Verify.Tests/NoLengthStream.cs
similarity index 53%
rename from src/Verify.Tests/UnboundedStream.cs
rename to src/Verify.Tests/NoLengthStream.cs
index fa8586ab9e..5865b9cc49 100644
--- a/src/Verify.Tests/UnboundedStream.cs
+++ b/src/Verify.Tests/NoLengthStream.cs
@@ -1,5 +1,5 @@
-class UnboundedStream :
- MemoryStream
+class NoLengthStream(byte[] bytes) :
+ MemoryStream(bytes)
{
public override long Length => throw new NotImplementedException();
}
\ No newline at end of file
diff --git a/src/Verify.Tests/StreamTests.NoLengthStream.verified.bin b/src/Verify.Tests/StreamTests.NoLengthStream.verified.bin
new file mode 100644
index 0000000000..6b2aaa7640
--- /dev/null
+++ b/src/Verify.Tests/StreamTests.NoLengthStream.verified.bin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/Verify.Tests/StreamTests.UnboundedStream.verified.txt b/src/Verify.Tests/StreamTests.UnboundedStream.verified.txt
deleted file mode 100644
index cfb012921d..0000000000
--- a/src/Verify.Tests/StreamTests.UnboundedStream.verified.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- Type: Exception,
- Message: Could not read Length property of target stream. Verify does not support unbounded streams.
-}
\ No newline at end of file
diff --git a/src/Verify.Tests/StreamTests.cs b/src/Verify.Tests/StreamTests.cs
index 710953ccbc..707a4893c4 100644
--- a/src/Verify.Tests/StreamTests.cs
+++ b/src/Verify.Tests/StreamTests.cs
@@ -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]
diff --git a/src/Verify/Compare/FileComparer.cs b/src/Verify/Compare/FileComparer.cs
index 4cbd0ccfd7..b0d41fa5c3 100644
--- a/src/Verify/Compare/FileComparer.cs
+++ b/src/Verify/Compare/FileComparer.cs
@@ -20,7 +20,7 @@ public static async Task 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);
@@ -66,14 +66,14 @@ async Task 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);
diff --git a/src/Verify/Extensions.cs b/src/Verify/Extensions.cs
index 994c6b4374..d4944670f6 100644
--- a/src/Verify/Extensions.cs
+++ b/src/Verify/Extensions.cs
@@ -1,4 +1,5 @@
-static class Extensions
+// ReSharper disable UnusedVariable
+static class Extensions
{
public static string Extension(this FileStream file) =>
FileExtensions.GetExtension(file.Name);
@@ -17,6 +18,37 @@ public static async Task> ToList(this IAsyncEnumerable 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');
diff --git a/src/Verify/IoHelpers.cs b/src/Verify/IoHelpers.cs
index f70ed728c3..74944d8515 100644
--- a/src/Verify/IoHelpers.cs
+++ b/src/Verify/IoHelpers.cs
@@ -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);
}
}
@@ -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.");
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Verify/Verifier/InnerVerifier_Stream.cs b/src/Verify/Verifier/InnerVerifier_Stream.cs
index 2b1d0d1c7f..d58ebfb86b 100644
--- a/src/Verify/Verifier/InnerVerifier_Stream.cs
+++ b/src/Verify/Verifier/InnerVerifier_Stream.cs
@@ -84,24 +84,6 @@ public async Task 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);