tightens up tests

This commit is contained in:
Adam Hathcock
2026-04-21 08:25:16 +01:00
parent 2a62f4dfe2
commit a026806b1c
6 changed files with 54 additions and 25 deletions

View File

@@ -27,6 +27,8 @@ Primary references:
- `TarWriterOptions.HeaderFormat` is now honored in sync and async file and directory write paths.
- Tar tests now cover `USTAR` and `GNU_TAR_LONG_LINK`, including USTAR long-name failure scenarios.
- Symlink coverage now includes `TarWithSymlink.tar.gz` for reader sync and async paths.
- Tar tests now explicitly cover unsupported tar wrapper compression writes (`Xz`, `ZStandard`, `Lzw`) for sync and async writer paths.
- `TarArchive.OpenAsyncArchive(Stream)` now enforces the same seekable-stream contract as `TarArchive.OpenArchive(Stream)`.
- Sparse handling remains explicitly unsupported.
- Non-modeled PAX keys remain explicitly unsupported.
@@ -183,20 +185,11 @@ This is not inherently wrong, but it should be clearly documented everywhere sup
## Sync and Async API Inconsistencies
### Seekability requirements differ at the API boundary
### Seekability contract alignment is resolved
Synchronous `TarArchive.OpenArchive(Stream)` explicitly throws if the stream is not seekable.
`TarArchive.OpenArchive(Stream)` and `TarArchive.OpenAsyncArchive(Stream)` now both enforce the same seekable-stream contract and throw `ArgumentException` for non-seekable input.
Asynchronous `TarArchive.OpenAsyncArchive(Stream)` does not perform the same public guard.
Impact:
- callers do not see the same contract from sync and async overloads
- behavior is harder to reason about from API docs alone
Recommended action:
- either align the contracts or document the difference explicitly
Tar tests include an async regression case for non-seekable stream open.
### Header format alignment between sync and async is resolved
@@ -236,19 +229,15 @@ Recommended action:
- either add fixtures and tests or document these as unsupported with no test coverage
### No tests for unsupported write wrappers
### Unsupported-wrapper writer coverage is now present
There are negative tests for an invalid `Rar` compression type, but not for unsupported tar wrappers that a user might reasonably infer from read support.
Missing negative cases include:
Tar writer tests now explicitly verify `InvalidFormatException` for unsupported tar wrapper compression types:
- `CompressionType.Xz`
- `CompressionType.ZStandard`
- `CompressionType.Lzw`
Recommended action:
- add explicit negative tests so the supported write matrix stays intentional
Coverage exists in both sync and async writer test paths.
## Documentation Gaps
@@ -287,8 +276,6 @@ Recommended action:
### Priority 1
- Add negative writer tests for unsupported wrapper compressions
- Evaluate whether sync and async archive open contracts should match exactly
- Improve metadata round-trip behavior only if there is a consumer need
- Evaluate whether non-modeled PAX keys should remain ignored or be surfaced in a future metadata API
@@ -298,7 +285,7 @@ The SharpCompress Tar implementation is strong on common read scenarios and basi
- documentation overstating or under-describing support
- incomplete feature coverage for less common tar dialect features
- archive/read behavioral differences that are not always explicit in docs
- intentionally deferred metadata and API-surface decisions
- test coverage holes around advanced tar metadata features
`docs/TAR_SPEC.md` should be treated as the implementation baseline. This document identifies where that baseline is incomplete, inconsistent, or incorrectly reflected elsewhere in the repository.

View File

@@ -149,11 +149,11 @@ Implementation files:
### Open Behavior
Synchronous `TarArchive.OpenArchive(Stream)` requires a seekable stream and throws `ArgumentException` when `CanSeek` is `false`.
`TarArchive.OpenArchive(Stream)` and `TarArchive.OpenAsyncArchive(Stream)` require a seekable stream and throw `ArgumentException` when `CanSeek` is `false`.
`TarArchive.OpenArchive(FileInfo)` and the list-based overloads use `SourceStream` and determine wrapper compression by calling `TarFactory.GetCompressionType`.
Asynchronous `OpenAsyncArchive` overloads use `TarFactory.GetCompressionTypeAsync` and do not enforce the same explicit seekability check at the public API boundary.
Asynchronous `OpenAsyncArchive` overloads use `TarFactory.GetCompressionTypeAsync` for wrapper detection.
### Entry Loading
@@ -417,7 +417,7 @@ This section documents current implementation limits, not desired future behavio
### Archive behavior limitations
- Sync archive open requires a seekable input stream
- Stream-based archive open requires a seekable input stream
- Compressed tar archive access is not full random-access in the same sense as uncompressed seekable tar
## Test Coverage Map

View File

@@ -103,6 +103,12 @@ public partial class TarArchive
)
{
stream.NotNull(nameof(stream));
if (stream is not { CanSeek: true })
{
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
var sourceStream = new SourceStream(
stream,
i => null,

View File

@@ -22,6 +22,18 @@ public class TarArchiveAsyncTests : ArchiveTests
[Fact]
public async ValueTask TarArchiveStreamRead_Async() => await ArchiveStreamReadAsync("Tar.tar");
[Fact]
public async ValueTask TarArchiveStreamRead_Async_Throws_On_NonSeekable_Stream()
{
using Stream stream = new ForwardOnlyStream(
File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"))
);
await Assert.ThrowsAsync<ArgumentException>(async () =>
await TarArchive.OpenAsyncArchive(new AsyncOnlyStream(stream))
);
}
[Fact]
public async ValueTask Tar_FileName_Exactly_100_Characters_Async()
{

View File

@@ -61,6 +61,21 @@ public class TarWriterAsyncTests : WriterTests
)
);
[Theory]
[InlineData(CompressionType.Xz)]
[InlineData(CompressionType.ZStandard)]
[InlineData(CompressionType.Lzw)]
public async ValueTask Tar_UnsupportedWrapperCompression_Write_Async(
CompressionType compressionType
) =>
await Assert.ThrowsAsync<InvalidFormatException>(async () =>
await WriteAsync(
compressionType,
"Zip.ppmd.noEmptyDirs.zip",
"Zip.ppmd.noEmptyDirs.zip"
)
);
[Theory]
[InlineData(true)]
[InlineData(false)]

View File

@@ -55,6 +55,15 @@ public class TarWriterTests : WriterTests
Write(CompressionType.Rar, "Zip.ppmd.noEmptyDirs.zip", "Zip.ppmd.noEmptyDirs.zip")
);
[Theory]
[InlineData(CompressionType.Xz)]
[InlineData(CompressionType.ZStandard)]
[InlineData(CompressionType.Lzw)]
public void Tar_UnsupportedWrapperCompression_Write(CompressionType compressionType) =>
Assert.Throws<InvalidFormatException>(() =>
Write(compressionType, "Zip.ppmd.noEmptyDirs.zip", "Zip.ppmd.noEmptyDirs.zip")
);
[Theory]
[InlineData(true)]
[InlineData(false)]