Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Common Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Contributors:
  10. * Xerox/PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.testing.harness.bridge;
  13. import java.io.File;
  14. import java.io.FileFilter;
  15. import java.util.ArrayList;
  16. import java.util.Arrays;
  17. import org.aspectj.bridge.ICommand;
  18. import org.aspectj.bridge.IMessage;
  19. import org.aspectj.bridge.IMessageHandler;
  20. import org.aspectj.testing.taskdefs.AjcTaskCompileCommand;
  21. import org.aspectj.testing.util.Diffs;
  22. import org.aspectj.util.FileUtil;
  23. import org.aspectj.util.LangUtil;
  24. /**
  25. * A sandbox holds state shared by AjcTest sub-runs,
  26. * mostly directories relevant to testing.
  27. * It permits a limited amount of coordination and
  28. * setup/cleanup operations (todo XXX).
  29. * <p>
  30. * AjcTest creates the Sandbox and initializes the final fields.
  31. * To coordinate with each other, run components may set and get values,
  32. * with the sources running first and the sinks second.
  33. * To make the interactions clear
  34. * (and to avoid accidentally violating these semantics),
  35. * setters/getters for a coordinated property are constrained two ways:
  36. * <li>Both have an extra (typed) "caller" parameter which must not
  37. * be null, authenticating that the caller is known & valid.</li>
  38. * <li>A getter throws IllegalStateException if called before the setter</li>
  39. * <li>A setter throws IllegalStateException if called after the getter<li>
  40. * XXX subclass more general sandbox?
  41. */
  42. public class Sandbox {
  43. /** classes directory token for DirChanges.Spec */
  44. public static final String RUN_DIR = "run";
  45. /** run directory token for DirChanges.Spec */
  46. public static final String CLASSES_DIR = "classes";
  47. private static boolean canRead(File dir) {
  48. return ((null != dir) && dir.isDirectory() && dir.canRead());
  49. }
  50. private static boolean canWrite(File dir) {
  51. return ((null != dir) && dir.isDirectory() && dir.canWrite());
  52. }
  53. private static void iaxWrite(File dir, String label) {
  54. if (!canWrite(dir)) {
  55. throw new IllegalArgumentException(label + " - " + dir);
  56. }
  57. }
  58. private static void iaxRead(File dir, String label) {
  59. if (!canRead(dir)) {
  60. throw new IllegalArgumentException(label + " - " + dir);
  61. }
  62. }
  63. /** @throws IllegalStateException(message) if test */
  64. private static void assertState(boolean test, String message) {
  65. if (!test) {
  66. throw new IllegalStateException(message);
  67. }
  68. }
  69. /**
  70. * The (read-only) base of the test sources (which may or may not
  71. * be the base of the java sources)
  72. */
  73. public final File testBaseDir;
  74. /** the parent of a temporary workspace, probably includes some others */
  75. public final File sandboxDir;
  76. /** a shared working dir */
  77. public final File workingDir;
  78. /** a shared classes dir */
  79. public final File classesDir;
  80. /** a run dir (which will be ignored in non-forking runs) */
  81. public final File runDir;
  82. /** staging directory for IAjcRun requiring files be copied, deleted, etc. */
  83. public final File stagingDir;
  84. /**
  85. * This manages creation and deletion of temporary directories.
  86. * We hold a reference so that our clients can signal whether
  87. * this should be deleted.
  88. */
  89. private final Validator validator; // XXX required after completing tests?
  90. /** original base of the original java sources, set by CompileRun.setup(..) */
  91. private File testBaseSrcDir;
  92. /** directories and libraries on the classpath, set by CompileRun.setup(..) */
  93. private File[] compileClasspath;
  94. private String bootClasspath;
  95. /** aspectpath entries, set by CompileRun.setup(..) */
  96. private File[] aspectpath;
  97. /** track whether classpath getter ran */
  98. private boolean gotClasspath;
  99. /** command shared between runs using sandbox - i.e., compiler */
  100. private ICommand command;
  101. /** track whether command getter ran */
  102. private boolean gotCommand;
  103. /** cache results of rendering final fields */
  104. private transient String toStringLeader;
  105. private transient boolean compilerRunInit;
  106. /** @throws IllegalArgumentException unless validator validates
  107. * testBaseDir as readable
  108. */
  109. public Sandbox(File testBaseDir, Validator validator) {
  110. LangUtil.throwIaxIfNull(validator, "validator");
  111. this.validator = validator;
  112. Sandbox.iaxRead(testBaseDir, "testBaseDir");
  113. this.testBaseDir = testBaseDir;
  114. {
  115. File baseDir = FileUtil.getTempDir("Sandbox");
  116. if (!baseDir.isAbsolute()) {
  117. baseDir = baseDir.getAbsoluteFile();
  118. }
  119. sandboxDir = baseDir;
  120. }
  121. Sandbox.iaxWrite(sandboxDir, "sandboxDir"); // XXX not really iax
  122. workingDir = FileUtil.makeNewChildDir(sandboxDir, "workingDir");
  123. Sandbox.iaxWrite(workingDir, "workingDir");
  124. classesDir = FileUtil.makeNewChildDir(sandboxDir, "classes");
  125. Sandbox.iaxWrite(classesDir, "classesDir");
  126. runDir = FileUtil.makeNewChildDir(sandboxDir, "run");
  127. Sandbox.iaxWrite(runDir, "runDir");
  128. stagingDir = FileUtil.makeNewChildDir(sandboxDir, "staging");
  129. Sandbox.iaxWrite(stagingDir, "stagingDir");
  130. validator.registerSandbox(this);
  131. }
  132. private String getToStringLeader() {
  133. if (null == toStringLeader) {
  134. toStringLeader = "Sandbox(" + sandboxDir.getName()
  135. + ", " + testBaseSrcDir.getName();
  136. }
  137. return toStringLeader;
  138. }
  139. /** @return "Sandbox(sandbox, src, classes)" with names only */
  140. public String toString() {
  141. return getToStringLeader() + ", " + classesDir.getName() + ")";
  142. }
  143. /** @return "Sandbox(sandbox, src, classes)" with paths */
  144. public String toLongString() {
  145. return getToStringLeader() + ", " + classesDir.getPath()
  146. + (null == command ? ", (null command)" : ", " + command) + ")";
  147. }
  148. void setCommand(ICommand command, CompilerRun caller) {
  149. LangUtil.throwIaxIfNull(caller, "caller");
  150. LangUtil.throwIaxIfNull(command, "command");
  151. LangUtil.throwIaxIfFalse(!gotCommand, "no command");
  152. this.command = command;
  153. }
  154. /** When test is completed, clear the compiler to avoid memory leaks */
  155. void clearCommand(AjcTest caller) {
  156. LangUtil.throwIaxIfNull(caller, "caller");
  157. if (null != command) { // need to add ICommand.quit()
  158. if (command instanceof AjcTaskCompileCommand) { // XXX urk!
  159. ((AjcTaskCompileCommand) command).quit();
  160. }
  161. command = null;
  162. }
  163. // also try to clear sandbox/filesystem.
  164. // If locked by suite, we can't.
  165. if (null != validator) {
  166. validator.deleteTempFiles(true);
  167. }
  168. }
  169. // /**
  170. // * Populate the staging directory by copying any files in the
  171. // * source directory ending with fromSuffix
  172. // * to the staging directory, after renaming them with toSuffix.
  173. // * If the source file name starts with "delete", then the
  174. // * corresponding file in the staging directory is deleting.
  175. // * @return a String[] of the files copied or deleted
  176. // * (path after suffix changes and relative to staging dir)
  177. // * @throws Error if no File using fromSuffix are found
  178. // */
  179. // String[] populateStagingDir(String fromSuffix, String toSuffix, IAjcRun caller) {
  180. // LangUtil.throwIaxIfNull(fromSuffix, "fromSuffix");
  181. // LangUtil.throwIaxIfNull(toSuffix, "toSuffix");
  182. // LangUtil.throwIaxIfNull(caller, "caller");
  183. //
  184. // ArrayList result = new ArrayList();
  185. // FileUtil.copyDir(
  186. // srcBase,
  187. // targetSrc,
  188. // fromSuffix,
  189. // toSuffix,
  190. // collector);
  191. //
  192. // final String canonicalFrom = srcBase.getCanonicalPath();
  193. // final Definition[] defs = getDefinitions(srcBase);
  194. // if ((null == defs) || (defs.length < 9)) {
  195. // throw new Error("did not get definitions");
  196. // }
  197. // MessageHandler compilerMessages = new MessageHandler();
  198. // StringBuffer commandLine = new StringBuffer();
  199. // for (int i = 1; result && (i < 10); i++) {
  200. // String fromSuffix = "." + i + "0.java";
  201. // // copy files, collecting as we go...
  202. // files.clear();
  203. // if (0 == files.size()) { // XXX detect incomplete?
  204. // break;
  205. // }
  206. //
  207. //
  208. // return (String[]) result.toArray(new String[0]);
  209. // }
  210. // XXX move to more general in FileUtil
  211. void reportClassDiffs(
  212. final IMessageHandler handler,
  213. IncCompilerRun caller,
  214. long classesDirStartTime,
  215. String[] expectedSources) {
  216. LangUtil.throwIaxIfFalse(0 < classesDirStartTime, "0 >= " + classesDirStartTime);
  217. boolean acceptPrefixes = true;
  218. Diffs diffs = org.aspectj.testing.util.FileUtil.dirDiffs(
  219. "classes",
  220. classesDir,
  221. classesDirStartTime,
  222. ".class",
  223. expectedSources,
  224. acceptPrefixes);
  225. diffs.report(handler, IMessage.ERROR);
  226. }
  227. // // XXX replace with IMessage-based implementation
  228. // // XXX move to more general in FileUtil
  229. // void reportClassesDirDiffs(final IMessageHandler handler, IncCompilerRun caller,
  230. // String[] expectedSources) {
  231. // // normalize sources to ignore
  232. // final ArrayList sources = new ArrayList();
  233. // if (!LangUtil.isEmpty(expectedSources)) {
  234. // for (int i = 0; i < expectedSources.length; i++) {
  235. // String srcPath = expectedSources[i];
  236. // int clip = FileUtil.sourceSuffixLength(srcPath);
  237. // if (0 != clip) {
  238. // srcPath = srcPath.substring(0, srcPath.length() - clip);
  239. // sources.add(FileUtil.weakNormalize(srcPath));
  240. // } else if (srcPath.endsWith(".class")) {
  241. // srcPath = srcPath.substring(0, srcPath.length() - 6);
  242. // sources.add(FileUtil.weakNormalize(srcPath));
  243. // } else {
  244. // MessageUtil.info(handler, "not source file: " + srcPath);
  245. // }
  246. // }
  247. // }
  248. //
  249. // // gather, normalize paths changed
  250. // final ArrayList changed = new ArrayList();
  251. // FileFilter touchedCollector = new FileFilter() {
  252. // public boolean accept(File file) {
  253. // if (file.lastModified() > classesDirTime) {
  254. // String path = file.getPath();
  255. // if (!path.endsWith(".class")) {
  256. // MessageUtil.info(handler, "changed file not a class: " + file);
  257. // } else {
  258. // String classPath = path.substring(0, path.length() - 6);
  259. // classPath = FileUtil.weakNormalize(classPath);
  260. // if (sources.contains(classPath)) {
  261. // sources.remove(classPath);
  262. // } else {
  263. // changed.add(classPath);
  264. // }
  265. // }
  266. // }
  267. // return false;
  268. // }
  269. // };
  270. // classesDir.listFiles(touchedCollector);
  271. //
  272. // // report any unexpected changes
  273. // Diffs diffs = new Diffs("classes", sources, changed, String.CASE_INSENSITIVE_ORDER);
  274. // diffs.report(handler, IMessage.ERROR);
  275. // }
  276. ICommand getCommand(CompilerRun caller) {
  277. LangUtil.throwIaxIfNull(caller, "caller");
  278. assertState(null != command, "command never set");
  279. return command;
  280. }
  281. ICommand getCommand(IncCompilerRun caller) {
  282. LangUtil.throwIaxIfNull(caller, "caller");
  283. assertState(null != command, "command never set");
  284. return command;
  285. }
  286. File getTestBaseSrcDir(IncCompilerRun caller) {
  287. LangUtil.throwIaxIfNull(caller, "caller");
  288. return testBaseSrcDir;
  289. }
  290. /**
  291. * Get the files with names (case-sensitive)
  292. * under the staging or test base directories.
  293. * @param names
  294. * @return
  295. */
  296. File[] findFiles(final String[] names) {
  297. ArrayList result = new ArrayList();
  298. NamesFilter filter = new NamesFilter(names);
  299. File[] bases = { testBaseDir, sandboxDir };
  300. for (int i = 0; i < bases.length; i++) {
  301. File base = bases[i];
  302. if ((null == base) || !base.canRead()) {
  303. continue;
  304. }
  305. result.addAll(Arrays.asList(FileUtil.listFiles(base, filter)));
  306. }
  307. return (File[]) result.toArray(new File[0]);
  308. }
  309. File getTestBaseSrcDir(JavaRun caller) {
  310. LangUtil.throwIaxIfNull(caller, "caller");
  311. return testBaseSrcDir;
  312. }
  313. void defaultTestBaseSrcDir(JavaRun caller) {
  314. LangUtil.throwIaxIfNull(caller, "caller");
  315. if (null != testBaseSrcDir) {
  316. throw new IllegalStateException("testBaseSrcDir not null");
  317. }
  318. testBaseSrcDir = testBaseDir;
  319. }
  320. static boolean readableDir(File dir) {
  321. return ((null != dir) && dir.isDirectory() && dir.canRead());
  322. }
  323. void compilerRunInit(CompilerRun caller, File testBaseSrcDir,
  324. File[] aspectPath, boolean aspectpathReadable,
  325. File[] classPath, boolean classpathReadable,
  326. String bootclassPath
  327. ) {
  328. if (null != testBaseSrcDir) {
  329. setTestBaseSrcDir(testBaseSrcDir, caller);
  330. }
  331. if ((null != aspectPath) && (0 < aspectPath.length)) {
  332. setAspectpath(aspectPath, aspectpathReadable, caller);
  333. }
  334. if ((null != classPath) && (0 < classPath.length)) {
  335. setClasspath(classPath, classpathReadable, caller);
  336. }
  337. setBootclasspath(bootclassPath, caller);
  338. compilerRunInit = true;
  339. }
  340. void javaRunInit(JavaRun caller) {
  341. if (!compilerRunInit) {
  342. testBaseSrcDir = testBaseDir;
  343. // default to aspectjrt.jar?
  344. compileClasspath = new File[0];
  345. }
  346. }
  347. /** @throws IllegalArgumentException unless a readable directory */
  348. private void setTestBaseSrcDir(File dir, CompilerRun caller) {
  349. LangUtil.throwIaxIfNull(caller, "caller");
  350. if ((null == dir) || !dir.isDirectory() || !dir.canRead()) {
  351. throw new IllegalArgumentException("bad test base src dir: " + dir);
  352. }
  353. testBaseSrcDir = dir;
  354. }
  355. /**
  356. * Set aspectpath.
  357. * @param readable if true, then throw IllegalArgumentException if not readable
  358. */
  359. private void setAspectpath(File[] files, boolean readable, CompilerRun caller) {
  360. LangUtil.throwIaxIfNull(files, "files");
  361. LangUtil.throwIaxIfNull(caller, "caller");
  362. assertState(null == aspectpath, "aspectpath already written");
  363. aspectpath = new File[files.length];
  364. for (int i = 0; i < files.length; i++) {
  365. LangUtil.throwIaxIfNull(files[i], "files[i]");
  366. if (readable && !files[i].canRead()) {
  367. throw new IllegalArgumentException("bad aspectpath entry: " + files[i]);
  368. }
  369. aspectpath[i] = files[i];
  370. }
  371. }
  372. /**
  373. * Set bootclasspath, presumed to be delimited by
  374. * File.pathSeparator and have valid entries.
  375. * @param bootClasspath
  376. * @param caller
  377. */
  378. private void setBootclasspath(String bootClasspath, CompilerRun caller) {
  379. this.bootClasspath = bootClasspath;
  380. }
  381. /**
  382. * Set compile classpath.
  383. * @param readable if true, then throw IllegalArgumentException if not readable
  384. */
  385. private void setClasspath(File[] files, boolean readable, CompilerRun caller) {
  386. LangUtil.throwIaxIfNull(files, "files");
  387. LangUtil.throwIaxIfNull(caller, "caller");
  388. assertState(!gotClasspath, "classpath already read");
  389. compileClasspath = new File[files.length];
  390. for (int i = 0; i < files.length; i++) {
  391. LangUtil.throwIaxIfNull(files[i], "files[i]");
  392. if (readable && !files[i].canRead()) {
  393. throw new IllegalArgumentException("bad classpath entry: " + files[i]);
  394. }
  395. compileClasspath[i] = files[i];
  396. }
  397. }
  398. // /**
  399. // * Get run classpath
  400. // * @param caller unused except to restrict usage to non-null JavaRun.
  401. // * @throws IllegalStateException if compileClasspath was not set.
  402. // * @throws IllegalArgumentException if caller is null
  403. // */
  404. // File[] getRunClasspath(JavaRun caller) {
  405. // LangUtil.throwIaxIfNull(caller, "caller");
  406. // assertState(null != compileClasspath, "classpath not set");
  407. // int compilePathLength = compileClasspath.length;
  408. // int aspectPathLength = (null == aspectpath ? 0 : aspectpath.length);
  409. // File[] result = new File[aspectPathLength + compilePathLength];
  410. // System.arraycopy(compileClasspath, 0, result, 0, compilePathLength);
  411. // if (0 < aspectPathLength) {
  412. // System.arraycopy(aspectpath, 0, result, compilePathLength, aspectPathLength);
  413. // }
  414. // return result;
  415. // }
  416. /**
  417. * Get directories for the run classpath by selecting them
  418. * from the compile classpath.
  419. * This ignores aspectpath since it may contain only jar files.
  420. * @param readable if true, omit non-readable directories
  421. */
  422. File[] getClasspathDirectories(
  423. boolean readable,
  424. JavaRun caller,
  425. boolean includeOutput) {
  426. LangUtil.throwIaxIfNull(caller, "caller");
  427. assertState(null != compileClasspath, "classpath not set");
  428. ArrayList result = new ArrayList();
  429. File[] src = compileClasspath;
  430. for (int i = 0; i < src.length; i++) {
  431. File f = src[i];
  432. if ((null != f) && (f.isDirectory()) && (!readable || f.canRead())) {
  433. result.add(f);
  434. }
  435. }
  436. if (includeOutput && (null != classesDir)
  437. && (!readable || classesDir.canRead())) {
  438. result.add(classesDir);
  439. }
  440. return (File[]) result.toArray(new File[0]);
  441. }
  442. /**
  443. * Get the jars belonging on the run classpath, including classpath
  444. * and aspectpath entries.
  445. * @param readable if true, omit non-readable directories
  446. */
  447. File[] getClasspathJars(boolean readable, JavaRun caller) {
  448. LangUtil.throwIaxIfNull(caller, "caller");
  449. assertState(null != compileClasspath, "classpath not set");
  450. ArrayList result = new ArrayList();
  451. File[][] src = new File[][] { compileClasspath, aspectpath };
  452. for (int i = 0; i < src.length; i++) {
  453. File[] paths = src[i];
  454. int len = (null == paths ? 0 : paths.length);
  455. for (int j = 0; j < len; j++) {
  456. File f = paths[j];
  457. if (FileUtil.isZipFile(f) && (!readable || f.canRead())) {
  458. result.add(f);
  459. }
  460. }
  461. }
  462. return (File[]) result.toArray(new File[0]);
  463. }
  464. /**
  465. * Get the list of aspect jars as a String.
  466. * @return String of classpath entries delimited internally by File.pathSeparator
  467. */
  468. String aspectpathToString(CompilerRun caller) {
  469. LangUtil.throwIaxIfNull(caller, "caller");
  470. return FileUtil.flatten(aspectpath, File.pathSeparator);
  471. }
  472. /**
  473. * Get the compile classpath as a String.
  474. * @return String of classpath entries delimited internally by File.pathSeparator
  475. */
  476. String classpathToString(CompilerRun caller) {
  477. LangUtil.throwIaxIfNull(caller, "caller");
  478. return FileUtil.flatten(compileClasspath, File.pathSeparator);
  479. }
  480. /**
  481. * Get the bootClasspath as a String.
  482. * @return String of bootclasspath entries delimited internally by File.pathSeparator
  483. */
  484. String getBootclasspath(CompilerRun caller) {
  485. LangUtil.throwIaxIfNull(caller, "caller");
  486. return bootClasspath;
  487. }
  488. /**
  489. * Get the bootClasspath as a String.
  490. * @return String of bootclasspath entries delimited internally by File.pathSeparator
  491. */
  492. String getBootclasspath(JavaRun caller) {
  493. LangUtil.throwIaxIfNull(caller, "caller");
  494. return bootClasspath;
  495. }
  496. private static class NamesFilter implements FileFilter {
  497. private final String[] names;
  498. private NamesFilter(String[] names) {
  499. this.names = names;
  500. }
  501. public boolean accept(File file) {
  502. if (null != file) {
  503. String name = file.getName();
  504. if ((null != name) && (null != names)) {
  505. for (int i = 0; i < names.length; i++) {
  506. if (name.equals(names[i])) {
  507. return true;
  508. }
  509. }
  510. }
  511. }
  512. return false;
  513. }
  514. }
  515. }