// 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"));
}
}
}