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

11 years ago
11 years ago
11 years ago
11 years ago
15 years ago
15 years ago
11 years ago
12 years ago
12 years ago
12 years ago
12 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
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. }