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
This commit is contained in:
Mark Rogers
2016-03-15 11:44:11 +00:00
parent ef4b24d2a6
commit ca1409b55b
34 changed files with 1199 additions and 0 deletions

View File

@@ -53,6 +53,37 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.schlichtherle.truezip</groupId>
<artifactId>truezip-driver-zip</artifactId>
<version>7.7.9</version>
</dependency>
<dependency>
<groupId>de.schlichtherle.truezip</groupId>
<artifactId>truezip-file</artifactId>
<version>7.7.9</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
<build>

View File

@@ -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<Path>
{
@Override
public int compare(Path p1, Path p2)
{
String pathStr1 = p1.toString();
String pathStr2 = p2.toString();
return pathStr1.compareTo(pathStr2);
}
}

View File

@@ -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);
}

View File

@@ -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<String> ignorePaths = new HashSet<>();
private final AntPathMatcher pathMatcher = new AntPathMatcher(File.separator);
public FileTreeCompareImpl()
{
this(new HashSet<String>());
}
public FileTreeCompareImpl(Set<String> 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<Result> 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.
* <p>
* 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);
}
}
}
}

View File

@@ -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("<!DOCTYPE HTML>");
pw.println("<html>");
pw.println("<head>");
pw.println("<title>File tree comparison results</title>");
pw.println("<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css\" integrity=\"sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7\" crossorigin=\"anonymous\">");
pw.println("</head>");
pw.println("<body>");
pw.println("<p>Files examined: <strong>"+resultSet.stats.resultCount+"</strong></p>");
pw.println("<p>Files with differences: <strong>"+resultSet.stats.differenceCount+"</strong></p>");
pw.println("<p>Ignored files: <strong>"+resultSet.stats.ignoredFileCount+"</strong></p>");
String passOrFail;
if (resultSet.stats.differenceCount > 0)
{
passOrFail = "<span style=\"color: red\"><strong>FAILED</strong></span>";
}
else
{
passOrFail = "<span style=\"color: green\"><strong>PASSED</strong></span>";
}
pw.println("<p>Status: "+passOrFail+"</p>");
outputResultsTable(resultSet.results, pw, 0);
pw.println("</body>");
pw.println("</html>");
}
}
private void outputResultsTable(List<Result> results, PrintWriter pw, int row)
{
pw.println("<table class=\"table table-striped table-hover\">");
pw.println("<thead>");
pw.println("<tr>");
pw.println("<th>#</th>");
pw.println("<th>Updated install</th>");
pw.println("<th>Fresh install</th>");
pw.println("</tr>");
pw.println("</thead>");
pw.println("<tbody>");
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("<tr><td>&nbsp;</td><td colspan=\"2\">");
outputResultsTable(r.subResults, pw, row);
pw.println("</td></tr>");
}
}
}
pw.println("</tbody>");
pw.println("</table>");
}
private void outputResult(PrintWriter pw, int row, Result r)
{
if (differencesOnly && r.equal)
{
return;
}
pw.println("<tr>");
pw.println("<td>"+row+"</td>");
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("<td class=\"%s\"><a href=\"file://%s\">%s</a></td><td class=\"%s\"><a href=\"file://%s\">%s</a></td>",
diffClass,
p1,
p1Abbr,
diffClass,
p2,
p2Abbr));
pw.println("</tr>");
}
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;
}
}

View File

@@ -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).
* <p>
* This field will be <code>null</code> 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}).
* <p>
* This field will be <code>null</code> 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.
* <p>
* 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<Result> 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);
}
}

View File

@@ -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);
}

View File

@@ -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<Result> results = new ArrayList<>();
final Stats stats = new Stats();
/**
* Class for aggregating basic statistics relating to the directory tree comparisons.
* <p>
* 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;
}
}

View File

@@ -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<Path>
{
private static final long serialVersionUID = 1L;
public SortedPathSet()
{
super(new CaseSensitivePathComparator());
}
}

View File

@@ -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);
}
}

View File

@@ -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<Path> 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<Result> 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<String> 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<Result> 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<Result> 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<Result> 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<Result> subResults = result.subResults;
System.out.println("subResults:");
for (Result r : subResults)
{
System.out.println("\t"+r);
}
Iterator<Result> 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<Result> 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("");
}
}
}

View File

@@ -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.
* <p>
* 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<Result> 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<String> 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<Result> 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);
}
}

View File

@@ -0,0 +1,2 @@
Sample content
More sample content

View File

@@ -0,0 +1,4 @@
#!/bin/bash
echo hello
echo world

View File

@@ -0,0 +1,7 @@
public class Aardvark
{
public static void main(String[] args)
{
System.out.println("Aardvark!");
}
}

View File

@@ -0,0 +1,7 @@
public class Aardvark
{
public static void main(String[] args)
{
System.out.println("Banana!");
}
}

View File

@@ -0,0 +1,5 @@
Once upon
A time
Lived
A
Troll

View File

@@ -0,0 +1,2 @@
Sample content
More sample content

View File

@@ -0,0 +1,4 @@
#!/bin/bash
echo hello
echo world

View File

@@ -0,0 +1,7 @@
public class Aardvark
{
public static void main(String[] args)
{
System.out.println("Hello Aardvark!");
}
}

View File

@@ -0,0 +1,2 @@
Sample content
More sample content

View File

@@ -0,0 +1,2 @@
echo Hello
echo World

View File

@@ -0,0 +1,4 @@
#!/bin/bash
echo hello
echo world

View File

@@ -0,0 +1,7 @@
public class Aardvark
{
public static void main(String[] args)
{
System.out.println("Aardvark!");
}
}

View File

@@ -0,0 +1,7 @@
public class Aardvark
{
public static void main(String[] args)
{
System.out.println("Banana!");
}
}

View File

@@ -0,0 +1,5 @@
Once upon
A time
Lived
A
Troll

View File

@@ -0,0 +1,2 @@
Sample content
More sample content

View File

@@ -0,0 +1,2 @@
echo Hello
echo World

View File

@@ -0,0 +1,4 @@
#!/bin/bash
echo hello
echo world

View File

@@ -0,0 +1,7 @@
public class Aardvark
{
public static void main(String[] args)
{
System.out.println("Hello Aardvark!");
}
}