diff --git a/src/SharpCompress/Archives/AbstractArchive.Async.cs b/src/SharpCompress/Archives/AbstractArchive.Async.cs index 7c68342f..7a826d2f 100644 --- a/src/SharpCompress/Archives/AbstractArchive.Async.cs +++ b/src/SharpCompress/Archives/AbstractArchive.Async.cs @@ -47,7 +47,7 @@ public abstract partial class AbstractArchive private async ValueTask EnsureEntriesLoadedAsync() { - await _lazyEntriesAsync.EnsureFullyLoaded().ConfigureAwait(false); + await foreach (var entry in EntriesAsync.ConfigureAwait(false)) { } await _lazyVolumesAsync.EnsureFullyLoaded().ConfigureAwait(false); } diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs index 1e4b353b..ac067a27 100644 --- a/src/SharpCompress/Archives/AbstractArchive.cs +++ b/src/SharpCompress/Archives/AbstractArchive.cs @@ -99,7 +99,8 @@ public abstract partial class AbstractArchive : IArchive, IAsyn private void EnsureEntriesLoaded() { - _lazyEntries.EnsureFullyLoaded(); + // Writable archives expose a shared view rather than this base cache. + Entries.ForEach(_ => { }); _lazyVolumes.EnsureFullyLoaded(); } diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveCacheTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveCacheTests.cs index ab6a1715..650cbfe6 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveCacheTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveCacheTests.cs @@ -51,6 +51,56 @@ public class GZipArchiveCacheTests : TestBase Assert.True(input.CanRead); } + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public async Task Completeness_Uses_The_Current_Entry_Cache(bool useAsync, bool partial) + { + using var input = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); + using var archive = GZipArchive.OpenArchive(input, ReaderOptions.ForExternalStream); + var asyncArchive = (IAsyncArchive)archive; + if (partial) + { + if (useAsync) + { + await asyncArchive.EntriesAsync.FirstAsync(); + } + else + { + _ = archive.Entries.First(); + } + } + Assert.True(useAsync ? await asyncArchive.IsCompleteAsync() : archive.IsComplete); + var entry = Assert.Single(archive.Entries); + Assert.Same(entry, Assert.Single(await asyncArchive.EntriesAsync.ToListAsync())); + Assert.True(archive.IsComplete); + Assert.True(await asyncArchive.IsCompleteAsync()); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task Completeness_Does_Not_Reparse_Already_Cached_Entries(bool useAsync) + { + using var input = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); + using var archive = GZipArchive.OpenArchive(input, ReaderOptions.ForExternalStream); + var asyncArchive = (IAsyncArchive)archive; + if (useAsync) + { + Assert.Single(await asyncArchive.EntriesAsync.ToListAsync()); + } + else + { + Assert.Single(archive.Entries); + } + // Completeness needs only cached metadata and must not move the source. + input.Position = input.Length; + Assert.True(useAsync ? await asyncArchive.IsCompleteAsync() : archive.IsComplete); + Assert.Equal(input.Length, input.Position); + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -77,4 +127,26 @@ public class GZipArchiveCacheTests : TestBase : await asyncArchive.EntriesAsync.ToListAsync(); Assert.Same(first, Assert.Single(entries)); } + + [Fact] + public async Task Resuming_And_Checking_Completeness_Do_Not_Use_Synchronous_Io() + { + using var input = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); + using var asyncOnly = new AsyncOnlyStream(input, disposeStream: false); + await using ( + var archive = await GZipArchive.OpenAsyncArchive( + asyncOnly, + ReaderOptions.ForExternalStream, + CancellationToken.None + ) + ) + { + var entry = await archive.EntriesAsync.FirstAsync(); + Assert.True(await archive.IsCompleteAsync()); + Assert.Same(entry, Assert.Single(await archive.EntriesAsync.ToListAsync())); + using var payload = await entry.OpenEntryStreamAsync(CancellationToken.None); + await payload.CopyToAsync(Stream.Null, 81920, CancellationToken.None); + } + Assert.True(input.CanRead); + } }