Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

AjcTestCase.java 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931
  1. /* *******************************************************************
  2. * Copyright (c) 2004 IBM Corporation
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Adrian Colyer,
  11. * ******************************************************************/
  12. package org.aspectj.tools.ajc;
  13. import java.io.ByteArrayOutputStream;
  14. import java.io.File;
  15. import java.io.FilePermission;
  16. import java.io.IOException;
  17. import java.io.OutputStream;
  18. import java.io.PrintStream;
  19. import java.lang.reflect.Constructor;
  20. import java.lang.reflect.InvocationTargetException;
  21. import java.lang.reflect.Method;
  22. import java.lang.reflect.ReflectPermission;
  23. import java.net.URL;
  24. import java.net.URLClassLoader;
  25. import java.security.Permission;
  26. import java.security.Policy;
  27. import java.security.ProtectionDomain;
  28. import java.util.ArrayList;
  29. import java.util.Collections;
  30. import java.util.Iterator;
  31. import java.util.List;
  32. import java.util.PropertyPermission;
  33. import java.util.StringTokenizer;
  34. import junit.framework.TestCase;
  35. import org.aspectj.bridge.IMessage;
  36. import org.aspectj.bridge.ISourceLocation;
  37. import org.aspectj.testing.util.TestUtil;
  38. /**
  39. * A TestCase class that acts as the superclass for all test cases wishing to drive the ajc compiler.
  40. * <p>
  41. * This class provides a number of utility methods that make programmatic testing of the compiler easy. See AjcTestCaseTest for a
  42. * couple of simple tests written using this class.
  43. * </p>
  44. * <p>
  45. * See the XMLBasedAjcTestCase subclass for TestCase class that can be used to drive compiler tests based on an ajcTests.xml format
  46. * test specification file.
  47. * </p>
  48. *
  49. * @see org.aspectj.tools.ajc.AjcTestCase.Message
  50. * @see org.aspectj.tools.ajc.AjcTestCase.MessageSpec
  51. * @see org.aspectj.tools.ajc.AjcTestCase.RunResult
  52. * @see org.aspectj.tools.ajc.AjcTestCaseTest
  53. * @see org.aspectj.testing.XMLBasedAjcTestCase
  54. */
  55. public class AjcTestCase extends TestCase {
  56. private RunResult lastRunResult;
  57. /**
  58. * The Ajc (compiler) instance used for the test. Created afresh during the test setup.
  59. */
  60. protected Ajc ajc;
  61. // see Ajc and AntSpec
  62. public static final String DEFAULT_CLASSPATH_ENTRIES = File.pathSeparator + ".." + File.separator + "bridge" + File.separator
  63. + "bin" + File.pathSeparator + ".." + File.separator + "util" + File.separator + "bin" + File.pathSeparator + ".."
  64. + File.separator + "loadtime" + File.separator + "bin" + File.pathSeparator + ".." + File.separator + "weaver"
  65. + File.separator + "bin" + File.pathSeparator + ".." + File.separator + "weaver5" + File.separator + "bin"
  66. + File.pathSeparator + ".." + File.separator + "asm" + File.separator + "bin" + File.pathSeparator + ".."
  67. + File.separator + "testing-client" + File.separator + "bin" + File.pathSeparator + ".." + File.separator + "runtime"
  68. + File.separator + "bin" + File.pathSeparator + ".." + File.separator + "aspectj5rt" + File.separator + "bin"
  69. + File.pathSeparator + ".." + File.separator + "org.aspectj.matcher" + File.separator + "bin" + File.pathSeparator
  70. + ".." + File.separator + "lib" + File.separator + "junit" + File.separator
  71. + "junit.jar"
  72. + File.pathSeparator
  73. + ".."
  74. + File.separator
  75. + "lib"
  76. + File.separator
  77. + "bcel"
  78. + File.separator
  79. + "bcel.jar"
  80. + File.pathSeparator
  81. + ".."
  82. + File.separator
  83. + "lib"
  84. + File.separator
  85. + "bcel"
  86. + File.separator
  87. + "bcel-verifier.jar"
  88. // When the build machine executes the tests, it is using code built into jars rather than code build into
  89. // bin directories. This means for the necessary types to be found we have to put these jars on the classpath:
  90. + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars" + File.separator + "bridge.jar"
  91. + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars" + File.separator + "util.jar"
  92. + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars" + File.separator
  93. + "org.aspectj.matcher.jar" + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars"
  94. + File.separator + "loadtime.jar" + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars"
  95. + File.separator + "weaver.jar" + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars"
  96. + File.separator + "weaver5.jar" + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars"
  97. + File.separator + "asm.jar" + File.pathSeparator + ".." + File.separator + "lib" + File.separator + "test"
  98. + File.separator + "testing-client.jar"
  99. // hmmm, this next one should perhaps point to an aj-build jar...
  100. + File.pathSeparator + ".." + File.separator + "lib" + File.separator + "test" + File.separator + "aspectjrt.jar";
  101. public static final String JAVA5_CLASSPATH_ENTRIES = File.pathSeparator + ".." + File.separator + "aspectj5rt" + File.separator
  102. + "bin" + File.pathSeparator + ".." + File.separator + "loadtime5" + File.separator + "bin" + File.pathSeparator + ".."
  103. + File.separator + "weaver5" + File.separator + "bin"
  104. + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars" + File.separator + "aspectj5rt.jar"
  105. + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars" + File.separator + "loadtime5.jar"
  106. + File.pathSeparator + ".." + File.separator + "aj-build" + File.separator + "jars" + File.separator + "weaver5.jar";
  107. /*
  108. * Save reference to real stderr and stdout before starting redirection
  109. */
  110. public final static PrintStream err = System.err;
  111. public final static PrintStream out = System.out;
  112. private final static DelegatingOutputStream delegatingErr;
  113. private final static DelegatingOutputStream delegatingOut;
  114. public final static boolean DEFAULT_VERBOSE = getBoolean("org.aspectj.tools.ajc.AjcTestCase.verbose", true);
  115. public final static boolean DEFAULT_ERR_VERBOSE = getBoolean("org.aspectj.tools.ajc.AjcTestCase.verbose.err", DEFAULT_VERBOSE);
  116. public final static boolean DEFAULT_OUT_VERBOSE = getBoolean("org.aspectj.tools.ajc.AjcTestCase.verbose.out", DEFAULT_VERBOSE);
  117. /**
  118. * Helper class that represents the specification of an individual message expected to be produced during a compilation run.
  119. * <p>
  120. * Message objects are combined in a MessageSpec which can then be passed to the various assertMessage methods.
  121. * </p>
  122. *
  123. * @see org.aspectj.tools.ajc.AjcTestCase.MessageSpec
  124. */
  125. public static class Message {
  126. private int line = -1;
  127. private String text;
  128. private String sourceFileName;
  129. private ISourceLocation[] seeAlsos;
  130. /**
  131. * Create a message that will match any compiler message on the given line.
  132. */
  133. public Message(int line) {
  134. this.line = line;
  135. }
  136. /**
  137. * Create a message that will match any compiler message on the given line, where the message text contains
  138. * <code>text</code>.
  139. */
  140. public Message(int line, String text) {
  141. this.line = line;
  142. this.text = text;
  143. }
  144. /**
  145. * Create a message that will match any compiler message on the given line, where the message text contains
  146. * <code>text</code>.
  147. * <p>
  148. * If srcFile is non-null, the source file location of the message must end with <code>srcFile</code>.
  149. * </p>
  150. * <p>
  151. * If <code>seeAlso</code> is non-null, each source location in seeAlso must be matched by an extraSourceLocation in the
  152. * message.
  153. * </p>
  154. */
  155. public Message(int line, String srcFile, String text, ISourceLocation[] seeAlso) {
  156. this.line = line;
  157. StringBuffer srcFileName = new StringBuffer();
  158. if (srcFile != null) {
  159. char[] chars = srcFile.toCharArray();
  160. for (int i = 0; i < chars.length; i++) {
  161. if ((chars[i] == '\\') || (chars[i] == '/')) {
  162. srcFileName.append(File.separator);
  163. } else {
  164. srcFileName.append(chars[i]);
  165. }
  166. }
  167. this.sourceFileName = srcFileName.toString();
  168. }
  169. this.text = text;
  170. this.seeAlsos = seeAlso;
  171. }
  172. /**
  173. * Create a message spec that will match any compiler message where the message text includes <code>text</code>.
  174. */
  175. public Message(String text) {
  176. this.text = text;
  177. }
  178. /**
  179. * Return true if this message spec matches the given compiler message.
  180. */
  181. public boolean matches(IMessage message) {
  182. ISourceLocation loc = message.getSourceLocation();
  183. if ((loc == null) && ((line != -1) || (sourceFileName != null))) {
  184. return false;
  185. }
  186. if (line != -1) {
  187. if (loc.getLine() != line) {
  188. return false;
  189. }
  190. }
  191. if (sourceFileName != null) {
  192. if (!loc.getSourceFile().getPath().endsWith(sourceFileName)) {
  193. return false;
  194. }
  195. }
  196. if (text != null) {
  197. if (message.getMessage().indexOf(text) == -1) {
  198. return false;
  199. }
  200. }
  201. if (seeAlsos != null) {
  202. List extraLocations = message.getExtraSourceLocations();
  203. if (extraLocations.size() != seeAlsos.length) {
  204. return false;
  205. }
  206. for (int i = 0; i < seeAlsos.length; i++) {
  207. if (!hasAMatch(extraLocations, seeAlsos[i])) {
  208. return false;
  209. }
  210. }
  211. }
  212. return true;
  213. }
  214. private boolean hasAMatch(List srcLocations, ISourceLocation sLoc) {
  215. for (Iterator iter = srcLocations.iterator(); iter.hasNext();) {
  216. ISourceLocation thisLoc = (ISourceLocation) iter.next();
  217. if (thisLoc.getLine() == sLoc.getLine()) {
  218. if (thisLoc.getSourceFile().getPath().equals(sLoc.getSourceFile().getPath())) {
  219. return true;
  220. }
  221. }
  222. }
  223. return false;
  224. }
  225. /**
  226. * Returns a string indicating what this <code>Message</code> will match.
  227. */
  228. public String toString() {
  229. StringBuffer buff = new StringBuffer();
  230. buff.append("message ");
  231. if (sourceFileName != null) {
  232. buff.append("in file ");
  233. buff.append(sourceFileName);
  234. buff.append(" ");
  235. }
  236. if (line != -1) {
  237. buff.append("on line ");
  238. buff.append(line);
  239. buff.append(" ");
  240. }
  241. if (text != null) {
  242. buff.append("containing text '");
  243. buff.append(text);
  244. buff.append("' ");
  245. }
  246. if (seeAlsos != null) {
  247. buff.append("\n\twith see alsos:");
  248. for (int i = 0; i < seeAlsos.length; i++) {
  249. buff.append("\t\t");
  250. buff.append(seeAlsos[i].getSourceFile().getPath());
  251. buff.append(":");
  252. buff.append(seeAlsos[i].getLine());
  253. }
  254. }
  255. return buff.toString();
  256. }
  257. }
  258. /**
  259. * Helper class that represents the specification of a set of messages expected to be produced from a compiler run.
  260. * <p>
  261. * Instances of MessageSpec are passed to the assertMessage methods to validate <code>CompilationResult</code>s.
  262. */
  263. public static class MessageSpec {
  264. /**
  265. * Convenience constant that matches a CompilationResult with any number of information messages, but no others.
  266. */
  267. public static final MessageSpec EMPTY_MESSAGE_SET = new MessageSpec(null, Collections.EMPTY_LIST, Collections.EMPTY_LIST,
  268. Collections.EMPTY_LIST, Collections.EMPTY_LIST);
  269. boolean ignoreInfos = true;
  270. public List fails;
  271. public List infos;
  272. public List warnings;
  273. public List errors;
  274. public List weaves;
  275. /**
  276. * Set to true to enable or disable comparison of information messages.
  277. */
  278. public void setInfoComparison(boolean enabled) {
  279. this.ignoreInfos = !enabled;
  280. }
  281. /**
  282. * True if information messages are not being included in matching.
  283. */
  284. public boolean isIgnoringInfoMessages() {
  285. return ignoreInfos;
  286. }
  287. /**
  288. * Create a message specification to test a CompilationResult for a given set of info, warning, error, and fail messages.
  289. *
  290. * @param infos The set of info messages to test for. Specifying a non-null value for this parameter enables info message
  291. * comparison.
  292. * @param warnings The set of warning messages to test for - can pass null to indicate empty set.
  293. * @param errors The set of error messages to test for - can pass null to indicate empty set.
  294. * @param fails The set of fail or abort messages to test for - can pass null to indicate empty set.
  295. */
  296. public MessageSpec(List infos, List warnings, List errors, List fails, List weaves) {
  297. if (infos != null) {
  298. this.infos = infos;
  299. ignoreInfos = false;
  300. } else {
  301. this.infos = Collections.EMPTY_LIST;
  302. }
  303. this.warnings = ((warnings == null) ? Collections.EMPTY_LIST : warnings);
  304. this.errors = ((errors == null) ? Collections.EMPTY_LIST : errors);
  305. this.fails = ((fails == null) ? Collections.EMPTY_LIST : fails);
  306. this.weaves = ((weaves == null) ? Collections.EMPTY_LIST : weaves);
  307. }
  308. /**
  309. * Create a message specification to test a CompilationResult for a given set of info, warning, and error messages. The
  310. * presence of any fail or abort messages in a CompilationResult will be a test failure.
  311. */
  312. public MessageSpec(List infos, List warnings, List errors) {
  313. this(infos, warnings, errors, null, null);
  314. }
  315. /**
  316. * Create a message specification to test a CompilationResult for a given set of warning, and error messages. The presence
  317. * of any fail or abort messages in a CompilationResult will be a test failure. Informational messages will be ignored.
  318. */
  319. public MessageSpec(List warnings, List errors) {
  320. this(null, warnings, errors, null, null);
  321. }
  322. }
  323. public static class EmptyMessageSpec extends MessageSpec {
  324. public EmptyMessageSpec() {
  325. super(null, null);
  326. }
  327. }
  328. /**
  329. * Helper class representing the results of running a test program built by the compiler. Provides access to the standard out
  330. * and error of the program, and the actual command that was executed.
  331. */
  332. public static class RunResult {
  333. private final String command;
  334. private final String stdOut;
  335. private final String stdErr;
  336. protected RunResult(String command, String stdOut, String stdErr) {
  337. this.command = command;
  338. this.stdOut = stdOut;
  339. this.stdErr = stdErr;
  340. }
  341. /**
  342. * Return the command that was executed, e.g. "java Driver".
  343. */
  344. public String getCommand() {
  345. return command;
  346. }
  347. /**
  348. * The standard output from the run.
  349. */
  350. public String getStdOut() {
  351. return stdOut;
  352. }
  353. /**
  354. * The standard error from the run.
  355. */
  356. public String getStdErr() {
  357. return stdErr;
  358. }
  359. /**
  360. * Returns the command that was executed to produce this result.
  361. */
  362. public String toString() {
  363. return command;
  364. }
  365. }
  366. /**
  367. * Assert that no (non-informational) messages where produced during a compiler run.
  368. */
  369. public void assertNoMessages(CompilationResult result) {
  370. assertNoMessages(result, "Not expecting any compiler messages to be produced");
  371. }
  372. /**
  373. * Assert that no (non-informational) messages where produced during a compiler run.
  374. */
  375. public void assertNoMessages(CompilationResult result, String message) {
  376. assertMessages(result, message, MessageSpec.EMPTY_MESSAGE_SET);
  377. }
  378. /**
  379. * Assert that messages in accordance with the <code>expected</code> message specification where produced during a compiler run.
  380. */
  381. public void assertMessages(CompilationResult result, MessageSpec expected) {
  382. assertMessages(result, "Compilation results did not meet expected messages specification", expected);
  383. }
  384. /**
  385. * Assert that messages in accordance with the <code>expected</code> message specification where produced during a compiler run.
  386. */
  387. public void assertMessages(CompilationResult result, String message, MessageSpec expected) {
  388. if (result == null)
  389. fail("Attempt to compare null compilation results against expected.");
  390. List missingFails = copyAll(expected.fails);
  391. List missingInfos = copyAll(expected.infos);
  392. List missingWarnings = copyAll(expected.warnings);
  393. List missingErrors = copyAll(expected.errors);
  394. List missingWeaves = copyAll(expected.weaves);
  395. List<IMessage> extraFails = copyAll(result.getFailMessages());
  396. List<IMessage> extraInfos = copyAll(result.getInfoMessages());
  397. List<IMessage> extraWarnings = copyAll(result.getWarningMessages());
  398. List<IMessage> extraErrors = copyAll(result.getErrorMessages());
  399. List<IMessage> extraWeaves = copyAll(result.getWeaveMessages());
  400. compare(expected.fails, result.getFailMessages(), missingFails, extraFails);
  401. compare(expected.warnings, result.getWarningMessages(), missingWarnings, extraWarnings);
  402. compare(expected.errors, result.getErrorMessages(), missingErrors, extraErrors);
  403. if (!expected.isIgnoringInfoMessages()) {
  404. compare(expected.infos, result.getInfoMessages(), missingInfos, extraInfos);
  405. }
  406. compare(expected.weaves, result.getWeaveMessages(), missingWeaves, extraWeaves);
  407. boolean infosEmpty = expected.isIgnoringInfoMessages() ? true : (missingInfos.isEmpty() && extraInfos.isEmpty());
  408. if (!(missingFails.isEmpty() && missingWarnings.isEmpty() && missingErrors.isEmpty() && missingWeaves.isEmpty()
  409. && extraFails.isEmpty() && extraWarnings.isEmpty() && extraErrors.isEmpty() && extraWeaves.isEmpty() && infosEmpty)) {
  410. StringBuffer failureReport = new StringBuffer(message);
  411. failureReport.append("\n");
  412. if (!expected.isIgnoringInfoMessages()) {
  413. addMissing(failureReport, "info", missingInfos);
  414. }
  415. addMissing(failureReport, "warning", missingWarnings);
  416. addMissing(failureReport, "error", missingErrors);
  417. addMissing(failureReport, "fail", missingFails);
  418. addMissing(failureReport, "weaveInfo", missingWeaves);
  419. if (!expected.isIgnoringInfoMessages()) {
  420. addExtra(failureReport, "info", extraInfos);
  421. }
  422. addExtra(failureReport, "warning", extraWarnings);
  423. addExtra(failureReport, "error", extraErrors);
  424. addExtra(failureReport, "fail", extraFails);
  425. addExtra(failureReport, "weaveInfo", extraWeaves);
  426. failureReport.append("\ncommand was: ajc");
  427. String[] args = result.getArgs();
  428. for (int i = 0; i < args.length; i++) {
  429. failureReport.append(" ");
  430. failureReport.append(args[i]);
  431. }
  432. String report = failureReport.toString();
  433. System.err.println(failureReport);
  434. fail(message + "\n" + report);
  435. }
  436. }
  437. /**
  438. * Helper method to build a new message list for passing to a MessageSpec.
  439. */
  440. protected List<Message> newMessageList(Message m1) {
  441. List<Message> ret = new ArrayList<Message>();
  442. ret.add(m1);
  443. return ret;
  444. }
  445. /**
  446. * Helper method to build a new message list for passing to a MessageSpec.
  447. */
  448. protected List<Message> newMessageList(Message m1, Message m2) {
  449. List<Message> ret = new ArrayList<Message>();
  450. ret.add(m1);
  451. ret.add(m2);
  452. return ret;
  453. }
  454. /**
  455. * Helper method to build a new message list for passing to a MessageSpec.
  456. */
  457. protected List<Message> newMessageList(Message m1, Message m2, Message m3) {
  458. List<Message> ret = new ArrayList<Message>();
  459. ret.add(m1);
  460. ret.add(m2);
  461. ret.add(m3);
  462. return ret;
  463. }
  464. /**
  465. * Helper method to build a new message list for passing to a MessageSpec.
  466. */
  467. protected List newMessageList(Message[] messages) {
  468. List ret = new ArrayList();
  469. for (int i = 0; i < messages.length; i++) {
  470. ret.add(messages[i]);
  471. }
  472. return ret;
  473. }
  474. /**
  475. * Perform a compilation and return the result.
  476. *
  477. * @param baseDir the base directory relative to which all relative paths and directories in the arguments will be interpreted.
  478. * @param args the compiler arguments, as you would specify on the command-line. See the Ajc class for a description of the
  479. * argument processing done in order to run the compilation in a sandbox.
  480. * @see org.aspectj.tools.ajc.Ajc
  481. */
  482. public CompilationResult ajc(File baseDir, String[] args) {
  483. try {
  484. ajc.setBaseDir(baseDir);
  485. args = fixupArgs(args);
  486. return ajc.compile(args);
  487. } catch (IOException ioEx) {
  488. fail("IOException thrown during compilation: " + ioEx);
  489. }
  490. return null;
  491. }
  492. public File getSandboxDirectory() {
  493. return ajc.getSandboxDirectory();
  494. }
  495. /**
  496. * Indicate whether or not the sandbox should be emptied before the next compile.
  497. *
  498. * @see org.aspectj.tools.ajc.Ajc#setShouldEmptySandbox(boolean)
  499. */
  500. public void setShouldEmptySandbox(boolean empty) {
  501. ajc.setShouldEmptySandbox(empty);
  502. }
  503. public RunResult getLastRunResult() {
  504. return lastRunResult;
  505. }
  506. public void testNothingForAntJUnit() {
  507. }
  508. /**
  509. * Run the given class (main method), and return the result in a RunResult. The program runs with a classpath containing the
  510. * sandbox directory, runtime, testing-client, bridge, and util projects (all used by the Tester class), and any jars in the
  511. * sandbox.
  512. */
  513. public RunResult run(String className) {
  514. return run(className, new String[0], null);
  515. }
  516. public RunResult run(String className, String[] args, String classpath) {
  517. return run(className, args, null, false);
  518. }
  519. /**
  520. * Run the given class, and return the result in a RunResult. The program runs with a classpath containing the sandbox
  521. * directory, runtime, testing-client, bridge, and util projects (all used by the Tester class), and any jars in the sandbox.
  522. *
  523. * @param args the arguments to pass to the program.
  524. * @param classpath the execution classpath, the sandbox directory, runtime, testing-client, bridge, and util projects will all
  525. * be appended to the classpath, as will any jars in the sandbox.
  526. */
  527. public RunResult run(String className, String[] args, final String classpath, boolean useLTW) {
  528. if (args != null) {
  529. for (int i = 0; i < args.length; i++) {
  530. args[i] = substituteSandbox(args[i]);
  531. }
  532. }
  533. lastRunResult = null;
  534. StringBuffer cp = new StringBuffer();
  535. if (classpath != null) {
  536. // allow replacing this special variable, rather than copying all files to allow tests of jars that don't end in .jar
  537. cp.append(substituteSandbox(classpath));
  538. cp.append(File.pathSeparator);
  539. }
  540. cp.append(ajc.getSandboxDirectory().getAbsolutePath());
  541. getAnyJars(ajc.getSandboxDirectory(), cp);
  542. URLClassLoader sandboxLoader;
  543. URLClassLoader testLoader = (URLClassLoader) getClass().getClassLoader();
  544. ClassLoader parentLoader = testLoader.getParent();
  545. /* Sandbox -> AspectJ -> Extension -> Bootstrap */
  546. if (useLTW) {
  547. /*
  548. * Create a new AspectJ class loader using the existing test CLASSPATH and any missing Java 5 projects
  549. */
  550. URL[] testUrls = testLoader.getURLs();
  551. URL[] java5Urls = getURLs(JAVA5_CLASSPATH_ENTRIES);
  552. URL[] urls = new URL[testUrls.length + java5Urls.length];
  553. System.arraycopy(testUrls, 0, urls, 0, testUrls.length);
  554. System.arraycopy(java5Urls, 0, urls, testUrls.length, java5Urls.length);
  555. // ClassLoader aspectjLoader = new URLClassLoader(getURLs(DEFAULT_CLASSPATH_ENTRIES),parent);
  556. ClassLoader aspectjLoader = new URLClassLoader(urls, parentLoader);
  557. URL[] sandboxUrls = getURLs(cp.toString());
  558. sandboxLoader = createWeavingClassLoader(sandboxUrls, aspectjLoader);
  559. // sandboxLoader = createWeavingClassLoader(sandboxUrls,testLoader);
  560. }
  561. /* Sandbox + AspectJ -> Extension -> Bootstrap */
  562. else {
  563. cp.append(DEFAULT_CLASSPATH_ENTRIES);
  564. URL[] urls = getURLs(cp.toString());
  565. sandboxLoader = new URLClassLoader(urls, parentLoader);
  566. }
  567. StringBuffer command = new StringBuffer("java -classpath ");
  568. command.append(cp.toString());
  569. command.append(" ");
  570. command.append(className);
  571. for (int i = 0; i < args.length; i++) {
  572. command.append(" ");
  573. command.append(args[i]);
  574. }
  575. // try {
  576. // // Enable the security manager
  577. // Policy.setPolicy(new MyPolicy());
  578. // SecurityManager sm = new SecurityManager();
  579. // System.setSecurityManager(sm);
  580. // } catch (SecurityException se) {
  581. // // SecurityManager already set
  582. // }
  583. ByteArrayOutputStream baosOut = new ByteArrayOutputStream();
  584. ByteArrayOutputStream baosErr = new ByteArrayOutputStream();
  585. ClassLoader contexClassLoader = Thread.currentThread().getContextClassLoader();
  586. try {
  587. try {
  588. Class testerClass = sandboxLoader.loadClass("org.aspectj.testing.Tester");
  589. Method setBaseDir = testerClass.getDeclaredMethod("setBASEDIR", new Class[] { File.class });
  590. setBaseDir.invoke(null, new Object[] { ajc.getSandboxDirectory() });
  591. } catch (InvocationTargetException itEx) {
  592. fail("Unable to prepare org.aspectj.testing.Tester for test run: " + itEx.getTargetException());
  593. } catch (Exception ex) {
  594. fail("Unable to prepare org.aspectj.testing.Tester for test run: " + ex);
  595. }
  596. startCapture(baosErr, baosOut);
  597. /* Frameworks like XML use context class loader for dynamic loading */
  598. Thread.currentThread().setContextClassLoader(sandboxLoader);
  599. Class toRun = sandboxLoader.loadClass(className);
  600. Method mainMethod = toRun.getMethod("main", new Class[] { String[].class });
  601. mainMethod.invoke(null, new Object[] { args });
  602. } catch (ClassNotFoundException cnf) {
  603. fail("Can't find class: " + className);
  604. } catch (NoSuchMethodException nsm) {
  605. fail(className + " does not have a main method");
  606. } catch (IllegalAccessException illEx) {
  607. fail("main method in class " + className + " is not public");
  608. } catch (InvocationTargetException invTgt) {
  609. // the main method threw an exception...
  610. fail("Exception thrown by " + className + ".main(String[]) :" + invTgt.getTargetException());
  611. } finally {
  612. // try {
  613. // // Enable the security manager
  614. // SecurityManager sm = new SecurityManager();
  615. // System.setSecurityManager(null);
  616. // } catch (SecurityException se) {
  617. // se.printStackTrace();
  618. // // SecurityManager already set
  619. // }
  620. Thread.currentThread().setContextClassLoader(contexClassLoader);
  621. stopCapture(baosErr, baosOut);
  622. lastRunResult = new RunResult(command.toString(), new String(baosOut.toByteArray()), new String(baosErr.toByteArray()));
  623. }
  624. return lastRunResult;
  625. }
  626. // static class MyPolicy extends Policy {
  627. //
  628. // @Override
  629. // public boolean implies(ProtectionDomain domain, Permission permission) {
  630. // // if (permission != SecurityConstants.GET_POLICY_PERMISSION) {
  631. // // // System.out.println(domain + " " + permission.getName());
  632. // // System.out.println(permission.getName());
  633. // // }
  634. // // if (true) {
  635. // // return true;
  636. // // }
  637. // if (permission instanceof PropertyPermission) {
  638. // return true;
  639. // }
  640. // if (permission instanceof RuntimePermission) {
  641. // return true;
  642. // }
  643. // if (permission instanceof FilePermission) {
  644. // // System.out.println(permission);
  645. // return true;
  646. // }
  647. // if (permission instanceof ReflectPermission) {
  648. // return true;
  649. // }
  650. // // System.out.println(permission);
  651. // return super.implies(domain, permission);
  652. // // return true;
  653. // }
  654. // }
  655. /*
  656. * Must create weaving class loader reflectively using new parent so we don't have a reference to a World loaded from CLASSPATH
  657. * which won't be able to resolve Java 5 specific extensions and may cause ClassCastExceptions
  658. */
  659. private URLClassLoader createWeavingClassLoader(URL[] urls, ClassLoader parent) {
  660. URLClassLoader loader = null;
  661. try {
  662. Class loaderClazz = Class.forName("org.aspectj.weaver.loadtime.WeavingURLClassLoader", false, parent);
  663. Class[] parameterTypes = new Class[] { urls.getClass(), ClassLoader.class };
  664. Object[] parameters = new Object[] { urls, parent };
  665. Constructor constructor = loaderClazz.getConstructor(parameterTypes);
  666. loader = (URLClassLoader) constructor.newInstance(parameters);
  667. } catch (InvocationTargetException ex) {
  668. ex.printStackTrace();
  669. fail("Cannot create weaving class loader: " + ex.getTargetException());
  670. } catch (Exception ex) {
  671. ex.printStackTrace();
  672. fail("Cannot create weaving class loader: " + ex.toString());
  673. }
  674. return loader;
  675. }
  676. private URL[] getURLs(String classpath) {
  677. StringTokenizer strTok = new StringTokenizer(classpath, File.pathSeparator);
  678. URL[] urls = new URL[strTok.countTokens()];
  679. try {
  680. for (int i = 0; i < urls.length; i++) {
  681. urls[i] = new File(strTok.nextToken()).getCanonicalFile().toURL();
  682. }
  683. } catch (Exception malEx) {
  684. fail("Bad classpath specification: " + classpath);
  685. }
  686. return urls;
  687. }
  688. private String substituteSandbox(String classpath) {
  689. // the longhand form of the non 1.3 API: classpath.replace("$sandbox", ajc.getSandboxDirectory().getAbsolutePath());
  690. while (classpath.indexOf("$sandbox") != -1) {
  691. int pos = classpath.indexOf("$sandbox");
  692. String firstbit = classpath.substring(0, pos);
  693. String endbit = classpath.substring(pos + 8);
  694. classpath = firstbit + ajc.getSandboxDirectory().getAbsolutePath() + endbit;
  695. }
  696. return classpath;
  697. }
  698. /**
  699. * Any central pre-processing of args. This supplies aspectjrt.jar if available and classpath not set.
  700. *
  701. * @param args the String[] args to fix up
  702. * @return the String[] args to use
  703. */
  704. protected String[] fixupArgs(String[] args) {
  705. if (null == args) {
  706. return null;
  707. }
  708. int cpIndex = -1;
  709. boolean hasruntime = false;
  710. for (int i = 0; i < args.length - 1; i++) {
  711. args[i] = adaptToPlatform(args[i]);
  712. if ("-classpath".equals(args[i])) {
  713. cpIndex = i;
  714. args[i + 1] = substituteSandbox(args[i + 1]);
  715. String next = args[i + 1];
  716. hasruntime = ((null != next) && (-1 != next.indexOf("aspectjrt.jar")));
  717. }
  718. }
  719. if (-1 == cpIndex) {
  720. String[] newargs = new String[args.length + 2];
  721. newargs[0] = "-classpath";
  722. newargs[1] = TestUtil.aspectjrtPath().getPath();
  723. System.arraycopy(args, 0, newargs, 2, args.length);
  724. args = newargs;
  725. } else {
  726. if (!hasruntime) {
  727. cpIndex++;
  728. String[] newargs = new String[args.length];
  729. System.arraycopy(args, 0, newargs, 0, args.length);
  730. newargs[cpIndex] = args[cpIndex] + File.pathSeparator + TestUtil.aspectjrtPath().getPath();
  731. args = newargs;
  732. }
  733. }
  734. return args;
  735. }
  736. private String adaptToPlatform(String s) {
  737. String ret = s.replace(';', File.pathSeparatorChar);
  738. // ret = ret.replace(':',File.pathSeparatorChar);
  739. return ret;
  740. }
  741. private List copyAll(List in) {
  742. if (in == Collections.EMPTY_LIST)
  743. return in;
  744. List out = new ArrayList();
  745. for (Iterator iter = in.iterator(); iter.hasNext();) {
  746. out.add(iter.next());
  747. }
  748. return out;
  749. }
  750. /**
  751. * Compare the set of expected messages against the set of actual messages, leaving in missingElements the set of messages that
  752. * were expected but did not occur, and in extraElements the set of messages that occured but were not excpected
  753. *
  754. * @param expected the expected messages
  755. * @param actual the actual messages
  756. * @param missingElements the missing messages, when passed in must contain all of the expected messages
  757. * @param extraElements the additional messages, when passed in must contain all of the actual messages
  758. */
  759. private void compare(List expected, List actual, List missingElements, List extraElements) {
  760. for (Iterator expectedIter = expected.iterator(); expectedIter.hasNext();) {
  761. Message expectedMessage = (Message) expectedIter.next();
  762. for (Iterator actualIter = actual.iterator(); actualIter.hasNext();) {
  763. IMessage actualMessage = (IMessage) actualIter.next();
  764. if (expectedMessage.matches(actualMessage)) {
  765. missingElements.remove(expectedMessage);
  766. extraElements.remove(actualMessage);
  767. }
  768. }
  769. }
  770. }
  771. private void addMissing(StringBuffer buff, String type, List messages) {
  772. if (!messages.isEmpty()) {
  773. buff.append("Missing expected ");
  774. buff.append(type);
  775. buff.append(" messages:\n");
  776. for (Iterator iter = messages.iterator(); iter.hasNext();) {
  777. buff.append("\t");
  778. buff.append(iter.next().toString());
  779. buff.append("\n");
  780. }
  781. }
  782. }
  783. private void addExtra(StringBuffer buff, String type, List messages) {
  784. if (!messages.isEmpty()) {
  785. buff.append("Unexpected ");
  786. buff.append(type);
  787. buff.append(" messages:\n");
  788. for (Iterator iter = messages.iterator(); iter.hasNext();) {
  789. buff.append("\t");
  790. buff.append(iter.next().toString());
  791. buff.append("\n");
  792. }
  793. }
  794. }
  795. // add any jars in the directory to the classpath
  796. private void getAnyJars(File dir, StringBuffer buff) {
  797. File[] files = dir.listFiles();
  798. for (int i = 0; i < files.length; i++) {
  799. if (files[i].getName().endsWith(".jar")) {
  800. buff.append(File.pathSeparator);
  801. buff.append(files[i].getAbsolutePath());
  802. } else if (files[i].isDirectory()) {
  803. getAnyJars(files[i], buff);
  804. }
  805. }
  806. }
  807. private static void startCapture(OutputStream errOS, OutputStream outOS) {
  808. delegatingErr.add(errOS);
  809. delegatingOut.add(outOS);
  810. delegatingErr.setVerbose(DEFAULT_ERR_VERBOSE);
  811. delegatingOut.setVerbose(DEFAULT_OUT_VERBOSE);
  812. }
  813. private static void stopCapture(OutputStream errOS, OutputStream outOS) {
  814. delegatingErr.setVerbose(true);
  815. delegatingOut.setVerbose(true);
  816. delegatingErr.remove(errOS);
  817. delegatingOut.remove(outOS);
  818. }
  819. private static boolean getBoolean(String name, boolean def) {
  820. String defaultValue = String.valueOf(def);
  821. String value = System.getProperty(name, defaultValue);
  822. return Boolean.valueOf(value).booleanValue();
  823. }
  824. /*
  825. * (non-Javadoc)
  826. *
  827. * @see junit.framework.TestCase#setUp()
  828. */
  829. protected void setUp() throws Exception {
  830. super.setUp();
  831. ajc = new Ajc();
  832. }
  833. /*
  834. * (non-Javadoc)
  835. *
  836. * @see junit.framework.TestCase#tearDown()
  837. */
  838. protected void tearDown() throws Exception {
  839. super.tearDown();
  840. // ajc = null;
  841. }
  842. static {
  843. // new RuntimeException("*** AjcTestCase.<clinit>()").printStackTrace();
  844. delegatingErr = new DelegatingOutputStream(err);
  845. System.setErr(new PrintStream(delegatingErr));
  846. delegatingOut = new DelegatingOutputStream(out);
  847. System.setOut(new PrintStream(delegatingOut));
  848. }
  849. }