|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614 |
- /* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation,
- * 2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved.
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Xerox/PARC initial implementation
- * ******************************************************************/
-
- package org.aspectj.testing.harness.bridge;
-
- import java.io.File;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
-
- import org.aspectj.bridge.IMessageHandler;
- import org.aspectj.bridge.MessageUtil;
- import org.aspectj.testing.util.TestUtil;
- import org.aspectj.testing.xml.IXmlWritable;
- import org.aspectj.testing.xml.XMLWriter;
- import org.aspectj.util.FileUtil;
- import org.aspectj.util.LangUtil;
-
- /**
- * Calculate changes in a directory tree.
- * This implements two different specification styles:
- * <ul>
- * <li>Specify files added, removed, updated, and/or a component
- * to check any existing files</li>
- * <li>Specify expected directory. When complete this checks that
- * any files in expected directory are matched in the actual.
- * (.class files are dissassembled before comparison.)
- * </li>
- * </ul>
- * Usage:
- * <ul>
- * <li>Set up with any expected changes and/or an expected directory</li>
- * <li>Set up with any file checker</li>
- * <li>start(..) before changes.
- * This issues messages for any removed files not found,
- * which represent an error in the expected changes.</li>
- * <li>Do whatever operations will change the directory</li>
- * <li>end(..).
- * This issues messages for any files not removed, added, or updated,
- * and, if any checker was set, any checker messages for matching
- * added or updated files</li>
- * </ul>
- * When comparing directories, this ignores any paths containing "CVS".
- */
- public class DirChanges {
-
- public static final String DELAY_NAME = "dir-changes.delay";
- private static final long DELAY;
- static {
- long delay = 10l;
- try {
- delay = Long.getLong(DELAY_NAME).longValue();
- if ((delay > 40000) || (delay < 0)) {
- delay = 10l;
- }
- } catch (Throwable t) {
- // ignore
- }
- DELAY = delay;
- }
-
- private static final boolean EXISTS = true;
-
- final Spec spec;
-
- /** start time, in milliseconds - valid only from start(..)..end(..) */
- long startTime;
-
- /** base directory of actual files - valid only from start(..)..end(..) */
- File baseDir;
-
- /** if set, this is run against any resulting existing files
- * specified in added/updated lists.
- * This does not affect expected-directory comparison.
- */
- IFileChecker fileChecker;
-
- /** handler valid from start..end of start(..) and end(..) methods */
- IMessageHandler handler;
-
- /**
- * Constructor for DirChanges.
- */
- public DirChanges(Spec spec) {
- LangUtil.throwIaxIfNull(spec, "spec");
- this.spec = spec;
- }
-
- /**
- * Inspect the base dir, and issue any messages for
- * removed files not present.
- * @param baseDir File for a readable directory
- * @return true if this started without sending messages
- * for removed files not present.
- */
- public boolean start(IMessageHandler handler, File baseDir) {
- FileUtil.throwIaxUnlessCanReadDir(baseDir, "baseDir");
- final IMessageHandler oldHandler = this.handler;
- this.handler = handler;
- this.baseDir = baseDir;
- startTime = 0l;
- final boolean doCompare = false;
- boolean result
- = exists("at start, did not expect added file to exist", !EXISTS, spec.added, doCompare);
- result &= exists("at start, expected unchanged file to exist", EXISTS, spec.unchanged, doCompare);
- result &= exists("at start, expected updated file to exist", EXISTS, spec.updated, doCompare);
- result &= exists("at start, expected removed file to exist", EXISTS, spec.removed, doCompare);
- startTime = System.currentTimeMillis();
- // ensure tests don't complete in < 1 second, otherwise can confuse fast machines.
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- this.handler = oldHandler;
- return result;
- }
-
-
- /**
- * Inspect the base dir, issue any messages for
- * files not added, files not updated, and files not removed,
- * and compare expected/actual files added or updated.
- * This sleeps before checking until at least DELAY milliseconds after start.
- * @throws IllegalStateException if called before start(..)
- */
- public boolean end(IMessageHandler handler, File srcBaseDir) {
- FileUtil.throwIaxUnlessCanReadDir(baseDir, "baseDir");
- if (0l == startTime) {
- throw new IllegalStateException("called before start");
- }
- final long targetTime = startTime + spec.delayInMilliseconds;
- do {
- long curTime = System.currentTimeMillis();
- if (curTime >= targetTime) {
- break;
- }
- try {
- Thread.sleep(targetTime-curTime);
- } catch (InterruptedException e) {
- break;
- }
- } while (true);
- final IMessageHandler oldHandler = this.handler;
- this.handler = handler;
- try {
- // variant 1: check specified files
- // deferring comparison to end...
- final boolean doCompare = (null != fileChecker);
- final boolean fastFail = spec.fastFail;
- boolean result
- = exists("at end, expected file was not added", EXISTS, spec.added, doCompare);
- if (result || !fastFail) {
- result &= exists("at end, expected file was not unchanged", EXISTS, spec.unchanged, doCompare, false);
- }
- if (result || !fastFail) {
- result &= exists("at end, expected file was not updated", EXISTS, spec.updated, doCompare);
- }
- if (result || !fastFail) {
- result &= exists("at end, file exists, was not removed", !EXISTS, spec.removed, doCompare);
- }
- // if (result || !fastFail) {
- // // XXX validate that unchanged mod-time did not change
- // }
- // variant 1: compare expected directory
- if (result || !fastFail) {
- result &= compareDir(srcBaseDir);
- }
- return result;
- } finally {
- this.handler = oldHandler;
- baseDir = null;
- startTime = 0l;
- }
- }
-
- /**
- * Verify that all files in any specified expected directory
- * have matching files in the base directory, putting any messages
- * in the handler (only one if the spec indicates fast-fail).
- * @param srcBaseDir the File for the base directory of the test sources
- * (any expected dir is specified relative to this directory)
- * @return true if the same, false otherwise
- */
- private boolean compareDir(File srcBaseDir) {
- if (null == spec.expDir) {
- return true;
- }
- File expDir = new File(srcBaseDir, spec.expDir);
- File actDir = baseDir;
- //System.err.println("XXX comparing actDir=" + actDir + " expDir=" + expDir);
- return TestUtil.sameDirectoryContents(handler, expDir, actDir, spec.fastFail);
- }
-
-
- /** @param comp FileMessageComparer (if any) given matching messages to compare */
- protected void setFileComparer(IFileChecker comp) {
- this.fileChecker = comp;
- }
-
-
-
- /**
- * Signal fail if any files do {not} exist or do {not} have last-mod-time after startTime
- * @param handler the IMessageHandler sink for messages
- * @param label the String infix for the fail message
- * @param exists if true, then file must exist and be modified after start time;
- * if false, then the file must not exist or must be modified before start time.
- * @param pathList the List of path (without any Spec.defaultSuffix) of File
- * in Spec.baseDir to find (or not, if !exists)
- */
- protected boolean exists(
- String label,
- boolean exists,
- List pathList,
- boolean doCompare) {
- // boolean expectStartEarlier = true;
- return exists(label, exists, pathList,doCompare, true);
- }
- protected boolean exists(
- String label,
- boolean exists,
- List pathList,
- boolean doCompare,
- boolean expectStartEarlier) {
- boolean result = true;
- if (!LangUtil.isEmpty(pathList)) {
- // final File expDir = ((!doCompare || (null == spec.expDir))
- // ? null
- // : new File(baseDir, spec.expDir));
- for (Iterator iter = pathList.iterator(); iter.hasNext();) {
- final String entry = (String) iter.next() ;
- String path = entry ;
- if (null != spec.defaultSuffix) {
- if (".class".equals(spec.defaultSuffix)) {
- path = path.replace('.', '/');
- }
- path = path + spec.defaultSuffix;
- }
- File actualFile = new File(baseDir, path);
- if (exists != (actualFile.canRead() && actualFile.isFile()
- && (expectStartEarlier
- ? startTime <= actualFile.lastModified()
- : startTime > actualFile.lastModified()
- ))) {
- failMessage(handler, exists, label, path, actualFile);
- if (result) {
- result = false;
- }
- } else if (exists && doCompare && (null != fileChecker)) {
- if (!fileChecker.checkFile(handler, path, actualFile) && result) {
- result = false;
- }
- }
- }
- }
- return result;
- }
-
- /**
- * Generate fail message "{un}expected {label} file {path} in {baseDir}".
- * @param handler the IMessageHandler sink
- * @param label String message infix
- * @param path the path to the file
- */
- protected void failMessage(
- IMessageHandler handler,
- boolean exists,
- String label,
- String path,
- File file) {
- MessageUtil.fail(handler, label + " \"" + path + "\" in " + baseDir);
- }
-
- /** Check actual File found at a path, usually to diff expected/actual contents */
- public static interface IFileChecker {
- /**
- * Check file found at path.
- * Implementations should return false when adding fail (or worse)
- * message to the handler, and true otherwise.
- * @param handler IMessageHandler sink for messages, esp. fail.
- * @param path String for normalized path portion of actualFile.getPath()
- * @param actualFile File to file found
- * @return false if check failed and messages added to handler
- */
- boolean checkFile(IMessageHandler handler, String path, File actualFile);
- }
- // File-comparison code with a bit more generality -- too unweildy
- // /**
- // * Default FileChecker compares files literally, transforming any
- // * with registered normalizers.
- // */
- // public static class FileChecker implements IFileChecker {
- // final File baseExpectedDir;
- // NormalizedCompareFiles fileComparer;
- //
- // public FileChecker(File baseExpectedDir) {
- // this.baseExpectedDir = baseExpectedDir;
- // fileComparer = new NormalizedCompareFiles();
- // }
- // public boolean checkFile(IMessageHandler handler, String path, File actualFile) {
- // if (null == baseExpectedDir) {
- // MessageUtil.error(handler, "null baseExpectedDir set on construction");
- // } else if (!baseExpectedDir.canRead() || !baseExpectedDir.isDirectory()) {
- // MessageUtil.error(handler, "bad baseExpectedDir: " + baseExpectedDir);
- // } else {
- // File expectedFile = new File(baseExpectedDir, path);
- // if (!expectedFile.canRead()) {
- // MessageUtil.fail(handler, "cannot read expected file: " + expectedFile);
- // } else {
- // return doCheckFile(handler, expectedFile, actualFile, path);
- // }
- // }
- // return false;
- // }
- //
- // protected boolean doCheckFile(
- // IMessageHandler handler,
- // File expectedFile,
- // File actualFile,
- // String path) {
- // fileComparer.setHandler(handler);
- // FileLine[] expected = fileComparer.diff();
- // return false;
- // }
- // }
-
- // /**
- // * CompareFiles implementation that pre-processes input
- // * to normalize it. Currently it reads all files except
- // * .class files, which it disassembles first.
- // */
- // public static class NormalizedCompareFiles extends CompareFiles {
- // private final static String[] NO_PATHS = new String[0];
- // private static String normalPath(File file) { // XXX util
- // if (null == file) {
- // return "";
- // }
- // return file.getAbsolutePath().replace('\\', '/');
- // }
- //
- // private String[] baseDirs;
- // private IMessageHandler handler;
- //
- // public NormalizedCompareFiles() {
- // }
- //
- // void init(IMessageHandler handler, File[] baseDirs) {
- // this.handler = handler;
- // if (null == baseDirs) {
- // this.baseDirs = NO_PATHS;
- // } else {
- // this.baseDirs = new String[baseDirs.length];
- // for (int i = 0; i < baseDirs.length; i++) {
- // this.baseDirs[i] = normalPath(baseDirs[i]) + "/";
- // }
- // }
- // }
- //
- // private String getClassName(File file) {
- // String result = null;
- // String path = normalPath(file);
- // if (!path.endsWith(".class")) {
- // MessageUtil.error(handler,
- // "NormalizedCompareFiles expected "
- // + file
- // + " to end with .class");
- // } else {
- // path = path.substring(0, path.length()-6);
- // for (int i = 0; i < baseDirs.length; i++) {
- // if (path.startsWith(baseDirs[i])) {
- // return path.substring(baseDirs[i].length()).replace('/', '.');
- // }
- // }
- // MessageUtil.error(handler,
- // "NormalizedCompareFiles expected "
- // + file
- // + " to start with one of "
- // + LangUtil.arrayAsList(baseDirs));
- // }
- //
- // return result;
- // }
- //
- // /**
- // * Read file as normalized lines, sending handler any messages
- // * ERROR for input failures and FAIL for processing failures.
- // * @return NOLINES on error or normalized lines from file otherwise
- // */
- // public FileLine[] getFileLines(File file) {
- // FileLineator capture = new FileLineator();
- // InputStream in = null;
- // try {
- // if (!file.getPath().endsWith(".class")) {
- // in = new FileInputStream(file);
- // FileUtil.copyStream(
- // new BufferedReader(new InputStreamReader(in)),
- // new PrintWriter(capture));
- // } else {
- // String name = getClassName(file);
- // if (null == name) {
- // return new FileLine[0];
- // }
- // String path = normalPath(file);
- // path = path.substring(0, path.length()-name.length());
- // // XXX sole dependency on bcweaver/bcel
- // LazyClassGen.disassemble(path, name, capture);
- // }
- // } catch (IOException e) {
- // MessageUtil.fail(handler,
- // "NormalizedCompareFiles IOException reading " + file, e);
- // return null;
- // } finally {
- // if (null != in) {
- // try { in.close(); }
- // catch (IOException e) {} // ignore
- // }
- // capture.flush();
- // capture.close();
- // }
- // String missed = capture.getMissed();
- // if (!LangUtil.isEmpty(missed)) {
- // MessageUtil.fail(handler,
- // "NormalizedCompareFiles missed input: "
- // + missed);
- // return null;
- // } else {
- // return capture.getFileLines();
- // }
- // }
- //
- //
- // }
-
- /**
- * Specification for a set of File added, removed, or updated
- * in a given directory, or for a directory base for a tree of expected files.
- * If defaultSuffix is specified, entries may be added without it.
- * Currently the directory tree
- * only is used to verify files that are expected
- * and found after the process completes.
- */
- public static class Spec implements IXmlWritable {
- /** XML element name */
- public static final String XMLNAME = "dir-changes";
-
- /** a symbolic name for the base directory */
- String dirToken; // XXX default to class?
-
- /** if set, then append to specified paths when seeking files */
- String defaultSuffix;
-
- /** relative path of dir with expected files for comparison */
- String expDir;
-
- long delayInMilliseconds = DELAY;
-
- /** if true, fail on first mis-match */
- boolean fastFail;
-
- /** relative paths (String) of expected files added */
- final ArrayList<String> added;
-
- /** relative paths (String) of expected files removed/deleted */
- final ArrayList<String> removed;
-
- /** relative paths (String) of expected files updated/changed */
- final ArrayList<String> updated;
-
- /** relative paths (String) of expected files NOT
- * added, removed, or changed
- * XXX unchanged unimplemented
- */
- final ArrayList<String> unchanged;
-
- public Spec() {
- added = new ArrayList<>();
- removed = new ArrayList<>();
- updated = new ArrayList<>();
- unchanged = new ArrayList<>();
- }
-
- /**
- * @param dirToken the symbol name of the base directory (classes, run)
- */
- public void setDirToken(String dirToken) {
- this.dirToken = dirToken;
- }
-
- /**
- * Set the directory containing the expected files.
- * @param expectedDirRelativePath path relative to the test base
- * of the directory containing expected results for the output dir.
- */
- public void setExpDir(String expectedDirRelativePath) {
- expDir = expectedDirRelativePath;
- }
-
- public void setDelay(String delay) {
- if (null != delay) {
- // let NumberFormatException propogate up
- delayInMilliseconds = Long.parseLong(delay);
- if (delayInMilliseconds < 0l) {
- delayInMilliseconds = 0l;
- }
- }
- }
-
- /**
- * @param clipSuffix the String suffix, if any, to clip automatically
- */
- public void setDefaultSuffix(String defaultSuffix) {
- this.defaultSuffix = defaultSuffix;
- }
-
- public void setAdded(String items) {
- XMLWriter.addFlattenedItems(added, items);
- }
-
- public void setRemoved(String items) {
- XMLWriter.addFlattenedItems(removed, items);
- }
-
- public void setUpdated(String items) {
- XMLWriter.addFlattenedItems(updated, items);
- }
-
- public void setUnchanged(String items) {
- XMLWriter.addFlattenedItems(unchanged, items);
- }
- public void setFastfail(boolean fastFail) {
- this.fastFail = fastFail;
- }
-
- /** @return true if some list was specified */
- private boolean hasFileList() {
- return (!LangUtil.isEmpty(added)
- || !LangUtil.isEmpty(removed)
- || !LangUtil.isEmpty(updated)
- || !LangUtil.isEmpty(unchanged)
- );
- }
-
- /**
- * Emit specification in XML form if not empty.
- * This writes nothing if there is no expected dir
- * and there are no added, removed, or changed.
- * fastFail is written only if true, since the default is false.
- */
- public void writeXml(XMLWriter out) {
- if (!hasFileList() && LangUtil.isEmpty(expDir)) {
- return;
- }
- // XXX need to permit defaults here...
- out.startElement(XMLNAME, false);
- if (!LangUtil.isEmpty(dirToken)) {
- out.printAttribute("dirToken", dirToken.trim());
- }
- if (!LangUtil.isEmpty(defaultSuffix)) {
- out.printAttribute("defaultSuffix", defaultSuffix.trim());
- }
- if (!LangUtil.isEmpty(expDir)) {
- out.printAttribute("expDir", expDir.trim());
- }
- if (!LangUtil.isEmpty(added)) {
- out.printAttribute("added", XMLWriter.flattenList(added));
- }
- if (!LangUtil.isEmpty(removed)) {
- out.printAttribute("removed", XMLWriter.flattenList(removed));
- }
- if (!LangUtil.isEmpty(updated)) {
- out.printAttribute("updated", XMLWriter.flattenList(updated));
- }
- if (!LangUtil.isEmpty(unchanged)) {
- out.printAttribute("unchanged", XMLWriter.flattenList(unchanged));
- }
- if (fastFail) {
- out.printAttribute("fastFail", "true");
- }
- out.endElement(XMLNAME);
- }
-
- /**
- * Write list as elements to XMLWriter.
- * @param out XMLWriter output sink
- * @param dirChanges List of DirChanges.Spec to write
- */
- public static void writeXml(XMLWriter out, List<DirChanges.Spec> dirChanges) {
- if (LangUtil.isEmpty(dirChanges)) {
- return;
- }
- LangUtil.throwIaxIfNull(out, "out");
- for (Iterator<DirChanges.Spec> iter = dirChanges.iterator(); iter.hasNext();) {
- DirChanges.Spec spec = iter.next();
- if (null == spec) {
- continue;
- }
- spec.writeXml(out);
- }
- }
-
- } // class Spec
-
- }
|