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.

AjcTestCase.java 37KB

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