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 40KB

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