From 6ddeb048f6ac0b25c18730e834ea6dbe8b316f1b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:55:12 +0000 Subject: [PATCH 1/3] Initial plan From d9320c0930f42c49b08e5b20327d4141dd10fbc1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:56:53 +0000 Subject: [PATCH 2/3] Initial plan for RAR5 async test improvements Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/packages.lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 29e7a1bd5..27e9e4968 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -216,9 +216,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA==" + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "sXdDtMf2qcnbygw9OdE535c2lxSxrZP8gO4UhDJ0xiJbl1wIqXS1OTcTDFTIJPOFd6Mhcm8gPEthqWGUxBsTqw==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -264,9 +264,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.22, )", - "resolved": "8.0.22", - "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" + "requested": "[8.0.23, )", + "resolved": "8.0.23", + "contentHash": "GqHiB1HbbODWPbY/lc5xLQH8siEEhNA0ptpJCC6X6adtAYNEzu5ZlqV3YHA3Gh7fuEwgA8XqVwMtH2KNtuQM1Q==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", From 20f2c9e0d3b5878d85afac45a357167544f54cab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 17:02:24 +0000 Subject: [PATCH 3/3] Add comprehensive RAR5 async extraction tests - Created Rar5AsyncExtractionTests with 12 tests that exercise Unpack5Async code path - Tests use AsyncOnlyStream to ensure async methods are called (not sync fallbacks) - Cover basic archives, solid archives, encrypted archives, and various extraction methods - Tests verify: * ExtractAllEntries for solid archives * Individual file extraction with WriteEntryToDirectoryAsync * OpenEntryStream for manual extraction * WriteToDirectory for full archive extraction * Encrypted files (both header+files and files only) * Special formats (Blake2 CRC, comments, uncompressed) * Skipping entries in solid archives - All tests passing on both net48 and net10.0 Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .../Rar/Rar5AsyncExtractionTests.cs | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 tests/SharpCompress.Test/Rar/Rar5AsyncExtractionTests.cs diff --git a/tests/SharpCompress.Test/Rar/Rar5AsyncExtractionTests.cs b/tests/SharpCompress.Test/Rar/Rar5AsyncExtractionTests.cs new file mode 100644 index 000000000..ed6395cf8 --- /dev/null +++ b/tests/SharpCompress.Test/Rar/Rar5AsyncExtractionTests.cs @@ -0,0 +1,243 @@ +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using SharpCompress.Archives; +using SharpCompress.Archives.Rar; +using SharpCompress.Common; +using SharpCompress.Readers; +using SharpCompress.Test.Mocks; +using Xunit; + +namespace SharpCompress.Test.Rar; + +/// +/// Tests specifically designed to exercise the Unpack5Async code path for RAR5 archives. +/// These tests use AsyncOnlyStream to ensure that async methods are actually being called. +/// +public class Rar5AsyncExtractionTests : ArchiveTests +{ + [Fact] + public async Task Rar5_Basic_Reader_ExtractAll_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.rar")); + await using var reader = await ReaderFactory.OpenAsyncReader(new AsyncOnlyStream(stream)); + + while (await reader.MoveToNextEntryAsync()) + { + if (!reader.Entry.IsDirectory) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); + } + } + VerifyFiles(); + } + + [Fact] + public async Task Rar5_None_Reader_ExtractAll_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.none.rar")); + await using var reader = await ReaderFactory.OpenAsyncReader(new AsyncOnlyStream(stream)); + + while (await reader.MoveToNextEntryAsync()) + { + if (!reader.Entry.IsDirectory) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); + } + } + VerifyFiles(); + } + + [Fact] + public async Task Rar5_Solid_ExtractAllEntries_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.solid.rar")); + await using var archive = RarArchive.OpenAsyncArchive(new AsyncOnlyStream(stream)); + + Assert.True(await archive.IsSolidAsync()); + + await using var reader = await archive.ExtractAllEntriesAsync(); + while (await reader.MoveToNextEntryAsync()) + { + if (!reader.Entry.IsDirectory) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); + } + } + VerifyFiles(); + } + + [Fact] + public async Task Rar5_Solid_Reader_ExtractAll_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.solid.rar")); + await using var reader = await ReaderFactory.OpenAsyncReader(new AsyncOnlyStream(stream)); + + while (await reader.MoveToNextEntryAsync()) + { + if (!reader.Entry.IsDirectory) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); + } + } + VerifyFiles(); + } + + [Fact] + public async Task Rar5_Reader_OpenEntryStream_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.rar")); + await using var reader = await ReaderFactory.OpenAsyncReader(new AsyncOnlyStream(stream)); + + while (await reader.MoveToNextEntryAsync()) + { + if (!reader.Entry.IsDirectory) + { + var entryStream = await reader.OpenEntryStreamAsync(); + try + { + var file = Path.GetFileName(reader.Entry.Key).NotNull(); + var folder = Path.GetDirectoryName(reader.Entry.Key) ?? ""; + var destdir = Path.Combine(SCRATCH_FILES_PATH, folder); + if (!Directory.Exists(destdir)) + { + Directory.CreateDirectory(destdir); + } + var destinationFileName = Path.Combine(destdir, file); + + using var fs = File.OpenWrite(destinationFileName); + await entryStream.CopyToAsync(fs); + } + finally + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + await entryStream.DisposeAsync(); +#else + entryStream.Dispose(); +#endif + } + } + } + VerifyFiles(); + } + + [Fact] + public async Task Rar5_Encrypted_FilesOnly_Reader_Async() + { + using var stream = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "Rar5.encrypted_filesOnly.rar") + ); + await using var reader = await ReaderFactory.OpenAsyncReader( + new AsyncOnlyStream(stream), + new ReaderOptions { Password = "test" } + ); + + while (await reader.MoveToNextEntryAsync()) + { + if (!reader.Entry.IsDirectory) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); + } + } + VerifyFiles(); + } + + [Fact] + public async Task Rar5_Encrypted_FilesAndHeader_Reader_Async() + { + using var stream = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "Rar5.encrypted_filesAndHeader.rar") + ); + await using var reader = await ReaderFactory.OpenAsyncReader( + new AsyncOnlyStream(stream), + new ReaderOptions { Password = "test" } + ); + + while (await reader.MoveToNextEntryAsync()) + { + if (!reader.Entry.IsDirectory) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); + } + } + VerifyFiles(); + } + + [Fact] + public async Task Rar5_CRC_Blake2_Reader_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.crc_blake2.rar")); + await using var reader = await ReaderFactory.OpenAsyncReader(new AsyncOnlyStream(stream)); + + while (await reader.MoveToNextEntryAsync()) + { + if (!reader.Entry.IsDirectory) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); + } + } + VerifyFiles(); + } + + [Fact] + public async Task Rar5_Comment_Reader_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.comment.rar")); + await using var reader = await ReaderFactory.OpenAsyncReader(new AsyncOnlyStream(stream)); + + while (await reader.MoveToNextEntryAsync()) + { + if (!reader.Entry.IsDirectory) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); + } + } + VerifyFiles(); + } + + [Fact] + public async Task Rar5_Solid_Skip_Some_Entries_Reader_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.solid.rar")); + await using var reader = await ReaderFactory.OpenAsyncReader(new AsyncOnlyStream(stream)); + + while (await reader.MoveToNextEntryAsync()) + { + // Only extract jpg files to test skipping in solid archive + if (!reader.Entry.IsDirectory && reader.Entry.Key.NotNull().Contains("jpg")) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); + } + } + } + + [Fact] + public async Task Rar5_WriteToDirectory_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.rar")); + await using var archive = RarArchive.OpenAsyncArchive(new AsyncOnlyStream(stream)); + + await archive.WriteToDirectoryAsync(SCRATCH_FILES_PATH); + VerifyFiles(); + } + + [Fact] + public async Task Rar5_Solid_WriteToDirectory_Async() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar5.solid.rar")); + await using var archive = RarArchive.OpenAsyncArchive(new AsyncOnlyStream(stream)); + + Assert.True(await archive.IsSolidAsync()); + await archive.WriteToDirectoryAsync(SCRATCH_FILES_PATH); + VerifyFiles(); + } +}