Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test two directory trees for equality

I am working on integration testing some part of my code that creates directory trees under SVN. This requires me to test if the directory structure and the files within are what I expect them to be.

On one hand I have the expected directory tree with the files I want and on the other, an export of the files from SVN (prefer svn export over svn co to avoid the .svn noise).

However, is there any library that can assert two directory trees? The last resort I have in mind is to do an iterative comparison myself.

Basically I am looking for an API that can just accept two directories and tell me if they are equal or not.

Something on the lines of

boolean areDirectoriesEqual(File dir1, File dir2)
like image 933
adarshr Avatar asked Jan 25 '13 12:01

adarshr


People also ask

How do I compare two directories in differences?

Click on the “Select Files or Folders” tab in the far left, to start a new comparison. Each comparison you run opens in a new tab. To start a new comparison, click on the “Select Files or Folders” tab in the far left, change the targets and click “Compare” again.

How do I compare two directories in UNIX?

Use the dircmp command to compare two directories specified by the Directory1 and Directory2 parameters and write information about their contents to standard output. First, the dircmp command compares the file names in each directory.

How do I check if two Python folders are identical?

cmp(f1, f2, shallow=True) This function compares the two files and returns True if they are identical, False otherwise.

How do you compare two folders in Python?

cmpfiles() method in Python is used to compare files in two directories. Multiple files can be compared using this method. This method returns three lists of file names namely match, mismatch and errors.


4 Answers

I am not using a third party lib but standard jdk lib.

private static void verifyDirsAreEqual(Path one, Path other) throws IOException {
    Files.walkFileTree(one, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path file,
                BasicFileAttributes attrs)
                throws IOException {
            FileVisitResult result = super.visitFile(file, attrs);

            // get the relative file name from path "one"
            Path relativize = one.relativize(file);
            // construct the path for the counterpart file in "other"
            Path fileInOther = other.resolve(relativize);
            log.debug("=== comparing: {} to {}", file, fileInOther);

            byte[] otherBytes = Files.readAllBytes(fileInOther);
            byte[] theseBytes = Files.readAllBytes(file);
            if (!Arrays.equals(otherBytes, theseBytes)) {
                throw new AssertionFailedError(file + " is not equal to " + fileInOther);
            }  
            return result;
        }
    });
}

Note: this is just comparing actual files under two folders. If you have empty folders etc you want to compare too, you may need to do some extra things.

like image 188
Patrick Avatar answered Oct 13 '22 13:10

Patrick


I am not aware of any areDirsEqual library; the closest I can think of is the listFiles method in Commons FileUtils.

If you put the resulting collections in a HashSet, you should be able to compare the two sets efficiently. And it can be done in 2 lines, maybe even a one-liner.

Something on this line:

public static boolean areDirsEqual(File dir, File dir2) {
  return (new HashSet<File>(FileUtils.listFiles(dir1,..))).
          containsAll(FileUtils.listFiles(dir2, ..))
}
like image 3
Lorenzo Dematté Avatar answered Oct 13 '22 12:10

Lorenzo Dematté


I had same problem and following Patrick and Lorenzo Dematté I found a solution that works for me. The following code walks through folder and:

  • for each sub-folder checks if file lists are the same
  • for each file compares the contents (in my case I have to compare two folders that contain csv files)

I tested it on linux.

  private static void verifyDirsAreEqual(File expected, File generated) 
                throws IOException {

    // Checks parameters 
    assertTrue("Generated Folder doesn't exist: " + generated,generated.exists());
    assertTrue("Generated is not a folder?!?!: " + generated,generated.isDirectory());

    assertTrue("Expected Folder doesn't exist: " + expected,expected.exists());
    assertTrue("Expected is not a folder?!?!: " + expected,expected.isDirectory());     

    Files.walkFileTree(expected.toPath(), new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir,
                BasicFileAttributes attrs)
                  throws IOException {
            FileVisitResult result = super.preVisitDirectory(dir, attrs);

            // get the relative file name from path "expected"
            Path relativize = expected.toPath().relativize(dir);
            // construct the path for the counterpart file in "generated"
            File otherDir = generated.toPath().resolve(relativize).toFile();
            log.debug("=== preVisitDirectory === compare " + dir + " to " + otherDir);
            assertEquals("Folders doesn't contain same file!?!?",
                    Arrays.toString(dir.toFile().list()),
                    Arrays.toString(otherDir.list()));
            return result;
        }
        @Override
        public FileVisitResult visitFile(Path file,
                BasicFileAttributes attrs)
                throws IOException {
            FileVisitResult result = super.visitFile(file, attrs);

            // get the relative file name from path "expected"
            Path relativize = expected.toPath().relativize(file);
            // construct the path for the counterpart file in "generated"
            File fileInOther = generated.toPath().resolve(relativize).toFile();
            log.debug("=== comparing: " + file + " to " + fileInOther);
            String expectedContents = FileUtils.readFileToString(file.toFile());
            String generatedContents = FileUtils.readFileToString(fileInOther);
            assertEquals("("+fileInOther+")  csv standard doesn't match expected ("+file+")!", expectedContents, generatedContents);                    
            return result;
        }
    });
}
like image 3
Lety Avatar answered Oct 13 '22 12:10

Lety


This is a simple iterative solution using the Java NIO package (without using the Visitor pattern, so it can be adapted for earlier Java versions as well).

Of course it could be tuned, but for now this is an easy solution checking from the view of both directories if every file occurs and optionally compare the file content using the Apache Commons FileUtils.

/**
 * checks if the directory file lists and file content is equal
 * 
 * @param directory
 *            the directory
 * @param compareDirectory
 *            the directory to compare with
 * @param checkFileContent
 *            also compare file content
 * @return true if directory and compareDirectory are equal
 * @throws IOException
 */
public static boolean isEqualDirectories(Path directory, Path compareDirectory, boolean checkFileContent) throws IOException {
    boolean check = isEverythingInCompareDirectory(directory, compareDirectory, checkFileContent);
    boolean checkOpposite = check && isEverythingInCompareDirectory(directory, compareDirectory, checkFileContent);
    return check && checkOpposite;

}

/**
 * checks if the directory file lists and file content is equal
 * 
 * @param directory
 *            the directory
 * @param compareDirectory
 *            the directory to compare with
 * @param checkFileContent
 *            also compare file content
 * @return true if directory and compareDirectory are equal
 * @throws IOException
 */
public static boolean isEverythingInCompareDirectory(Path directory, Path compareDirectory, boolean checkFileContent)
        throws IOException {

    try {
        LOGGER.info("checking directory " + directory);

        File directoryFile = directory.toFile();
        File compareFile = compareDirectory.toFile();

        // check, if there is the same number of files/subdirectories
        File[] directoryFiles = directoryFile.listFiles();
        File[] compareFiles = compareFile.listFiles();

        if (directoryFiles.length == compareFiles.length) {
            return compareDirectoryContents(directory, compareDirectory, checkFileContent);

        } else {
            LOGGER.info("number of files in directory are different " + directoryFiles.length + " vs compareDirectory: " + compareFiles.length);
            return false;
        }

    } catch (IOException e) {
        throw new RuntimeException("Failed to assert that all files are equal", e);
    }
}

public static boolean compareDirectoryContents(Path directory, Path compareDirectory, boolean checkFileContent) throws IOException {
    try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) {

        for (Path directoryFilePath : directoryStream) {

            // search for directoryFile in the compareDirectory
            Path compareFilePath = compareDirectory.resolve(directoryFilePath.getFileName());

            if (compareFilePath != null) {

                File directoryFile = directoryFilePath.toFile();
                if (directoryFile.isFile()) {
                    LOGGER.info("checking file " + directoryFilePath);
                    if (checkFileContent && !FileUtils.contentEquals(compareFilePath.toFile(), directoryFile)) {
                        LOGGER.info("files not equal: compare: " + compareFilePath.toFile() + ", directory: " + directoryFilePath.getFileName() + "!");
                        return false;
                    }

                } else {
                    LOGGER.info("going into recursion with directory " + directoryFilePath);
                    boolean result = isEverythingInCompareDirectory(directoryFilePath, compareFilePath, checkFileContent);
                    // cancel if not equal, otherwise continue processing
                    if (!result) {
                        return false;
                    }

                }
            } else {
                LOGGER.info(directoryFilePath.toString() + ": compareFilepath not found");
                return false;
            }

        }
    }

    return true;
}
like image 2
jfx Avatar answered Oct 13 '22 14:10

jfx