using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using SharpCompress.Archives; using SharpCompress.Archives.Zip; using SharpCompress.Common; using SharpCompress.Compressors.Deflate; using SharpCompress.Compressors.Xz; using SharpCompress.Crypto; using SharpCompress.Test.Mocks; using SharpCompress.Writers; using SharpCompress.Writers.Zip; using Xunit; namespace SharpCompress.Test.Zip; public class ZipTypesLevelsWithCrcRatioAsyncTests : ArchiveTests { public ZipTypesLevelsWithCrcRatioAsyncTests() => UseExtensionInsteadOfNameToVerify = true; [Theory] [InlineData(CompressionType.Deflate, 1, 1, 0.11f)] // was 0.8f, actual 0.104 [InlineData(CompressionType.Deflate, 3, 1, 0.08f)] // was 0.8f, actual 0.078 [InlineData(CompressionType.Deflate, 6, 1, 0.05f)] // was 0.8f, actual ~0.042 [InlineData(CompressionType.Deflate, 9, 1, 0.04f)] // was 0.7f, actual 0.038 [InlineData(CompressionType.ZStandard, 1, 1, 0.025f)] // was 0.8f, actual 0.023 [InlineData(CompressionType.ZStandard, 3, 1, 0.015f)] // was 0.7f, actual 0.013 [InlineData(CompressionType.ZStandard, 9, 1, 0.006f)] // was 0.7f, actual 0.005 [InlineData(CompressionType.ZStandard, 22, 1, 0.005f)] // was 0.7f, actual 0.004 [InlineData(CompressionType.BZip2, 0, 1, 0.035f)] // was 0.8f, actual 0.033 [InlineData(CompressionType.LZMA, 0, 1, 0.005f)] // was 0.8f, actual 0.004 [InlineData(CompressionType.None, 0, 1, 1.001f)] // was 1.1f, actual 1.000 [InlineData(CompressionType.Deflate, 6, 2, 0.045f)] // was 0.8f, actual 0.042 [InlineData(CompressionType.ZStandard, 3, 2, 0.012f)] // was 0.7f, actual 0.010 [InlineData(CompressionType.BZip2, 0, 2, 0.035f)] // was 0.8f, actual 0.032 [InlineData(CompressionType.Deflate, 9, 3, 0.04f)] // was 0.7f, actual 0.038 [InlineData(CompressionType.ZStandard, 9, 3, 0.003f)] // was 0.7f, actual 0.002 public async ValueTask Zip_Create_Archive_With_3_Files_Crc32_Test_Async( CompressionType compressionType, int compressionLevel, int sizeMb, float expectedRatio ) { const int OneMiB = 1024 * 1024; var baseSize = sizeMb * OneMiB; // Generate test content for files with sizes based on the sizeMb parameter var file1Data = TestPseudoTextStream.Create(baseSize); var file2Data = TestPseudoTextStream.Create(baseSize * 2); var file3Data = TestPseudoTextStream.Create(baseSize * 3); var expectedFiles = new Dictionary { [$"file1_{sizeMb}MiB.txt"] = (file1Data, CalculateCrc32(file1Data)), [$"data/file2_{sizeMb * 2}MiB.txt"] = (file2Data, CalculateCrc32(file2Data)), [$"deep/nested/file3_{sizeMb * 3}MiB.txt"] = (file3Data, CalculateCrc32(file3Data)), }; // Create zip archive in memory using var zipStream = new MemoryStream(); using ( var writer = CreateWriterWithLevelAsync(zipStream, compressionType, compressionLevel) ) { await writer.WriteAsync($"file1_{sizeMb}MiB.txt", new MemoryStream(file1Data)); await writer.WriteAsync($"data/file2_{sizeMb * 2}MiB.txt", new MemoryStream(file2Data)); await writer.WriteAsync( $"deep/nested/file3_{sizeMb * 3}MiB.txt", new MemoryStream(file3Data) ); } // Calculate and output actual compression ratio var originalSize = file1Data.Length + file2Data.Length + file3Data.Length; var actualRatio = (double)zipStream.Length / originalSize; // Verify compression occurred (except for None compression type) if (compressionType != CompressionType.None) { Assert.True( zipStream.Length < originalSize, $"Compression failed: compressed={zipStream.Length}, original={originalSize}" ); } // Verify compression ratio VerifyCompressionRatio( originalSize, zipStream.Length, expectedRatio, $"{compressionType} level {compressionLevel}" ); // Verify archive content and CRC32 await VerifyArchiveContentAsync(zipStream, expectedFiles); // Verify compression type is correctly set VerifyCompressionType(zipStream, compressionType); } [Theory] [InlineData(CompressionType.Deflate, 1, 4, 0.11f)] // was 0.8, actual 0.105 [InlineData(CompressionType.Deflate, 3, 4, 0.08f)] // was 0.8, actual 0.077 [InlineData(CompressionType.Deflate, 6, 4, 0.045f)] // was 0.8, actual 0.042 [InlineData(CompressionType.Deflate, 9, 4, 0.04f)] // was 0.8, actual 0.037 [InlineData(CompressionType.ZStandard, 1, 4, 0.025f)] // was 0.8, actual 0.022 [InlineData(CompressionType.ZStandard, 3, 4, 0.012f)] // was 0.8, actual 0.010 [InlineData(CompressionType.ZStandard, 9, 4, 0.003f)] // was 0.8, actual 0.002 [InlineData(CompressionType.ZStandard, 22, 4, 0.003f)] // was 0.8, actual 0.002 [InlineData(CompressionType.BZip2, 0, 4, 0.035f)] // was 0.8, actual 0.032 [InlineData(CompressionType.LZMA, 0, 4, 0.003f)] // was 0.8, actual 0.002 public async ValueTask Zip_WriterFactory_Crc32_Test_Async( CompressionType compressionType, int compressionLevel, int sizeMb, float expectedRatio ) { var fileSize = sizeMb * 1024 * 1024; var testData = TestPseudoTextStream.Create(fileSize); var expectedCrc = CalculateCrc32(testData); // Create archive with specified compression level using var zipStream = new MemoryStream(); var writerOptions = new ZipWriterOptions(compressionType) { CompressionLevel = compressionLevel, }; using ( var writer = WriterFactory.OpenAsyncWriter( new AsyncOnlyStream(zipStream), ArchiveType.Zip, writerOptions ) ) { await writer.WriteAsync( $"{compressionType}_level_{compressionLevel}_{sizeMb}MiB.txt", new MemoryStream(testData) ); } // Calculate and output actual compression ratio var actualRatio = (double)zipStream.Length / testData.Length; VerifyCompressionRatio( testData.Length, zipStream.Length, expectedRatio, $"{compressionType} level {compressionLevel}" ); // Verify the archive zipStream.Position = 0; using var archive = ZipArchive.OpenArchive(zipStream); var entry = archive.Entries.Single(e => !e.IsDirectory); using var entryStream = await entry.OpenEntryStreamAsync(); using var extractedStream = new MemoryStream(); await entryStream.CopyToAsync(extractedStream); var extractedData = extractedStream.ToArray(); var actualCrc = CalculateCrc32(extractedData); Assert.Equal(compressionType, entry.CompressionType); Assert.Equal(expectedCrc, actualCrc); Assert.Equal(testData.Length, extractedData.Length); Assert.Equal(testData, extractedData); } [Theory] [InlineData(CompressionType.Deflate, 1, 2, 0.11f)] // was 0.8, actual 0.104 [InlineData(CompressionType.Deflate, 3, 2, 0.08f)] // was 0.8, actual 0.077 [InlineData(CompressionType.Deflate, 6, 2, 0.045f)] // was 0.8, actual 0.042 [InlineData(CompressionType.Deflate, 9, 2, 0.04f)] // was 0.7, actual 0.038 [InlineData(CompressionType.ZStandard, 1, 2, 0.025f)] // was 0.8, actual 0.023 [InlineData(CompressionType.ZStandard, 3, 2, 0.015f)] // was 0.7, actual 0.012 [InlineData(CompressionType.ZStandard, 9, 2, 0.006f)] // was 0.7, actual 0.005 [InlineData(CompressionType.ZStandard, 22, 2, 0.005f)] // was 0.7, actual 0.004 [InlineData(CompressionType.BZip2, 0, 2, 0.035f)] // was 0.8, actual 0.032 [InlineData(CompressionType.LZMA, 0, 2, 0.005f)] // was 0.8, actual 0.004 public async ValueTask Zip_ZipArchiveOpen_Crc32_Test_Async( CompressionType compressionType, int compressionLevel, int sizeMb, float expectedRatio ) { var fileSize = sizeMb * 1024 * 1024; var testData = TestPseudoTextStream.Create(fileSize); var expectedCrc = CalculateCrc32(testData); // Create archive with specified compression and level using var zipStream = new MemoryStream(); using ( var writer = CreateWriterWithLevelAsync(zipStream, compressionType, compressionLevel) ) { await writer.WriteAsync( $"{compressionType}_{compressionLevel}_{sizeMb}MiB.txt", new MemoryStream(testData) ); } // Calculate and output actual compression ratio var actualRatio = (double)zipStream.Length / testData.Length; // Verify the archive zipStream.Position = 0; using var archive = ZipArchive.OpenArchive(zipStream); var entry = archive.Entries.Single(e => !e.IsDirectory); using var entryStream = await entry.OpenEntryStreamAsync(); using var extractedStream = new MemoryStream(); await entryStream.CopyToAsync(extractedStream); var extractedData = extractedStream.ToArray(); var actualCrc = CalculateCrc32(extractedData); Assert.Equal(compressionType, entry.CompressionType); Assert.Equal(expectedCrc, actualCrc); Assert.Equal(testData.Length, extractedData.Length); // For smaller files, verify full content; for larger, spot check if (testData.Length <= sizeMb * 2) { Assert.Equal(testData, extractedData); } else { VerifyDataSpotCheck(testData, extractedData); } VerifyCompressionRatio( testData.Length, zipStream.Length, expectedRatio, $"{compressionType} Level {compressionLevel}" ); } // Helper method for async archive content verification private async ValueTask VerifyArchiveContentAsync( MemoryStream zipStream, Dictionary expectedFiles ) { zipStream.Position = 0; using var archive = ZipArchive.OpenArchive(zipStream); foreach (var entry in archive.Entries.Where(e => !e.IsDirectory)) { Assert.True( expectedFiles.ContainsKey(entry.Key!), $"Unexpected file in archive: {entry.Key}" ); var expected = expectedFiles[entry.Key!]; using var entryStream = await entry.OpenEntryStreamAsync(); using var extractedStream = new MemoryStream(); await entryStream.CopyToAsync(extractedStream); var extractedData = extractedStream.ToArray(); var actualCrc = CalculateCrc32(extractedData); Assert.Equal(expected.crc, actualCrc); Assert.Equal(expected.data.Length, extractedData.Length); // For larger files, just spot check, for smaller verify full content var expectedData = expected.data; if (expectedData.Length <= 2 * 1024 * 1024) { Assert.Equal(expectedData, extractedData); } else { VerifyDataSpotCheck(expectedData, extractedData); } } Assert.Equal(expectedFiles.Count, archive.Entries.Count(e => !e.IsDirectory)); } }