You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DirChanges.java 22KB

21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /* *******************************************************************
  2. * Copyright (c) 1999-2001 Xerox Corporation,
  3. * 2002 Palo Alto Research Center, Incorporated (PARC).
  4. * All rights reserved.
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Common Public License v1.0
  7. * which accompanies this distribution and is available at
  8. * http://www.eclipse.org/legal/cpl-v10.html
  9. *
  10. * Contributors:
  11. * Xerox/PARC initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.testing.harness.bridge;
  14. import java.io.File;
  15. import java.util.ArrayList;
  16. import java.util.Iterator;
  17. import java.util.List;
  18. import org.aspectj.bridge.IMessageHandler;
  19. import org.aspectj.bridge.MessageUtil;
  20. import org.aspectj.testing.util.TestUtil;
  21. import org.aspectj.testing.xml.IXmlWritable;
  22. import org.aspectj.testing.xml.XMLWriter;
  23. import org.aspectj.util.FileUtil;
  24. import org.aspectj.util.LangUtil;
  25. /**
  26. * Calculate changes in a directory tree.
  27. * This implements two different specification styles:
  28. * <ul>
  29. * <li>Specify files added, removed, updated, and/or a component
  30. * to check any existing files</li>
  31. * <li>Specify expected directory. When complete this checks that
  32. * any files in expected directory are matched in the actual.
  33. * (.class files are dissassembled before comparison.)
  34. * </li>
  35. * </ul>
  36. * Usage:
  37. * <ul>
  38. * <li>Set up with any expected changes and/or an expected directory</li>
  39. * <li>Set up with any file checker</li>
  40. * <li>start(..) before changes.
  41. * This issues messages for any removed files not found,
  42. * which represent an error in the expected changes.</li>
  43. * <li>Do whatever operations will change the directory</li>
  44. * <li>end(..).
  45. * This issues messages for any files not removed, added, or updated,
  46. * and, if any checker was set, any checker messages for matching
  47. * added or updated files</li>
  48. * </ul>
  49. * When comparing directories, this ignores any paths containing "CVS".
  50. */
  51. public class DirChanges {
  52. private static final boolean EXISTS = true;
  53. final Spec spec;
  54. /** start time, in milliseconds - valid only from start(..)..end(..) */
  55. long startTime;
  56. /** base directory of actual files - valid only from start(..)..end(..) */
  57. File baseDir;
  58. /** if set, this is run against any resulting existing files
  59. * specified in added/updated lists.
  60. * This does not affect expected-directory comparison.
  61. */
  62. IFileChecker fileChecker;
  63. /** handler valid from start..end of start(..) and end(..) methods */
  64. IMessageHandler handler;
  65. /**
  66. * Constructor for DirChanges.
  67. */
  68. public DirChanges(Spec spec) {
  69. LangUtil.throwIaxIfNull(spec, "spec");
  70. this.spec = spec;
  71. }
  72. /**
  73. * Inspect the base dir, and issue any messages for
  74. * removed files not present.
  75. * @param baseDir File for a readable directory
  76. * @return true if this started without sending messages
  77. * for removed files not present.
  78. */
  79. public boolean start(IMessageHandler handler, File baseDir) {
  80. FileUtil.throwIaxUnlessCanReadDir(baseDir, "baseDir");
  81. final IMessageHandler oldHandler = this.handler;
  82. this.handler = handler;
  83. this.baseDir = baseDir;
  84. startTime = 0l;
  85. final boolean doCompare = false;
  86. boolean result
  87. = exists("at start, did not expect file to be added", !EXISTS, spec.added, doCompare);
  88. result &= exists("at start, expected file to be unchanged", EXISTS, spec.unchanged, doCompare);
  89. result &= exists("at start, expected file to be updated", EXISTS, spec.updated, doCompare);
  90. result &= exists("at start, expected file to be removed", EXISTS, spec.removed, doCompare);
  91. startTime = System.currentTimeMillis();
  92. this.handler = oldHandler;
  93. return result;
  94. }
  95. /**
  96. * Inspect the base dir, issue any messages for
  97. * files not added, files not updated, and files not removed,
  98. * and compare expected/actual files added or updated.
  99. * @throws IllegalStateException if called before start(..)
  100. */
  101. public boolean end(IMessageHandler handler, File srcBaseDir) {
  102. FileUtil.throwIaxUnlessCanReadDir(baseDir, "baseDir");
  103. if (0l == startTime) {
  104. throw new IllegalStateException("called before start");
  105. }
  106. final IMessageHandler oldHandler = this.handler;
  107. this.handler = handler;
  108. try {
  109. // variant 1: check specified files
  110. // deferring comparison to end...
  111. final boolean doCompare = false && (null != fileChecker);
  112. final boolean fastFail = spec.fastFail;
  113. boolean result
  114. = exists("at end, expected file was not added", EXISTS, spec.added, doCompare);
  115. if (result || !fastFail) {
  116. result &= exists("at end, expected file was not unchanged", EXISTS, spec.unchanged, doCompare, false);
  117. }
  118. if (result || !fastFail) {
  119. result &= exists("at end, expected file was not updated", EXISTS, spec.updated, doCompare);
  120. }
  121. if (result || !fastFail) {
  122. result &= exists("at end, file exists, was not removed", !EXISTS, spec.removed, doCompare);
  123. }
  124. // if (result || !fastFail) {
  125. // // XXX validate that unchanged mod-time did not change
  126. // }
  127. // variant 1: compare expected directory
  128. if (result || !fastFail) {
  129. result &= compareDir(srcBaseDir);
  130. }
  131. return result;
  132. } finally {
  133. this.handler = oldHandler;
  134. baseDir = null;
  135. startTime = 0l;
  136. }
  137. }
  138. /**
  139. * Verify that all files in any specified expected directory
  140. * have matching files in the base directory, putting any messages
  141. * in the handler (only one if the spec indicates fast-fail).
  142. * @param srcBaseDir the File for the base directory of the test sources
  143. * (any expected dir is specified relative to this directory)
  144. * @return true if the same, false otherwise
  145. */
  146. private boolean compareDir(File srcBaseDir) {
  147. if (null == spec.expDir) {
  148. return true;
  149. }
  150. File expDir = new File(srcBaseDir, spec.expDir);
  151. File actDir = baseDir;
  152. //System.err.println("XXX comparing actDir=" + actDir + " expDir=" + expDir);
  153. return TestUtil.sameDirectoryContents(handler, expDir, actDir, spec.fastFail);
  154. }
  155. /** @param comp FileMessageComparer (if any) given matching messages to compare */
  156. protected void setFileComparer(IFileChecker comp) {
  157. this.fileChecker = comp;
  158. }
  159. /**
  160. * Signal fail if any files do {not} exist or do {not} have last-mod-time after startTime
  161. * @param handler the IMessageHandler sink for messages
  162. * @param label the String infix for the fail message
  163. * @param exists if true, then file must exist and be modified after start time;
  164. * if false, then the file must not exist or must be modified before start time.
  165. * @param pathList the List of path (without any Spec.defaultSuffix) of File
  166. * in Spec.baseDir to find (or not, if !exists)
  167. */
  168. protected boolean exists(
  169. String label,
  170. boolean exists,
  171. List pathList,
  172. boolean doCompare) {
  173. // boolean expectStartEarlier = true;
  174. return exists(label, exists, pathList,doCompare, true);
  175. }
  176. protected boolean exists(
  177. String label,
  178. boolean exists,
  179. List pathList,
  180. boolean doCompare,
  181. boolean expectStartEarlier) {
  182. boolean result = true;
  183. if (!LangUtil.isEmpty(pathList)) {
  184. // final File expDir = ((!doCompare || (null == spec.expDir))
  185. // ? null
  186. // : new File(baseDir, spec.expDir));
  187. for (Iterator iter = pathList.iterator(); iter.hasNext();) {
  188. final String entry = (String) iter.next() ;
  189. String path = entry ;
  190. if (null != spec.defaultSuffix) {
  191. if (".class".equals(spec.defaultSuffix)) {
  192. path = path.replace('.', '/');
  193. }
  194. path = path + spec.defaultSuffix;
  195. }
  196. File actualFile = new File(baseDir, path);
  197. if (exists != (actualFile.canRead() && actualFile.isFile()
  198. && (expectStartEarlier
  199. ? startTime <= actualFile.lastModified()
  200. : startTime > actualFile.lastModified()
  201. ))) {
  202. failMessage(handler, exists, label, path, actualFile);
  203. if (result) {
  204. result = false;
  205. }
  206. } else if (exists && doCompare && (null != fileChecker)) {
  207. if (!fileChecker.checkFile(handler, path, actualFile) && result) {
  208. result = false;
  209. }
  210. }
  211. }
  212. }
  213. return result;
  214. }
  215. /**
  216. * Generate fail message "{un}expected {label} file {path} in {baseDir}".
  217. * @param handler the IMessageHandler sink
  218. * @param label String message infix
  219. * @param path the path to the file
  220. */
  221. protected void failMessage(
  222. IMessageHandler handler,
  223. boolean exists,
  224. String label,
  225. String path,
  226. File file) {
  227. MessageUtil.fail(handler, label + " \"" + path + "\" in " + baseDir);
  228. }
  229. /** Check actual File found at a path, usually to diff expected/actual contents */
  230. public static interface IFileChecker {
  231. /**
  232. * Check file found at path.
  233. * Implementations should return false when adding fail (or worse)
  234. * message to the handler, and true otherwise.
  235. * @param handler IMessageHandler sink for messages, esp. fail.
  236. * @param path String for normalized path portion of actualFile.getPath()
  237. * @param actualFile File to file found
  238. * @return false if check failed and messages added to handler
  239. */
  240. boolean checkFile(IMessageHandler handler, String path, File actualFile);
  241. }
  242. // File-comparison code with a bit more generality -- too unweildy
  243. // /**
  244. // * Default FileChecker compares files literally, transforming any
  245. // * with registered normalizers.
  246. // */
  247. // public static class FileChecker implements IFileChecker {
  248. // final File baseExpectedDir;
  249. // NormalizedCompareFiles fileComparer;
  250. //
  251. // public FileChecker(File baseExpectedDir) {
  252. // this.baseExpectedDir = baseExpectedDir;
  253. // fileComparer = new NormalizedCompareFiles();
  254. // }
  255. // public boolean checkFile(IMessageHandler handler, String path, File actualFile) {
  256. // if (null == baseExpectedDir) {
  257. // MessageUtil.error(handler, "null baseExpectedDir set on construction");
  258. // } else if (!baseExpectedDir.canRead() || !baseExpectedDir.isDirectory()) {
  259. // MessageUtil.error(handler, "bad baseExpectedDir: " + baseExpectedDir);
  260. // } else {
  261. // File expectedFile = new File(baseExpectedDir, path);
  262. // if (!expectedFile.canRead()) {
  263. // MessageUtil.fail(handler, "cannot read expected file: " + expectedFile);
  264. // } else {
  265. // return doCheckFile(handler, expectedFile, actualFile, path);
  266. // }
  267. // }
  268. // return false;
  269. // }
  270. //
  271. // protected boolean doCheckFile(
  272. // IMessageHandler handler,
  273. // File expectedFile,
  274. // File actualFile,
  275. // String path) {
  276. // fileComparer.setHandler(handler);
  277. // FileLine[] expected = fileComparer.diff();
  278. // return false;
  279. // }
  280. // }
  281. // /**
  282. // * CompareFiles implementation that pre-processes input
  283. // * to normalize it. Currently it reads all files except
  284. // * .class files, which it disassembles first.
  285. // */
  286. // public static class NormalizedCompareFiles extends CompareFiles {
  287. // private final static String[] NO_PATHS = new String[0];
  288. // private static String normalPath(File file) { // XXX util
  289. // if (null == file) {
  290. // return "";
  291. // }
  292. // return file.getAbsolutePath().replace('\\', '/');
  293. // }
  294. //
  295. // private String[] baseDirs;
  296. // private IMessageHandler handler;
  297. //
  298. // public NormalizedCompareFiles() {
  299. // }
  300. //
  301. // void init(IMessageHandler handler, File[] baseDirs) {
  302. // this.handler = handler;
  303. // if (null == baseDirs) {
  304. // this.baseDirs = NO_PATHS;
  305. // } else {
  306. // this.baseDirs = new String[baseDirs.length];
  307. // for (int i = 0; i < baseDirs.length; i++) {
  308. // this.baseDirs[i] = normalPath(baseDirs[i]) + "/";
  309. // }
  310. // }
  311. // }
  312. //
  313. // private String getClassName(File file) {
  314. // String result = null;
  315. // String path = normalPath(file);
  316. // if (!path.endsWith(".class")) {
  317. // MessageUtil.error(handler,
  318. // "NormalizedCompareFiles expected "
  319. // + file
  320. // + " to end with .class");
  321. // } else {
  322. // path = path.substring(0, path.length()-6);
  323. // for (int i = 0; i < baseDirs.length; i++) {
  324. // if (path.startsWith(baseDirs[i])) {
  325. // return path.substring(baseDirs[i].length()).replace('/', '.');
  326. // }
  327. // }
  328. // MessageUtil.error(handler,
  329. // "NormalizedCompareFiles expected "
  330. // + file
  331. // + " to start with one of "
  332. // + LangUtil.arrayAsList(baseDirs));
  333. // }
  334. //
  335. // return result;
  336. // }
  337. //
  338. // /**
  339. // * Read file as normalized lines, sending handler any messages
  340. // * ERROR for input failures and FAIL for processing failures.
  341. // * @return NOLINES on error or normalized lines from file otherwise
  342. // */
  343. // public FileLine[] getFileLines(File file) {
  344. // FileLineator capture = new FileLineator();
  345. // InputStream in = null;
  346. // try {
  347. // if (!file.getPath().endsWith(".class")) {
  348. // in = new FileInputStream(file);
  349. // FileUtil.copyStream(
  350. // new BufferedReader(new InputStreamReader(in)),
  351. // new PrintWriter(capture));
  352. // } else {
  353. // String name = getClassName(file);
  354. // if (null == name) {
  355. // return new FileLine[0];
  356. // }
  357. // String path = normalPath(file);
  358. // path = path.substring(0, path.length()-name.length());
  359. // // XXX sole dependency on bcweaver/bcel
  360. // LazyClassGen.disassemble(path, name, capture);
  361. // }
  362. // } catch (IOException e) {
  363. // MessageUtil.fail(handler,
  364. // "NormalizedCompareFiles IOException reading " + file, e);
  365. // return null;
  366. // } finally {
  367. // if (null != in) {
  368. // try { in.close(); }
  369. // catch (IOException e) {} // ignore
  370. // }
  371. // capture.flush();
  372. // capture.close();
  373. // }
  374. // String missed = capture.getMissed();
  375. // if (!LangUtil.isEmpty(missed)) {
  376. // MessageUtil.fail(handler,
  377. // "NormalizedCompareFiles missed input: "
  378. // + missed);
  379. // return null;
  380. // } else {
  381. // return capture.getFileLines();
  382. // }
  383. // }
  384. //
  385. //
  386. // }
  387. /**
  388. * Specification for a set of File added, removed, or updated
  389. * in a given directory, or for a directory base for a tree of expected files.
  390. * If defaultSuffix is specified, entries may be added without it.
  391. * Currently the directory tree
  392. * only is used to verify files that are expected
  393. * and found after the process completes.
  394. */
  395. public static class Spec implements IXmlWritable {
  396. /** XML element name */
  397. public static final String XMLNAME = "dir-changes";
  398. /** a symbolic name for the base directory */
  399. String dirToken; // XXX default to class?
  400. /** if set, then append to specified paths when seeking files */
  401. String defaultSuffix;
  402. /** relative path of dir with expected files for comparison */
  403. String expDir;
  404. /** if true, fail on first mis-match */
  405. boolean fastFail;
  406. /** relative paths (String) of expected files added */
  407. final ArrayList added;
  408. /** relative paths (String) of expected files removed/deleted */
  409. final ArrayList removed;
  410. /** relative paths (String) of expected files updated/changed */
  411. final ArrayList updated;
  412. /** relative paths (String) of expected files NOT
  413. * added, removed, or changed
  414. * XXX unchanged unimplemented
  415. */
  416. final ArrayList unchanged;
  417. public Spec() {
  418. added = new ArrayList();
  419. removed = new ArrayList();
  420. updated = new ArrayList();
  421. unchanged = new ArrayList();
  422. }
  423. /**
  424. * @param dirToken the symbol name of the base directory (classes, run)
  425. */
  426. public void setDirToken(String dirToken) {
  427. this.dirToken = dirToken;
  428. }
  429. /**
  430. * Set the directory containing the expected files.
  431. * @param expectedDirRelativePath path relative to the test base
  432. * of the directory containing expected results for the output dir.
  433. */
  434. public void setExpDir(String expectedDirRelativePath) {
  435. expDir = expectedDirRelativePath;
  436. }
  437. /**
  438. * @param clipSuffix the String suffix, if any, to clip automatically
  439. */
  440. public void setDefaultSuffix(String defaultSuffix) {
  441. this.defaultSuffix = defaultSuffix;
  442. }
  443. public void setAdded(String items) {
  444. XMLWriter.addFlattenedItems(added, items);
  445. }
  446. public void setRemoved(String items) {
  447. XMLWriter.addFlattenedItems(removed, items);
  448. }
  449. public void setUpdated(String items) {
  450. XMLWriter.addFlattenedItems(updated, items);
  451. }
  452. public void setUnchanged(String items) {
  453. XMLWriter.addFlattenedItems(unchanged, items);
  454. }
  455. public void setFastfail(boolean fastFail) {
  456. this.fastFail = fastFail;
  457. }
  458. /** @return true if some list was specified */
  459. private boolean hasFileList() {
  460. return (!LangUtil.isEmpty(added)
  461. || !LangUtil.isEmpty(removed)
  462. || !LangUtil.isEmpty(updated)
  463. || !LangUtil.isEmpty(unchanged)
  464. );
  465. }
  466. /**
  467. * Emit specification in XML form if not empty.
  468. * This writes nothing if there is no expected dir
  469. * and there are no added, removed, or changed.
  470. * fastFail is written only if true, since the default is false.
  471. */
  472. public void writeXml(XMLWriter out) {
  473. if (!hasFileList() && LangUtil.isEmpty(expDir)) {
  474. return;
  475. }
  476. // XXX need to permit defaults here...
  477. out.startElement(XMLNAME, false);
  478. if (!LangUtil.isEmpty(dirToken)) {
  479. out.printAttribute("dirToken", dirToken.trim());
  480. }
  481. if (!LangUtil.isEmpty(defaultSuffix)) {
  482. out.printAttribute("defaultSuffix", defaultSuffix.trim());
  483. }
  484. if (!LangUtil.isEmpty(expDir)) {
  485. out.printAttribute("expDir", expDir.trim());
  486. }
  487. if (!LangUtil.isEmpty(added)) {
  488. out.printAttribute("added", XMLWriter.flattenList(added));
  489. }
  490. if (!LangUtil.isEmpty(removed)) {
  491. out.printAttribute("removed", XMLWriter.flattenList(removed));
  492. }
  493. if (!LangUtil.isEmpty(updated)) {
  494. out.printAttribute("updated", XMLWriter.flattenList(updated));
  495. }
  496. if (!LangUtil.isEmpty(unchanged)) {
  497. out.printAttribute("unchanged", XMLWriter.flattenList(unchanged));
  498. }
  499. if (fastFail) {
  500. out.printAttribute("fastFail", "true");
  501. }
  502. out.endElement(XMLNAME);
  503. }
  504. /**
  505. * Write list as elements to XMLWriter.
  506. * @param out XMLWriter output sink
  507. * @param dirChanges List of DirChanges.Spec to write
  508. */
  509. public static void writeXml(XMLWriter out, List dirChanges) {
  510. if (LangUtil.isEmpty(dirChanges)) {
  511. return;
  512. }
  513. LangUtil.throwIaxIfNull(out, "out");
  514. for (Iterator iter = dirChanges.iterator(); iter.hasNext();) {
  515. DirChanges.Spec spec = (DirChanges.Spec) iter.next();
  516. if (null == spec) {
  517. continue;
  518. }
  519. spec.writeXml(out);
  520. }
  521. }
  522. } // class Spec
  523. }