diff --git a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs index 502a419e..c0e58656 100644 --- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs @@ -108,15 +108,12 @@ public static class IArchiveEntryExtensions throw new ExtractionException("Entry is a file directory and cannot be extracted."); } -#if LEGACY_DOTNET - using var entryStream = await archiveEntry + var entryStream = await archiveEntry .OpenEntryStreamAsync(cancellationToken) .ConfigureAwait(false); -#else - await using var entryStream = await archiveEntry - .OpenEntryStreamAsync(cancellationToken) + await using var entryStreamScope = entryStream + .DisposeAsyncScope() .ConfigureAwait(false); -#endif var checkedStream = options is null ? entryStream : IEntryExtensions.WrapWithChecksumValidation(archiveEntry, entryStream, options); diff --git a/src/SharpCompress/Common/EntryStream.Async.cs b/src/SharpCompress/Common/EntryStream.Async.cs index a58afe70..b7112da7 100644 --- a/src/SharpCompress/Common/EntryStream.Async.cs +++ b/src/SharpCompress/Common/EntryStream.Async.cs @@ -17,7 +17,6 @@ public partial class EntryStream _completed = true; } -#if !LEGACY_DOTNET public override async ValueTask DisposeAsync() { if (_isDisposed) @@ -43,9 +42,8 @@ public partial class EntryStream } } await base.DisposeAsync().ConfigureAwait(false); - await _stream.DisposeAsync().ConfigureAwait(false); + await _stream.DisposeAsyncCompat().ConfigureAwait(false); } -#endif public override async Task ReadAsync( byte[] buffer, diff --git a/src/SharpCompress/Common/EntryStream.cs b/src/SharpCompress/Common/EntryStream.cs index b8ccf339..ed4e8fc6 100644 --- a/src/SharpCompress/Common/EntryStream.cs +++ b/src/SharpCompress/Common/EntryStream.cs @@ -8,7 +8,7 @@ using SharpCompress.Readers; namespace SharpCompress.Common; -public partial class EntryStream : Stream +public partial class EntryStream : AsyncDisposableStream { private readonly IReader _reader; private readonly Stream _stream; diff --git a/src/SharpCompress/IO/AsyncDisposableStream.cs b/src/SharpCompress/IO/AsyncDisposableStream.cs new file mode 100644 index 00000000..4c7fafbe --- /dev/null +++ b/src/SharpCompress/IO/AsyncDisposableStream.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace SharpCompress.IO; + +/// +/// A that is guaranteed to be asynchronously disposable on every target framework. +/// +/// +/// +/// On .NET Framework 4.8 and .NET Standard 2.0, has no DisposeAsync. +/// Microsoft.Bcl.AsyncInterfaces supplies the interface on those +/// targets but cannot retrofit it onto the BCL's , and C# will not accept an +/// extension method for the pattern - await using requires a reachable instance +/// DisposeAsync. Deriving from this class instead of therefore makes a type +/// usable with await using uniformly, with no conditional compilation at the call site. +/// +/// +/// The fallback below is the same behaviour as the BCL's own default , +/// so a derived type may call await base.DisposeAsync() unconditionally on any target. +/// +/// +public abstract class AsyncDisposableStream : Stream +#if NO_STREAM_DISPOSEASYNC + , IAsyncDisposable +#endif +{ +#if NO_STREAM_DISPOSEASYNC + public virtual ValueTask DisposeAsync() + { + Dispose(); + return default; + } +#endif +} diff --git a/src/SharpCompress/IO/AsyncDisposeScope.cs b/src/SharpCompress/IO/AsyncDisposeScope.cs new file mode 100644 index 00000000..d8167a6c --- /dev/null +++ b/src/SharpCompress/IO/AsyncDisposeScope.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; + +namespace SharpCompress.IO; + +/// +/// Makes any resource usable with await using, disposing it asynchronously when the runtime type +/// supports it and synchronously otherwise. +/// +/// +/// Needed for locals whose static type is (or another type that +/// only sometimes has DisposeAsync), where await using cannot bind directly on +/// .NET Framework 4.8 / .NET Standard 2.0. Unlike a compile-time guard, this picks the asynchronous path +/// based on the runtime type, so a stream that really is asynchronously disposable is disposed that way on +/// every target framework. Prefer deriving from where the type is ours. +/// +internal readonly struct AsyncDisposeScope(IDisposable? resource) : IAsyncDisposable +{ + public ValueTask DisposeAsync() + { + if (resource is IAsyncDisposable asyncDisposable) + { + return asyncDisposable.DisposeAsync(); + } + + resource?.Dispose(); + return default; + } + + /// + /// Mirrors ConfiguredAsyncDisposable so await using can specify context capture without + /// boxing this struct through . + /// + public ConfiguredAsyncDisposeScope ConfigureAwait(bool continueOnCapturedContext) => + new(resource, continueOnCapturedContext); +} \ No newline at end of file diff --git a/src/SharpCompress/IO/ConfiguredAsyncDisposeScope.cs b/src/SharpCompress/IO/ConfiguredAsyncDisposeScope.cs new file mode 100644 index 00000000..f9d631c9 --- /dev/null +++ b/src/SharpCompress/IO/ConfiguredAsyncDisposeScope.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace SharpCompress.IO; + +internal readonly struct ConfiguredAsyncDisposeScope(IDisposable? resource, bool continueOnCapturedContext) +{ + public ConfiguredValueTaskAwaitable DisposeAsync() + { + if (resource is IAsyncDisposable asyncDisposable) + { + return asyncDisposable.DisposeAsync().ConfigureAwait(continueOnCapturedContext); + } + + resource?.Dispose(); + return default(ValueTask).ConfigureAwait(continueOnCapturedContext); + } +} diff --git a/src/SharpCompress/Polyfills/StreamExtensions.cs b/src/SharpCompress/Polyfills/StreamExtensions.cs index c6e66b82..d4e80630 100644 --- a/src/SharpCompress/Polyfills/StreamExtensions.cs +++ b/src/SharpCompress/Polyfills/StreamExtensions.cs @@ -25,6 +25,20 @@ public static class StreamExtensions public void Skip() => stream.CopyTo(Stream.Null); + /// + /// Returns a scope that disposes this stream when awaited, asynchronously where the runtime type + /// supports it. Lets await using be written against a -typed local on + /// every target framework. + /// + internal AsyncDisposeScope DisposeAsyncScope() => new(stream); + + /// + /// Disposes this stream, asynchronously where the runtime type supports it. Use where the static + /// type is , which has no DisposeAsync on .NET Framework 4.8 / + /// .NET Standard 2.0. + /// + internal ValueTask DisposeAsyncCompat() => new AsyncDisposeScope(stream).DisposeAsync(); + public async ValueTask SkipAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/SharpCompress/Readers/AbstractReader.Async.cs b/src/SharpCompress/Readers/AbstractReader.Async.cs index 8628c0c2..969c73e6 100644 --- a/src/SharpCompress/Readers/AbstractReader.Async.cs +++ b/src/SharpCompress/Readers/AbstractReader.Async.cs @@ -104,13 +104,8 @@ public abstract partial class AbstractReader } } //don't know the size so we have to try to decompress to skip -#if LEGACY_DOTNET - using var s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false); - await s.SkipEntryAsync(cancellationToken).ConfigureAwait(false); -#else await using var s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false); await s.SkipEntryAsync(cancellationToken).ConfigureAwait(false); -#endif } public async ValueTask WriteEntryToAsync( @@ -139,19 +134,11 @@ public abstract partial class AbstractReader private async ValueTask WriteAsync(Stream writeStream, CancellationToken cancellationToken) { -#if LEGACY_DOTNET - using Stream s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false); + await using var s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false); var sourceStream = WrapWithProgress(s, Entry); await sourceStream .CopyToAsync(writeStream, Options.BufferSize, cancellationToken) .ConfigureAwait(false); -#else - await using Stream s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false); - var sourceStream = WrapWithProgress(s, Entry); - await sourceStream - .CopyToAsync(writeStream, Options.BufferSize, cancellationToken) - .ConfigureAwait(false); -#endif } public async ValueTask OpenEntryStreamAsync( diff --git a/src/SharpCompress/Readers/IAsyncReaderExtensions.cs b/src/SharpCompress/Readers/IAsyncReaderExtensions.cs index 7515b257..5b4e667b 100644 --- a/src/SharpCompress/Readers/IAsyncReaderExtensions.cs +++ b/src/SharpCompress/Readers/IAsyncReaderExtensions.cs @@ -108,15 +108,9 @@ public static class IAsyncReaderExtensions CancellationToken cancellationToken ) { -#if LEGACY_DOTNET - using var entryStream = await reader - .OpenEntryStreamAsync(cancellationToken) - .ConfigureAwait(false); -#else await using var entryStream = await reader .OpenEntryStreamAsync(cancellationToken) .ConfigureAwait(false); -#endif var checkedStream = IEntryExtensions.WrapWithChecksumValidation( reader.Entry, entryStream, diff --git a/tests/SharpCompress.Test/BZip2/BZip2StreamAsyncTests.cs b/tests/SharpCompress.Test/BZip2/BZip2StreamAsyncTests.cs index d5b2ef78..fc7e649c 100644 --- a/tests/SharpCompress.Test/BZip2/BZip2StreamAsyncTests.cs +++ b/tests/SharpCompress.Test/BZip2/BZip2StreamAsyncTests.cs @@ -151,19 +151,8 @@ public class BZip2StreamAsyncTests Assert.True(compressed.Length > 0); // Decompress and verify -#if LEGACY_DOTNET + // MemoryStream has nothing to dispose asynchronously using (var readStream = new MemoryStream(compressed)) - { - using ( - var bzip2Stream = await BZip2Stream.CreateAsync( - new AsyncOnlyStream(readStream), - SharpCompress.Compressors.CompressionMode.Decompress, - false - ) - ) - { -#else - await using (var readStream = new MemoryStream(compressed)) { await using ( var bzip2Stream = await BZip2Stream.CreateAsync( @@ -173,7 +162,6 @@ public class BZip2StreamAsyncTests ) ) { -#endif var result = new StringBuilder(); var buffer = new byte[256]; int bytesRead; diff --git a/tests/SharpCompress.Test/GZip/GZipCrcExtractionTests.cs b/tests/SharpCompress.Test/GZip/GZipCrcExtractionTests.cs index 2e98c962..acabcb4b 100644 --- a/tests/SharpCompress.Test/GZip/GZipCrcExtractionTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipCrcExtractionTests.cs @@ -68,11 +68,8 @@ public class GZipCrcExtractionTests : TestBase [Fact] public async Task GZipArchive_WriteToFileAsync_Throws_On_Crc_Mismatch() { -#if LEGACY_DOTNET + // MemoryStream has nothing to dispose asynchronously using var stream = new MemoryStream(ReadCorruptedGZipTrailer(corruptCrc: true)); -#else - await using var stream = new MemoryStream(ReadCorruptedGZipTrailer(corruptCrc: true)); -#endif await using var archive = await GZipArchive.OpenAsyncArchive(stream); var entry = await archive.EntriesAsync.SingleAsync(); var destination = Path.Combine(SCRATCH_FILES_PATH, Guid.NewGuid().ToString()); diff --git a/tests/SharpCompress.Test/LargeArchiveTests.cs b/tests/SharpCompress.Test/LargeArchiveTests.cs index 18b427e2..50f1cfec 100644 --- a/tests/SharpCompress.Test/LargeArchiveTests.cs +++ b/tests/SharpCompress.Test/LargeArchiveTests.cs @@ -40,24 +40,15 @@ public class LargeArchiveTests : TestBase [InlineData("Large/Large.7z")] public async Task OpenAsyncArchive_ShouldStreamLargeEntry(string fixtureName) { -#if LEGACY_DOTNET - using var stream = new AsyncOnlyStream( - File.OpenRead(await GetMaterializedFixturePathAsync(fixtureName)) - ); -#else await using var stream = new AsyncOnlyStream( File.OpenRead(await GetMaterializedFixturePathAsync(fixtureName)) ); -#endif await using var archive = await ArchiveFactory.OpenAsyncArchive(stream); var entry = await GetSingleEntryAsync(archive); -#if LEGACY_DOTNET - using var entryStream = await entry.OpenEntryStreamAsync(); -#else - await using var entryStream = await entry.OpenEntryStreamAsync(); -#endif + var entryStream = await entry.OpenEntryStreamAsync(); + await using var entryStreamScope = entryStream.DisposeAsyncScope(); await VerifyContentAsync(entry.Key, entryStream); } @@ -83,25 +74,15 @@ public class LargeArchiveTests : TestBase [InlineData("Large/Large.tar.gz")] public async Task OpenAsyncReader_ShouldStreamLargeEntry(string fixtureName) { -#if LEGACY_DOTNET - using var stream = new AsyncOnlyStream( - File.OpenRead(await GetMaterializedFixturePathAsync(fixtureName)) - ); -#else await using var stream = new AsyncOnlyStream( File.OpenRead(await GetMaterializedFixturePathAsync(fixtureName)) ); -#endif await using var reader = await ReaderFactory.OpenAsyncReader(stream); Assert.True(await reader.MoveToNextEntryAsync()); Assert.False(reader.Entry.IsDirectory); -#if LEGACY_DOTNET - using var entryStream = await reader.OpenEntryStreamAsync(); -#else await using var entryStream = await reader.OpenEntryStreamAsync(); -#endif await VerifyContentAsync(reader.Entry.Key, entryStream); Assert.False(await reader.MoveToNextEntryAsync()); } diff --git a/tests/SharpCompress.Test/Mocks/AsyncOnlyStream.cs b/tests/SharpCompress.Test/Mocks/AsyncOnlyStream.cs index 232f60ee..9dec5e5a 100644 --- a/tests/SharpCompress.Test/Mocks/AsyncOnlyStream.cs +++ b/tests/SharpCompress.Test/Mocks/AsyncOnlyStream.cs @@ -2,10 +2,11 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; +using SharpCompress.IO; namespace SharpCompress.Test.Mocks; -public class AsyncOnlyStream(Stream stream, bool disposeStream = true) : Stream +public class AsyncOnlyStream(Stream stream, bool disposeStream = true) : AsyncDisposableStream { private readonly Stream _stream = stream ?? throw new ArgumentNullException(nameof(stream)); diff --git a/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs b/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs index 63f20702..28dff313 100644 --- a/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs +++ b/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Threading.Tasks; +using SharpCompress.IO; namespace SharpCompress.Test.Mocks; @@ -8,7 +9,7 @@ namespace SharpCompress.Test.Mocks; // CryptoStream doesn't always trigger the Flush, so this class is used instead // See https://referencesource.microsoft.com/#mscorlib/system/security/cryptography/cryptostream.cs,141 -public class FlushOnDisposeStream(Stream innerStream) : Stream +public class FlushOnDisposeStream(Stream innerStream) : AsyncDisposableStream { public override bool CanRead => innerStream.CanRead; @@ -48,12 +49,10 @@ public class FlushOnDisposeStream(Stream innerStream) : Stream base.Dispose(disposing); } -#if !LEGACY_DOTNET public override async ValueTask DisposeAsync() { await innerStream.FlushAsync(); innerStream.Close(); await base.DisposeAsync(); } -#endif } diff --git a/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs b/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs index 32961ebc..f6b7ec8e 100644 --- a/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs +++ b/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; +using SharpCompress.IO; namespace SharpCompress.Test.Mocks; @@ -9,7 +10,7 @@ namespace SharpCompress.Test.Mocks; /// A forward-only stream wrapper that delegates directly to the underlying stream /// without any buffering. Supports reading and writing but not seeking. /// -public class ForwardOnlyStream : Stream +public class ForwardOnlyStream : AsyncDisposableStream { private readonly Stream _stream; private bool _isDisposed; @@ -142,17 +143,15 @@ public class ForwardOnlyStream : Stream } } -#if !LEGACY_DOTNET public override async ValueTask DisposeAsync() { if (!_isDisposed) { - await _stream.DisposeAsync(); + await _stream.DisposeAsyncCompat(); _isDisposed = true; } await base.DisposeAsync(); } -#endif private void ThrowIfDisposed() { diff --git a/tests/SharpCompress.Test/Mocks/TestStream.cs b/tests/SharpCompress.Test/Mocks/TestStream.cs index d3ff48a8..5061afc3 100644 --- a/tests/SharpCompress.Test/Mocks/TestStream.cs +++ b/tests/SharpCompress.Test/Mocks/TestStream.cs @@ -2,10 +2,11 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using SharpCompress.IO; namespace SharpCompress.Test.Mocks; -public class TestStream(Stream stream, bool read, bool write, bool seek) : Stream +public class TestStream(Stream stream, bool read, bool write, bool seek) : AsyncDisposableStream { public TestStream(Stream stream) : this(stream, stream.CanRead, stream.CanWrite, stream.CanSeek) { } @@ -50,14 +51,14 @@ public class TestStream(Stream stream, bool read, bool write, bool seek) : Strea Memory buffer, CancellationToken cancellationToken = default ) => stream.ReadAsync(buffer, cancellationToken); +#endif public override async ValueTask DisposeAsync() { await base.DisposeAsync(); - await stream.DisposeAsync(); + await stream.DisposeAsyncCompat(); IsDisposed = true; } -#endif public override long Seek(long offset, SeekOrigin origin) => stream.Seek(offset, origin); diff --git a/tests/SharpCompress.Test/Mocks/ThrowOnFlushStream.cs b/tests/SharpCompress.Test/Mocks/ThrowOnFlushStream.cs index 2cf1a84c..911762a5 100644 --- a/tests/SharpCompress.Test/Mocks/ThrowOnFlushStream.cs +++ b/tests/SharpCompress.Test/Mocks/ThrowOnFlushStream.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; +using SharpCompress.IO; namespace SharpCompress.Test.Mocks; @@ -9,7 +10,7 @@ namespace SharpCompress.Test.Mocks; /// A stream wrapper that throws NotSupportedException on Flush() calls. /// This is used to test that archive iteration handles streams that don't support flushing. /// -public class ThrowOnFlushStream : Stream +public class ThrowOnFlushStream : AsyncDisposableStream { private readonly Stream inner; diff --git a/tests/SharpCompress.Test/Mocks/TruncatedStream.cs b/tests/SharpCompress.Test/Mocks/TruncatedStream.cs index 8699a430..55037f2e 100644 --- a/tests/SharpCompress.Test/Mocks/TruncatedStream.cs +++ b/tests/SharpCompress.Test/Mocks/TruncatedStream.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using SharpCompress.IO; namespace SharpCompress.Test.Mocks; @@ -7,7 +8,7 @@ namespace SharpCompress.Test.Mocks; /// A stream wrapper that truncates the underlying stream after reading a specified number of bytes. /// Used for testing error handling when streams end prematurely. /// -public class TruncatedStream : Stream +public class TruncatedStream : AsyncDisposableStream { private readonly Stream baseStream; private readonly long truncateAfterBytes; diff --git a/tests/SharpCompress.Test/ReaderTests.cs b/tests/SharpCompress.Test/ReaderTests.cs index 4c9024c9..64469219 100644 --- a/tests/SharpCompress.Test/ReaderTests.cs +++ b/tests/SharpCompress.Test/ReaderTests.cs @@ -159,18 +159,12 @@ public abstract class ReaderTests : TestBase { using var file = File.OpenRead(testArchive); -#if !LEGACY_DOTNET - await using var protectedStream = SharpCompressStream.CreateNonDisposing( + // SharpCompressStream is not yet an AsyncDisposableStream, so scope its disposal + var protectedStream = SharpCompressStream.CreateNonDisposing( new ForwardOnlyStream(file, options.BufferSize) ); + await using var protectedStreamScope = protectedStream.DisposeAsyncScope(); await using var testStream = new TestStream(protectedStream); -#else - - using var protectedStream = SharpCompressStream.CreateNonDisposing( - new ForwardOnlyStream(file, options.BufferSize) - ); - using var testStream = new TestStream(protectedStream); -#endif await using ( var reader = await ReaderFactory.OpenAsyncReader( new AsyncOnlyStream(testStream), diff --git a/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs index 3a1b95cc..dabc0fa2 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs @@ -187,10 +187,6 @@ public class TarArchiveAsyncTests : ArchiveTests } } } -#if LEGACY_DOTNET - //add a delay because old .net sucks on DisposeAsync - await Task.Delay(TimeSpan.FromSeconds(1)); -#endif } [Fact] @@ -338,11 +334,8 @@ public class TarArchiveAsyncTests : ArchiveTests { ++numberOfEntries; -#if LEGACY_DOTNET - using var tarEntryStream = await entry.OpenEntryStreamAsync(); -#else - await using var tarEntryStream = await entry.OpenEntryStreamAsync(); -#endif + var tarEntryStream = await entry.OpenEntryStreamAsync(); + await using var tarEntryStreamScope = tarEntryStream.DisposeAsyncScope(); using var testFileStream = new MemoryStream(); await tarEntryStream.CopyToAsync(testFileStream); Assert.Equal(testBytes.Length, testFileStream.Length); diff --git a/tests/SharpCompress.Test/Tar/TarReaderAsyncTests.cs b/tests/SharpCompress.Test/Tar/TarReaderAsyncTests.cs index 90bce78e..9307b7d3 100644 --- a/tests/SharpCompress.Test/Tar/TarReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/Tar/TarReaderAsyncTests.cs @@ -337,13 +337,8 @@ public class TarReaderAsyncTests : ReaderTests Assert.True(await reader.MoveToNextEntryAsync()); Assert.Equal("inner.tar.gz", reader.Entry.Key); -#if !LEGACY_DOTNET await using var entryStream = await reader.OpenEntryStreamAsync(); await using var flushingStream = new FlushOnDisposeStream(entryStream); -#else - using var entryStream = await reader.OpenEntryStreamAsync(); - using var flushingStream = new FlushOnDisposeStream(entryStream); -#endif // Extract inner.tar.gz await using var innerReader = await ReaderFactory.OpenAsyncReader(flushingStream); diff --git a/tests/SharpCompress.Test/Tar/TarWriterNonSeekableTests.cs b/tests/SharpCompress.Test/Tar/TarWriterNonSeekableTests.cs index 454c20a6..218533db 100644 --- a/tests/SharpCompress.Test/Tar/TarWriterNonSeekableTests.cs +++ b/tests/SharpCompress.Test/Tar/TarWriterNonSeekableTests.cs @@ -89,11 +89,8 @@ public class TarWriterNonSeekableTests { 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 + var entryStream = await entry.OpenEntryStreamAsync(); + await using (entryStream.DisposeAsyncScope()) { await entryStream.CopyToAsync(extracted); } diff --git a/tests/SharpCompress.Test/Zip/Zip64AsyncTests.cs b/tests/SharpCompress.Test/Zip/Zip64AsyncTests.cs index 5124b6b0..626265cd 100644 --- a/tests/SharpCompress.Test/Zip/Zip64AsyncTests.cs +++ b/tests/SharpCompress.Test/Zip/Zip64AsyncTests.cs @@ -198,11 +198,7 @@ public class Zip64AsyncTests : WriterTests count++; lastKey = rd.Entry.Key; -#if LEGACY_DOTNET - using var entryStream = await rd.OpenEntryStreamAsync(); -#else await using var entryStream = await rd.OpenEntryStreamAsync(); -#endif if (rd.Entry.Key == "small") { using var ms = new MemoryStream(); @@ -337,17 +333,10 @@ public class Zip64AsyncTests : WriterTests ); while (await rd.MoveToNextEntryAsync()) { -#if LEGACY_DOTNET - using (var entryStream = await rd.OpenEntryStreamAsync()) - { - await entryStream.SkipEntryAsync(); - } -#else await using (var entryStream = await rd.OpenEntryStreamAsync()) { await entryStream.SkipEntryAsync(); } -#endif count++; if (prev != null) { diff --git a/tests/SharpCompress.Test/Zip/ZipCrcExtractionTests.cs b/tests/SharpCompress.Test/Zip/ZipCrcExtractionTests.cs index cd0e610c..4a61b150 100644 --- a/tests/SharpCompress.Test/Zip/ZipCrcExtractionTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipCrcExtractionTests.cs @@ -122,11 +122,8 @@ public class ZipCrcExtractionTests : ArchiveTests using var zipStream = CreateZipWithInvalidCrc(useDataDescriptor: false); using var archive = ZipArchive.OpenArchive(zipStream); var entry = archive.Entries.Single(e => !e.IsDirectory); -#if LEGACY_DOTNET + // MemoryStream has nothing to dispose asynchronously using var destination = new MemoryStream(); -#else - await using var destination = new MemoryStream(); -#endif var exception = await Assert.ThrowsAsync(async () => await entry.WriteToAsync(destination, new ExtractionOptions { CheckCrc = true }) @@ -141,11 +138,8 @@ public class ZipCrcExtractionTests : ArchiveTests using var zipStream = CreateZipWithInvalidCrc(useDataDescriptor: false); using var archive = ZipArchive.OpenArchive(zipStream); var entry = archive.Entries.Single(e => !e.IsDirectory); -#if LEGACY_DOTNET + // MemoryStream has nothing to dispose asynchronously using var destination = new MemoryStream(); -#else - await using var destination = new MemoryStream(); -#endif await entry.WriteToAsync(destination, new ExtractionOptions { CheckCrc = false }); diff --git a/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs index 4c7cd627..78f70b28 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs @@ -315,11 +315,7 @@ public class ZipReaderAsyncTests : ReaderTests { if (!reader.Entry.IsDirectory) { -#if LEGACY_DOTNET - using var entryStream = await reader.OpenEntryStreamAsync(); -#else await using var entryStream = await reader.OpenEntryStreamAsync(); -#endif // Read some data var buffer = new byte[1024]; await entryStream.ReadAsync(buffer, 0, buffer.Length); @@ -342,11 +338,7 @@ public class ZipReaderAsyncTests : ReaderTests { if (!reader.Entry.IsDirectory) { -#if LEGACY_DOTNET - using var entryStream = await reader.OpenEntryStreamAsync(); -#else await using var entryStream = await reader.OpenEntryStreamAsync(); -#endif // Read some data var buffer = new byte[1024]; await entryStream.ReadAsync(buffer, 0, buffer.Length); diff --git a/tests/SharpCompress.Test/Zip/ZipWriterNonSeekableTests.cs b/tests/SharpCompress.Test/Zip/ZipWriterNonSeekableTests.cs index 4dd10b57..95cdcc56 100644 --- a/tests/SharpCompress.Test/Zip/ZipWriterNonSeekableTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipWriterNonSeekableTests.cs @@ -163,11 +163,8 @@ public class ZipWriterNonSeekableTests { var entry = archive.Entries.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 + var entryStream = await entry.OpenEntryStreamAsync(); + await using (entryStream.DisposeAsyncScope()) { await entryStream.CopyToAsync(extracted); }