at.spardat.xma.xdelta.test.JarDeltaJarPatcherTest.java Source code

Java tutorial

Introduction

Here is the source code for at.spardat.xma.xdelta.test.JarDeltaJarPatcherTest.java

Source

/*
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 */
package at.spardat.xma.xdelta.test;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Enumeration;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import at.spardat.xma.xdelta.JarDelta;
import at.spardat.xma.xdelta.JarPatcher;

/**
 * This class tests JarDelta and JarPatcher with randomly generated zip files.
 *
 * @author S3460
 */
public class JarDeltaJarPatcherTest {
    /** The random. */
    private SecureRandom random;
    /** The byte max length. */
    private int byteMaxLength = 1000;
    /** The entry max size. */
    private int entryMaxSize = 10;
    /** The source file. */
    private File sourceFile; //das originale File
    /** The target file. */
    private File targetFile; //die neue Generation
    /** The patch file. */
    private File patchFile; //die Unterschiede
    /** The result file. */
    private File resultFile; //das erechnete Result

    /**
     * Instantiates a new jar delta jar patcher test.
     */
    public JarDeltaJarPatcherTest() {
        try {
            random = SecureRandom.getInstance("SHA1PRNG");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * Sets the up.
     *
     * @throws Exception the exception
     */
    @Before
    public void setUp() throws Exception {
        sourceFile = File.createTempFile("JarDeltaJarPatcherTest_Source", ".zip");
        sourceFile.deleteOnExit();
        targetFile = File.createTempFile("JarDeltaJarPatcherTest_Target", ".zip");
        targetFile.deleteOnExit();
        patchFile = File.createTempFile("JarDeltaJarPatcherTest_Patch", ".zip");
        patchFile.deleteOnExit();
        resultFile = File.createTempFile("JarDeltaJarPatcherTest_Result", ".zip");
        resultFile.deleteOnExit();
    }

    /**
     * Tear down.
     *
     * @throws Exception the exception
     */
    @After
    public void tearDown() throws Exception {
        cleanUp();
    }

    /**
     * Clean up.
     *
     * @author S3460
     * @since version_number
     */
    private void cleanUp() {
        if (sourceFile != null) {
            sourceFile.delete();
        }
        if (targetFile != null) {
            targetFile.delete();
        }
        if (patchFile != null) {
            patchFile.delete();
        }
        if (resultFile != null) {
            resultFile.delete();
        }
    }

    /**
     * Creates a zip file with random content.
     *
     * @author S3460
     * @param source the source
     * @return the zip file
     * @throws Exception the exception
     */
    private ZipFile makeSourceZipFile(File source) throws Exception {
        ZipArchiveOutputStream out = new ZipArchiveOutputStream(new FileOutputStream(source));
        int size = randomSize(entryMaxSize);
        for (int i = 0; i < size; i++) {
            out.putArchiveEntry(new ZipArchiveEntry("zipentry" + i));
            int anz = randomSize(10);
            for (int j = 0; j < anz; j++) {
                byte[] bytes = getRandomBytes();
                out.write(bytes, 0, bytes.length);
            }
            out.flush();
            out.closeArchiveEntry();
        }
        //add leeres Entry
        out.putArchiveEntry(new ZipArchiveEntry("zipentry" + size));
        out.flush();
        out.closeArchiveEntry();
        out.flush();
        out.finish();
        out.close();
        return new ZipFile(source);
    }

    /**
     * Writes a modified version of zip_Source into target.
     *
     * @author S3460
     * @param zipSource the zip source
     * @param target the target
     * @return the zip file
     * @throws Exception the exception
     */
    private ZipFile makeTargetZipFile(ZipFile zipSource, File target) throws Exception {
        ZipArchiveOutputStream out = new ZipArchiveOutputStream(new FileOutputStream(target));
        for (Enumeration<ZipArchiveEntry> enumer = zipSource.getEntries(); enumer.hasMoreElements();) {
            ZipArchiveEntry sourceEntry = enumer.nextElement();
            out.putArchiveEntry(new ZipArchiveEntry(sourceEntry.getName()));
            byte[] oldBytes = toBytes(zipSource, sourceEntry);
            byte[] newBytes = getRandomBytes();
            byte[] mixedBytes = mixBytes(oldBytes, newBytes);
            out.write(mixedBytes, 0, mixedBytes.length);
            out.flush();
            out.closeArchiveEntry();
        }
        out.putArchiveEntry(new ZipArchiveEntry("zipentry" + entryMaxSize + 1));
        byte[] bytes = getRandomBytes();
        out.write(bytes, 0, bytes.length);
        out.flush();
        out.closeArchiveEntry();
        out.putArchiveEntry(new ZipArchiveEntry("zipentry" + (entryMaxSize + 2)));
        out.closeArchiveEntry();
        out.flush();
        out.finish();
        out.close();
        return new ZipFile(targetFile);
    }

    /**
     * Copies a modified version of oldBytes into newBytes by mixing some bytes.
     *
     * @param oldBytes the old bytes
     * @param newBytes the new bytes
     * @return the byte[]
     */
    private byte[] mixBytes(byte[] oldBytes, byte[] newBytes) {
        byte[] bytes = new byte[oldBytes.length + newBytes.length];
        if (oldBytes.length == 0) {
            return newBytes;
        }
        if (newBytes.length == 0) {
            return oldBytes;
        }
        System.arraycopy(oldBytes, 0, bytes, 0, oldBytes.length / 2);
        System.arraycopy(newBytes, 0, bytes, oldBytes.length / 2 - 1, newBytes.length / 2);
        System.arraycopy(oldBytes, oldBytes.length / 2, bytes, oldBytes.length / 2 + newBytes.length / 2 - 1,
                oldBytes.length / 2);
        System.arraycopy(newBytes, newBytes.length / 2, bytes, oldBytes.length + newBytes.length / 2 - 1,
                newBytes.length / 2);
        //      System.arraycopy(oldBytes,0,bytes,0,oldBytes.length);
        //      System.arraycopy(newBytes,0,bytes,oldBytes.length-1,newBytes.length);
        //        int chunkSize = randomSize(10);
        //        for (int i = chunkSize; i > 0; i--) {
        //            System.arraycopy(oldBytes,oldBytes.length/i,bytes,0,oldBytes.length/chunkSize);
        //            System.arraycopy(newBytes,newBytes.length/i,bytes,oldBytes.length/chunkSize-1,newBytes.length/chunkSize);
        //        }
        return bytes;
    }

    /**
     * Converts the given zip entry to a byte array.
     *
     * @author S3460
     * @param zipfile the zipfile
     * @param entry the entry
     * @return the byte[]
     * @throws Exception the exception
     */
    private byte[] toBytes(ZipFile zipfile, ZipArchiveEntry entry) throws Exception {
        int entrySize = (int) entry.getSize();
        byte[] bytes = new byte[entrySize];
        InputStream entryStream = zipfile.getInputStream(entry);
        for (int erg = entryStream.read(bytes); erg < bytes.length; erg += entryStream.read(bytes, erg,
                bytes.length - erg))
            ;
        return bytes;
    }

    /**
     * Returns a byte array of random length filled with random bytes.
     *
     * @author S3460
     * @return the random bytes
     */
    private byte[] getRandomBytes() {
        int lengt = randomSize(byteMaxLength);
        byte[] bytes = new byte[lengt];
        random.nextBytes(bytes);
        return bytes;
    }

    /**
     * Returns a random integer <= maxSize.
     *
     * @author S3460
     * @param maxSize the max size
     * @return the int
     */
    private int randomSize(int maxSize) {
        return ((int) (maxSize * random.nextFloat())) + 1;
    }

    /**
     * Compares the content of two zip files. The zip files are considered equal, if
     * the content of all zip entries is equal to the content of its corresponding entry
     * in the other zip file.
     *
     * @author S3460
     * @param zipSource the zip source
     * @param resultZip the result zip
     * @throws Exception the exception
     */
    private void compareFiles(ZipFile zipSource, ZipFile resultZip) throws Exception {
        boolean rc = false;
        try {
            for (Enumeration<ZipArchiveEntry> enumer = zipSource.getEntries(); enumer.hasMoreElements();) {
                ZipArchiveEntry sourceEntry = enumer.nextElement();
                ZipArchiveEntry resultEntry = resultZip.getEntry(sourceEntry.getName());
                assertNotNull("Entry nicht generiert: " + sourceEntry.getName(), resultEntry);
                byte[] oldBytes = toBytes(zipSource, sourceEntry);
                byte[] newBytes = toBytes(resultZip, resultEntry);
                rc = equal(oldBytes, newBytes);
                assertTrue("bytes the same " + sourceEntry, rc);
            }
        } finally {
            zipSource.close();
            resultZip.close();
        }
    }

    /**
     * Equal.
     *
     * @param source the source
     * @param target the target
     * @return true, if successful
     */
    public boolean equal(byte[] source, byte[] target) {
        if (source.length != target.length)
            return false;
        for (int i = 0; i < source.length; i++) {
            if (source[i] != target[i])
                return false;
        }
        return true;
    }

    /**
     * Uses JarDelta to create a patch file and tests if JarPatcher correctly reconstructs
     * newZip using this patch file.
     *
     * @author S3460
     * @param originalName the original name
     * @param targetName the target name
     * @param originalZip the original zip
     * @param newZip the new zip
     * @throws Exception the exception
     */
    private void runJarPatcher(String originalName, String targetName, ZipFile originalZip, ZipFile newZip)
            throws Exception {
        runJarPatcher(originalName, targetName, originalZip, newZip, true);
    }

    /**
     * Run jar patcher.
     *
     * @param originalName the original name
     * @param targetName the target name
     * @param originalZip the original zip
     * @param newZip the new zip
     * @param comparefiles the comparefiles
     * @throws Exception the exception
     */
    private void runJarPatcher(String originalName, String targetName, ZipFile originalZip, ZipFile newZip,
            boolean comparefiles) throws Exception {
        try (ZipArchiveOutputStream output = new ZipArchiveOutputStream(new FileOutputStream(patchFile))) {
            new JarDelta().computeDelta(originalName, targetName, originalZip, newZip, output);
        }
        ZipFile patch = new ZipFile(patchFile);
        ZipArchiveEntry listEntry = patch.getEntry("META-INF/file.list");
        if (listEntry == null) {
            patch.close();
            throw new IOException("Invalid patch - list entry 'META-INF/file.list' not found");
        }
        BufferedReader patchlist = new BufferedReader(new InputStreamReader(patch.getInputStream(listEntry)));
        String next = patchlist.readLine();
        String sourceName = next;
        next = patchlist.readLine();
        new JarPatcher(patchFile.getName(), sourceName).applyDelta(patch, new ZipFile(originalName),
                new ZipArchiveOutputStream(new FileOutputStream(resultFile)), patchlist);
        if (comparefiles) {
            compareFiles(new ZipFile(targetName), new ZipFile(resultFile));
        }
    }

    /**
     * Run jar patcher derived file.
     *
     * @throws Exception the exception
     */
    private void runJarPatcherDerivedFile() throws Exception {
        ZipFile orginalZip = makeSourceZipFile(sourceFile);
        ZipFile derivedZip = makeTargetZipFile(orginalZip, targetFile);
        runJarPatcher(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), orginalZip, derivedZip);
    }

    /**
     * Run jar patcher complete differnt file.
     *
     * @throws Exception the exception
     */
    private void runJarPatcherCompleteDifferntFile() throws Exception {
        ZipFile orginalZip = makeSourceZipFile(sourceFile);
        ZipFile derivedZip = makeSourceZipFile(targetFile);
        runJarPatcher(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), orginalZip, derivedZip);
    }

    /**
     * Test jar patcher derived file.
     *
     * @throws Exception the exception
     */
    @Test
    public void testJarPatcherDerivedFile() throws Exception {
        //byteMaxLength = 10000;
        //entrySize = 10;
        runJarPatcherDerivedFile();
    }

    /**
     * Test jar patcher derived file big.
     *
     * @throws Exception the exception
     */
    @Test
    public void testJarPatcherDerivedFileBig() throws Exception {
        byteMaxLength = 100000;
        entryMaxSize = 100;
        runJarPatcherDerivedFile();
    }

    /**
     * No test jar patcher derived file very big.
     *
     * @throws Exception the exception
     */
    @Ignore
    public void noTestJarPatcherDerivedFileVeryBig() throws Exception {
        byteMaxLength = 100000;
        entryMaxSize = 100;
        runJarPatcherDerivedFile();
        for (int i = 0; i < 100; i++) {
            //runJarPatcherDerivedFile();
        }
    }

    /**
     * No test jar patcher derived file stressed.
     *
     * @throws Exception the exception
     */
    @Ignore
    public void noTestJarPatcherDerivedFileStressed() throws Exception {
        byteMaxLength = 100000;
        entryMaxSize = 1000;
        runJarPatcherDerivedFile();
    }

    /**
     * Test jar patcher complete differnt file.
     *
     * @throws Exception the exception
     */
    @Test
    public void testJarPatcherCompleteDifferntFile() throws Exception {
        runJarPatcherCompleteDifferntFile();
    }

    /**
     * Test jar patcher complete differnt file big.
     *
     * @throws Exception the exception
     */
    @Test
    public void testJarPatcherCompleteDifferntFileBig() throws Exception {
        byteMaxLength = 10000;
        entryMaxSize = 100;
        runJarPatcherCompleteDifferntFile();
    }

    // Throws exception from patching if target and output crc:s don't match except for zip files, 
    // which may get different crc's due to different compression options
    /**
     * Test embedded zip.
     *
     * @throws Exception the exception
     */
    @Test
    public void testEmbeddedZip() throws Exception {
        String file1 = "src/test/resources/embedded1.zip";
        String file2 = "src/test/resources/embedded2.zip";
        runJarPatcher(file1, file2, new ZipFile(file1), new ZipFile(file2), false);
    }

    /**
     * No test jar patcher complete differnt stressed.
     *
     * @throws Exception the exception
     */
    @Ignore
    public void noTestJarPatcherCompleteDifferntStressed() throws Exception {
        byteMaxLength = 100000;
        entryMaxSize = 1000;
        runJarPatcherCompleteDifferntFile();
    }

    /**
     * Tests JarDelta and JarPatcher on two identical files.
     *
     * @throws Exception the exception
     */
    @Test
    public void testJarPatcherIdentFile() throws Exception {
        ZipFile originalZip = makeSourceZipFile(sourceFile);
        new JarDelta().computeDelta(sourceFile.getAbsolutePath(), sourceFile.getAbsolutePath(), originalZip,
                originalZip, new ZipArchiveOutputStream(new FileOutputStream(patchFile)));
        ZipFile patch = new ZipFile(patchFile);
        ZipArchiveEntry listEntry = patch.getEntry("META-INF/file.list");
        if (listEntry == null) {
            patch.close();
            throw new IOException("Invalid patch - list entry 'META-INF/file.list' not found");
        }
        BufferedReader patchlist = new BufferedReader(new InputStreamReader(patch.getInputStream(listEntry)));
        String next = patchlist.readLine();
        String sourceName = next;
        next = patchlist.readLine();
        ZipFile source = new ZipFile(sourceFile);
        new JarPatcher(patchFile.getName(), sourceName).applyDelta(patch, source,
                new ZipArchiveOutputStream(new FileOutputStream(resultFile)), patchlist);
        compareFiles(new ZipFile(sourceFile), new ZipFile(resultFile));
    }

    /**
     * Tests JarDelta and JarPatcher on two big identical files.
     *
     * @throws Exception the exception
     */
    @Ignore
    public void noTestJarPatcherIdentFileBig() throws Exception {
        byteMaxLength = 100000;
        entryMaxSize = 1000;
        testJarPatcherIdentFile();
    }
}