javajunittarapache-commons-compress

What is the best way to compare tar archives in junit testing


I am attempting to create jUnit tests for some code that generates tar files. During testing I will be creating a variety of tar files and comparing them to "Gold" tar images of the expected output. I have been struggling to create an assertTarEquals(String file1, String file2) function, and was hoping someone could provide guidance as to the best approach. It's not important to have the tar file entries in the same order, or with the same attributes. I just need to verify that they have all the same files and that those files contain the same content. I have created a assertZipEquals(String file1, String file2) based on the example provided here: http://www.java2s.com/Tutorial/Java/0180__File/Comparetwozipfiles.htm but the ZipFile.getInputSteam(EntryName) does not appear to have a parallel function in the Commons Tar classes, as they have not implemented markers in the TarInputStream.


Solution

  • I would typically agree that unit testing is no place for testing Archives, but the code that is being tested manages the creation of archives, so in my mind it is not only appropriate but necessary. The code below is pretty ugly, and I would never want to use something like this in production code, but for testing I guess it's ok.... here's the code I'm using for assertArchiveEquals, it supports both tar and zip files.... as always all feedback is welcome.

    package com.foo.util.merge;
    
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.security.NoSuchAlgorithmException;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipException;
    import java.util.zip.ZipFile;
    import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
    import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
    import org.apache.commons.io.IOUtils;
    
    import static org.junit.Assert.*;
    
    public final class CompareArchives {
    
        public static final void assertArchiveEquals(String type, String archive1, String archive2) throws NoSuchAlgorithmException, IOException {
            if (type.endsWith("zip")) {
                assertZipEquals(archive1, archive2);
            } else {
                assertTarEquals(archive1, archive2);
            }
        }
    
        /**
         * @param archive1
         * @param archive2
         * @throws ZipException 
         * @throws IOException
         */
        public static final void assertZipEquals(String archive1, String archive2) throws ZipException, IOException  {
            // Get Archives
            ZipFile zipFile1 = new ZipFile(new File(archive1));
            ZipFile zipFile2 = new ZipFile(new File(archive2));
    
            // Get Member Hash
            HashMap<String, ZipEntry> files1 = getMembers(zipFile1);
            HashMap<String, ZipEntry> files2 = getMembers(zipFile2);
    
            // Compare Files
            assertMembersEqual(zipFile1, files1, zipFile2, files2);
        }
    
        /**
         * @param archive
         * @return
         * @throws IOException
         */
        private static final HashMap<String, ZipEntry> getMembers(ZipFile archive) throws IOException {
            HashMap<String, ZipEntry> map = new HashMap<String, ZipEntry>();
            @SuppressWarnings("unchecked")
            Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) archive.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                map.put(entry.getName(), entry);
            }
            return map;
        }
    
        /**
         * @param zip1
         * @param files1
         * @param zip2
         * @param files2
         * @throws IOException
         */
        private static final void assertMembersEqual(ZipFile zip1, HashMap<String, ZipEntry> files1, 
                                                     ZipFile zip2, HashMap<String, ZipEntry> files2) throws IOException {
            if (files1.size() != files2.size()) {
                fail("Different Sizes, expected " + Integer.toString(files1.size()) + " found " + Integer.toString(files2.size()));
            }
    
            for (String key : files1.keySet()) {
                if (!files2.containsKey(key)) {
                    fail("Expected file not in target " + key);
                }
                String file1 = IOUtils.toString(zip1.getInputStream(files1.get(key)));
                String file2 = IOUtils.toString(zip2.getInputStream(files2.get(key)));
                assertEquals(file1, file2);
            }
        }
    
        /**
         * @param archive1
         * @param archive2
         * @throws IOException
         * @throws NoSuchAlgorithmException
         */
        public static final void assertTarEquals(String archive1, String archive2) throws IOException, NoSuchAlgorithmException {
            // Get Member Hash
            HashMap<String, TarArchiveEntry> files1 = getMembers(archive1);
            HashMap<String, TarArchiveEntry> files2 = getMembers(archive2);
    
            // Compare Files
            assertMembersEqual(archive1, files1, archive2, files2);
        }
    
        /**
         * @param archive
         * @return
         * @throws IOException
         */
        private static final HashMap<String,TarArchiveEntry> getMembers(String archive) throws IOException {
            TarArchiveInputStream input = new TarArchiveInputStream(
                    new BufferedInputStream(
                    new FileInputStream(archive)));
    
            TarArchiveEntry entry;
            HashMap<String, TarArchiveEntry> map = new HashMap<String, TarArchiveEntry>();
            while ((entry = input.getNextTarEntry()) != null) {
                map.put(entry.getName(), entry);
            }
            input.close();
            return map;
        }
    
        /**
         * @param tar1
         * @param files1
         * @param tar2
         * @param files2
         * @throws IOException
         */
        private static final void assertMembersEqual(String tar1, HashMap<String, TarArchiveEntry> files1, 
                                                     String tar2, HashMap<String, TarArchiveEntry> files2) throws IOException {
            if (files1.size() != files2.size()) {
                fail("Different Sizes, expected " + Integer.toString(files1.size()) + " found " + Integer.toString(files2.size()));
            }
    
            for (String key : files1.keySet()) {
                if (!files2.containsKey(key)) {
                    fail("Expected file not in target " + key);
                }
            }
    
            for (String key : files1.keySet()) {
                if (!files2.containsKey(key)) {
                    fail("Expected file not in target " + key);
                }
            }
            for (String key : files1.keySet()) {
                String file1 = getTarFile(tar1, key);
                String file2 = getTarFile(tar2, key);
                assertEquals(file1, file2);
            }
        }
    
        /**
         * @param archive
         * @param name
         * @return
         * @throws IOException
         */
        private static final String getTarFile(String archive, String name) throws IOException {
            TarArchiveInputStream input = new TarArchiveInputStream(
                    new BufferedInputStream(
                    new FileInputStream(archive)));
            TarArchiveEntry entry;
            while ((entry = input.getNextTarEntry()) != null) {
                if (entry.getName().equals(name)) {
                    byte[] content = new byte[(int) entry.getSize()];
                    input.read(content, 0, content.length);
                    input.close();
                    return new String(content);
                }
            }
            input.close();
            return "";
        }
    
    }