// Zip64Tests.cs // ------------------------------------------------------------------ // // Copyright (c) 2009-2011 Dino Chiesa // All rights reserved. // // This code module is part of DotNetZip, a zipfile class library. // // ------------------------------------------------------------------ // // This code is licensed under the Microsoft Public License. // See the file License.txt for the license details. // More info on: http://dotnetzip.codeplex.com // // ------------------------------------------------------------------ // // last saved (in emacs): // Time-stamp: <2011-July-10 20:31:24> // // ------------------------------------------------------------------ // // This module defines the tests for the ZIP64 capability within DotNetZip. These // tests can take a long time to run, as the files can be quite large - 10g or // more. Merely generating the content for these tests can take an hour. Most tests // in the DotNetZip test suite are self-standing - they generate the content they // need, and then remove it after completion, either success or failure. With ZIP64, // because content creation is expensive, for update operations this module uses a // cache of large zip files. See _CreateHugeZipfiles(). The method looks for // large files in a well-known location on the filesystem, in fodderDir. // // ------------------------------------------------------------------ using System; using System.Linq; using System.IO; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using Ionic.Zip; using Ionic.Zip.Tests.Utilities; namespace Ionic.Zip.Tests.Zip64 { [TestClass] public class Zip64Tests : IonicTestClass { string fodderDir = "c:\\users\\dino\\Downloads"; string homeDir = System.Environment.GetEnvironmentVariable("TEMP"); public Zip64Tests() : base() { } private static string[] _HugeZipFiles; private string[] GetHugeZipFiles() { if (_HugeZipFiles == null) { _HugeZipFiles = _CreateHugeZipfiles(); } return _HugeZipFiles; } [ClassCleanup()] public static void MyClassCleanup() { if (_HugeZipFiles != null) { // Keep this huge zip file around, because it takes so much // time to create it. But Delete the directory if one of the files no // longer exists. if (!File.Exists(_HugeZipFiles[0]) || !File.Exists(_HugeZipFiles[1])) { //File.Delete(_HugeZipFile); string d= Path.GetDirectoryName(_HugeZipFiles[0]); if (Directory.Exists(d)) Directory.Delete(d, true); } } } EncryptionAlgorithm[] crypto = { EncryptionAlgorithm.None, EncryptionAlgorithm.PkzipWeak, EncryptionAlgorithm.WinZipAes128, EncryptionAlgorithm.WinZipAes256, }; Ionic.Zlib.CompressionLevel[] compLevels = { Ionic.Zlib.CompressionLevel.None, Ionic.Zlib.CompressionLevel.BestSpeed, Ionic.Zlib.CompressionLevel.Default, Ionic.Zlib.CompressionLevel.BestCompression, }; Zip64Option[] z64 = { Zip64Option.Never, Zip64Option.AsNecessary, Zip64Option.Always, }; private Object LOCK = new Object(); /// /// Create 2 large zip64 zip files - one from DNZ, one from WinZip. Each /// test that updates a zip file should use both. There are slight /// differences between a zip from DNZ and one from WinZip, specifically /// regarding metadata. These differences should be inconsequential for /// updates of zips, and that is what some of the zip64 tests seek to /// verify. /// private string[] _CreateHugeZipfiles() { string[] zipsToCreate = { "Zip64Test-createdBy-WZ.zip", "Zip64Test-createdBy-DNZ.zip" }; // lock in case more than one test calls this at a time. lock (LOCK) { TestContext.WriteLine("CreateHugeZipFiles"); TestContext.WriteLine("Start - " + DateTime.Now.ToString("G")); // STEP 1: // look for existing directories, and re-use the large zip files // there, if it exists, and if it is large enough. string testDir = null; var filesToAdd = new List(); var oldDirs = Directory.GetDirectories(homeDir, "*.Zip64Tests"); string zipFileToCreate = null; List found = null; // first pass to check if any dirs, have both files, // second pass to check if any dirs have one file plus fodder files. for (int pass=0; pass < 2; pass++) { foreach (var dir in oldDirs) { found = new List(); for (int m=0; m < zipsToCreate.Length; m++) { zipFileToCreate = Path.Combine(dir, zipsToCreate[m]); if (File.Exists(zipFileToCreate)) { TestContext.WriteLine("File exists: {0}", zipFileToCreate); FileInfo fi = new FileInfo(zipFileToCreate); if (fi.Length < (long)System.UInt32.MaxValue) { TestContext.WriteLine("deleting file (too small): {0}", zipFileToCreate); File.Delete(zipFileToCreate); } else found.Add(m); } } int fc = found.Count(); switch (fc) { case 0: case 1: // check for fodder files testDir = dir; string fdir = Path.Combine(dir,"dir"); if (Directory.Exists(fdir)) { var fodderFiles = Directory.GetFiles(fdir, "*.txt"); if (fodderFiles == null || fodderFiles.Length <= 6) try { Directory.Delete(dir, true); } catch { } } else try { Directory.Delete(dir, true); } catch { } break; case 2: // found both large zips, so use them. zipsToCreate[0] = Path.Combine(dir, zipsToCreate[0]); zipsToCreate[1] = Path.Combine(dir, zipsToCreate[1]); TestContext.WriteLine("Using the existing zips in: {0}", dir); return zipsToCreate; } if (pass == 1 && Directory.Exists(dir) && fc==1) { // on pass 2, take 1st dir with at least one zip break; } } } // remember the current directory so we can restore later string originalDir = Directory.GetCurrentDirectory(); // CurrentDir is the dir that holds the test temp directory (or directorIES) Directory.SetCurrentDirectory(CurrentDir); if (!Directory.Exists(testDir)) { // create the dir if it does not exist string pname = Path.GetFileName(TestUtilities.GenerateUniquePathname("Zip64Tests")); testDir = Path.Combine(homeDir, pname); Directory.CreateDirectory(testDir); Directory.SetCurrentDirectory(testDir); } else { Directory.SetCurrentDirectory(testDir); string fdir = Path.Combine(testDir,"dir"); filesToAdd.AddRange(Directory.GetFiles(fdir, "*.txt")); } TestContext.WriteLine("Creating new zip files..."); // create a huge ZIP64 archive with a true 64-bit offset. _txrx = TestUtilities.StartProgressMonitor("Zip64_Setup", "Zip64 Test Setup", "Creating files"); //Directory.SetCurrentDirectory(testDir); // create a directory with some files in it, to zip string dirToZip = "dir"; Directory.CreateDirectory(dirToZip); _txrx.Send("pb 0 step"); System.Threading.Thread.Sleep(120); _txrx.Send("bars 3"); System.Threading.Thread.Sleep(220); _txrx.Send("pb 0 max 4"); System.Threading.Thread.Sleep(220); int numFilesToAdd = _rnd.Next(4) + 7; _txrx.Send("pb 1 max " + numFilesToAdd); // These params define the size range for the large, random text // files that are created below. Creating files this size takes // about 1 minute per file _sizeBase = 0x16000000; _sizeRandom = 0x20000000; //_sizeBase = 0x160000; //_sizeRandom = 0x200000; if (filesToAdd.Count() == 0) { int n; var buf = new byte[2048]; for (int i = 0; i < numFilesToAdd; i++) { System.Threading.Thread.Sleep(220); _txrx.Send("title Zip64 Create Huge Zip files"); // in case it was missed System.Threading.Thread.Sleep(220); int fnameLength = _rnd.Next(25) + 6; string filename = TestUtilities.GenerateRandomName(fnameLength) + ".txt"; _txrx.Send(String.Format("status create {0} ({1}/{2})", filename, i+1, numFilesToAdd)); int totalSize = _sizeBase + _rnd.Next(_sizeRandom); System.Threading.Thread.Sleep(220); _txrx.Send(String.Format("pb 2 max {0}", totalSize)); System.Threading.Thread.Sleep(220); _txrx.Send("pb 2 value 0"); int writtenSoFar = 0; int cycles = 0; using (var input = new Ionic.Zip.Tests.Utilities.RandomTextInputStream(totalSize)) { using (var output = File.Create(Path.Combine(dirToZip,filename))) { while ((n = input.Read(buf,0,buf.Length)) > 0) { output.Write(buf,0,n); writtenSoFar+=n; cycles++; if (cycles % 640 == 0) { _txrx.Send(String.Format("pb 2 value {0}", writtenSoFar)); } } } } filesToAdd.Add(filename); _txrx.Send("pb 1 step"); } } _txrx.Send("pb 0 step"); System.Threading.Thread.Sleep(220); _txrx.Send("pb 1 value 0"); // Add links to a few very large files into the same directory. We // do this because creating such large files from nothing would take // a very very long time. if (CreateLinksToLargeFiles(dirToZip)) return null; Directory.SetCurrentDirectory(testDir); // again if (found == null || !found.Contains(0)) { // create a zip file, using WinZip // This will take 50 minutes or so, no progress updates possible. System.Threading.Thread.Sleep(220); _txrx.Send("title Zip64 Create Huge Zip files"); // in case it was missed System.Threading.Thread.Sleep(220); _txrx.Send("pb 2 value 0"); zipFileToCreate = zipsToCreate[0]; zipsToCreate[0] = Path.Combine(testDir, zipsToCreate[0]); // wzzip.exe will create a zip64 file automatically, as necessary. // There is no explicit switch required. // switches: // -a add // -r recurse // -p store folder names // -yx store extended timestamps var sb1 = new System.Text.StringBuilder(); sb1.Append("-a -p -r -yx \"") .Append(zipFileToCreate) .Append("\" \"") .Append(dirToZip) .Append("\" "); string args = sb1.ToString(); System.Threading.Thread.Sleep(220); _txrx.Send("status wzzip.exe " + args); TestContext.WriteLine("Exec: wzzip {0}", args); string wzzipOut = this.Exec(wzzip, args); TestContext.WriteLine("Done with wzzip.exe"); _txrx.Send("status wzzip.exe: Done"); } if (found == null || !found.Contains(1)) { // Create a zip file using DotNetZip // This will take 50 minutes or so. // pb1 and pb2 will be set in the {Add,Save}Progress handlers _txrx.Send("pb 0 step"); System.Threading.Thread.Sleep(120); _txrx.Send("status Saving the zip..."); System.Threading.Thread.Sleep(120); _txrx.Send(String.Format("pb 1 max {0}", numFilesToAdd + Directory.GetFiles(dirToZip).Length)); _testTitle = "Zip64 Create Huge Zip files"; // used in Zip64_SaveProgress _pb1Set = false; zipFileToCreate = Path.Combine(testDir, zipsToCreate[1]); zipsToCreate[1] = zipFileToCreate; using (ZipFile zip = new ZipFile()) { zip.SaveProgress += Zip64SaveProgress; zip.AddProgress += Zip64AddProgress; zip.UpdateDirectory(dirToZip, ""); foreach (var e in zip) { if (e.FileName.EndsWith(".pst") || e.FileName.EndsWith(".ost") || e.FileName.EndsWith(".zip")) e.CompressionMethod = CompressionMethod.None; } zip.UseZip64WhenSaving = Zip64Option.Always; // use large buffer to speed up save for large files: zip.BufferSize = 1024 * 756; zip.CodecBufferSize = 1024 * 128; zip.Save(zipFileToCreate); } } _txrx.Send("pb 0 step"); System.Threading.Thread.Sleep(120); // Delete the fodder dir only if we have both zips. // This is helpful when modifying or editing this method. // With repeated runs you don't have to re-create all the data // each time. if (File.Exists(zipsToCreate[0]) && File.Exists(zipsToCreate[1])) Directory.Delete(dirToZip, true); _txrx.Send("pb 0 step"); System.Threading.Thread.Sleep(120); _txrx.Send("stop"); // restore the cwd: Directory.SetCurrentDirectory(originalDir); TestContext.WriteLine("All done - " + DateTime.Now.ToString("G")); return zipsToCreate; } } private bool CreateLinksToLargeFiles(string dirForLinks) { var candidates = from fi in (from fn in Directory.GetFiles(fodderDir) select new FileInfo(fn)) where fi.Length > 0x10000000 orderby fi.Length descending select fi; if (candidates.Count() < 3) { TestContext.WriteLine("Found {0} files, not enough to proceed.", candidates.Count()); return true; } TestContext.WriteLine("Found {0} large files, which seems enough to proceed.", candidates.Count()); string current = Directory.GetCurrentDirectory(); _txrx.Send("status Creating links"); string subdir = Path.Combine(dirForLinks, "00-largelinks"); if (Directory.Exists(subdir)) Directory.Delete(subdir, true); Directory.CreateDirectory(subdir); Directory.SetCurrentDirectory(subdir); var w = System.Environment.GetEnvironmentVariable("Windir"); Assert.IsTrue(Directory.Exists(w), "%windir% does not exist ({0})", w); var fsutil = Path.Combine(Path.Combine(w, "system32"), "fsutil.exe"); Assert.IsTrue(File.Exists(fsutil), "fsutil.exe does not exist ({0})", fsutil); string ignored; Int64 totalLength = 0; int cycle = 0; const Int64 threshold = (long)(11L * 1024 * 1024 * 1024); while (totalLength < threshold) { cycle++; foreach (var fi in candidates) { string cmd = String.Format("hardlink create \"{0}-Copy{1}{2}\" \"{3}\"", Path.GetFileNameWithoutExtension(fi.Name), cycle, Path.GetExtension(fi.Name), Path.Combine(fodderDir,fi.Name)); TestContext.WriteLine("Command: fsutil {0}", cmd); _txrx.Send("status " + cmd); TestUtilities.Exec_NoContext(fsutil, cmd, out ignored); totalLength += fi.Length; if (totalLength > threshold) break; // enough } } Directory.SetCurrentDirectory(current); return false; } [TestMethod] public void Zip64_Create() { Zip64Option[] Options = { Zip64Option.Always, Zip64Option.Never, Zip64Option.AsNecessary }; for (int k = 0; k < Options.Length; k++) { string filename = null; Directory.SetCurrentDirectory(TopLevelDir); TestContext.WriteLine("\n\n==================Trial {0}...", k); string zipFileToCreate = String.Format("Zip64_Create-{0}.zip", k); TestContext.WriteLine("Creating file {0}", zipFileToCreate); TestContext.WriteLine(" ZIP64 option: {0}", Options[k].ToString()); int entries = _rnd.Next(5) + 13; var checksums = new Dictionary(); using (ZipFile zip1 = new ZipFile()) { for (int i = 0; i < entries; i++) { if (_rnd.Next(2) == 1) { filename = Path.Combine(TopLevelDir, String.Format("Data{0}.bin", i)); int filesize = _rnd.Next(44000) + 5000; TestUtilities.CreateAndFillFileBinary(filename, filesize); } else { filename = Path.Combine(TopLevelDir, String.Format("Data{0}.txt", i)); int filesize = _rnd.Next(44000) + 5000; TestUtilities.CreateAndFillFileText(filename, filesize); } zip1.AddFile(filename, ""); var chk = TestUtilities.ComputeChecksum(filename); checksums.Add(Path.GetFileName(filename), TestUtilities.CheckSumToString(chk)); } zip1.UseZip64WhenSaving = Options[k]; zip1.Comment = String.Format("This archive uses zip64 option: {0}", Options[k].ToString()); zip1.Save(zipFileToCreate); if (Options[k] == Zip64Option.Always) Assert.IsTrue(zip1.OutputUsedZip64.Value); else if (Options[k] == Zip64Option.Never) Assert.IsFalse(zip1.OutputUsedZip64.Value); } BasicVerifyZip(zipFileToCreate); TestContext.WriteLine("---------------Reading {0}...", zipFileToCreate); using (ZipFile zip2 = ZipFile.Read(zipFileToCreate)) { string extractDir = String.Format("extract{0}", k); foreach (var e in zip2) { TestContext.WriteLine(" Entry: {0} c({1}) unc({2})", e.FileName, e.CompressedSize, e.UncompressedSize); e.Extract(extractDir); filename = Path.Combine(extractDir, e.FileName); string actualCheckString = TestUtilities.CheckSumToString(TestUtilities.ComputeChecksum(filename)); Assert.IsTrue(checksums.ContainsKey(e.FileName), "Checksum is missing"); Assert.AreEqual(checksums[e.FileName], actualCheckString, "Checksums for ({0}) do not match.", e.FileName); TestContext.WriteLine(" Checksums match ({0}).\n", actualCheckString); } } } } [TestMethod] public void Zip64_Convert() { string trialDescription = "Trial {0}/{1}: create archive as 'zip64={2}', then open it and re-save with 'zip64={3}'"; Zip64Option[] z64a = { Zip64Option.Never, Zip64Option.Always, Zip64Option.AsNecessary}; // ?? for (int u = 0; u < 2; u++) { for (int m = 0; m < z64a.Length; m++) { for (int n = 0; n < z64a.Length; n++) { int k = m * z64a.Length + n; string filename = null; Directory.SetCurrentDirectory(TopLevelDir); TestContext.WriteLine("\n\n==================Trial {0}...", k); TestContext.WriteLine(trialDescription, k, (z64a.Length * z64a.Length) - 1, z64a[m], z64a[n]); string zipFileToCreate = Path.Combine(TopLevelDir, String.Format("Zip64_Convert-{0}.A.zip", k)); int entries = _rnd.Next(8) + 6; //int entries = 2; TestContext.WriteLine("Creating file {0}, zip64={1}, {2} entries", Path.GetFileName(zipFileToCreate), z64a[m].ToString(), entries); var checksums = new Dictionary(); using (ZipFile zip1 = new ZipFile()) { for (int i = 0; i < entries; i++) { if (_rnd.Next(2) == 1) { filename = Path.Combine(TopLevelDir, String.Format("Data{0}.bin", i)); TestUtilities.CreateAndFillFileBinary(filename, _rnd.Next(44000) + 5000); } else { filename = Path.Combine(TopLevelDir, String.Format("Data{0}.txt", i)); TestUtilities.CreateAndFillFileText(filename, _rnd.Next(44000) + 5000); } zip1.AddFile(filename, ""); var chk = TestUtilities.ComputeChecksum(filename); checksums.Add(Path.GetFileName(filename), TestUtilities.CheckSumToString(chk)); } TestContext.WriteLine("---------------Saving to {0} with Zip64={1}...", Path.GetFileName(zipFileToCreate), z64a[m].ToString()); zip1.UseZip64WhenSaving = z64a[m]; zip1.Comment = String.Format("This archive uses Zip64Option={0}", z64a[m].ToString()); zip1.Save(zipFileToCreate); } Assert.AreEqual(TestUtilities.CountEntries(zipFileToCreate), entries, "The Zip file has the wrong number of entries."); string newFile = zipFileToCreate.Replace(".A.", ".B."); using (ZipFile zip2 = ZipFile.Read(zipFileToCreate)) { TestContext.WriteLine("---------------Extracting {0} ...", Path.GetFileName(zipFileToCreate)); string extractDir = String.Format("extract-{0}-{1}.A", k, u); foreach (var e in zip2) { TestContext.WriteLine(" {0} crc({1:X8}) c({2:X8}) unc({3:X8})", e.FileName, e.Crc, e.CompressedSize, e.UncompressedSize); e.Extract(extractDir); filename = Path.Combine(extractDir, e.FileName); string actualCheckString = TestUtilities.CheckSumToString(TestUtilities.ComputeChecksum(filename)); Assert.IsTrue(checksums.ContainsKey(e.FileName), "Checksum is missing"); Assert.AreEqual(checksums[e.FileName], actualCheckString, "Checksums for ({0}) do not match.", e.FileName); } if (u==1) { TestContext.WriteLine("---------------Updating: Renaming an entry..."); zip2[4].FileName += ".renamed"; string entriesToRemove = (_rnd.Next(2) == 0) ? "*.txt" : "*.bin"; TestContext.WriteLine("---------------Updating: Removing {0} entries...", entriesToRemove); zip2.RemoveSelectedEntries(entriesToRemove); } TestContext.WriteLine("---------------Saving to {0} with Zip64={1}...", Path.GetFileName(newFile), z64a[n].ToString()); zip2.UseZip64WhenSaving = z64a[n]; zip2.Comment = String.Format("This archive uses Zip64Option={0}", z64a[n].ToString()); zip2.Save(newFile); } using (ZipFile zip3 = ZipFile.Read(newFile)) { TestContext.WriteLine("---------------Extracting {0} ...", Path.GetFileName(newFile)); string extractDir = String.Format("extract-{0}-{1}.B", k, u); foreach (var e in zip3) { TestContext.WriteLine(" {0} crc({1:X8}) c({2:X8}) unc({3:X8})", e.FileName, e.Crc, e.CompressedSize, e.UncompressedSize); e.Extract(extractDir); filename = Path.Combine(extractDir, e.FileName); string actualCheckString = TestUtilities.CheckSumToString(TestUtilities.ComputeChecksum(filename)); if (!e.FileName.EndsWith(".renamed")) { Assert.IsTrue(checksums.ContainsKey(e.FileName), "Checksum is missing"); Assert.AreEqual(checksums[e.FileName], actualCheckString, "Checksums for ({0}) do not match.", e.FileName); } } } } } } } bool _pb2Set; bool _pb1Set; string _testTitle; int _sizeBase; int _sizeRandom; int _numSaving; int _totalToSave; int _spCycles; private void Zip64SaveProgress(object sender, SaveProgressEventArgs e) { string msg; switch (e.EventType) { case ZipProgressEventType.Saving_Started: _txrx.Send("status saving started..."); _pb1Set = false; _numSaving= 1; break; case ZipProgressEventType.Saving_BeforeWriteEntry: _txrx.Send(String.Format("status Compressing {0}", e.CurrentEntry.FileName)); _spCycles = 0; if (!_pb1Set) { _txrx.Send(String.Format("pb 1 max {0}", e.EntriesTotal)); _pb1Set = true; } _totalToSave = e.EntriesTotal; _pb2Set = false; break; case ZipProgressEventType.Saving_EntryBytesRead: _spCycles++; if ((_spCycles % 128) == 0) { if (!_pb2Set) { _txrx.Send(String.Format("pb 2 max {0}", e.TotalBytesToTransfer)); _pb2Set = true; } _txrx.Send(String.Format("status Saving entry {0}/{1} :: {2} :: {3}/{4}mb {5:N0}%", _numSaving, _totalToSave, e.CurrentEntry.FileName, e.BytesTransferred/(1024*1024), e.TotalBytesToTransfer/(1024*1024), ((double)e.BytesTransferred) / (0.01 * e.TotalBytesToTransfer))); msg = String.Format("pb 2 value {0}", e.BytesTransferred); _txrx.Send(msg); } break; case ZipProgressEventType.Saving_AfterWriteEntry: _txrx.Send("test " + _testTitle); // just in case it was missed _txrx.Send("pb 1 step"); _numSaving++; break; case ZipProgressEventType.Saving_Completed: _txrx.Send("status Save completed"); _pb1Set = false; _pb2Set = false; _txrx.Send("pb 1 max 1"); _txrx.Send("pb 1 value 1"); break; } } void Zip64AddProgress(object sender, AddProgressEventArgs e) { switch (e.EventType) { case ZipProgressEventType.Adding_Started: _txrx.Send("status Adding files to the zip..."); break; case ZipProgressEventType.Adding_AfterAddEntry: _txrx.Send(String.Format("status Adding file {0}", e.CurrentEntry.FileName)); break; case ZipProgressEventType.Adding_Completed: _txrx.Send("status Added all files"); break; } } private int _numExtracted; private int _epCycles; private int _numFilesToExtract; void Zip64ExtractProgress(object sender, ExtractProgressEventArgs e) { switch (e.EventType) { case ZipProgressEventType.Extracting_BeforeExtractEntry: if (!_pb1Set) { _txrx.Send(String.Format("pb 1 max {0}", _numFilesToExtract)); _pb1Set = true; } _pb2Set = false; _epCycles = 0; break; case ZipProgressEventType.Extracting_EntryBytesWritten: _epCycles++; if ((_epCycles % 512) == 0) { if (!_pb2Set) { _txrx.Send(String.Format("pb 2 max {0}", e.TotalBytesToTransfer)); _pb2Set = true; } _txrx.Send(String.Format("status {0} entry {1}/{2} :: {3} :: {4}/{5}mb :: {6:N0}%", verb, _numExtracted, _numFilesToExtract, e.CurrentEntry.FileName, e.BytesTransferred/(1024*1024), e.TotalBytesToTransfer/(1024*1024), ((double)e.BytesTransferred) / (0.01 * e.TotalBytesToTransfer) )); string msg = String.Format("pb 2 value {0}", e.BytesTransferred); _txrx.Send(msg); } break; case ZipProgressEventType.Extracting_AfterExtractEntry: _numExtracted++; if (_numFilesToExtract < 1024 || (_numExtracted % 128) == 0) { _txrx.Send("test " + _testTitle); // just in case it was missed while (_numExtracted > _numFilesToExtract) _numExtracted--; _txrx.Send("pb 1 value " + _numExtracted); if (_numExtracted == _numFilesToExtract) { _txrx.Send("status All done " + verb); } else { _txrx.Send(String.Format("status {0} entry {1}/{2} {3:N0}%", verb, _numExtracted, _numFilesToExtract, _numExtracted / (0.01 *_numFilesToExtract))); } } break; } } string verb; private void Zip64VerifyZip(string zipfile) { _pb1Set = false; Stream bitBucket = Stream.Null; TestContext.WriteLine(""); TestContext.WriteLine("Checking file {0}", zipfile); verb = "Verifying"; using (ZipFile zip = ZipFile.Read(zipfile)) { // large buffer better for large files zip.BufferSize = 65536*4; // 65536 * 8 = 512k _numFilesToExtract = zip.Entries.Count; _numExtracted= 1; zip.ExtractProgress += Zip64ExtractProgress; foreach (var s in zip.EntryFileNames) { TestContext.WriteLine(" Entry: {0}", s); zip[s].Extract(bitBucket); } } System.Threading.Thread.Sleep(0x500); } [Timeout(3 * 60*60*1000), TestMethod] // 60*60*1000 = 1hr public void Zip64_Update_WZ() { // this should take about an hour string[] zipFilesToUpdate = GetHugeZipFiles(); Assert.IsFalse(zipFilesToUpdate == null, "No files were created."); // this should take a little less than an hour Zip64UpdateAddFiles(zipFilesToUpdate[0], "WinZip"); } [Timeout(3 * 60*60*1000), TestMethod] // 60*60*1000 = 1hr public void Zip64_Update_DNZ() { // this should take about an hour string[] zipFilesToUpdate = GetHugeZipFiles(); Assert.IsFalse(zipFilesToUpdate == null, "No files were created."); // this should take a little less than an hour Zip64UpdateAddFiles(zipFilesToUpdate[1], "DNZ"); } private void Zip64UpdateAddFiles(string zipFileToUpdate, string marker) { _txrx = TestUtilities.StartProgressMonitor("Zip64-Update", "Zip64 Update - " + marker, "Starting up..."); int numUpdates = 2; int baseSize = _rnd.Next(0x1000ff) + 80000; System.Threading.Thread.Sleep(120); // pb 0: numUpdates + before+after verify steps _txrx.Send( String.Format("pb 0 max {0}", numUpdates + 2)); string workingZipFile = "Z64Update." + marker + ".zip"; _testTitle = "Zip64 Update - " + marker + " - initial verify"; _txrx.Send("pb 0 value 0"); Assert.IsTrue(File.Exists(zipFileToUpdate), "The required ZIP file does not exist ({0})", zipFileToUpdate); // make sure the zip is larger than the 4.2gb size FileInfo fi = new FileInfo(zipFileToUpdate); Assert.IsTrue(fi.Length > (long)System.UInt32.MaxValue, "The zip file ({0}) is not large enough.", zipFileToUpdate); // this usually takes 10-12 minutes TestContext.WriteLine("Verifying the zip - " + DateTime.Now.ToString("G")); _txrx.Send("status Verifying the zip"); Zip64VerifyZip(zipFileToUpdate); _txrx.Send("pb 0 value 1"); var sw = new StringWriter(); for (int j=0; j < numUpdates; j++) { _testTitle = String.Format("Zip64 Update - {0} - ({1}/{2})", marker, j+1, numUpdates); _pb1Set = false; System.Threading.Thread.Sleep(220); _txrx.Send("test " + _testTitle); // create another folder with a single file in it string subdir = String.Format("newfolder-{0}", j); Directory.CreateDirectory(subdir); string fileName = Path.Combine(subdir, System.Guid.NewGuid().ToString() + ".txt"); long size = baseSize + _rnd.Next(28000); TestUtilities.CreateAndFillFileText(fileName, size); TestContext.WriteLine(""); TestContext.WriteLine("Updating the zip file, cycle {0}...{1}", j, DateTime.Now.ToString("G")); _txrx.Send("status Updating the zip file..."); // update the zip with that new folder+file // will take a long time for large files using (ZipFile zip = ZipFile.Read(zipFileToUpdate)) { zip.SaveProgress += Zip64SaveProgress; zip.StatusMessageTextWriter = sw; zip.UpdateDirectory(subdir, subdir); zip.UseZip64WhenSaving = Zip64Option.Always; zip.BufferSize = 65536*8; // 65536 * 8 = 512k zip.Save(workingZipFile); } TestContext.WriteLine("Save complete " + DateTime.Now.ToString("G")); zipFileToUpdate = workingZipFile; // for subsequent updates // emit status into the log if available string status = sw.ToString(); if (status != null && status != "") { var lines = status.Split('\n'); TestContext.WriteLine("status: (" + DateTime.Now.ToString("G") + ")"); foreach (string line in lines) TestContext.WriteLine(line); } _txrx.Send("pb 0 value " + (j+2)); } System.Threading.Thread.Sleep(120); // make sure the zip is larger than the 4.2gb size fi = new FileInfo(workingZipFile); Assert.IsTrue(fi.Length > (long)System.UInt32.MaxValue, "The zip file ({0}) is not large enough.", workingZipFile); TestContext.WriteLine(""); TestContext.WriteLine("Verifying the zip again... " + DateTime.Now.ToString("G")); _txrx.Send("status Verifying the zip again..."); _testTitle = String.Format("Zip64 Update - {0} - final verify", marker); _pb1Set = false; System.Threading.Thread.Sleep(220); _txrx.Send("test " + _testTitle); Zip64VerifyZip(workingZipFile); _txrx.Send( String.Format("pb 0 value {0}", numUpdates + 1)); } [TestMethod] public void Zip64_Winzip_Unzip_OneFile() { string testBin = TestUtilities.GetTestBinDir(CurrentDir); string fileToZip = Path.Combine(testBin, "Ionic.Zip.dll"); Directory.SetCurrentDirectory(TopLevelDir); for (int p=0; p < compLevels.Length; p++) { for (int n=0; n < crypto.Length; n++) { for (int m=0; m < z64.Length; m++) { string zipFile = String.Format("WZ-Unzip.OneFile.{0}.{1}.{2}.zip", compLevels[p].ToString(), crypto[n].ToString(), z64[m].ToString()); string password = Path.GetRandomFileName(); TestContext.WriteLine("================================="); TestContext.WriteLine("Creating {0}...", Path.GetFileName(zipFile)); TestContext.WriteLine("Encryption:{0} Zip64:{1} pw={2} compLevel={3}", crypto[n].ToString(), z64[m].ToString(), password, compLevels[p].ToString()); using (var zip = new ZipFile()) { zip.Comment = String.Format("Encryption={0} Zip64={1} pw={2}", crypto[n].ToString(), z64[m].ToString(), password); zip.Encryption = crypto[n]; zip.Password = password; zip.CompressionLevel = compLevels[p]; zip.UseZip64WhenSaving = z64[m]; zip.AddFile(fileToZip, "file"); zip.Save(zipFile); } TestContext.WriteLine("Unzipping with WinZip..."); string extractDir = String.Format("extract.{0}.{1}.{2}",p,n,m); Directory.CreateDirectory(extractDir); // this will throw if the command has a non-zero exit code. this.Exec(wzunzip, String.Format("-s{0} -d {1} {2}\\", password, zipFile, extractDir)); } } } } [Timeout((int)(1 * 60*60*1000)), TestMethod] // in milliseconds. public void Zip64_Winzip_Unzip_Huge() { string[] zipFilesToExtract = GetHugeZipFiles(); // may take a long time int baseSize = _rnd.Next(0x1000ff) + 80000; _txrx = TestUtilities.StartProgressMonitor("Zip64-WinZip-Unzip", "Zip64 WinZip unzip Huge", "Setting up..."); string extractDir = "extract"; Directory.SetCurrentDirectory(TopLevelDir); Directory.CreateDirectory(extractDir); _txrx.Send("pb 0 max " + zipFilesToExtract.Length); for (int k=0; k < zipFilesToExtract.Length; k++) { string zipFileToExtract = zipFilesToExtract[k]; Assert.IsTrue(File.Exists(zipFileToExtract), "required ZIP file does not exist ({0})", zipFileToExtract); System.Threading.Thread.Sleep(120); _txrx.Send("pb 1 max 3"); System.Threading.Thread.Sleep(120); _txrx.Send("pb 1 value 0"); // make sure the zip is larger than the 4.2gb size FileInfo fi = new FileInfo(zipFileToExtract); Assert.IsTrue(fi.Length > (long)System.UInt32.MaxValue, "The zip file ({0}) is not large enough.", zipFileToExtract); _txrx.Send("pb 1 step"); _txrx.Send("status Counting entries in the zip file..."); int numEntries = TestUtilities.CountEntries(zipFileToExtract); _txrx.Send("status Using WinZip to list the entries..."); // Examine and unpack the zip archive via WinZip // first, examine the zip entry metadata: string wzzipOut = this.Exec(wzzip, "-vt " + zipFileToExtract); TestContext.WriteLine(wzzipOut); int x = 0; int y = 0; int wzzipEntryCount=0; string textToLookFor= "Filename: "; TestContext.WriteLine("================"); TestContext.WriteLine("Files listed by WinZip:"); while (true) { x = wzzipOut.IndexOf(textToLookFor, y); if (x < 0) break; y = wzzipOut.IndexOf("\n", x); string name = wzzipOut.Substring(x + textToLookFor.Length, y-x-1).Trim(); TestContext.WriteLine(" {0}", name); if (!name.EndsWith("\\")) { wzzipEntryCount++; if (wzzipEntryCount > numEntries * 3) throw new Exception("too many entries!"); } } TestContext.WriteLine("================"); Assert.AreEqual(numEntries, wzzipEntryCount, "Unexpected number of entries found by WinZip."); _txrx.Send("pb 1 step"); System.Threading.Thread.Sleep(120); _txrx.Send(String.Format("pb 1 max {0}", numEntries*2)); x=0; y = 0; _txrx.Send("status Extracting the entries..."); int nCycles = 0; while (true) { _txrx.Send("test Zip64 WinZip unzip - " + Path.GetFileName(zipFileToExtract)); x = wzzipOut.IndexOf(textToLookFor, y); if (x < 0) break; if (nCycles > numEntries * 4) throw new Exception("too many entries?"); y = wzzipOut.IndexOf("\n", x); string name = wzzipOut.Substring(x + textToLookFor.Length, y-x-1).Trim(); if (!name.EndsWith("\\")) { nCycles++; _txrx.Send(String.Format("status Extracting {1}/{2} :: {0}", name, nCycles, wzzipEntryCount)); this.Exec(wzunzip, String.Format("-d {0} {1}\\ \"{2}\"", Path.GetFileName(zipFileToExtract), extractDir, name)); string path = Path.Combine(extractDir, name); _txrx.Send("pb 1 step"); Assert.IsTrue(File.Exists(path), "extracted file ({0}) does not exist", path); File.Delete(path); System.Threading.Thread.Sleep(120); _txrx.Send("pb 1 step"); } } _txrx.Send("pb 0 step"); System.Threading.Thread.Sleep(120); } } private void CreateLargeFiles(int numFilesToAdd, int baseSize, string dir) { bool firstFileDone = false; string fileName = ""; long fileSize = 0; _txrx.Send(String.Format("pb 1 max {0}", numFilesToAdd)); Action progressUpdate = (x) => { _txrx.Send(String.Format("pb 2 value {0}", x)); _txrx.Send(String.Format("status Creating {0}, [{1}/{2}] ({3:N0}%)", fileName, x, fileSize, ((double)x)/ (0.01 * fileSize) )); }; // It takes some time to create a large file. And we need // a bunch of them. for (int i = 0; i < numFilesToAdd; i++) { // randomly select binary or text int n = _rnd.Next(2); fileName = string.Format("Pippo{0}.{1}", i, (n==0) ? "bin" : "txt" ); if (i != 0) { int x = _rnd.Next(6); if (x != 0) { string folderName = string.Format("folder{0}", x); fileName = Path.Combine(folderName, fileName); if (!Directory.Exists(Path.Combine(dir, folderName))) Directory.CreateDirectory(Path.Combine(dir, folderName)); } } fileName = Path.Combine(dir, fileName); // first file is 2x larger fileSize = (firstFileDone) ? (baseSize + _rnd.Next(0x880000)) : (2*baseSize); _txrx.Send(String.Format("pb 2 max {0}", fileSize)); if (n==0) TestUtilities.CreateAndFillFileBinary(fileName, fileSize, progressUpdate); else TestUtilities.CreateAndFillFileText(fileName, fileSize, progressUpdate); firstFileDone = true; _txrx.Send("pb 1 step"); } } [Timeout((int)(4 * 60*60*1000)), TestMethod] // 60*60*1000 == 1 hr public void Zip64_Winzip_Zip_Huge() { // This TestMethod tests if DNZ can read a huge (>4.2gb) zip64 file // created by winzip. int baseSize = _rnd.Next(80000) + 0x1000ff; _txrx = TestUtilities.StartProgressMonitor("Zip64-WinZip-Zip-Huge", "Zip64_Winzip_Zip_Huge()", "Creating links"); string contentDir = "fodder"; //Directory.SetCurrentDirectory(TopLevelDir); Directory.CreateDirectory(contentDir); _txrx.Send("pb 0 max 5"); if (CreateLinksToLargeFiles(contentDir)) return; TestContext.WriteLine("Creating large files..." + DateTime.Now.ToString("G")); CreateLargeFiles(_rnd.Next(4) + 4, baseSize, contentDir); _txrx.Send("pb 0 step"); TestContext.WriteLine("Creating a new Zip with winzip - " + DateTime.Now.ToString("G")); var fileList = Directory.GetFiles(contentDir, "*.*", SearchOption.AllDirectories); // create a zip archive via WinZip string wzzipOut= null; string zipFileToCreate = "Zip64-WinZip-Zip-Huge.zip"; int nCycles= 0; _txrx.Send(String.Format("pb 1 max {0}", fileList.Length)); System.Threading.Thread.Sleep(120); _txrx.Send("pb 2 value 0"); // Add one file at a time, invoking wzzip.exe for each. After it // completes, delete the just-added file. This allows coarse-grained // status updates in the progress window. Not sure about the exact // impact on disk space, or execution time; my observation is that the // time-cost to add one entry increases, as the zip file gets // larger. Each successive cycle takes a little longer. It's tolerable // I guess. A tradeoff to get visual progress feedback. foreach (var filename in fileList) { nCycles++; _txrx.Send(String.Format("status wzzip.exe adding {0}...({1}/{2})", filename, nCycles, fileList.Length+1)); wzzipOut = this.Exec(wzzip, String.Format("-a -p -r -yx \"{0}\" \"{1}\"", zipFileToCreate, filename)); TestContext.WriteLine(wzzipOut); _txrx.Send("pb 1 step"); System.Threading.Thread.Sleep(420); File.Delete(filename); } // Create one additional small text file and add it to the zip. For // this test, it must be added last, at the end of the ZIP file. TestContext.WriteLine("Inserting one additional file with wzzip.exe - " + DateTime.Now.ToString("G")); nCycles++; var newfile = Path.Combine(contentDir, "zzz-" + Path.GetRandomFileName() + ".txt"); _txrx.Send(String.Format("status adding {0}...({1}/{2})", newfile, nCycles, fileList.Length+1)); int filesize = _rnd.Next(50000) + 440000; TestUtilities.CreateAndFillFileText(newfile, filesize); wzzipOut = this.Exec(wzzip, String.Format("-a -p -r -yx \"{0}\" \"{1}\"", zipFileToCreate, newfile)); TestContext.WriteLine(wzzipOut); _txrx.Send("pb 1 step"); System.Threading.Thread.Sleep(120); File.Delete(newfile); _txrx.Send("pb 0 step"); System.Threading.Thread.Sleep(120); // make sure the zip is larger than the 4.2gb size FileInfo fi = new FileInfo(zipFileToCreate); Assert.IsTrue(fi.Length > (long)System.UInt32.MaxValue, "The zip file ({0}) is not large enough.", zipFileToCreate); // Now use DotNetZip to extract the large zip file to the bit bucket. TestContext.WriteLine("Verifying the new Zip with DotNetZip - " + DateTime.Now.ToString("G")); _txrx.Send("status Verifying the zip"); verb = "verifying"; _testTitle = "Zip64 Winzip Zip Huge - final verify"; Zip64VerifyZip(zipFileToCreate); _txrx.Send("pb 0 step"); System.Threading.Thread.Sleep(120); } [TestMethod, Timeout((int)(2 * 60*60*1000))] // 60*60*1000 = 1 hr public void Zip64_Winzip_Setup() { // Not really a test. This thing just sets up the big zip file. TestContext.WriteLine("This test merely checks for existence of two large zip"); TestContext.WriteLine("files, in a well-known place, and creates them as"); TestContext.WriteLine("necessary. The zips are then used by other tests."); TestContext.WriteLine(" "); GetHugeZipFiles(); // usually takes about an hour } private void EmitStatus(String s) { TestContext.WriteLine("status:"); foreach (string line in s.Split('\n')) TestContext.WriteLine(line); } [TestMethod, Timeout(1 * 60*60*1000)] public void Zip64_Over_4gb() { Int64 desiredSize= System.UInt32.MaxValue; desiredSize+= System.Int32.MaxValue/4; desiredSize+= _rnd.Next(0x1000000); _testTitle = "Zip64 Create/Zip/Extract a file > 4.2gb"; _txrx = TestUtilities.StartProgressMonitor("Zip64-Over-4gb", _testTitle, "starting up..."); string zipFileToCreate = Path.Combine(TopLevelDir, "Zip64_Over_4gb.zip"); Directory.SetCurrentDirectory(TopLevelDir); string nameOfFodderFile="VeryVeryLargeFile.txt"; string nameOfExtractedFile = nameOfFodderFile + ".extracted"; // Steps in this test: 4 _txrx.Send("pb 0 max 4"); TestContext.WriteLine(""); TestContext.WriteLine("Creating a large file..." + DateTime.Now.ToString("G")); // create a very large file Action progressUpdate = (x) => { _txrx.Send(String.Format("pb 1 value {0}", x)); _txrx.Send(String.Format("status Creating {0}, [{1}/{2}mb] ({3:N0}%)", nameOfFodderFile, x/(1024*1024), desiredSize/(1024*1024), ((double)x)/ (0.01 * desiredSize))); }; // This takes a few minutes... _txrx.Send(String.Format("pb 1 max {0}", desiredSize)); TestUtilities.CreateAndFillFileText(nameOfFodderFile, desiredSize, progressUpdate); // make sure it is larger than 4.2gb FileInfo fi = new FileInfo(nameOfFodderFile); Assert.IsTrue(fi.Length > (long)System.UInt32.MaxValue, "The fodder file ({0}) is not large enough.", nameOfFodderFile); TestContext.WriteLine(""); TestContext.WriteLine("computing checksum..." + DateTime.Now.ToString("G")); _txrx.Send("status computing checksum..."); var chk1 = TestUtilities.ComputeChecksum(nameOfFodderFile); _txrx.Send("pb 0 step"); var sw = new StringWriter(); using (var zip = new ZipFile()) { zip.StatusMessageTextWriter = sw; zip.UseZip64WhenSaving = Zip64Option.Always; zip.BufferSize = 65536*8; // 65536 * 8 = 512k zip.SaveProgress += Zip64SaveProgress; var e = zip.AddFile(nameOfFodderFile, ""); _txrx.Send("status Saving......"); TestContext.WriteLine("zipping one file......" + DateTime.Now.ToString("G")); zip.Save(zipFileToCreate); } EmitStatus(sw.ToString()); File.Delete(nameOfFodderFile); TestContext.WriteLine(""); TestContext.WriteLine("Extracting the zip..." + DateTime.Now.ToString("G")); _txrx.Send("status Extracting the file..."); _txrx.Send("pb 0 step"); var options = new ReadOptions { StatusMessageWriter= new StringWriter() }; verb = "Extracting"; _pb1Set = false; using (var zip = ZipFile.Read(zipFileToCreate, options)) { Assert.AreEqual(1, zip.Entries.Count, "Incorrect number of entries in the zip file"); zip.ExtractProgress += Zip64ExtractProgress; _numFilesToExtract = zip.Entries.Count; _numExtracted= 1; ZipEntry e = zip[0]; e.FileName = nameOfExtractedFile; _txrx.Send("status extracting......"); e.Extract(); } EmitStatus(options.StatusMessageWriter.ToString()); _txrx.Send("pb 0 step"); System.Threading.Thread.Sleep(120); TestContext.WriteLine(""); TestContext.WriteLine("computing checksum..." + DateTime.Now.ToString("G")); _txrx.Send("status computing checksum..."); var chk2 = TestUtilities.ComputeChecksum(nameOfExtractedFile); Assert.AreEqual(TestUtilities.CheckSumToString(chk1), TestUtilities.CheckSumToString(chk2), "Checksum mismatch"); _txrx.Send("pb 0 step"); } [TestMethod, Timeout(1 * 60*60*1000)] public void Z64_ManyEntries_NoEncryption_DefaultCompression_AsNecessary() { _Zip64_Over65534Entries(Zip64Option.AsNecessary, EncryptionAlgorithm.None, Ionic.Zlib.CompressionLevel.Default); } [TestMethod, Timeout(1 * 60*60*1000)] public void Z64_ManyEntries_PkZipEncryption_DefaultCompression_AsNecessary() { _Zip64_Over65534Entries(Zip64Option.AsNecessary, EncryptionAlgorithm.PkzipWeak, Ionic.Zlib.CompressionLevel.Default); } [TestMethod, Timeout(2 * 60*60*1000)] public void Z64_ManyEntries_WinZipEncryption_DefaultCompression_AsNecessary() { _Zip64_Over65534Entries(Zip64Option.AsNecessary, EncryptionAlgorithm.WinZipAes256, Ionic.Zlib.CompressionLevel.Default); } [TestMethod, Timeout(1 * 60*60*1000)] public void Z64_ManyEntries_NoEncryption_DefaultCompression_Always() { _Zip64_Over65534Entries(Zip64Option.Always, EncryptionAlgorithm.None, Ionic.Zlib.CompressionLevel.Default); } [TestMethod, Timeout(1 * 60*60*1000)] public void Z64_ManyEntries_PkZipEncryption_DefaultCompression_Always() { _Zip64_Over65534Entries(Zip64Option.Always, EncryptionAlgorithm.PkzipWeak, Ionic.Zlib.CompressionLevel.Default); } [TestMethod, Timeout(2 * 60*60*1000)] public void Z64_ManyEntries_WinZipEncryption_DefaultCompression_Always() { _Zip64_Over65534Entries(Zip64Option.Always, EncryptionAlgorithm.WinZipAes256, Ionic.Zlib.CompressionLevel.Default); } [TestMethod, Timeout(30 * 60*1000)] [ExpectedException(typeof(Ionic.Zip.ZipException))] public void Z64_ManyEntries_NOZIP64() { _Zip64_Over65534Entries(Zip64Option.Never, EncryptionAlgorithm.None, Ionic.Zlib.CompressionLevel.Default); } void _Zip64_Over65534Entries(Zip64Option z64option, EncryptionAlgorithm encryption, Ionic.Zlib.CompressionLevel compression) { // Emitting a zip file with > 65534 entries requires the use of ZIP64 in // the central directory. int numTotalEntries = _rnd.Next(4616)+65534; //int numTotalEntries = _rnd.Next(461)+6534; //int numTotalEntries = _rnd.Next(46)+653; string enc = encryption.ToString(); if (enc.StartsWith("WinZip")) enc = enc.Substring(6); else if (enc.StartsWith("Pkzip")) enc = enc.Substring(0,5); string zipFileToCreate = String.Format("Zip64.ZF_Over65534.{0}.{1}.{2}.zip", z64option.ToString(), enc, compression.ToString()); _testTitle = String.Format("ZipFile #{0} 64({1}) E({2}), C({3})", numTotalEntries, z64option.ToString(), enc, compression.ToString()); _txrx = TestUtilities.StartProgressMonitor(zipFileToCreate, _testTitle, "starting up..."); _txrx.Send("pb 0 max 4"); // 3 stages: AddEntry, Save, Verify _txrx.Send("pb 0 value 0"); string password = Path.GetRandomFileName(); string statusString = String.Format("status Encrypt:{0} Compress:{1}...", enc, compression.ToString()); int numSaved = 0; var saveProgress = new EventHandler( (sender, e) => { switch (e.EventType) { case ZipProgressEventType.Saving_Started: _txrx.Send("status saving..."); _txrx.Send("pb 1 max " + numTotalEntries); numSaved= 0; break; case ZipProgressEventType.Saving_AfterWriteEntry: numSaved++; if ((numSaved % 128) == 0) { _txrx.Send("pb 1 value " + numSaved); _txrx.Send(String.Format("status Saving entry {0}/{1} ({2:N0}%)", numSaved, numTotalEntries, numSaved / (0.01 * numTotalEntries) )); } break; case ZipProgressEventType.Saving_Completed: _txrx.Send("status Save completed"); _txrx.Send("pb 1 max 1"); _txrx.Send("pb 1 value 1"); break; } }); string contentFormatString = "This is the content for entry #{0}.\r\n\r\n" + "AAAAAAA BBBBBB AAAAA BBBBB AAAAA BBBBB AAAAA\r\n"+ "AAAAAAA BBBBBB AAAAA BBBBB AAAAA BBBBB AAAAA\r\n"; _txrx.Send(statusString); int dirCount= 0; using (var zip = new ZipFile()) { _txrx.Send(String.Format("pb 1 max {0}", numTotalEntries)); _txrx.Send("pb 1 value 0"); zip.Password = password; zip.Encryption = encryption; zip.CompressionLevel = compression; zip.SaveProgress += saveProgress; zip.UseZip64WhenSaving = z64option; // save space when saving the file: zip.EmitTimesInWindowsFormatWhenSaving = false; zip.EmitTimesInUnixFormatWhenSaving = false; // add files: for (int m=0; m(numTotalEntries-dirCount, TestUtilities.CountEntries(zipFileToCreate)); _txrx.Send("pb 0 step"); } [Timeout(3 * 60*60*1000), TestMethod] // 60*60*1000 = 1 hr public void Zip64_UpdateEntryComment_wi9214_WZ() { // Should take 2.5 hrs when creating the huge files, about 1 hr when the // files already exist. string[] zipFilesToUpdate = GetHugeZipFiles(); Assert.IsTrue(File.Exists(zipFilesToUpdate[0]), "The required ZIP file does not exist ({0})", zipFilesToUpdate[0]); Z64UpdateHugeZipWithComment(zipFilesToUpdate[0], "WinZip"); } [Timeout(3 * 60*60*1000), TestMethod] // 60*60*1000 = 1 hr public void Zip64_UpdateEntryComment_wi9214_DNZ() { // Should take 2.5 hrs when creating the huge files, about 1 hr when the // files already exist. string[] zipFilesToUpdate = GetHugeZipFiles(); Assert.IsTrue(File.Exists(zipFilesToUpdate[1]), "The required ZIP file does not exist ({0})", zipFilesToUpdate[1]); Z64UpdateHugeZipWithComment(zipFilesToUpdate[1], "DNZ"); } void Z64UpdateHugeZipWithComment(string zipFileToUpdate, string marker) { // Test whether opening a huge zip64 file and then re-saving, allows the // file to remain valid. Must use a huge zip64 file over 4gb in order // to verify this, and at least one entry must be > 4gb uncompressed. // Want to check that UseZip64WhenSaving is automatically selected as // appropriate. string zipFileToCreate = "update-huge-zip.zip"; string newComment = "This is an updated comment on the first entry"+ "in the zip that is larger than 4gb. (" + DateTime.Now.ToString("u") +")"; // start the progress monitor _txrx = TestUtilities.StartProgressMonitor("Zip64-update", "Zip64 update", "Checking file..."); // make sure the zip is larger than the 4.2gb size FileInfo fi = new FileInfo(zipFileToUpdate); Assert.IsTrue(fi.Length > (long)System.UInt32.MaxValue, "The zip file ({0}) is not large enough.", zipFileToUpdate); TestContext.WriteLine("Verifying the zip file..." + DateTime.Now.ToString("G")); System.Threading.Thread.Sleep(220); _txrx.Send("status Verifying the zip"); System.Threading.Thread.Sleep(220); _txrx.Send("title Zip64 Update Entry Comment wi9214"); // According to workitem 9214, the comment must be modified // on an entry that is larger than 4gb (uncompressed). // Check that here. using (ZipFile zip = ZipFile.Read(zipFileToUpdate)) { ZipEntry bigEntry = null; foreach (var e2 in zip) { if (e2.UncompressedSize > (long)System.UInt32.MaxValue) { bigEntry = e2; break; } } Assert.IsTrue(bigEntry != null && bigEntry.UncompressedSize > (long)System.UInt32.MaxValue, "Minimum size constraint not met."); } // Verify the zip is correct, can be extracted. // This will take some time for a large zip. // (+1 to _numFilesToExtract for the directory) _numFilesToExtract = TestUtilities.CountEntries(zipFileToUpdate) + 1; _numExtracted= 1; verb = "extract"; _pb1Set = false; // _testTitle is used in Zip64{Save,Extract}Progress _testTitle = "Zip64 Update Entry Comment - " + marker; var extract1 = BasicVerifyZip(zipFileToUpdate, null, false, Zip64ExtractProgress); _txrx.Send("status removing the extract directory..."); Directory.Delete(extract1, true); TestContext.WriteLine("Updating the zip file..." + DateTime.Now.ToString("G")); _txrx.Send("status Updating the zip file..."); // update the zip with one small change: a new comment on // the biggest entry. var sw = new StringWriter(); using (ZipFile zip = ZipFile.Read(zipFileToUpdate)) { // required: the option must be set automatically and intelligently Assert.IsTrue(zip.UseZip64WhenSaving == Zip64Option.Always, "The UseZip64WhenSaving option is set incorrectly ({0})", zip.UseZip64WhenSaving); // according to workitem 9214, the comment must be modified // on an entry that is larger than 4gb (uncompressed) ZipEntry bigEntry = null; foreach (var e2 in zip) { if (e2.UncompressedSize > (long)System.UInt32.MaxValue) { bigEntry = e2; break; } } // redundant with the check above, but so what? Assert.IsTrue(bigEntry != null && bigEntry.UncompressedSize > (long)System.UInt32.MaxValue, "Minimum size constraint not met."); bigEntry.Comment = newComment; zip.SaveProgress += Zip64SaveProgress; zip.StatusMessageTextWriter = sw; zip.Save(zipFileToCreate); } string status = sw.ToString(); if (status != null && status != "") { var lines = status.Split('\n'); TestContext.WriteLine("status: (" + DateTime.Now.ToString("G") + ")"); foreach (string line in lines) TestContext.WriteLine(line); } TestContext.WriteLine("Verifying the updated zip... " + DateTime.Now.ToString("G")); _txrx.Send("status Verifying the updated zip"); Zip64VerifyZip(zipFileToCreate); // can take an hour or more // finally, verify that the modified comment is correct. _txrx.Send("status checking the updated comment"); using (ZipFile zip = ZipFile.Read(zipFileToCreate)) { // check that the z64 option is set automatically and intelligently Assert.IsTrue(zip.UseZip64WhenSaving == Zip64Option.Always, "The UseZip64WhenSaving option is set incorrectly ({0})", zip.UseZip64WhenSaving); ZipEntry e = null; foreach (var e2 in zip) { if (e2.UncompressedSize > (long)System.UInt32.MaxValue) { e = e2; break; } } Assert.IsTrue(e != null && e.UncompressedSize > (long)System.UInt32.MaxValue, "No entry in the zip file is large enough."); Assert.AreEqual(newComment, e.Comment, "The comment on the entry is unexpected."); TestContext.WriteLine("The comment on the entry is {0}", e.Comment); } TestContext.WriteLine("All done... " + DateTime.Now.ToString("G")); } } }