Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

DirChanges.java 22KB

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 org.aspectj.bridge.IMessageHandler;
  15. import org.aspectj.bridge.MessageUtil;
  16. import org.aspectj.testing.util.TestUtil;
  17. import org.aspectj.testing.xml.IXmlWritable;
  18. import org.aspectj.testing.xml.XMLWriter;
  19. import org.aspectj.util.FileUtil;
  20. import org.aspectj.util.LangUtil;
  21. import java.io.File;
  22. import java.util.ArrayList;
  23. import java.util.Iterator;
  24. import java.util.List;
  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. }