测试两个目录树的相等性

我正在集成测试我的代码的一部分,在SVN下创建目录树。 这要求我测试一下目录结构和文件是否是我所期望的。

一方面,我有期望的目录树与我想要的文件,另一方面,从SVN文件svn export (喜欢svn export svn co避免.svn噪音)。

但是,有没有可以断言两个目录树的库? 我想到的最后一招是自己做一个迭代比较。

基本上我正在寻找一个API可以接受两个目录,并告诉我,如果他们是平等的或不。

东西在线上

 boolean areDirectoriesEqual(File dir1, File dir2) 

我不使用第三方库,但标准的jdk库。

 private static void verifyDirsAreEqual(Path one, Path other) throws IOException { Files.walkFileTree(one, new SimpleFileVisitor() { @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[] thisBytes = Files.readAllBytes(file); if (!Arrays.equals(otherBytes, thisTypes)) { throw new AssertionFailedError(file + " is not equal to " + fileInOther); } return result; } }); } 

注意:这只是比较两个文件夹下的实际文件。 如果你有空的文件夹等你也想比较,你可能需要做一些额外的事情。

我有同样的问题,并跟随帕特里克和洛伦佐Dematté我find了一个解决方案,为我工作。 以下代码遍历文件夹并:

  • 为每个子文件夹检查文件列表是否相同
  • 为每个文件比较内容(在我的情况下,我必须比较包含csv文件的两个文件夹)

我在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() { @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; } }); } 

我不知道有任何areDirsEqual库; 我能想到的最接近的是Commons FileUtils的listFiles方法。

如果你把得到的集合放在一个HashSet ,你应该能够比较这两个集合。 它可以在两行中完成,甚至可以是一行代码。

这条线上的东西:

 public static boolean areDirsEqual(File dir, File dir2) { return (new HashSet(FileUtils.listFiles(dir1,..))). containsAll(FileUtils.listFiles(dir2, ..)) } 

好的,所以我不知道任何准备好的代码,搜索也没有帮助。 所以这里是我将如何实现它

  1. 在所有文件夹和文件上递归迭代
  2. 将所有文件名都保存在一个哈希集合中,其中相对路径是键/值
  3. 对第二个目录结构进行递归迭代,并从每个路径创建一个匹配hashet中的键(如果文件夹/文件存在)

如果你只想把树标记为已更改/未更改,则可以保存每个文件的散列值,然后您需要散列表而不是散列集,其中每个文件的散列值都是散列表的值

希望这可以帮助

这是一个使用Java NIO包的简单迭代解决方案(不使用访问者模式,因此它也可以适用于较早的Java版本)。

当然可以调整,但现在这是一个简单的解决方案,从两个目录的视图中检查每个文件是否出现,并可选择使用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 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; } 

我在Kotlin写了这个小代码。 它不检查文件的内容,但完全依赖于apache的md5。

 import org.apache.commons.codec.digest.DigestUtils fun File.calcMD5() = DigestUtils.md5Hex(FileUtils.readFileToByteArray(this)) fun compareTwoDirs(dir1: File, dir2: File): Boolean { val files1 = dir1.listFiles().sorted() val files2 = dir2.listFiles().sorted() if (files1.size != files2.size) return false return files1.zip(files2).all { equate(it.first, it.second) } } fun equate(fl: File, fl2: File): Boolean { if (fl.isFile && fl2.isFile) return fl.calcMD5() == fl2.calcMD5() if (fl.isDirectory && fl2.isDirectory) return compareTwoDirs(fl, fl2) return false } 
 import java.io.File; /** * * FileUtils is a collection of routines for common file system operations. * * @author Dan Jemiolo (danj) * */ public final class FileUtils { /** * * This is a convenience method that calls find(File, String, boolean) with * the last parameter set to "false" (does not match directories). * * @see #find(File, String, boolean) * */ public static File find(File contextRoot, String fileName) { return find(contextRoot, fileName, false); } /** * * Searches through the directory tree under the given context directory and * finds the first file that matches the file name. If the third parameter is * true, the method will also try to match directories, not just "regular" * files. * * @param contextRoot * The directory to start the search from. * * @param fileName * The name of the file (or directory) to search for. * * @param matchDirectories * True if the method should try and match the name against directory * names, not just file names. * * @return The java.io.File representing the first file or * directory with the given name, or null if it was not found. * */ public static File find(File contextRoot, String fileName, boolean matchDirectories) { if (contextRoot == null) throw new NullPointerException("NullContextRoot"); if (fileName == null) throw new NullPointerException("NullFileName"); if (!contextRoot.isDirectory()) { Object[] filler = { contextRoot.getAbsolutePath() }; String message = "NotDirectory"; throw new IllegalArgumentException(message); } File[] files = contextRoot.listFiles(); // // for all children of the current directory... // for (int n = 0; n < files.length; ++n) { String nextName = files[n].getName(); // // if we find a directory, there are two possibilities: // // 1. the names match, AND we are told to match directories. // in this case we're done // // 2. not told to match directories, so recurse // if (files[n].isDirectory()) { if (nextName.equals(fileName) && matchDirectories) return files[n]; File match = find(files[n], fileName); if (match != null) return match; } // // in the case of regular files, just check the names // else if (nextName.equals(fileName)) return files[n]; } return null; } }