Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions LibZipSharp.UnitTest/ZipTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,85 @@ public void CheckForUnknownCompressionMethods ()
}
}

[Test]
public void SimulateXamarinAndroidUsage ([Values (true, false)] bool copyArchive, [Values (true, false)] bool useFiles)
{
string filePath = Path.GetFullPath ("packaged_resources");
if (!File.Exists (filePath)) {
filePath = Path.GetFullPath (Path.Combine ("LibZipSharp.UnitTest", "packaged_resources"));
}

string baseArchive = $"base_{copyArchive}_{useFiles}.zip";

if (File.Exists (baseArchive))
File.Delete (baseArchive);

var mode = FileMode.Create;
if (copyArchive) {
File.Copy (filePath, baseArchive);
mode = FileMode.Open;
}

List<(string name, CompressionMethod c, ulong size)> expectedFiles = new List<(string name, CompressionMethod c, ulong size)> ();
// use a specific seed so we always generate the same files
Random rnd = new Random (3456464);

using (var baseZip = new ZipWrapper (baseArchive, mode)) {
using (var zip = ZipArchive.Open (filePath, FileMode.Open)) {
foreach (var entry in zip) {
var entryName = entry.FullName;
if (entryName.Contains ("\\")) {
entryName = entryName.Replace ('\\', '/');
}
expectedFiles.Add ((entryName, entry.CompressionMethod, entry.Size));
if (copyArchive)
continue;
var ms = new MemoryStream ();
entry.Extract (ms);
TestContext.Out.WriteLine ($"Adding {entryName} to base.zip");
baseZip.Archive.AddStream (ms, entryName, compressionMethod: entry.CompressionMethod);
}
}

baseZip.FixupWindowsPathSeparators ((a, b) => TestContext.Out.WriteLine ($"Fixing up malformed entry `{a}` -> `{b}`"));

for (int i=0; i< 200; i++) {
uint fileSize = (uint)rnd.Next (341, 3535592);
byte[] buffer = new byte[fileSize];
rnd.NextBytes (buffer);
CompressionMethod compression = rnd.NextDouble () < 0.8 ? CompressionMethod.Deflate : CompressionMethod.Store;
string entryName = $"temp/file_{i}_size_{fileSize}_{compression}.bin";
string archivePath = rnd.NextDouble () < 0.3 ? entryName : Path.GetFileName (entryName);
TestContext.Out.WriteLine ($"Adding {entryName} to base.zip");
if (useFiles) {
Directory.CreateDirectory ("temp");
File.WriteAllBytes (entryName, buffer);
baseZip.Archive.AddFile (entryName, archivePath, compressionMethod: compression);
} else {
var ms = new MemoryStream (buffer);
ms.Position = 0;
baseZip.Archive.AddStream (ms, archivePath, compressionMethod: compression);
}
expectedFiles.Add ((archivePath, compression, fileSize));

if (rnd.NextDouble () < 0.2)
baseZip.Flush ();
}

}
using (var zip = ZipArchive.Open (baseArchive, FileMode.Open, strictConsistencyChecks: true)) {
foreach (var file in expectedFiles) {
Assert.IsTrue (zip.ContainsEntry (file.name), $"zip should contain {file}.");
var e = zip.ReadEntry(file.name);
Assert.AreEqual (file.size, e.Size, $"{file} should be {file.size} but was {e.Size}.");
Assert.AreEqual (file.c, e.CompressionMethod, $"{file} should be {file.c} but was {e.CompressionMethod}.");
}
}

if (File.Exists (baseArchive))
File.Delete (baseArchive);
}

[Test]
public void InMemoryZipFile ()
{
Expand Down
65 changes: 65 additions & 0 deletions LibZipSharp.UnitTest/ZipWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using Xamarin.Tools.Zip;
using Unix = Mono.Unix;

namespace Tests {
public class ZipWrapper : IDisposable {
ZipArchive archive;
string filename;
public ZipArchive Archive => archive;

public ZipWrapper (string file, FileMode mode = FileMode.CreateNew) {
filename = file;
archive = ZipArchive.Open (filename, mode);
}

public void Flush () {
if (archive != null) {
archive.Close ();
archive.Dispose ();
archive = null;
}
archive = ZipArchive.Open (filename, FileMode.Open);
}

/// <summary>
/// HACK: aapt2 is creating zip entries on Windows such as `assets\subfolder/asset2.txt`
/// </summary>
public void FixupWindowsPathSeparators (Action<string, string> onRename)
{
bool modified = false;
foreach (var entry in archive) {
if (entry.FullName.Contains ("\\")) {
var name = entry.FullName.Replace ('\\', '/');
onRename?.Invoke (entry.FullName, name);
entry.Rename (name);
modified = true;
}
}
if (modified) {
Flush ();
}
}

public void Dispose ()
{
Dispose(true);
GC.SuppressFinalize (this);
}

protected virtual void Dispose(bool disposing) {
if (disposing) {
if (archive != null) {
archive.Close ();
archive.Dispose ();
archive = null;
}
}
}
}
}
10 changes: 8 additions & 2 deletions LibZipSharp/Xamarin.Tools.Zip/ZipArchive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -852,8 +852,14 @@ internal static unsafe Int64 stream_callback (IntPtr state, IntPtr data, UInt64
length = (int)Math.Min (stream.Length - stream.Position, length);
buffer = ArrayPool<byte>.Shared.Rent (length);
try {
int bytesRead = stream.Read (buffer, 0, length);
Marshal.Copy (buffer, 0, data, bytesRead);
int bytesRead = 0;
int startIndex = 0;
while (length > 0) {
bytesRead = stream.Read (buffer, 0, length);
Marshal.Copy (buffer, startIndex, data, bytesRead);
startIndex += bytesRead;
length -= bytesRead;
}
return bytesRead;
} finally {
ArrayPool<byte>.Shared.Return (buffer);
Expand Down