/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.test.util;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Utility for comparing a given 'expected' zip file's contents with
* a 'actual' zip contents.
* <p>
* Two zips are considered equal if they have the same files / folders
* in their table-of contents and each of the files has identical contents.
* <p>
* This differ doesn't actually compare two files directly but rather computes
* a hash of the file contents instead and compares the hashes. So...
* their is a small chance that a difference may not be detected.
*
* @author Kris De Volder
*/
public class ZipDiff {
final Map<String, Long> expectedHashes;
public ZipDiff(InputStream expectedZipData) throws IOException {
expectedHashes = computeHashes(expectedZipData);
}
public Map<String, Long> computeHashes(InputStream expectedZipData) throws IOException {
Map<String, Long> hashes = new HashMap<>();
ZipInputStream zip = new ZipInputStream(expectedZipData);
ZipEntry ze;
byte[] buffer = new byte[1024];
while (null != (ze = zip.getNextEntry())) {
String path = ze.getName();
if (ze.isDirectory()) {
hashes.put(path, -1L);
} else {
CRC32 hasher = new CRC32();
int len;
while ((len = zip.read(buffer)) > 0) {
hasher.update(buffer, 0, len);
}
hashes.put(path, hasher.getValue());
}
}
return hashes;
}
/**
* Read data from 'actualBits', interpret is as a ZipInputStream,
* and compare its contents to the expected contents.
* <p>
* Throws an exception when a difference is found.
*/
public void assertEqual(InputStream actualBits) throws IOException {
Map<String, Long> actualHashes = computeHashes(actualBits);
//Check that everything that is in 'actualBits' was as expected
for (Entry<String, Long> actual : actualHashes.entrySet()) {
if (!expectedHashes.containsKey(actual.getKey())) {
fail("ZipEntry found but not expected: "+actual.getKey());
}
long expectedHash = expectedHashes.get(actual.getKey());
long actualHash = actual.getValue();
if (expectedHash!=actualHash) {
fail("ZipEntry with different hashes: "+actual.getKey()+" "+expectedHash+"!="+actualHash);
}
}
//Check that there's nothing that was expected but was missing from 'actual'
for (String expected : expectedHashes.keySet()) {
if (!actualHashes.containsKey(expected)) {
fail("ZipEntry expected but not found: "+expected);
}
}
}
}