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

AjcTestCase.java 39KB

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