add more tests

This commit is contained in:
Adam Hathcock
2026-07-23 10:17:28 +01:00
parent e3b7a560ba
commit 152a66f802
3 changed files with 227 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpCompress.Archives.GZip;
using SharpCompress.Test.Mocks;
using SharpCompress.Writers.GZip;
using Xunit;
namespace SharpCompress.Test.GZip;
/// <summary>
/// Regression tests for writing gzip streams to non-seekable (forward-only) output streams.
/// GZip is inherently a single forward stream with no seek-dependent layout, so streaming to
/// a <see cref="ForwardOnlyStream"/> must simply produce a valid, readable gzip stream.
/// </summary>
public class GZipWriterNonSeekableTests
{
private const string EntryName = "content.txt";
private static byte[] CreateContent() =>
Encoding.UTF8.GetBytes(string.Concat(Enumerable.Repeat("Hello streaming gzip! ", 500)));
private static async Task<byte[]> WriteToNonSeekableAsync(byte[] content)
{
using var ms = new MemoryStream();
await using (var writer = new GZipWriter(new ForwardOnlyStream(ms)))
{
using var source = new MemoryStream(content);
await writer.WriteAsync(EntryName, source, null);
}
return ms.ToArray();
}
private static byte[] WriteToNonSeekableSync(byte[] content)
{
using var ms = new MemoryStream();
using (var writer = new GZipWriter(new ForwardOnlyStream(ms)))
{
using var source = new MemoryStream(content);
writer.Write(EntryName, source, null);
}
return ms.ToArray();
}
private static void AssertRoundTrips(byte[] gz, byte[] expected)
{
using var archive = GZipArchive.OpenArchive(new MemoryStream(gz));
var entry = archive.Entries.First();
using var extracted = new MemoryStream();
using (var entryStream = entry.OpenEntryStream())
{
entryStream.CopyTo(extracted);
}
Assert.Equal(expected, extracted.ToArray());
}
[Fact]
public async Task GZip_Async_NonSeekable_RoundTrips()
{
var content = CreateContent();
var gz = await WriteToNonSeekableAsync(content);
AssertRoundTrips(gz, content);
}
[Fact]
public void GZip_Sync_NonSeekable_RoundTrips()
{
var content = CreateContent();
var gz = WriteToNonSeekableSync(content);
AssertRoundTrips(gz, content);
}
}

View File

@@ -0,0 +1,25 @@
using System.IO;
using SharpCompress.Common;
using SharpCompress.Test.Mocks;
using SharpCompress.Writers.SevenZip;
using Xunit;
namespace SharpCompress.Test.SevenZip;
/// <summary>
/// 7z writing requires a seekable output stream so the signature header can be back-patched on
/// finalize (see SevenZipWriter.cs). Unlike Zip, it cannot fall back to a streaming layout, so
/// constructing a writer over a non-seekable output must fail fast. This pins that documented
/// limitation against silent regression.
/// </summary>
public class SevenZipWriterNonSeekableTests
{
[Fact]
public void SevenZip_NonSeekable_Output_Throws()
{
using var ms = new MemoryStream();
Assert.Throws<ArchiveOperationException>(() =>
new SevenZipWriter(new ForwardOnlyStream(ms), new SevenZipWriterOptions())
);
}
}

View File

@@ -0,0 +1,129 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Test.Mocks;
using SharpCompress.Writers.Tar;
using Xunit;
namespace SharpCompress.Test.Tar;
/// <summary>
/// Regression tests for writing tar archives to non-seekable (forward-only) output streams.
/// Tar is a forward-only format with no header back-patching, so streaming to a
/// <see cref="ForwardOnlyStream"/> must produce a valid, readable archive. The sources are
/// seekable so the writer can derive each entry's size up front (see TarWriter.cs).
/// </summary>
public class TarWriterNonSeekableTests
{
private static readonly DateTime FixedModificationTime = new(2024, 5, 15, 10, 30, 0);
private static (string Name, byte[] Content)[] CreateStreamingTestEntries() =>
[
(
"first.txt",
Encoding.UTF8.GetBytes(
string.Concat(Enumerable.Repeat("Hello streaming tar! ", 500))
)
),
(
"nested/second.txt",
Encoding.UTF8.GetBytes(
string.Concat(Enumerable.Repeat("Another entry with different content. ", 300))
)
),
];
private static async Task<byte[]> WriteArchiveToNonSeekableAsync(
(string Name, byte[] Content)[] entries
)
{
using var ms = new MemoryStream();
await using (
var writer = new TarWriter(
new ForwardOnlyStream(ms),
new TarWriterOptions(CompressionType.None, true)
)
)
{
foreach (var (name, content) in entries)
{
using var source = new MemoryStream(content);
await writer.WriteAsync(name, source, FixedModificationTime);
}
}
return ms.ToArray();
}
private static byte[] WriteArchiveToNonSeekableSync((string Name, byte[] Content)[] entries)
{
using var ms = new MemoryStream();
using (
var writer = new TarWriter(
new ForwardOnlyStream(ms),
new TarWriterOptions(CompressionType.None, true)
)
)
{
foreach (var (name, content) in entries)
{
using var source = new MemoryStream(content);
writer.Write(name, source, FixedModificationTime);
}
}
return ms.ToArray();
}
private static async Task AssertRoundTripsAsync(
byte[] tar,
(string Name, byte[] Content)[] entries
)
{
using var archive = TarArchive.OpenArchive(new MemoryStream(tar));
var fileEntries = archive.Entries.Where(e => !e.IsDirectory).ToList();
Assert.Equal(entries.Length, fileEntries.Count);
foreach (var (name, content) in entries)
{
var entry = fileEntries.Single(e => e.Key == name);
using var extracted = new MemoryStream();
#if LEGACY_DOTNET
using (var entryStream = await entry.OpenEntryStreamAsync())
#else
await using (var entryStream = await entry.OpenEntryStreamAsync())
#endif
{
await entryStream.CopyToAsync(extracted);
}
Assert.Equal(content, extracted.ToArray());
}
}
[Fact]
public async Task Tar_Async_NonSeekable_RoundTrips()
{
var entries = CreateStreamingTestEntries();
var tar = await WriteArchiveToNonSeekableAsync(entries);
await AssertRoundTripsAsync(tar, entries);
}
[Fact]
public async Task Tar_Sync_NonSeekable_RoundTrips()
{
var entries = CreateStreamingTestEntries();
var tar = WriteArchiveToNonSeekableSync(entries);
await AssertRoundTripsAsync(tar, entries);
}
[Fact]
public async Task Tar_Async_NonSeekable_Matches_Sync_Output()
{
var entries = CreateStreamingTestEntries();
var asyncTar = await WriteArchiveToNonSeekableAsync(entries);
var syncTar = WriteArchiveToNonSeekableSync(entries);
Assert.Equal(syncTar, asyncTar);
}
}