From ca1409b55bc2c9c3e2bc064c620fcdc9efe69f68 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Tue, 15 Mar 2016 11:44:11 +0000 Subject: [PATCH] Merged WOLF-6 (WOLF.0.0) to 5.1.1 (5.1.1) 122680 mward: UTF-383: moved diff tool from update-tool to full-installer code base. Had erroneously added the code to the wrong project. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/services/full-installer/branches/5.1.1@123991 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- enterprise-update-test/pom.xml | 31 ++ .../dircomp/CaseSensitivePathComparator.java | 28 ++ .../update/tool/dircomp/FileTreeCompare.java | 22 ++ .../tool/dircomp/FileTreeCompareImpl.java | 241 ++++++++++++++ .../tool/dircomp/HtmlResultFormatter.java | 155 +++++++++ .../alfresco/update/tool/dircomp/Result.java | 95 ++++++ .../update/tool/dircomp/ResultFormatter.java | 28 ++ .../update/tool/dircomp/ResultSet.java | 40 +++ .../update/tool/dircomp/SortedPathSet.java | 28 ++ .../exception/FileTreeCompareException.java | 31 ++ .../tool/dircomp/FileTreeCompareImplTest.java | 298 ++++++++++++++++++ .../tool/dircomp/HtmlResultFormatterTest.java | 118 +++++++ .../tree1/b/alfresco-testdata-webapp.war | Bin 0 -> 3162 bytes .../file_folders_plus_war/tree1/b/blah.txt | 2 + .../tree1/c/c1/commands.bat | 2 + .../tree1/c/c1/commands.sh | 4 + .../tree1/c/c2/Aardvark.java | 7 + .../tree1/c/c2/Banana.java | 7 + .../file_folders_plus_war/tree2/a/story.txt | 5 + .../tree2/b/alfresco-testdata-webapp.war | Bin 0 -> 3726 bytes .../file_folders_plus_war/tree2/b/blah.txt | 2 + .../tree2/c/c1/commands.bat | 2 + .../tree2/c/c1/commands.sh | 4 + .../tree2/c/c2/Aardvark.java | 7 + .../simple_file_folders/tree1/b/blah.txt | 2 + .../tree1/c/c1/commands.bat | 2 + .../tree1/c/c1/commands.sh | 4 + .../tree1/c/c2/Aardvark.java | 7 + .../tree1/c/c2/Banana.java | 7 + .../simple_file_folders/tree2/a/story.txt | 5 + .../simple_file_folders/tree2/b/blah.txt | 2 + .../tree2/c/c1/commands.bat | 2 + .../tree2/c/c1/commands.sh | 4 + .../tree2/c/c2/Aardvark.java | 7 + 34 files changed, 1199 insertions(+) create mode 100644 enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/CaseSensitivePathComparator.java create mode 100644 enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/FileTreeCompare.java create mode 100644 enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/FileTreeCompareImpl.java create mode 100644 enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/HtmlResultFormatter.java create mode 100644 enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/Result.java create mode 100644 enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ResultFormatter.java create mode 100644 enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ResultSet.java create mode 100644 enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/SortedPathSet.java create mode 100644 enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/exception/FileTreeCompareException.java create mode 100644 enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/FileTreeCompareImplTest.java create mode 100644 enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/HtmlResultFormatterTest.java create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/b/alfresco-testdata-webapp.war create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/b/blah.txt create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/c/c1/commands.bat create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/c/c1/commands.sh create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/c/c2/Aardvark.java create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/c/c2/Banana.java create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/a/story.txt create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/b/alfresco-testdata-webapp.war create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/b/blah.txt create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c1/commands.bat create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c1/commands.sh create mode 100644 enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c2/Aardvark.java create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/b/blah.txt create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c1/commands.bat create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c1/commands.sh create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c2/Aardvark.java create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c2/Banana.java create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/a/story.txt create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/b/blah.txt create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c1/commands.bat create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c1/commands.sh create mode 100644 enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c2/Aardvark.java diff --git a/enterprise-update-test/pom.xml b/enterprise-update-test/pom.xml index c423f66a7c..c2d254b468 100644 --- a/enterprise-update-test/pom.xml +++ b/enterprise-update-test/pom.xml @@ -53,6 +53,37 @@ test + + de.schlichtherle.truezip + truezip-driver-zip + 7.7.9 + + + de.schlichtherle.truezip + truezip-file + 7.7.9 + + + commons-io + commons-io + 2.4 + + + org.apache.logging.log4j + log4j-api + 2.3 + + + org.apache.logging.log4j + log4j-core + 2.3 + + + + org.springframework + spring-core + 4.2.4.RELEASE + diff --git a/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/CaseSensitivePathComparator.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/CaseSensitivePathComparator.java new file mode 100644 index 0000000000..56c03633ba --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/CaseSensitivePathComparator.java @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp; + +import java.nio.file.Path; +import java.util.Comparator; + +/** + * Provides a platform agnostic and consistent sorting mechanism + * for {@link java.nio.file.Path} objects. + * + * @author Matt Ward + */ +public class CaseSensitivePathComparator implements Comparator +{ + @Override + public int compare(Path p1, Path p2) + { + String pathStr1 = p1.toString(); + String pathStr2 = p2.toString(); + return pathStr1.compareTo(pathStr2); + } +} diff --git a/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/FileTreeCompare.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/FileTreeCompare.java new file mode 100644 index 0000000000..4c164402df --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/FileTreeCompare.java @@ -0,0 +1,22 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp; + +import java.nio.file.Path; +import java.util.List; + + +/** + * File tree comparison tool interface. + * + * @author Matt Ward + */ +public interface FileTreeCompare +{ + ResultSet compare(Path p1, Path p2); +} diff --git a/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/FileTreeCompareImpl.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/FileTreeCompareImpl.java new file mode 100644 index 0000000000..86dda25b16 --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/FileTreeCompareImpl.java @@ -0,0 +1,241 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.alfresco.update.tool.dircomp.exception.FileTreeCompareException; +import org.apache.commons.io.FileUtils; + +import de.schlichtherle.truezip.file.TArchiveDetector; +import de.schlichtherle.truezip.file.TConfig; +import de.schlichtherle.truezip.file.TFile; +import de.schlichtherle.truezip.file.TVFS; +import de.schlichtherle.truezip.fs.archive.zip.ZipDriver; +import de.schlichtherle.truezip.socket.sl.IOPoolLocator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.util.AntPathMatcher; + +/** + * Class capable of comparing two trees of files to determine which directories or + * files appear in one tree and not the other, or whether a file that appears in + * both has differences in its content. + * + * @author Matt Ward + */ +public class FileTreeCompareImpl implements FileTreeCompare +{ + private static final Logger log = LogManager.getLogger(FileTreeCompareImpl.class); + private final Set ignorePaths = new HashSet<>(); + private final AntPathMatcher pathMatcher = new AntPathMatcher(File.separator); + + public FileTreeCompareImpl() + { + this(new HashSet()); + } + + public FileTreeCompareImpl(Set ignorePaths) + { + // This config MUST be present before any TFile objects etc. are created. + TConfig config = TConfig.get(); + config.setArchiveDetector(new TArchiveDetector("war|jar", new ZipDriver(IOPoolLocator.SINGLETON))); + this.ignorePaths.addAll(ignorePaths); + } + + @Override + public ResultSet compare(Path p1, Path p2) + { + ResultSet resultSet = new ResultSet(); + try + { + compare(resultSet.stats, resultSet.results, p1, p2); + } + catch (Exception e) + { + throw new FileTreeCompareException("Unable to compare file trees.", e); + } + return resultSet; + } + + private void compare(ResultSet.Stats stats, List results, Path tree1, Path tree2) throws IOException + { + SortedPathSet set1 = sortedPaths(tree1); + SortedPathSet set2 = sortedPaths(tree2); + + SortedPathSet all = new SortedPathSet(); + all.addAll(set1); + all.addAll(set2); + + for (Path pathToFind : all) + { + if (pathMatchesIgnorePattern(pathToFind)) + { + // Skip paths that we don't want to examine, e.g. tomcat/temp + log.debug("Skipping path: "+pathToFind); + stats.ignoredFileCount++; + continue; + } + + Result result = new Result(); + results.add(result); + stats.resultCount++; + + if (set1.contains(pathToFind) && set2.contains(pathToFind)) + { + log.debug("In both: "+pathToFind); + // Set the results, translating paths back to absolute as required. + result.p1 = tree1.resolve(pathToFind); + result.p2 = tree2.resolve(pathToFind); + boolean contentMatches = false; + if (Files.isRegularFile(result.p1) && Files.isRegularFile(result.p2)) + { + contentMatches = FileUtils.contentEquals(result.p1.toFile(), result.p2.toFile()); + if (!contentMatches && isSpecialArchive(pathToFind)) + { + Path archive1 = extract(result.p1); + Path archive2 = extract(result.p2); + result.subTree1 = archive1; + result.subTree2 = archive2; + final int diffBefore = stats.differenceCount; + compare(stats, result.subResults, archive1, archive2); + final int diffAfter = stats.differenceCount; + if (diffAfter == diffBefore) + { + // No significant differences were found in the (recursive) subtree comparison. + // We can therefore mark the special archive files matching in both trees. + contentMatches = true; + } + } + } + else if (Files.isDirectory(result.p1) && Files.isDirectory(result.p2)) + { + // Two directories are counted as the same. + contentMatches = true; + } + result.equal = contentMatches; + } + else if (set1.contains(pathToFind)) + { + log.debug("In tree1 only: "+pathToFind); + result.p1 = tree1.resolve(pathToFind); + result.p2 = null; + } + else if (set2.contains(pathToFind)) + { + log.debug("In tree2 only: "+pathToFind); + result.p1 = null; + result.p2 = tree2.resolve(pathToFind); + } + else + { + throw new IllegalStateException( + "Something went wrong. The path is not found in either tree: "+pathToFind); + } + + if (!result.equal) + { + stats.differenceCount++; + } + } + } + + private boolean isSpecialArchive(Path pathToFind) + { + return pathToFind.getFileName().toString().toLowerCase().endsWith(".war") || + pathToFind.getFileName().toString().toLowerCase().endsWith(".jar"); + } + + /** + * If the set of paths to ignore ({@link #ignorePaths}) contains + * a pattern matching the specified path, then true is returned. + *

+ * Patterns are ant-style patterns. + * + * @param path The path to check + * @return True if there is a path in the ignorePaths set that is a prefix of the path. + */ + private boolean pathMatchesIgnorePattern(String path) + { + for (String pattern : ignorePaths) + { + if (pathMatcher.match(pattern, path)) + { + return true; + } + } + return false; + } + + /** + * @see #pathMatchesIgnorePattern(String) + */ + private boolean pathMatchesIgnorePattern(Path path) + { + return pathMatchesIgnorePattern(path.toString()); + } + + private Path extract(Path archivePath) throws IOException + { + String destDirName = archivePath.getFileName().toString(); + Path dest = Files.createTempDirectory(destDirName); + extract(archivePath, dest); + return dest; + } + + private void extract(Path archivePath, Path destPath) throws IOException + { + TFile archive = new TFile(archivePath.toFile()); + TFile dest = new TFile(destPath.toFile(), TArchiveDetector.NULL); + try + { + // Unzip the archive. + archive.cp_rp(dest); + } + finally + { + TVFS.umount(archive); + TVFS.umount(dest); + } + } + + /** + * Traverse path and create a {@link SortedPathSet} containing the set + * of paths encountered. The {@link Path} instances are relative to + * the base path provided as a parameter to this method. + * + * @param path The path to traverse. + * @return SortedPathSet + * @throws IOException + */ + protected SortedPathSet sortedPaths(Path path) throws IOException + { + SortedPathSet sortedPaths = new SortedPathSet(); + collectPaths(sortedPaths, path, path.toFile()); + return sortedPaths; + } + + private void collectPaths(SortedPathSet sortedSet, Path root, File path) throws IOException + { + for (File f : path.listFiles()) + { + Path relativePath = root.relativize(f.toPath()); + sortedSet.add(relativePath); + if (f.isDirectory()) + { + collectPaths(sortedSet, root, f); + } + } + } +} diff --git a/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/HtmlResultFormatter.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/HtmlResultFormatter.java new file mode 100644 index 0000000000..b934897628 --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/HtmlResultFormatter.java @@ -0,0 +1,155 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.List; + +/** + * Format results as HTML. + * + * @author Matt Ward + */ +public class HtmlResultFormatter implements ResultFormatter +{ + private int maxPathDisplayLength = 80; + private boolean differencesOnly; + + + @Override + public void format(ResultSet resultSet, OutputStream out) + { + try(PrintWriter pw = new PrintWriter(out)) + { + pw.println(""); + pw.println(""); + pw.println(""); + pw.println("File tree comparison results"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println("

Files examined: "+resultSet.stats.resultCount+"

"); + pw.println("

Files with differences: "+resultSet.stats.differenceCount+"

"); + pw.println("

Ignored files: "+resultSet.stats.ignoredFileCount+"

"); + String passOrFail; + if (resultSet.stats.differenceCount > 0) + { + passOrFail = "FAILED"; + } + else + { + passOrFail = "PASSED"; + } + pw.println("

Status: "+passOrFail+"

"); + + outputResultsTable(resultSet.results, pw, 0); + + pw.println(""); + pw.println(""); + } + } + + private void outputResultsTable(List results, PrintWriter pw, int row) + { + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + + for (Result r : results) + { + ++row; + + outputResult(pw, row, r); + + if (!r.subResults.isEmpty()) + { + // Only show the subresults if there are + if (!differencesOnly || !r.equal) + { + pw.println(""); + } + } + } + + pw.println(""); + pw.println("
#Updated installFresh install
 "); + outputResultsTable(r.subResults, pw, row); + pw.println("
"); + } + + private void outputResult(PrintWriter pw, int row, Result r) + { + if (differencesOnly && r.equal) + { + return; + } + pw.println(""); + + pw.println(""+row+""); + + String p1 = (r.p1 == null) ? "" : r.p1.toString(); + String p1Abbr = abbreviate(p1, maxPathDisplayLength); + String p2 = (r.p2 == null) ? "" : r.p2.toString(); + String p2Abbr = abbreviate(p2, maxPathDisplayLength); + + // TODO: URL/HTML-encode as appropriate + String diffClass; + if (r.equal) + { + if (r.subResults.isEmpty()) + { + // Result refers to a normal file or directory. + diffClass = "info"; + } + else + { + // File is a special archive, but no differences in the sub-results are considered important. + diffClass = "warning"; + } + } + else + { + // The file/directory appears different in each tree. If it is a special archive, then there + // are differences that we care about. + diffClass = "danger"; + } + + pw.println( + String.format("%s%s", + diffClass, + p1, + p1Abbr, + diffClass, + p2, + p2Abbr)); + + pw.println(""); + } + + private String abbreviate(String str, int maxLength) + { + return (str.length() > maxLength) ? "..."+str.substring(str.length()-maxLength) : str; + } + + public void setMaxPathDisplayLength(int maxPathDisplayLength) + { + this.maxPathDisplayLength = maxPathDisplayLength; + } + + public void setDifferencesOnly(boolean differencesOnly) + { + this.differencesOnly = differencesOnly; + } +} diff --git a/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/Result.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/Result.java new file mode 100644 index 0000000000..355cebb239 --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/Result.java @@ -0,0 +1,95 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * Simple struct-style result data class. + * + * @author Matt Ward + */ +public final class Result +{ + /** + * The path within tree1 being compared (intended to be the updated Alfresco installation). + *

+ * This field will be null if the file in question appears only in the tree2. + */ + Path p1; + /** + * The path within tree2 being compared (intended to be the freshly installed equivalent of {@link #p1}). + *

+ * This field will be null if the file in question appears only in the tree1. + */ + Path p2; + /** + * Are the paths in {@link #p1} and {@link #p2} of identical content? If they refer to a directory, then + * equal in this sense is to have the same directory name. If they refer to a plain file, then equal means + * that they contain the exact same contents. + */ + boolean equal; + /** + * The root path of sub-tree1 to which {@link #subResults} refers. + * @see #subResults + */ + Path subTree1; + /** + * The root path of sub-tree2 to which {@link #subResults} refers. + * @see #subResults + */ + Path subTree2; + /** + * If p1 and p2 refer to a special archive with differences, e.g. they refer to alfresco.war, + * then a deep comparison of the archives will be performed and the results stored here. + *

+ * The paths to the expanded archives will be stored in {@link #subTree1} and {@link #subTree2} + * and all the paths in {@link #subResults} will be within those new roots. + */ + List subResults = new ArrayList<>(); + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (this.equal ? 1231 : 1237); + result = prime * result + ((this.p1 == null) ? 0 : this.p1.hashCode()); + result = prime * result + ((this.p2 == null) ? 0 : this.p2.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Result other = (Result) obj; + if (this.equal != other.equal) return false; + if (this.p1 == null) + { + if (other.p1 != null) return false; + } + else if (!this.p1.equals(other.p1)) return false; + if (this.p2 == null) + { + if (other.p2 != null) return false; + } + else if (!this.p2.equals(other.p2)) return false; + return true; + } + + @Override + public String toString() + { + return String.format("Result[p1=%s, p2=%s, equal=%b]", p1, p2, equal); + } +} diff --git a/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ResultFormatter.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ResultFormatter.java new file mode 100644 index 0000000000..6eddc194a1 --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ResultFormatter.java @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp; + +import java.io.OutputStream; +import java.util.Collection; + +/** + * Format a set of {@link Result} objects. + * + * @author Matt Ward + */ +public interface ResultFormatter +{ + /** + * Format the result set to the supplied {@link OutputStream}. The caller + * must take care of creating and destroying the OutputStream correctly. + * + * @param results The results to format. + * @param out The stream to format the results to. + */ + void format(ResultSet results, OutputStream out); +} diff --git a/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ResultSet.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ResultSet.java new file mode 100644 index 0000000000..fecd88a1ff --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ResultSet.java @@ -0,0 +1,40 @@ +package org.alfresco.update.tool.dircomp; + +import java.util.ArrayList; +import java.util.List; + +/* + * Copyright 2015-2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +public class ResultSet +{ + final List results = new ArrayList<>(); + final Stats stats = new Stats(); + + /** + * Class for aggregating basic statistics relating to the directory tree comparisons. + *

+ * For all counts, unless specified otherwise, if a file appears in both trees, it is + * counted only once, as it is relative files we are tracking regardless of which + * tree(s) they exist in. + */ + public static class Stats + { + /** + * The number of files (including directories) examined. + */ + int resultCount; + /** + * The number of files discovered to have differences. + */ + int differenceCount; + /** + * The number of files that were completely ignored due to being in the ignore list. + */ + int ignoredFileCount; + } +} diff --git a/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/SortedPathSet.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/SortedPathSet.java new file mode 100644 index 0000000000..52c7087cb6 --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/SortedPathSet.java @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp; + +import java.nio.file.Path; +import java.util.TreeSet; + +/** + * Sorted set of {@link Path} objects that provides consistent + * cross-platform sort order. + * + * @see java.util.TreeSet + * @author Matt Ward + */ +public class SortedPathSet extends TreeSet +{ + private static final long serialVersionUID = 1L; + + public SortedPathSet() + { + super(new CaseSensitivePathComparator()); + } +} diff --git a/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/exception/FileTreeCompareException.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/exception/FileTreeCompareException.java new file mode 100644 index 0000000000..ebf1e4545f --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/exception/FileTreeCompareException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp.exception; + +import org.alfresco.update.tool.dircomp.FileTreeCompare; + +/** + * Exception class representing failures during file tree comparison. + * + * @see FileTreeCompare + * @author Matt Ward + */ +public class FileTreeCompareException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public FileTreeCompareException(String message) + { + super(message); + } + + public FileTreeCompareException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/FileTreeCompareImplTest.java b/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/FileTreeCompareImplTest.java new file mode 100644 index 0000000000..5abde16cf1 --- /dev/null +++ b/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/FileTreeCompareImplTest.java @@ -0,0 +1,298 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.util.AntPathMatcher; + +/** + * Tests for the {@link FileTreeCompareImpl} class. + * + * @author Matt Ward + */ +public class FileTreeCompareImplTest +{ + FileTreeCompareImpl comparator; + + @Before + public void setUp() throws Exception + { + comparator = new FileTreeCompareImpl(); + } + + @Test + public void canGetSortedPathSet() throws IOException + { + Path tree = pathFromClasspath("dir_compare/simple_file_folders/tree1"); + SortedPathSet paths = comparator.sortedPaths(tree); + Iterator it = paths.iterator(); + + System.out.println("Paths:"); + for (Path p : paths) + { + System.out.println("\t"+p); + } + + assertEquals(11, paths.size()); + + assertEquals("a", unixPathStr(tree, it.next())); + assertEquals("b", unixPathStr(tree, it.next())); + assertEquals("b/blah.txt", unixPathStr(tree, it.next())); + assertEquals("c", unixPathStr(tree, it.next())); + assertEquals("c/c1", unixPathStr(tree, it.next())); + assertEquals("c/c1/commands.bat", unixPathStr(tree, it.next())); + assertEquals("c/c1/commands.sh", unixPathStr(tree, it.next())); + assertEquals("c/c2", unixPathStr(tree, it.next())); + assertEquals("c/c2/Aardvark.java", unixPathStr(tree, it.next())); + assertEquals("c/c2/Banana.java", unixPathStr(tree, it.next())); + assertEquals("d", unixPathStr(tree, it.next())); + } + + private String unixPathStr(Path root, Path path) + { + // Allow test to run on Windows also + String pathStr = path.toString(); + pathStr = pathStr.replace(File.separatorChar, '/'); + return pathStr; + } + + @Test + public void canDiffSimpleTreesOfFilesAndFolders() + { + Path tree1 = pathFromClasspath("dir_compare/simple_file_folders/tree1"); + Path tree2 = pathFromClasspath("dir_compare/simple_file_folders/tree2"); + + ResultSet resultSet = comparator.compare(tree1, tree2); + + System.out.println("Comparison results:"); + for (Result r : resultSet.results) + { + System.out.println("\t"+r); + } + + // One result for each relative file/folder + assertEquals(13, resultSet.results.size()); + assertEquals(13, resultSet.stats.resultCount); + + Iterator rit = resultSet.results.iterator(); + // TODO: currently all of the files are in one, other or both but where they + // are in both, the file *contents* are identical. + // TODO: evolve test data and functionality to cope with different file contents. + assertResultEquals(tree1.resolve("a"), tree2.resolve("a"), true, rit.next()); + assertResultEquals(null, tree2.resolve("a/story.txt"), false, rit.next()); + assertResultEquals(tree1.resolve("b"), tree2.resolve("b"), true, rit.next()); + assertResultEquals(tree1.resolve("b/blah.txt"), tree2.resolve("b/blah.txt"), true, rit.next()); + assertResultEquals(tree1.resolve("c"), tree2.resolve("c"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c1"), tree2.resolve("c/c1"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c1/commands.bat"), tree2.resolve("c/c1/commands.bat"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c1/commands.sh"), tree2.resolve("c/c1/commands.sh"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c2"), tree2.resolve("c/c2"), true, rit.next()); + // Aardvark.java appears in both trees but is not the same! + assertResultEquals(tree1.resolve("c/c2/Aardvark.java"), tree2.resolve("c/c2/Aardvark.java"), false, rit.next()); + assertResultEquals(tree1.resolve("c/c2/Banana.java"), null, false, rit.next()); + assertResultEquals(tree1.resolve("d"), null, false, rit.next()); + assertResultEquals(null, tree2.resolve("e"), false, rit.next()); + } + + /** + * A "learning test" allowing me to check my assumptions and document the expected behaviour. + */ + @Test + public void testAntPathMatcher() + { + AntPathMatcher matcher = new AntPathMatcher(); + assertTrue(matcher.match("**/common/lib/**/*.pc", "prefix/common/lib/pkgconfig/ImageMagick++-6.Q16.pc")); + assertFalse(matcher.match("**/common/lib/**/*.pc", "/absolute/prefix/common/lib/pkgconfig/ImageMagick++-6.Q16.pc")); + assertTrue(matcher.match("/**/common/lib/**/*.pc", "/absolute/prefix/common/lib/pkgconfig/ImageMagick++-6.Q16.pc")); + assertTrue(matcher.match("common/lib/**/*.pc", "common/lib/pkgconfig/Wand.pc")); + assertTrue(matcher.match("**/*.pc", "common/lib/pkgconfig/Wand.pc")); + assertFalse(matcher.match("*.pc", "common/lib/pkgconfig/Wand.pc")); + + assertTrue(matcher.match("libreoffice.app/Contents/Resources/bootstraprc", "libreoffice.app/Contents/Resources/bootstraprc")); + assertTrue(matcher.match("*.sh", "alfresco.sh")); + assertFalse(matcher.match("*.sh", "a/different/alfresco.sh")); + + // What about path separators? + assertTrue(matcher.match("**\\common\\lib\\**\\*.pc", "prefix\\common\\lib\\pkgconfig\\ImageMagick++-6.Q16.pc")); + assertTrue(matcher.match("\\**\\common\\lib\\**\\*.pc", "\\absolute\\prefix\\common\\lib\\pkgconfig\\ImageMagick++-6.Q16.pc")); + + // Path separator must be set before this will work + assertFalse(matcher.match("**/common/lib/**/*.pc", "prefix\\common\\lib\\pkgconfig\\ImageMagick++-6.Q16.pc")); + matcher.setPathSeparator("\\"); + assertTrue(matcher.match("**/common/lib/**/*.pc", "prefix\\common\\lib\\pkgconfig\\ImageMagick++-6.Q16.pc")); + } + + @Test + public void canIgnoreSpecifiedPaths() + { + Path tree1 = pathFromClasspath("dir_compare/simple_file_folders/tree1"); + Path tree2 = pathFromClasspath("dir_compare/simple_file_folders/tree2"); + + Set ignorePaths = new HashSet<>(); + ignorePaths.add("b/blah.txt"); + ignorePaths.add("c/c2/**"); + ignorePaths.add("d/**"); + ignorePaths.add("e/**"); + comparator = new FileTreeCompareImpl(ignorePaths); + + // Perform the comparison + ResultSet resultSet = comparator.compare(tree1, tree2); + + System.out.println("Comparison results:"); + for (Result r : resultSet.results) + { + System.out.println("\t"+r); + } + + Iterator rit = resultSet.results.iterator(); + assertResultEquals(tree1.resolve("a"), tree2.resolve("a"), true, rit.next()); + assertResultEquals(null, tree2.resolve("a/story.txt"), false, rit.next()); + assertResultEquals(tree1.resolve("b"), tree2.resolve("b"), true, rit.next()); + // No b/blah.txt here. + assertResultEquals(tree1.resolve("c"), tree2.resolve("c"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c1"), tree2.resolve("c/c1"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c1/commands.bat"), tree2.resolve("c/c1/commands.bat"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c1/commands.sh"), tree2.resolve("c/c1/commands.sh"), true, rit.next()); + // No c/c2, c/c2/Aardvark.java, c/c2/Banana.java, d or e here. + + List results = resultSet.results; + assertResultNotPresent(tree1.resolve("b/blah.txt"), tree2.resolve("b/blah.txt"), true, results); + assertResultNotPresent(tree1.resolve("c/c2"), tree2.resolve("c/c2"), true, results); + assertResultNotPresent(tree1.resolve("c/c2/Aardvark.java"), tree2.resolve("c/c2/Aardvark.java"), false, results); + assertResultNotPresent(tree1.resolve("c/c2/Banana.java"), null, false, results); + assertResultNotPresent(tree1.resolve("d"), null, false, results); + assertResultNotPresent(null, tree2.resolve("e"), false, results); + assertEquals(7, results.size()); + + // TODO: What about paths within war/jar/zip files? + } + + @Test + public void canDiffTreesContainingWarFiles() + { + Path tree1 = pathFromClasspath("dir_compare/file_folders_plus_war/tree1"); + Path tree2 = pathFromClasspath("dir_compare/file_folders_plus_war/tree2"); + + ResultSet resultSet = comparator.compare(tree1, tree2); + + System.out.println("Comparison results:"); + for (Result r : resultSet.results) + { + System.out.println("\t"+r); + } + + // The 14 top-level results + 17 sub-results. + assertEquals(31, resultSet.stats.resultCount); + + // One result for each relative file/folder + assertEquals(14, resultSet.results.size()); + + + Iterator rit = resultSet.results.iterator(); + // TODO: currently all of the files are in one, other or both but where they + // are in both, the file *contents* are identical. + // TODO: evolve test data and functionality to cope with different file contents. + assertResultEquals(tree1.resolve("a"), tree2.resolve("a"), true, rit.next()); + assertResultEquals(null, tree2.resolve("a/story.txt"), false, rit.next()); + assertResultEquals(tree1.resolve("b"), tree2.resolve("b"), true, rit.next()); + + // Examine the results of the war file comparison + Result result = rit.next(); + // The WAR files are different. + assertResultEquals( + tree1.resolve("b/alfresco-testdata-webapp.war"), + tree2.resolve("b/alfresco-testdata-webapp.war"), + false, + result); + List subResults = result.subResults; + System.out.println("subResults:"); + for (Result r : subResults) + { + System.out.println("\t"+r); + } + Iterator subIt = subResults.iterator(); + Path subTree1 = result.subTree1; + Path subTree2 = result.subTree2; + assertEquals(17, subResults.size()); + assertResultEquals(subTree1.resolve("META-INF"), subTree2.resolve("META-INF"), true, subIt.next()); + assertResultEquals(subTree1.resolve("META-INF/MANIFEST.MF"), subTree2.resolve("META-INF/MANIFEST.MF"), false, subIt.next()); + assertResultEquals(subTree1.resolve("META-INF/maven"), subTree2.resolve("META-INF/maven"), true, subIt.next()); + assertResultEquals(subTree1.resolve("META-INF/maven/org.alfresco.dummy"), subTree2.resolve("META-INF/maven/org.alfresco.dummy"), true, subIt.next()); + assertResultEquals(subTree1.resolve("META-INF/maven/org.alfresco.dummy/alfresco-testdata-webapp"), subTree2.resolve("META-INF/maven/org.alfresco.dummy/alfresco-testdata-webapp"), true, subIt.next()); + assertResultEquals(subTree1.resolve("META-INF/maven/org.alfresco.dummy/alfresco-testdata-webapp/pom.properties"), subTree2.resolve("META-INF/maven/org.alfresco.dummy/alfresco-testdata-webapp/pom.properties"), false, subIt.next()); + assertResultEquals(subTree1.resolve("META-INF/maven/org.alfresco.dummy/alfresco-testdata-webapp/pom.xml"), subTree2.resolve("META-INF/maven/org.alfresco.dummy/alfresco-testdata-webapp/pom.xml"), true, subIt.next()); + assertResultEquals(subTree1.resolve("WEB-INF"), subTree2.resolve("WEB-INF"), true, subIt.next()); + assertResultEquals(subTree1.resolve("WEB-INF/classes"), subTree2.resolve("WEB-INF/classes"), true, subIt.next()); + assertResultEquals(subTree1.resolve("WEB-INF/classes/org"), subTree2.resolve("WEB-INF/classes/org"), true, subIt.next()); + assertResultEquals(subTree1.resolve("WEB-INF/classes/org/alfresco"), subTree2.resolve("WEB-INF/classes/org/alfresco"), true, subIt.next()); + assertResultEquals(subTree1.resolve("WEB-INF/classes/org/alfresco/testdata"), subTree2.resolve("WEB-INF/classes/org/alfresco/testdata"), true, subIt.next()); + assertResultEquals(subTree1.resolve("WEB-INF/classes/org/alfresco/testdata/webapp"), subTree2.resolve("WEB-INF/classes/org/alfresco/testdata/webapp"), true, subIt.next()); + assertResultEquals(null, subTree2.resolve("WEB-INF/classes/org/alfresco/testdata/webapp/Another.class"), false, subIt.next()); + assertResultEquals(subTree1.resolve("WEB-INF/classes/org/alfresco/testdata/webapp/ExampleJavaClass.class"), subTree2.resolve("WEB-INF/classes/org/alfresco/testdata/webapp/ExampleJavaClass.class"), true, subIt.next()); + assertResultEquals(subTree1.resolve("WEB-INF/web.xml"), subTree2.resolve("WEB-INF/web.xml"), true, subIt.next()); + assertResultEquals(subTree1.resolve("index.jsp"), subTree2.resolve("index.jsp"), false, subIt.next()); + + // Back up to the top-level comparisons + assertResultEquals(tree1.resolve("b/blah.txt"), tree2.resolve("b/blah.txt"), true, rit.next()); + assertResultEquals(tree1.resolve("c"), tree2.resolve("c"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c1"), tree2.resolve("c/c1"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c1/commands.bat"), tree2.resolve("c/c1/commands.bat"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c1/commands.sh"), tree2.resolve("c/c1/commands.sh"), true, rit.next()); + assertResultEquals(tree1.resolve("c/c2"), tree2.resolve("c/c2"), true, rit.next()); + // Aardvark.java appears in both trees but is not the same! + assertResultEquals(tree1.resolve("c/c2/Aardvark.java"), tree2.resolve("c/c2/Aardvark.java"), false, rit.next()); + assertResultEquals(tree1.resolve("c/c2/Banana.java"), null, false, rit.next()); + assertResultEquals(tree1.resolve("d"), null, false, rit.next()); + assertResultEquals(null, tree2.resolve("e"), false, rit.next()); + } + + private void assertResultNotPresent(Path p1, Path p2, boolean contentEqual, List results) + { + Result r = new Result(); + r.p1 = p1; + r.p2 = p2; + r.equal = contentEqual; + assertFalse("Result should not be present: "+r, results.contains(r)); + } + + private void assertResultEquals(Path p1, Path p2, boolean contentEqual, Result result) + { + Result expected = new Result(); + expected.p1 = p1; + expected.p2 = p2; + expected.equal = contentEqual; + assertEquals(expected, result); + } + + private Path pathFromClasspath(String path) + { + try + { + return Paths.get(getClass().getClassLoader().getResource(path).toURI()); + } + catch (URISyntaxException error) + { + throw new RuntimeException(""); + } + } +} diff --git a/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/HtmlResultFormatterTest.java b/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/HtmlResultFormatterTest.java new file mode 100644 index 0000000000..09d7919ba4 --- /dev/null +++ b/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/HtmlResultFormatterTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2016 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.update.tool.dircomp; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.springframework.util.AntPathMatcher; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests for the {@link HtmlResultFormatter} class. + *

+ * TODO: currently these aren't tests so much as useful utilities to help with manual testing. + * + * @author Matt Ward + */ +public class HtmlResultFormatterTest +{ + @Before + public void setUp() throws Exception + { + } + + @Test + public void canFormatToHTML() throws IOException + { + ResultSet resultSet = new ResultSet(); + List results = resultSet.results; + addResult(results, "/t1/a", "/t2/a", true); + addResult(results, "/t1/a/b", "/t2/a/b", true); + addResult(results, "/t1/a/c", "/t2/a/c", true); + addResult(results, "/t1/a/b/c/something.txt", "/t2/a/b/c/something.txt", true); + addResult(results, "/t1/a/b/c/another.txt", "/t2/a/b/c/another.txt", false); + addResult(results, null, "/t2/a/b/c/blah.txt", false); + addResult(results, "/t1/dir-only-in-p1", null, false); + addResult(results, null, "/t2/dir-only-in-p2", false); + + try(ByteArrayOutputStream os = new ByteArrayOutputStream()) + { + HtmlResultFormatter formatter = new HtmlResultFormatter(); + formatter.format(resultSet, os); + System.out.println(os.toString()); + + // Uncomment to write to file +// Path file = Files.createTempFile(getClass().getSimpleName(), ".html"); +// FileUtils.write(file.toFile(), os.toString()); +// System.out.println("File: "+file); + } + } + + @Ignore + @Test + public void bigDiff() throws IOException + { + Path path1 = Paths.get("/Users/MWard/dev2/alf-installs/alf-5.1-b667"); + Path path2 = Paths.get("/Users/MWard/dev2/alf-installs/alf-5.1-b669"); + + Set ignores = new HashSet<>(); + ignores.add("alf_data/postgresql/**"); + ignores.add("META-INF/MANIFEST.MF"); + ignores.add("META-INF/maven/**"); + ignores.add("README.txt"); + + // Temporary ignores, until we sort out custom diffing or pre-processing before diffs. + ignores.add("**/*.sh"); + ignores.add("**/*.bat"); + ignores.add("uninstall.app/**"); + ignores.add("common/bin/**"); + ignores.add("common/include/**"); + ignores.add("common/lib/**/*.pc"); + ignores.add("common/lib/**/*.la"); + ignores.add("libreoffice.app/Contents/Resources/bootstraprc"); + ignores.add("postgresql/bin/**"); + + FileTreeCompare comparator = new FileTreeCompareImpl(ignores); + ResultSet resultSet = comparator.compare(path1, path2); + + Path file = Files.createTempFile(getClass().getSimpleName(), ".html"); + HtmlResultFormatter formatter = new HtmlResultFormatter(); + formatter.setDifferencesOnly(true); + try(FileOutputStream fos = new FileOutputStream(file.toFile()); + BufferedOutputStream bos = new BufferedOutputStream(fos)) + { + formatter.format(resultSet, bos); + } + System.out.println("File: "+file); + } + + private void addResult(List results, String p1, String p2, boolean contentMatch) + { + Result r = new Result(); + r.p1 = p1 != null ? Paths.get(p1) : null; + r.p2 = p2 != null ? Paths.get(p2) : null; + r.equal = contentMatch; + results.add(r); + } +} diff --git a/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/b/alfresco-testdata-webapp.war b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/b/alfresco-testdata-webapp.war new file mode 100644 index 0000000000000000000000000000000000000000..8f1c6ac5f2deabd1b77d898a424608942540e5d7 GIT binary patch literal 3162 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 TkUInd2R;r!*~Nf%hd}-V9Nxz3 literal 0 HcmV?d00001 diff --git a/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/b/blah.txt b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/b/blah.txt new file mode 100644 index 0000000000..e508b95435 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/b/blah.txt @@ -0,0 +1,2 @@ +Sample content +More sample content diff --git a/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c1/commands.bat b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c1/commands.bat new file mode 100644 index 0000000000..fb7b3bb193 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c1/commands.bat @@ -0,0 +1,2 @@ +echo Hello +echo World diff --git a/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c1/commands.sh b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c1/commands.sh new file mode 100644 index 0000000000..35d5edb327 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c1/commands.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo hello +echo world diff --git a/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c2/Aardvark.java b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c2/Aardvark.java new file mode 100644 index 0000000000..bf3d78b9f5 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/c/c2/Aardvark.java @@ -0,0 +1,7 @@ +public class Aardvark +{ + public static void main(String[] args) + { + System.out.println("Hello Aardvark!"); + } +} diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/b/blah.txt b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/b/blah.txt new file mode 100644 index 0000000000..e508b95435 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/b/blah.txt @@ -0,0 +1,2 @@ +Sample content +More sample content diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c1/commands.bat b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c1/commands.bat new file mode 100644 index 0000000000..fb7b3bb193 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c1/commands.bat @@ -0,0 +1,2 @@ +echo Hello +echo World diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c1/commands.sh b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c1/commands.sh new file mode 100644 index 0000000000..35d5edb327 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c1/commands.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo hello +echo world diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c2/Aardvark.java b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c2/Aardvark.java new file mode 100644 index 0000000000..5733268500 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c2/Aardvark.java @@ -0,0 +1,7 @@ +public class Aardvark +{ + public static void main(String[] args) + { + System.out.println("Aardvark!"); + } +} diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c2/Banana.java b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c2/Banana.java new file mode 100644 index 0000000000..a9d5570d63 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree1/c/c2/Banana.java @@ -0,0 +1,7 @@ +public class Aardvark +{ + public static void main(String[] args) + { + System.out.println("Banana!"); + } +} diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/a/story.txt b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/a/story.txt new file mode 100644 index 0000000000..69c3b8a81a --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/a/story.txt @@ -0,0 +1,5 @@ +Once upon +A time +Lived +A +Troll diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/b/blah.txt b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/b/blah.txt new file mode 100644 index 0000000000..e508b95435 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/b/blah.txt @@ -0,0 +1,2 @@ +Sample content +More sample content diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c1/commands.bat b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c1/commands.bat new file mode 100644 index 0000000000..fb7b3bb193 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c1/commands.bat @@ -0,0 +1,2 @@ +echo Hello +echo World diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c1/commands.sh b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c1/commands.sh new file mode 100644 index 0000000000..35d5edb327 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c1/commands.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo hello +echo world diff --git a/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c2/Aardvark.java b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c2/Aardvark.java new file mode 100644 index 0000000000..bf3d78b9f5 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/simple_file_folders/tree2/c/c2/Aardvark.java @@ -0,0 +1,7 @@ +public class Aardvark +{ + public static void main(String[] args) + { + System.out.println("Hello Aardvark!"); + } +}