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

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