diff --git a/enterprise-update-test/mer.bat b/enterprise-update-test/mer.bat new file mode 100644 index 0000000000..5626a8ec94 --- /dev/null +++ b/enterprise-update-test/mer.bat @@ -0,0 +1 @@ +mvn install -Dthis.installer.location=c:\demo\alfresco-one-installer-20160203-SNAPSHOT-664-win-x64.exe -Dbase.installer.location=c:\demo\alfresco-one-installer-20160203-SNAPSHOT-664-win-x64.exe diff --git a/enterprise-update-test/pom.xml b/enterprise-update-test/pom.xml new file mode 100644 index 0000000000..e55b052fa0 --- /dev/null +++ b/enterprise-update-test/pom.xml @@ -0,0 +1,273 @@ + + 4.0.0 + alfresco-enterprise-update-test + End to end test of the installer and update + + org.alfresco + alfresco-full-installation + 5.1.1-SNAPSHOT + + + + + + + + + + ${project.build.directory}/test-data/base-alf-installation + ${project.build.directory}/test-data/this-alf-installation + + --mode unattended --alfresco_admin_password admin --jdbc_username alfresco --jdbc_password alfresco + --tomcat_server_domain ${HOSTNAME} --disable-components postgres --enable-components javaalfresco,libreofficecomponent,alfrescosolr,alfrescosolr4,aosmodule,alfrescowcmqs,alfrescogoogledocs + --alfrescocustomstack_services_startup demand + + + ${project.build.directory}/test-data/alfresco-one-update-package + + + + + + + junit + junit + 4.12 + 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 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + + unpack-update-package + generate-resources + + unpack + + + + + org.alfresco + alfresco-one-update-package + ${project.version} + true + ${unpacked.update.package} + zip + + + + + + + + + + maven-antrun-plugin + + + fetch-installer + pre-integration-test + + run + + + + + + + + + + + + + + Is base.installer.version provided ? + + + + + + + + + + + + + + + + + + + + + + Downloading Base Alfresco installer ${base.installer.version}... + + + + Installing the base Version of Alfresco... to ${base.alfresco.instance} + + + + + + + + + + + + + + + + + + + Installing newly generated version of Alfresco... + + + + + + + + + + + org.apache.ant + ant-jsch + 1.8.2 + + + ant-contrib + ant-contrib + 1.0b3 + + + ant + ant + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + default-test + test + + test + + + + **/*IntegrationTest.java + + true + alphabetical + + ${alfresco.update.package} + ${project.build.directory} + ${this.alfresco.instance} + ${base.alfresco.instance} + + + + + integration-test + integration-test + + test + + + false + ${project.build.directory} + true + alphabetical + + none + + + **/*IntegrationTest.java + + + ${project.artifactId}-${installer.version.name}.zip + ${project.build.directory} + + + + + + + + + + + + + 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..99472447ea --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/FileTreeCompareImpl.java @@ -0,0 +1,395 @@ +/* + * 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 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.alfresco.update.tool.dircomp.exception.FileTreeCompareException; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.util.AntPathMatcher; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 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 Set allowedDiffsPaths = new HashSet<>(); + private final AntPathMatcher pathMatcher = new AntPathMatcher(File.separator); + + public FileTreeCompareImpl() + { + this(null, null); + } + + public FileTreeCompareImpl(Set ignorePaths, Set allowedDiffsPaths) + { + // This config MUST be present before any TFile objects etc. are created. + TConfig config = TConfig.get(); + config.setArchiveDetector(new TArchiveDetector("war|jar|amp", new ZipDriver(IOPoolLocator.SINGLETON))); + if (ignorePaths == null) + { + // Add default ignores + ignorePaths = new HashSet<>(); + ignorePaths.add(toPlatformPath("alf_data/postgresql/**")); + ignorePaths.add(toPlatformPath("alf_data/oouser/user/**")); + ignorePaths.add(toPlatformPath("alf_data/solr/*.war")); + ignorePaths.add(toPlatformPath("common/**")); + ignorePaths.add(toPlatformPath("META-INF/MANIFEST.MF")); + ignorePaths.add(toPlatformPath("META-INF/maven/**")); + ignorePaths.add(toPlatformPath("licenses/notice.txt")); + ignorePaths.add(toPlatformPath("uninstall.app/**")); + ignorePaths.add(toPlatformPath("uninstall/**")); + ignorePaths.add(toPlatformPath("uninstall.exe")); + ignorePaths.add(toPlatformPath("uninstall.dat")); + ignorePaths.add(toPlatformPath("libreoffice.app/**")); + ignorePaths.add(toPlatformPath("libreoffice/**")); + ignorePaths.add(toPlatformPath("java/**")); + ignorePaths.add(toPlatformPath("applied-updates/**")); + ignorePaths.add(toPlatformPath("~build/**")); + ignorePaths.add(toPlatformPath("properties.ini")); + ignorePaths.add(toPlatformPath("**/log.txt")); + ignorePaths.add(toPlatformPath("**/solrcore.properties")); + ignorePaths.add(toPlatformPath("**/modifications.install")); + ignorePaths.add(toPlatformPath("tomcat/webapps/ROOT.war")); + + // Ignore for 5.1 MNT-14307 + ignorePaths.add(toPlatformPath("tomcat/shared/classes/alfresco/web-extension/share-config-custom.xml")); + + + } + if (allowedDiffsPaths == null) + { + // Add default paths where certain differences are allowed, e.g. absolute path references. + allowedDiffsPaths = new HashSet<>(); + allowedDiffsPaths.add(toPlatformPath("common/bin/**")); + allowedDiffsPaths.add(toPlatformPath("common/include/**/*.h")); + allowedDiffsPaths.add(toPlatformPath("common/lib/**/*.pc")); + allowedDiffsPaths.add(toPlatformPath("common/lib/**/*.la")); + allowedDiffsPaths.add(toPlatformPath("libreoffice.app/Contents/Resources/bootstraprc")); + allowedDiffsPaths.add(toPlatformPath("postgresql/bin/**")); + allowedDiffsPaths.add(toPlatformPath("**/*.sh")); + allowedDiffsPaths.add(toPlatformPath("**/*.bat")); + allowedDiffsPaths.add(toPlatformPath("**/*.ini")); + allowedDiffsPaths.add(toPlatformPath("**/*.properties")); + allowedDiffsPaths.add(toPlatformPath("**/*.xml")); + allowedDiffsPaths.add(toPlatformPath("**/*.sample")); + allowedDiffsPaths.add(toPlatformPath("**/*.txt")); + allowedDiffsPaths.add(toPlatformPath("tomcat/conf/Catalina/localhost/solr4.xml")); + allowedDiffsPaths.add(toPlatformPath("tomcat/conf/Catalina/localhost/solr.xml")); + } + this.ignorePaths.addAll(ignorePaths); + this.allowedDiffsPaths.addAll(allowedDiffsPaths); + } + + private String toPlatformPath(String path) + { + return path.replace("/", File.separator); + } + + @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) + { + if (pathMatchesAllowedDiffsPattern(pathToFind)) + { + File f1 = preprocessFile(tree1, result.p1.toFile()); + File f2 = preprocessFile(tree2, result.p2.toFile()); + contentMatches = FileUtils.contentEquals(f1, f2); + // Delete the files now that we no longer need them. The originals are still available. + f1.delete(); + f2.delete(); + if (contentMatches) + { + // If the preprocessed files match, then although the files didn't + // match when first compared byte-for-byte, they do match as far as we are concerned. + // But add to the stats that this is what has happened. + stats.suppressedDifferenceCount++; + } + } + else if (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 File preprocessFile(Path tree, File orig) throws IOException + { + // Create a set of replacements that we intend to make. Replacing them with + // a known token allows us to remove differences (that we're not interested in) in the files. + Map replacements = new HashMap<>(); + replacements.put(tree.toRealPath().toString(), replacementToken("comparison_root")); + + // Create a pattern for module.installDate + Pattern installDatePattern = Pattern.compile("module.installDate=.*[\n\r\f]*$"); + Pattern commentPattern = Pattern.compile("^#.*"); + + File processed = Files.createTempFile(orig.getName(), ".tmp").toFile(); + try(Reader r = new FileReader(orig); + BufferedReader br = new BufferedReader(r); + Writer w = new FileWriter(processed); + PrintWriter pw = new PrintWriter(w)) + { + String line; + + while ((line = br.readLine()) != null) + { + for (String replKey : replacements.keySet()) + { + String replVal = replacements.get(replKey); + line = line.replace(replKey, replVal); + } + Matcher m = installDatePattern.matcher(line); + if(m.matches()) + { + // replace module.installDate + line = m.replaceFirst("module.installDate="); + } + Matcher cp = commentPattern.matcher(line); + if(cp.matches()) + { + // replace module.installDate + line = "# {comment suppressed}\n"; + } + + pw.println(line); + } + } + return processed; + } + + private String replacementToken(String label) + { + return String.format("@$@$@$@${{TREE_COMPARE_%s}}", label); + } + + private boolean isSpecialArchive(Path pathToFind) + { + return pathToFind.getFileName().toString().toLowerCase().endsWith(".war") || + pathToFind.getFileName().toString().toLowerCase().endsWith(".jar") || + pathToFind.getFileName().toString().toLowerCase().endsWith(".amp"); + } + + /** + * If the set of paths to allow certain differences ({@link #allowedDiffsPaths}) + * 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 pattern in the allowedDiffsPaths set matching the path. + */ + private boolean pathMatchesAllowedDiffsPattern(String path) + { + return pathMatchesPattern(path, allowedDiffsPaths); + } + + /** + * @see #pathMatchesAllowedDiffsPattern(String) + */ + private boolean pathMatchesAllowedDiffsPattern(Path path) + { + return pathMatchesAllowedDiffsPattern(path.toString()); + } + + /** + * 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) + { + return pathMatchesPattern(path, ignorePaths); + } + + /** + * @see #pathMatchesIgnorePattern(String) + */ + private boolean pathMatchesIgnorePattern(Path path) + { + return pathMatchesIgnorePattern(path.toString()); + } + + private boolean pathMatchesPattern(String path, Set patterns) + { + for (String pattern : patterns) + { + if (pathMatcher.match(pattern, path)) + { + return true; + } + } + return false; + } + + 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..ec8fa8ebc1 --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/HtmlResultFormatter.java @@ -0,0 +1,170 @@ +/* + * 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 = 50; + private boolean differencesOnly; + + + @Override + public void format(ResultSet resultSet, OutputStream out) + { + boolean failed = resultSet.stats.differenceCount > 0; + + 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(""); + pw.println("

"); + + String passOrFail; + String alertClass; + if (failed) + { + alertClass = "alert-danger"; + passOrFail = "FAILED"; + } + else + { + alertClass = "alert-success"; + passOrFail = "PASSED"; + } + + pw.println("
\n" + + "

Fresh installation vs updated installation Diff tool results

\n" + + "
"); + pw.println("
"); + pw.println("

Status: "+passOrFail+"

"); + pw.println("

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

"); + pw.println("

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

"); + pw.println("

Files with allowed differences: "+resultSet.stats.suppressedDifferenceCount+"

"); + pw.println("

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

"); + pw.println("
"); + outputResultsTable(resultSet.results, pw, 0); + + pw.println("
"); + 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..a96a426569 --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ResultSet.java @@ -0,0 +1,44 @@ +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<>(); + public 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. + */ + public int resultCount; + /** + * The number of files discovered to have differences that are not allowed or ignored. + */ + public int differenceCount; + /** + * The number of files discovered to have differences, but the difference is allowed. + */ + public int suppressedDifferenceCount; + /** + * The number of files that were completely ignored due to being in the ignore list. + */ + public 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/ZipResultFormatter.java b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ZipResultFormatter.java new file mode 100644 index 0000000000..0ec0aac001 --- /dev/null +++ b/enterprise-update-test/src/main/java/org/alfresco/update/tool/dircomp/ZipResultFormatter.java @@ -0,0 +1,73 @@ +package org.alfresco.update.tool.dircomp; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Path; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * Format the org.alfresco.update.tool.dircomp.ResultSet as a set of files inside a zip file. + * + * Files that are equal are not added to the zip file. + * + * @author mrogers + */ +public class ZipResultFormatter implements ResultFormatter +{ + @Override + public void format(ResultSet resultSet, OutputStream out) + { + ZipOutputStream zos = (ZipOutputStream)out; + + for(Result result : resultSet.results) + { + if(!result.equal) + { + try + { + putFile(result.p1, zos); + putFile(result.p2, zos); + } + catch (IOException ie) + { + // Do nothing + } + } + } + } + + private void putFile(Path path, ZipOutputStream zos) throws IOException + { + + if(path != null) + { + byte[] buffer = new byte[1024]; + File file = path.toFile(); + if(file.isFile()) + { + ZipEntry zipEntry = new ZipEntry(getEntryName(path)); + zipEntry.setTime(file.lastModified()); + try (FileInputStream ins = new FileInputStream(file)) + { + zos.putNextEntry(zipEntry); + int len; + while ((len = ins.read(buffer)) > 0) + { + zos.write(buffer, 0, len); + } + zos.closeEntry(); + } + } + } + } + + private String getEntryName(Path path) + { + // eg differences/xml-data/foo/bar + return "differences" + path.normalize().toString().replace('\\', '/').trim(); + } + +} 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/EndToEndIntegrationTest.java b/enterprise-update-test/src/test/java/EndToEndIntegrationTest.java new file mode 100644 index 0000000000..53fec9617c --- /dev/null +++ b/enterprise-update-test/src/test/java/EndToEndIntegrationTest.java @@ -0,0 +1,303 @@ +/* + * 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. + */ + +import org.alfresco.update.tool.dircomp.FileTreeCompare; +import org.alfresco.update.tool.dircomp.FileTreeCompareImpl; +import org.alfresco.update.tool.dircomp.HtmlResultFormatter; +import org.alfresco.update.tool.dircomp.Result; +import org.alfresco.update.tool.dircomp.ResultSet; +import org.alfresco.update.tool.dircomp.ZipResultFormatter; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import java.io.*; +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.Set; +import java.util.zip.ZipOutputStream; + +import static org.junit.Assert.assertTrue; + +public class EndToEndIntegrationTest +{ + private final static String JAR_FILE_NAME = "alfresco-update-tool.jar"; + + File targetDir; + + private String getBasePath() + { + String basePath = System.getProperty("base.alfresco.instance"); + if (basePath == null) + { + basePath = "./test-data/base-alf-installation"; + } + File base = new File(targetDir, basePath); + assertTrue("base instance does not exist :" + base, base.exists()); + + return base.getAbsolutePath(); + } + + private String getThisPath() + { + String basePath = System.getProperty("this.alfresco.instance"); + if (basePath == null) + { + basePath = "./test-data/this-alf-installation"; + } + File base = new File(targetDir, basePath); + assertTrue("this instance (the one to update) does not exist :" + base, base.exists()); + + return base.getAbsolutePath(); + } + + private String getUpdatePath() + { + String basePath = System.getProperty("unpacked.update.package"); + if (basePath == null) + { + basePath = "./test-data/alfresco-one-update-package"; + } + File base = new File(targetDir, basePath); + assertTrue(base.isDirectory()); + assertTrue("the update package does not exist :" + base, base.exists()); + String[] dirs = base.list(); + assertTrue(dirs.length == 1); + + return new File(base, dirs[0]).getAbsolutePath(); + } + + private void initTargetDir() + { + String targetDir = System.getProperty("alfresco.target.dir"); + if (targetDir == null) + { + targetDir = "./target"; // test needs to be run in target dir. + } + this.targetDir = new File(targetDir); + assertTrue("target dir does not exist :" + targetDir, this.targetDir.exists()); + } + + @Before + public void setUp() throws Exception + { + initTargetDir(); + } + + + @Test + public void testEndToEndUpdate() throws Exception + { + File updateThisOne = new File(getBasePath()); + + File referenceInstance = new File(getThisPath()); + + File updatePackage = new File(getUpdatePath()); + + // Run the update + runUpdateTool(updateThisOne, updatePackage); + + // Run the diff tool + compare(referenceInstance, updateThisOne); + + } + + /** + * Run the update tool + */ + public void runUpdateTool(File instanceToUpdate, File updatePackage) throws Exception + { + // expect to find jar at "lib/alfresco-update-tool.jar" + File jar = new File(updatePackage, "lib/" + JAR_FILE_NAME); + assertTrue("lib/" + JAR_FILE_NAME, jar.exists()); + + // expect to find update resources + + String options = " --assumeyes -u " + updatePackage; + String cmd = "java -jar " + jar.getAbsolutePath() + options + " " + instanceToUpdate.getPath(); + + boolean found = runCommand( + targetDir, + cmd, + null, + 0, + "The update was successful" + ); + + assertTrue("The update was successful", found); + + } + + /** + * Run the diff tool + * + * @param freshInstallation + * @param updatedInstallation + */ + public void compare(File freshInstallation, File updatedInstallation) throws IOException + { + FileTreeCompare comparator = new FileTreeCompareImpl(); + ResultSet resultSet = comparator.compare(updatedInstallation.toPath(), freshInstallation.toPath()); + + File dircompDir = new File(targetDir, "installation-diff"); + dircompDir.mkdirs(); + + // Format the results as an HTML report. + File file = new File(dircompDir, "installation-diff-report.html"); + file.createNewFile(); + + HtmlResultFormatter formatter = new HtmlResultFormatter(); + formatter.setDifferencesOnly(true); + try(FileOutputStream fos = new FileOutputStream(file); + BufferedOutputStream bos = new BufferedOutputStream(fos)) + { + formatter.format(resultSet, bos); + } + + File zipFile = new File(dircompDir, "installation-diff-report.zip"); + zipFile.createNewFile(); + + ZipResultFormatter zformatter = new ZipResultFormatter(); + + try (FileOutputStream fos = new FileOutputStream(zipFile); + ZipOutputStream zos = new ZipOutputStream(fos)) + { + zformatter.format(resultSet, zos); + } + + + assertTrue("update test has found unexpected differences, see the installation-diff-report for further details", resultSet.stats.differenceCount == 0); + + } + + /** + * Utility/harness to allow easy testing of {@link #compare(File, File)}. + *

+ * Uncomment @Ignore, but do not check in. + * + * @throws IOException + */ + @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"); + + compare(path1.toFile(), path2.toFile()); + } + + /* + * Method to execute a command + * + * This variant of runCommand takes multiple expected messages. + * If a message is repeated twice in expectedMessage then the message must appear in + * the command line output at least twice to return true. + * + * @param targetLocation location for executing the command + * @param cmd the command to be executed + * @param input - input for command line prompts, may be null if not required + * @param expectedMessage... messages to be verified + * @param expectedReturnCode 0 for success + * @return true the messages are all found + * @throws IOException + */ + public boolean runCommand(File targetLocation, String cmd, String[] input, int expectedReturnCode, String... expectedMessage) throws IOException, InterruptedException + { + Runtime rt = Runtime.getRuntime(); + String line = null; + Process pr = rt.exec(cmd, null, targetLocation); + if(input != null && input.length > 0) + { + Input inputThread = new Input(pr.getOutputStream(), input); + inputThread.start(); + } + + ArrayList toFind = new ArrayList(); + for(int i = 0; i < expectedMessage.length; i++) + { + toFind.add(expectedMessage[i]); + } + + int found; + try (BufferedReader out = new BufferedReader(new InputStreamReader(pr.getInputStream()))) + { + while ((line = out.readLine()) != null) + { + found = -1; + for(int i = 0; i < toFind.size() ; i++) + { + if (line.contains(toFind.get(i))) + { + found = i; + } + } + System.out.println(line); + + if(found >= 0) + { + toFind.remove(found); + } + } + } + + int retCode = pr.waitFor(); + + if(retCode != expectedReturnCode) + { + System.out.println("Not expected return code expected:" + expectedReturnCode + " actual: " + retCode); + return false; + } + + if(toFind.size() == 0) + { + return true; + } + + System.out.println("Did not find expected message: " + toFind); + + return false; + } + + /** + * + */ + protected class Input extends Thread + { + OutputStream is; + String[] input; + + Input(OutputStream is, String[] input) + { + this.is = is; + this.input = input; + } + + public void run() + { + try (BufferedWriter in = new BufferedWriter(new OutputStreamWriter(is));) + { + for (String line : input) + { + in.write(line); + in.newLine(); + System.out.println("wrote : " + line); + } + } + catch (IOException e) + { + + } + } + } + + +} 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..938ef2722a --- /dev/null +++ b/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/FileTreeCompareImplTest.java @@ -0,0 +1,415 @@ +/* + * 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.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.commons.io.FileUtils; +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(new HashSet(), new HashSet()); + } + + @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")); + + // Windows matcher + // It seems that changing the path separator on an instance that's already been + // used isn't a good idea due to pattern caching. + matcher = new AntPathMatcher("\\"); + 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")); + + assertTrue(matcher.match("b\\blah.txt", "b\\blah.txt")); + } + + @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(toPlatformPath("b/blah.txt")); + ignorePaths.add(toPlatformPath("c/c2/**")); + ignorePaths.add(toPlatformPath("d/**")); + ignorePaths.add(toPlatformPath("e/**")); + comparator = new FileTreeCompareImpl(ignorePaths, new HashSet()); + + // 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? + // ...at the moment, if we specify a path of "mydir/README.txt" to be ignored, + // this will be ignored in the main tree, e.g. /mydir/README.txt but also + // within sub-trees if there is a match, e.g. /mydir/README.txt + } + + @Test + public void canSpecifyFilesThatShouldHaveCertainDifferencesAllowed() throws IOException + { + Path tree1 = pathFromClasspath("dir_compare/allowed_differences/tree1"); + Path tree2 = pathFromClasspath("dir_compare/allowed_differences/tree2"); + + // Check that two identical trees are... identical! + ResultSet resultSet = comparator.compare(tree1, tree2); + + System.out.println("Comparison results:"); + for (Result r : resultSet.results) + { + System.out.println("\t"+r); + } + assertEquals(0, resultSet.stats.differenceCount); + assertEquals(0, resultSet.stats.ignoredFileCount); + assertEquals(4, resultSet.stats.resultCount); + assertEquals(4, resultSet.results.size()); + + // Now add files that are different only in there use of tree1 and tree2's absolute paths. + File t1File = new File(tree1.toFile(), "different.txt"); + t1File.deleteOnExit(); + FileUtils.write(t1File, sampleText(tree1.toAbsolutePath().toString())); + + File t2File = new File(tree2.toFile(), "different.txt"); + t2File.deleteOnExit(); + FileUtils.write(t2File, sampleText(tree2.toAbsolutePath().toString())); + + // Now add a module.properties that are different in their "installDate" property only. + File t3File = new File(tree1.toFile(), "module.properties"); + t3File.deleteOnExit(); + Date date = new Date(); + FileUtils.write(t3File, sampleModuleProperties("2016-02-29T16\\:26\\:18.053Z")); + + File t4File = new File(tree2.toFile(), "module.properties"); + t4File.deleteOnExit(); + FileUtils.write(t4File, sampleModuleProperties("2016-02-28T14\\:30\\:14.035Z")); + + + // Perform the comparison + comparator = new FileTreeCompareImpl(new HashSet(), new HashSet()); + resultSet = comparator.compare(tree1, tree2); + System.out.println("Comparison results:"); + for (Result r : resultSet.results) + { + System.out.println("\t"+r); + } + + // We should see a difference + assertEquals(0, resultSet.stats.suppressedDifferenceCount); + assertEquals(2, resultSet.stats.differenceCount); + assertEquals(0, resultSet.stats.ignoredFileCount); + assertEquals(6, resultSet.stats.resultCount); + assertEquals(6, resultSet.results.size()); + + Iterator rit = resultSet.results.iterator(); + assertResultEquals(tree1.resolve("different.txt"), tree2.resolve("different.txt"), false, rit.next()); + + // Perform the comparison again, but after allowing the files to be different. + Set allowedDiffsPaths = new HashSet<>(); + allowedDiffsPaths.add(toPlatformPath("**/*.txt")); + allowedDiffsPaths.add(toPlatformPath("**/module.properties")); + + // Perform the comparison, this time with some allowed differences + comparator = new FileTreeCompareImpl(new HashSet(), allowedDiffsPaths); + resultSet = comparator.compare(tree1, tree2); + + // We should see a difference - but it is in the 'suppressed' list. + assertEquals(2, resultSet.stats.suppressedDifferenceCount); + assertEquals(0, resultSet.stats.differenceCount); + assertEquals(0, resultSet.stats.ignoredFileCount); + assertEquals(6, resultSet.stats.resultCount); + assertEquals(6, resultSet.results.size()); + + rit = resultSet.results.iterator(); + assertResultEquals(tree1.resolve("different.txt"), tree2.resolve("different.txt"), true, rit.next()); + } + + private String sampleText(String absPath) + { + StringBuilder sb = new StringBuilder(); + sb.append("This is some example text\n"); + sb.append("...in tree: "+absPath); + sb.append(" ...and here is some more text.\n"); + sb.append("...but wait! here's an absolute path again:"+absPath+", yes."); + sb.append("The End."); + return sb.toString(); + } + + private String sampleModuleProperties(String installDateAsString) + { + StringBuilder sb = new StringBuilder(); + sb.append("# " + installDateAsString + "\n"); + sb.append("module.id=org.alfresco.integrations.share.google.docs\n"); + sb.append("module.version=3.0.3\n"); + sb.append("module.buildnumber=4ent\n"); + sb.append("module.title=Alfresco / Google Docs Share Module\n"); + sb.append("module.description=The Share side artifacts of the Alfresco / Google Docs Integration.\n"); + sb.append("module.repo.version.min=5.0.0\n"); + sb.append("module.repo.version.max=5.99.99\n"); + sb.append("module.installState=INSTALLED\n"); + // this is the problem we are trying to solve + sb.append("module.installDate=" + installDateAsString + "\n"); + + return sb.toString(); + } + + @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(""); + } + } + + private String toPlatformPath(String path) + { + return path.replace("/", File.separator); + } +} 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..bd387646db --- /dev/null +++ b/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/HtmlResultFormatterTest.java @@ -0,0 +1,85 @@ +/* + * 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); + + resultSet.stats.suppressedDifferenceCount = 2; + resultSet.stats.differenceCount = 4; + resultSet.stats.ignoredFileCount = 0; + resultSet.stats.resultCount = results.size(); + + 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); + } + } + + 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/java/org/alfresco/update/tool/dircomp/ZipResultFormatterTest.java b/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/ZipResultFormatterTest.java new file mode 100644 index 0000000000..bca1660d28 --- /dev/null +++ b/enterprise-update-test/src/test/java/org/alfresco/update/tool/dircomp/ZipResultFormatterTest.java @@ -0,0 +1,82 @@ +package org.alfresco.update.tool.dircomp; + +import static org.junit.Assert.*; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.zip.ZipOutputStream; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link ZipResultFormatter} class. + *

+ * + * @author Mark Rogers + */ +public class ZipResultFormatterTest +{ + @Before + public void setUp() throws Exception + { + } + + @Test + public void canFormatToZip() throws IOException + { + ResultSet resultSet = new ResultSet(); + List results = resultSet.results; + + URL srcDir = getClass().getClassLoader().getResource("dir_compare/allowed_differences/tree1"); + assertNotNull(srcDir.getPath()); + + File f = new File(srcDir.getPath()); + + File[] files = f.listFiles(); + for(File file : files) + { + addResult(results, file.getAbsolutePath(), null, false); + } + + resultSet.stats.suppressedDifferenceCount = 2; + resultSet.stats.differenceCount = 4; + resultSet.stats.ignoredFileCount = 0; + resultSet.stats.resultCount = results.size(); + + ZipResultFormatter zof = new ZipResultFormatter(); + + Path file = Files.createTempFile(getClass().getSimpleName(), ".zip"); + + File zipFile = file.toFile(); + zipFile.createNewFile(); + zipFile.deleteOnExit(); + + ZipResultFormatter zformatter = new ZipResultFormatter(); + + try (FileOutputStream fos = new FileOutputStream(zipFile); + ZipOutputStream zos = new ZipOutputStream(fos)) + { + zof.format(resultSet, zos); + } + + assertTrue(zipFile.length() > 0); + + } + + 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/allowed_differences/tree1/x/story.txt b/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree1/x/story.txt new file mode 100644 index 0000000000..69c3b8a81a --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree1/x/story.txt @@ -0,0 +1,5 @@ +Once upon +A time +Lived +A +Troll diff --git a/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree1/y/blah.txt b/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree1/y/blah.txt new file mode 100644 index 0000000000..e508b95435 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree1/y/blah.txt @@ -0,0 +1,2 @@ +Sample content +More sample content diff --git a/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree2/x/story.txt b/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree2/x/story.txt new file mode 100644 index 0000000000..69c3b8a81a --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree2/x/story.txt @@ -0,0 +1,5 @@ +Once upon +A time +Lived +A +Troll diff --git a/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree2/y/blah.txt b/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree2/y/blah.txt new file mode 100644 index 0000000000..e508b95435 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/allowed_differences/tree2/y/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/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 0000000000..8f1c6ac5f2 Binary files /dev/null and b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/b/alfresco-testdata-webapp.war differ diff --git a/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/b/blah.txt b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/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/tree1/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/tree1/c/c1/commands.bat b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/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/tree1/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/tree1/c/c1/commands.sh b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/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/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/file_folders_plus_war/tree1/c/c2/Aardvark.java b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/c/c2/Aardvark.java new file mode 100644 index 0000000000..5733268500 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/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/file_folders_plus_war/tree1/c/c2/Banana.java b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree1/c/c2/Banana.java new file mode 100644 index 0000000000..a9d5570d63 --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/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/file_folders_plus_war/tree2/a/story.txt b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/a/story.txt new file mode 100644 index 0000000000..69c3b8a81a --- /dev/null +++ b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/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/file_folders_plus_war/tree2/b/alfresco-testdata-webapp.war b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/b/alfresco-testdata-webapp.war new file mode 100644 index 0000000000..af3bdb7237 Binary files /dev/null and b/enterprise-update-test/src/test/resources/dir_compare/file_folders_plus_war/tree2/b/alfresco-testdata-webapp.war differ 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!"); + } +} diff --git a/enterprise-update/assemblies/update.xml b/enterprise-update/assemblies/update.xml deleted file mode 100644 index 74674a26a3..0000000000 --- a/enterprise-update/assemblies/update.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - update - - zip - - - false - - - - - false - true - assets/web-server/webapps - alfresco.war - - org.alfresco:alfresco-platform-enterprise:war - - - - - - false - true - assets/web-server/webapps - share.war - - org.alfresco:share:war - - - - - - diff --git a/enterprise-update/assemblies/updateTool.xml b/enterprise-update/assemblies/updateTool.xml index 22fcb17693..bdbefa8ebe 100644 --- a/enterprise-update/assemblies/updateTool.xml +++ b/enterprise-update/assemblies/updateTool.xml @@ -9,44 +9,231 @@ zip - false + true - - - ${project.basedir}/target/update-contents-${installer.version.name}.zip - update - - - - - - - - false - true - bin - alfresco-update-tool.jar - - org.alfresco:alfresco-update-tool:jar - - - - - false - true + + + + ${unpacked.update.tool}/bin - true - - keep - - META-INF/** - - - org.alfresco:alfresco-update-tool-distribution:jar + *.bat + + dos + + + + ${unpacked.update.tool}/bin + + + *.sh + + unix + + 0755 + + + + + ${unpacked.alfresco.platform.distribution} + resources/distribution/platform + + package.properties + + dos + + + + ${unpacked.alfresco.one.distribution} + resources/distribution/platform + + README.txt + + dos + + + + + ${unpacked.alfresco.platform.distribution} + resources/distribution/platform_only + + README.txt + package.properties + + dos + + + + + ${unpacked.alfresco.share.distribution} + resources/distribution/share_only + + README.txt + package.properties + + dos + + + + + ${unpacked.alfresco.platform.distribution}/bin + resources/distribution/common/bin + + alfresco-mmt.jar + alfresco-spring-encryptor.jar + + keep + + + + + ${unpacked.solr4.distribution}/archive-SpacesStore/conf + resources/distribution/common/solr4/archive-SpacesStore/conf + + schema.xml + + keep + + + + + ${unpacked.org.alfresco.aos.distribution} + resources/war + + *.war + + keep + + + + + ${unpacked.org.alfresco.aos.distribution} + resources/amp/platform + + *.amp + + keep + + + + ${unpacked.update.tool}/bin + lib + + *.jar + + + + + ${unpacked.update.tool} + + + README.txt + package.properties + + dos + + + + ${unpacked.update.script} + /resources/script + + *.auas + + keep + + + + + ${unpacked.alfresco.wcmqs.distribution} + resources/amp/platform + + alfresco-wcmqs.amp + + + + + ${unpacked.alfresco.wcmqs.distribution} + resources/amp/share + + alfresco-wcmqs-share.amp + + + + + ${unpacked.alfresco.wcmqs.distribution} + resources/war + + *.war + + + + + + + + + + false + true + resources/war + alfresco.war + + org.alfresco:alfresco-platform-enterprise:war - + + + + false + true + resources/war + share.war + + org.alfresco:share:war + + + + + + false + true + resources/war + solr4.war + + org.alfresco:alfresco-solr4:war + + + + + + false + true + resources/amp/platform + + org.alfresco.integrations:alfresco-googledocs-repo:amp:enterprise:* + + + + + + false + true + resources/amp/platform + alfresco-share-services.amp + + org.alfresco:alfresco-share-services:amp:* + + + + + false + true + resources/amp/share + + org.alfresco.integrations:alfresco-googledocs-share:amp:enterprise:* + + + - + diff --git a/enterprise-update/pom.xml b/enterprise-update/pom.xml index b4b5b0f5ce..72f58ae528 100644 --- a/enterprise-update/pom.xml +++ b/enterprise-update/pom.xml @@ -7,12 +7,24 @@ alfresco-full-installation 5.1.2-SNAPSHOT - alfresco-enterprise-update-package + alfresco-one-update-package jar - Alfresco Enterprise Update Package + Alfresco One Update Package + + ${project.build.directory}/dependency/alfresco-update-tool-distribution-${alfresco.updatetool.version} + ${project.build.directory}/dependency/alfresco-one-platform-distributionzip-${alfresco.platform.version} + ${project.build.directory}/dependency/alfresco-share-distribution-${alfresco.share.version} + ${project.build.directory}/dependency/wcmqs-${project.version} + ${project.build.directory}/dependency/aos-${alfresco.aos-module.version} + ${project.build.directory}/dependency/alfresco-one-distribution-${project.version} + ${project.build.directory}/dependency/solr4-distribution-${project.version} + + + ${project.build.directory}/classes/script/${version.major}.${version.minor} + - + org.alfresco alfresco-platform-enterprise @@ -20,6 +32,7 @@ war + org.alfresco share @@ -27,22 +40,88 @@ war - + org.alfresco - alfresco-update-tool - ${alfresco.updatetool.version} - jar + alfresco-solr4 + ${alfresco.platform.version} + war - + + - org.alfresco - alfresco-update-tool-distribution - ${alfresco.updatetool.version} - jar + org.alfresco.integrations + alfresco-googledocs-repo + ${alfresco.googledocs.version} + enterprise + amp + + + + org.alfresco.integrations + alfresco-googledocs-share + ${alfresco.googledocs.version} + enterprise + amp - + + + org.alfresco + alfresco-share-services + ${alfresco.share.version} + amp + + + + org.alfresco + alfresco-wcmqs-distribution + ${alfresco.share.version} + zip + + + + + org.alfresco + alfresco-update-tool-distribution + ${alfresco.updatetool.version} + zip + + + + + org.alfresco + alfresco-one-platform-distributionzip + ${alfresco.platform.version} + zip + + + + + org.alfresco + alfresco-share-distribution + ${alfresco.share.version} + zip + + + + + org.alfresco + alfresco-one-distribution + ${project.version} + jar + + + + + org.alfresco + alfresco-solr4 + ${alfresco.platform.version} + config + zip + + + junit junit @@ -53,24 +132,195 @@ ${project.artifactId}-${installer.version.name} + src/main/java + + + src/main/resources + + + src/test/java + + + src/test/resources + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-wcmqs-distribution + generate-resources + + unpack + + + + + org.alfresco + alfresco-wcmqs-distribution + ${alfresco.share.version} + zip + ${unpacked.alfresco.wcmqs.distribution} + + + + + + unpack-update-tool-distribution + generate-resources + + unpack + + + + + org.alfresco + alfresco-update-tool-distribution + ${alfresco.updatetool.version} + true + zip + + + + + + unpack-alfresco-enterprise-distribution + generate-resources + + unpack + + + + + org.alfresco + alfresco-one-platform-distributionzip + ${alfresco.platform.version} + true + zip + + + + + + unpack-alfresco-share-distribution + generate-resources + + unpack + + + + + org.alfresco + alfresco-share-distribution + ${alfresco.share.version} + true + zip + + + + + + + unpack-aos-distribution + generate-resources + + unpack + + + + + org.alfresco.aos-module + alfresco-aos-module-distributionzip + ${alfresco.aos-module.version} + true + zip + ${unpacked.org.alfresco.aos.distribution} + + + + + + + unpack-one-distribution + generate-resources + + unpack + + + + + org.alfresco + alfresco-one-distribution + ${project.version} + true + jar + ${unpacked.alfresco.one.distribution} + + + + + + + unpack-solr4-distribution + generate-resources + + unpack + + + + + org.alfresco + alfresco-solr4 + ${alfresco.platform.version} + config + true + zip + ${unpacked.solr4.distribution} + + + + + + + + + com.google.code.maven-replacer-plugin + replacer + 1.5.3 + + + prepare-package + + replace + + + + + ${unpacked.update.tool}/README.txt,${unpacked.update.tool}/package.properties + + + @Version@ + ${version} + + + @UpdateAssistantVersion@ + ${alfresco.updatetool.version} + + + + + maven-assembly-plugin - - update - package - - single - - - false - - assemblies/update.xml - - update-contents-${installer.version.name} - - + update-tool package @@ -90,16 +340,26 @@ - + + org.apache.maven.plugins maven-surefire-plugin - - - **/*IntegrationTest.java - - + + default-test + test + + test + + + + **/*IntegrationTest.java + + true + alphabetical + + integration-test integration-test @@ -109,18 +369,19 @@ false ${project.build.directory} - false + true alphabetical - - ${project.artifactId}-${installer.version.name}.zip - update-contents-${installer.version.name}.zip - none **/*IntegrationTest.java + + ${project.artifactId}-${installer.version.name}.zip + ${project.artifactId}-${installer.version.name}.tgz + ${project.build.directory} + diff --git a/enterprise-update/src/main/resources/script/5.0/update5-0.auas b/enterprise-update/src/main/resources/script/5.0/update5-0.auas new file mode 100644 index 0000000000..078afab98c --- /dev/null +++ b/enterprise-update/src/main/resources/script/5.0/update5-0.auas @@ -0,0 +1,5 @@ +// ALFRESCO UPDATE ASSISTANT SCRIPT for 5.0 +// update5-0.auas + +DELETE /tomcat/endorsed/xalan-2.7.2.jar +DELETE /tomcat/endorsed/serializer-2.7.2.jar \ No newline at end of file diff --git a/enterprise-update/src/main/resources/script/5.1/update5-1.auas b/enterprise-update/src/main/resources/script/5.1/update5-1.auas new file mode 100644 index 0000000000..3910132c08 --- /dev/null +++ b/enterprise-update/src/main/resources/script/5.1/update5-1.auas @@ -0,0 +1,4 @@ +// ALFRESCO UPDATE ASSISTANT SCRIPT for 5.1 +// update5-1.auas + +// EMPTY AT THE MOMENT diff --git a/enterprise-update/src/test/java/org/alfresco/update/packaging/PackagingIntegrationTest.java b/enterprise-update/src/test/java/org/alfresco/update/packaging/PackagingIntegrationTest.java index f20577a13b..80031b0b2b 100644 --- a/enterprise-update/src/test/java/org/alfresco/update/packaging/PackagingIntegrationTest.java +++ b/enterprise-update/src/test/java/org/alfresco/update/packaging/PackagingIntegrationTest.java @@ -1,4 +1,10 @@ - +/* + * Copyright 2015-2015 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.packaging; import static org.junit.Assert.*; @@ -20,8 +26,7 @@ import org.junit.Test; *

* To run these tests in Eclipse, add the following to the "VM arguments" for the junit Run Configuration: *

- *   -Dalfresco.update.package=target/alfresco-enterprise-update-package-2015-1-EA-SNAPSHOT.zip
- *   -Dalfresco.contents.package=target/update-contents-2015-1-EA-SNAPSHOT.zip
+ *   -Dalfresco.update.package.zip=target/alfresco-enterprise-update-package-2015-1-EA-SNAPSHOT.zip
  * 
* * ...or similar, depending on current version etc. There probably is a better way. @@ -31,18 +36,13 @@ import org.junit.Test; public class PackagingIntegrationTest { private File updatePackage; - private File contentsPackage; @Before public void setUp() throws Exception { - String pkgName = System.getProperty("alfresco.update.package"); - assertNotNull("Could not determine package name."); - updatePackage = new File(pkgName); - - String contentsPkgName = System.getProperty("alfresco.contents.package"); - assertNotNull("Could not determine content package name."); - contentsPackage = new File(contentsPkgName); + String pkgName = System.getProperty("alfresco.update.package.zip"); + assertNotNull("Could not determine package name.", pkgName); + updatePackage = new File(pkgName); } @Test @@ -51,30 +51,28 @@ public class PackagingIntegrationTest // Check the package exists before we go any further assertTrue("Update package does not exist.", updatePackage.exists()); - Set paths = listFiles(updatePackage); + Set paths = listFiles(updatePackage); + + assertTrue("too few paths in the update package", paths.size() > 3); + String firstPath = (String)paths.toArray()[0]; + String dirs[] = firstPath.split("/"); + // Are the binaries present? - assertPathPresent(paths, "bin/alfresco-update-tool.jar"); - assertPathPresent(paths, "bin/apply_updates.sh"); - assertPathPresent(paths, "bin/apply_updates.bat"); + assertPathPresent(paths, dirs[0] + "/lib/alfresco-update-tool.jar"); + assertPathPresent(paths, dirs[0] + "/apply_updates.sh"); + assertPathPresent(paths, dirs[0] + "/apply_updates.bat"); // Is the content sub-package present? - assertPathPresent(paths, "update/"+contentsPackage.getName()); - } - - @Test - public void testContentsPackageStructureIsAsExpected() throws ZipException, IOException - { - // Check the package exists before we go any further - assertTrue("Contents package does not exist.", contentsPackage.exists()); + assertPathPresent(paths, dirs[0] + "/resources/war/alfresco.war"); + assertPathPresent(paths, dirs[0] + "/resources/war/share.war"); - Set paths = listFiles(contentsPackage); + // Is the mmt in the correct place ? + assertPathPresent(paths, dirs[0] + "/resources/distribution/common/bin/alfresco-mmt.jar"); + assertPathPresent(paths, dirs[0] + "/resources/distribution/common/bin/alfresco-spring-encryptor.jar"); - // Are the webapps present? - assertPathPresent(paths, "assets/web-server/webapps/alfresco.war"); - assertPathPresent(paths, "assets/web-server/webapps/share.war"); } - + private void assertPathPresent(Set pathsToCheck, String expectedPath) { assertTrue("Expected path to be present, but was not: "+expectedPath, @@ -102,3 +100,4 @@ public class PackagingIntegrationTest return paths; } } + diff --git a/enterprise-update/src/test/java/org/alfresco/update/pkg/test/AbstractIntegrationTest.java b/enterprise-update/src/test/java/org/alfresco/update/pkg/test/AbstractIntegrationTest.java new file mode 100644 index 0000000000..b0ff8da80f --- /dev/null +++ b/enterprise-update/src/test/java/org/alfresco/update/pkg/test/AbstractIntegrationTest.java @@ -0,0 +1,47 @@ +package org.alfresco.update.pkg.test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.junit.Before; + +/** + * Abstract base class for tests. + * + * @author Matt Ward + */ +public abstract class AbstractIntegrationTest +{ + protected File targetDir; + + @Before + public void setUp() throws Exception + { + initTargetDir(); + } + + private void initTargetDir() + { + String targetDir = System.getProperty("alfresco.target.dir"); + if (targetDir == null) + { + targetDir = "./target"; // test needs to be run in target dir. + } + this.targetDir = new File(targetDir); + assertTrue("target dir does not exist :" + targetDir, this.targetDir.exists()); + } + + + protected boolean runningOnWindows() + { + String os = System.getProperty("os.name").toLowerCase(); + + if (os.contains("windows")) + { + return true; + } + return false; + } +} diff --git a/enterprise-update/src/test/java/org/alfresco/update/pkg/test/ZipFormatIntegrationTest.java b/enterprise-update/src/test/java/org/alfresco/update/pkg/test/ZipFormatIntegrationTest.java new file mode 100644 index 0000000000..98e514af34 --- /dev/null +++ b/enterprise-update/src/test/java/org/alfresco/update/pkg/test/ZipFormatIntegrationTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2005-2015 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.pkg.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Enumeration; + +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.apache.commons.compress.compressors.CompressorException; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the tgz format packaging. + * + * To run these tests in Eclipse, add the following to the "VM arguments" for the junit Run Configuration: + *
+ *   -Dalfresco.update.package.zip=target/alfresco-enterprise-update-package-2015-1-EA-SNAPSHOT.zip
+ * 
+ * + * @author Matt Ward + */ +public class ZipFormatIntegrationTest extends AbstractIntegrationTest +{ + + File updatePackage; + + @Before + public void setUp() throws Exception + { + super.setUp(); + String pkgName = System.getProperty("alfresco.update.package.zip"); + assertNotNull("Could not determine package name.", pkgName); + updatePackage = new File(pkgName); + } + + @Test + public void applyUpdatesScriptHasExecutableBitsSet() throws FileNotFoundException, ArchiveException, IOException, CompressorException + { + assertTrue("File does not exist: "+ updatePackage, updatePackage.exists()); + try (ZipFile zipFile = new ZipFile(updatePackage)) + { + Enumeration e = zipFile.getEntries(); + boolean found = false; + while (!found && e.hasMoreElements()) + { + ZipArchiveEntry entry = e.nextElement(); + File f = new File(entry.getName()); + if (f.getName().equalsIgnoreCase("apply_updates.sh")) + { + found = true; + System.out.println("Found the unix shell wrapper script."); + final int expectedPerms = 0755; + // Other bits may be set, but check 755 octal are set. + System.out.println("File has permissions: "+Integer.toString(entry.getUnixMode(), 8)); + assertEquals(expectedPerms, entry.getUnixMode() & expectedPerms); + } + } + } + } +} diff --git a/pom.xml b/pom.xml index 86a6116133..c07af3c0ce 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,7 @@ enterprise-distribution enterprise-update enterprise-installer + enterprise-update-test