From 90df62186a9a7c1d45e6328f5454b1d4451ec99f Mon Sep 17 00:00:00 2001 From: Ian Hays Date: Wed, 1 Jun 2016 11:02:58 -0700 Subject: [PATCH] Add Compression binary compat tests We don't currently test binary equality of outputted test zips, so tested compatibility is limited to the parts of the header we expose via a ZipArchive API. This commit adds some tests that compare normal and unicode zips between the current version and .NET 4.5 and .NET 4.6. Commit migrated from https://github.com/dotnet/corefx/commit/f7840734bdcf1686d185a6df3405d0c5128ff280 --- .../tests/System/IO/Compression/StreamHelpers.cs | 2 +- .../tests/System/IO/Compression/ZipTestHelper.cs | 10 +- .../tests/System.IO.Compression.Tests.csproj | 2 +- .../ZipArchive/zip_ManualAndCompatibilityTests.cs | 155 ++++++++++++++++++++- 4 files changed, 160 insertions(+), 9 deletions(-) diff --git a/src/libraries/Common/tests/System/IO/Compression/StreamHelpers.cs b/src/libraries/Common/tests/System/IO/Compression/StreamHelpers.cs index 1c7fd57..20d4007 100644 --- a/src/libraries/Common/tests/System/IO/Compression/StreamHelpers.cs +++ b/src/libraries/Common/tests/System/IO/Compression/StreamHelpers.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; public static partial class StreamHelpers { - public static async Task CreateTempCopyStream(String path) + public static async Task CreateTempCopyStream(String path) { var bytes = File.ReadAllBytes(path); diff --git a/src/libraries/Common/tests/System/IO/Compression/ZipTestHelper.cs b/src/libraries/Common/tests/System/IO/Compression/ZipTestHelper.cs index 3fa59a1..2809b77 100644 --- a/src/libraries/Common/tests/System/IO/Compression/ZipTestHelper.cs +++ b/src/libraries/Common/tests/System/IO/Compression/ZipTestHelper.cs @@ -180,7 +180,7 @@ namespace System.IO.Compression.Tests DateTime lower = file.LastModifiedDate.AddSeconds(-zipTimestampResolution); DateTime upper = file.LastModifiedDate.AddSeconds(zipTimestampResolution); Assert.InRange(entry.LastWriteTime.Ticks, lower.Ticks, upper.Ticks); -} + } Assert.Equal(file.Name, entry.Name); Assert.Equal(entryName, entry.FullName); @@ -292,7 +292,8 @@ namespace System.IO.Compression.Tests { String entryName = i.FullName; - archive.CreateEntry(entryName.Replace('\\', '/') + "/"); + ZipArchiveEntry e = archive.CreateEntry(entryName.Replace('\\', '/') + "/"); + e.LastWriteTime = i.LastModifiedDate; } } @@ -307,10 +308,7 @@ namespace System.IO.Compression.Tests if (installStream != null) { ZipArchiveEntry e = archive.CreateEntry(entryName.Replace('\\', '/')); - try - { e.LastWriteTime = i.LastModifiedDate; } - catch (ArgumentOutOfRangeException) - { e.LastWriteTime = DateTimeOffset.Now; } + e.LastWriteTime = i.LastModifiedDate; using (Stream entryStream = e.Open()) { installStream.CopyTo(entryStream); diff --git a/src/libraries/System.IO.Compression/tests/System.IO.Compression.Tests.csproj b/src/libraries/System.IO.Compression/tests/System.IO.Compression.Tests.csproj index 79c0768..c6185f6 100644 --- a/src/libraries/System.IO.Compression/tests/System.IO.Compression.Tests.csproj +++ b/src/libraries/System.IO.Compression/tests/System.IO.Compression.Tests.csproj @@ -65,7 +65,7 @@ - + %(RecursiveDir)%(Filename)%(Extension) diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ManualAndCompatibilityTests.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ManualAndCompatibilityTests.cs index 3ed95b4..af4e7a3 100644 --- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ManualAndCompatibilityTests.cs +++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ManualAndCompatibilityTests.cs @@ -17,7 +17,8 @@ namespace System.IO.Compression.Tests [InlineData("xceedstreaming.zip", "normal", true, true)] public static async Task CompatibilityTests(string zipFile, string zipFolder, bool dontRequireExplicit, bool dontCheckTimes) { - IsZipSameAsDir(await StreamHelpers.CreateTempCopyStream(compat(zipFile)), zfolder(zipFolder), ZipArchiveMode.Update, dontRequireExplicit, dontCheckTimes);} + IsZipSameAsDir(await StreamHelpers.CreateTempCopyStream(compat(zipFile)), zfolder(zipFolder), ZipArchiveMode.Update, dontRequireExplicit, dontCheckTimes); + } [Theory] [InlineData("excel.xlsx", "excel", true, true)] @@ -54,5 +55,157 @@ namespace System.IO.Compression.Tests Assert.Equal(fileName, entry.Name); } } + + /// + /// This test compares binary content of a zip produced by the current version with a zip produced by + /// other frameworks. It does this by searching the two zips for the header signature and then + /// it compares the subsequent header values for equality. + /// + /// This test looks for the local file headers that each entry within a zip possesses and compares these + /// values: + /// local file header signature 4 bytes (0x04034b50) + /// version needed to extract 2 bytes + /// general purpose bit flag 2 bytes + /// compression method 2 bytes + /// last mod file time 2 bytes + /// last mod file date 2 bytes + /// + /// it does not compare these values: + /// + /// crc-32 4 bytes + /// compressed size 4 bytes + /// uncompressed size 4 bytes + /// file name length 2 bytes + /// extra field length 2 bytes + /// file name(variable size) + /// extra field(variable size) + /// + [Theory] + [InlineData("net45_unicode.zip", "unicode")] + [InlineData("net46_unicode.zip", "unicode")] + [InlineData("net45_normal.zip", "normal")] + [InlineData("net46_normal.zip", "normal")] + public static async Task ZipBinaryCompat_LocalFileHeaders(string zipFile, string zipFolder) + { + using (MemoryStream actualArchiveStream = new MemoryStream()) + using (MemoryStream expectedArchiveStream = await StreamHelpers.CreateTempCopyStream(compat(zipFile))) + { + byte[] localFileHeaderSignature = new byte[] { 0x50, 0x4b, 0x03, 0x04 }; + + // Produce a ZipFile + await CreateFromDir(zfolder(zipFolder), actualArchiveStream, ZipArchiveMode.Create); + + // Read the streams to byte arrays + byte[] actualBytes = actualArchiveStream.ToArray(); + byte[] expectedBytes = expectedArchiveStream.ToArray(); + + // Search for the file headers + int actualIndex = 0, expectedIndex = 0; + while ((expectedIndex = FindIndexOfSequence(expectedBytes, expectedIndex, localFileHeaderSignature)) != -1) + { + actualIndex = FindIndexOfSequence(actualBytes, actualIndex, localFileHeaderSignature); + Assert.NotEqual(-1, actualIndex); + for (int i = 0; i < 14; i++) + { + Assert.Equal(expectedBytes[expectedIndex], actualBytes[actualIndex]); + } + expectedIndex += 14; + actualIndex += 14; + } + } + } + + /// + /// This test compares binary content of a zip produced by the current version with a zip produced by + /// other frameworks. It does this by searching the two zips for the header signature and then + /// it compares the subsequent header values for equality. + /// + /// This test looks for the central directory headers that each entry within a zip possesses and compares these + /// values: + /// central file header signature 4 bytes (0x02014b50) + /// version made by 2 bytes + /// version needed to extract 2 bytes + /// general purpose bit flag 2 bytes + /// compression method 2 bytes + /// last mod file time 2 bytes + /// last mod file date 2 bytes + /// uncompressed size 4 bytes + /// file name length 2 bytes + /// extra field length 2 bytes + /// file comment length 2 bytes + /// disk number start 2 bytes + /// internal file attributes 2 bytes + /// external file attributes 4 bytes + /// + /// it does not compare these values: + /// crc-32 4 bytes + /// compressed size 4 bytes + /// relative offset of local header 4 bytes + /// file name (variable size) + /// extra field (variable size) + /// file comment (variable size) + /// + [Theory] + [InlineData("net45_unicode.zip", "unicode")] + [InlineData("net46_unicode.zip", "unicode")] + [InlineData("net45_normal.zip", "normal")] + [InlineData("net46_normal.zip", "normal")] + public static async Task ZipBinaryCompat_CentralDirectoryHeaders(string zipFile, string zipFolder) + { + using (MemoryStream actualArchiveStream = new MemoryStream()) + using (MemoryStream expectedArchiveStream = await StreamHelpers.CreateTempCopyStream(compat(zipFile))) + { + byte[] signature = new byte[] { 0x50, 0x4b, 0x03, 0x04 }; + + // Produce a ZipFile + await CreateFromDir(zfolder(zipFolder), actualArchiveStream, ZipArchiveMode.Create); + + // Read the streams to byte arrays + byte[] actualBytes = actualArchiveStream.ToArray(); + byte[] expectedBytes = expectedArchiveStream.ToArray(); + + // Search for the file headers + int actualIndex = 0, expectedIndex = 0; + while ((expectedIndex = FindIndexOfSequence(expectedBytes, expectedIndex, signature)) != -1) + { + actualIndex = FindIndexOfSequence(actualBytes, actualIndex, signature); + Assert.NotEqual(-1, actualIndex); + for (int i = 0; i < 16; i++) + { + Assert.Equal(expectedBytes[expectedIndex], actualBytes[actualIndex]); + } + for (int i = 24; i < 42; i++) + { + Assert.Equal(expectedBytes[expectedIndex], actualBytes[actualIndex]); + } + expectedIndex += 38; + actualIndex += 38; + } + } + } + + /// + /// Simple helper method to search for the exact byte sequence specified by + /// , starting at . + /// + /// The first index of the first element in the matching sequence + public static int FindIndexOfSequence(byte[] bytesToSearch, int startIndex, byte[] sequenceToFind) + { + for (int index = startIndex; index < bytesToSearch.Length - sequenceToFind.Length; index++) + { + bool equal = true; + for (int i = 0; i < sequenceToFind.Length; i++) + { + if (bytesToSearch[index + i] != sequenceToFind[i]) + { + equal = false; + break; + } + } + if (equal) + return index; + } + return -1; + } } } -- 2.7.4