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.

AntSpec.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /*******************************************************************************
  2. * Copyright (c) 2005 Contributors.
  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. * Alexandre Vasseur initial implementation
  11. *******************************************************************************/
  12. package org.aspectj.testing;
  13. import java.io.BufferedReader;
  14. import java.io.File;
  15. import java.io.FileReader;
  16. import java.util.StringTokenizer;
  17. import org.apache.tools.ant.BuildEvent;
  18. import org.apache.tools.ant.BuildException;
  19. import org.apache.tools.ant.DefaultLogger;
  20. import org.apache.tools.ant.Project;
  21. import org.apache.tools.ant.ProjectHelper;
  22. import org.apache.tools.ant.Target;
  23. import org.apache.tools.ant.taskdefs.Java;
  24. import org.apache.tools.ant.types.Path;
  25. import org.aspectj.tools.ajc.AjcTestCase;
  26. import static org.aspectj.util.LangUtil.is16VMOrGreater;
  27. /**
  28. * Element that allow to run an abritrary Ant target in a sandbox.
  29. * <p/>
  30. * Such a spec is used in a {@code <ajc-test><ant file="myAnt.xml" [target="..."] [verbose="true"]/>} XML element. The
  31. * {@code target} is optional. If not set, default <i>myAnt.xml</i> target is used. The {@code file} is looked up from
  32. * the {@code <ajc-test dir="..."/>} attribute. If @{code verbose} is set to {@code true}, the {@code ant -v} output is
  33. * piped, else nothing is reported except errors.
  34. * <p/>
  35. * The called Ant target benefits from some implicit variables:
  36. * <ul>
  37. * <li>{@code ${aj.sandbox}} points to the test current sandbox folder.</li>
  38. * <li>
  39. * {@code ${aj.path}} is an Ant refid on the classpath formed with the sandbox folder + ltw + the AjcTestCase
  40. * classpath (i.e. usually aspectjrt, junit, and testing infra).
  41. * </li>
  42. * <li>
  43. * For Java 16+, {@code ${aj.addOpensKey}} and {@code ${aj.addOpensValue}} together add {@code --add-opens} and
  44. * {@code java.base/java.lang=ALL-UNNAMED} as JVM parameters. They have to be used together and consecutively in
  45. * this order as {@code jvmarg} parameter tags inside the {@code java} Ant task.
  46. * </li>
  47. * </ul>
  48. * <p/>
  49. * Element {@code <stdout><line text="..">} and {@code <stderr><line text="..">} can be used. For now, a full match is
  50. * performed on the output of the runned target only (not the whole Ant invocation). This is experimental and you are
  51. * advised to use a {@code <junit>} task instead or a {@code <java>} whose main throws some exception upon failure.
  52. *
  53. * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
  54. */
  55. public class AntSpec implements ITestStep {
  56. public static String outputFolders(String... modules) {
  57. StringBuilder s = new StringBuilder();
  58. for (String module: modules) {
  59. s.append(File.pathSeparator + ".." +File.separator + module + File.separator + "target" + File.separator + "classes");
  60. }
  61. return s.toString();
  62. }
  63. // ALSO SEE AJC
  64. private final static String DEFAULT_LTW_CLASSPATH_ENTRIES =
  65. outputFolders("asm", "bridge", "loadtime", "weaver", "org.aspectj.matcher", "bcel-builder");
  66. private boolean m_verbose = false;
  67. private AjcTest m_ajcTest;
  68. private OutputSpec m_stdErrSpec;
  69. private OutputSpec m_stdOutSpec;
  70. private String m_antFile;
  71. private String m_antTarget;
  72. public void execute(final AjcTestCase inTestCase) {
  73. final String failMessage = "test \"" + m_ajcTest.getTitle() + "\" failed: ";
  74. File buildFile = new File(m_ajcTest.getDir() + File.separatorChar + m_antFile);
  75. if (!buildFile.exists()) {
  76. AjcTestCase.fail(failMessage + "no such Ant file " + buildFile.getAbsolutePath());
  77. }
  78. Project p = new Project();
  79. final StringBuffer stdout = new StringBuffer();
  80. final StringBuffer stderr = new StringBuffer();
  81. final StringBuffer verboseLog = new StringBuffer();
  82. try {
  83. // read the Ant file
  84. p.init();
  85. p.setUserProperty("ant.file", buildFile.getAbsolutePath());
  86. // setup aj.sandbox
  87. p.setUserProperty("aj.sandbox", inTestCase.getSandboxDirectory().getAbsolutePath());
  88. // setup aj.dir "modules" folder
  89. p.setUserProperty("aj.root", new File("..").getAbsolutePath());
  90. // On Java 16+, LTW no longer works without this parameter. Add the argument here and not in AjcTestCase::run,
  91. // because even if 'useLTW' and 'useFullLTW' are not set, we might in the future have tests for weaver attachment
  92. // during runtime. See also docs/dist/doc/README-1.8.7.html.
  93. //
  94. // Attention: Ant 1.6.3 under Linux neither likes "" (empty string) nor " " (space), on Windows it would not be
  95. // a problem. So we use "_dummy" Java system properties, even though they pollute the command line.
  96. p.setUserProperty("aj.addOpensKey", is16VMOrGreater() ? "--add-opens" : "-D_dummy");
  97. p.setUserProperty("aj.addOpensValue", is16VMOrGreater() ? "java.base/java.lang=ALL-UNNAMED" : "-D_dummy");
  98. // create the test implicit path aj.path that contains the sandbox + regular test infra path
  99. Path path = new Path(p, inTestCase.getSandboxDirectory().getAbsolutePath());
  100. populatePath(path, DEFAULT_LTW_CLASSPATH_ENTRIES);
  101. populatePath(path, AjcTestCase.DEFAULT_CLASSPATH_ENTRIES);
  102. p.addReference("aj.path", path);
  103. p.setBasedir(buildFile.getAbsoluteFile().getParent());
  104. ProjectHelper helper = ProjectHelper.getProjectHelper();
  105. helper.parse(p, buildFile);
  106. // use default target if no target specified
  107. if (m_antTarget == null) {
  108. m_antTarget = p.getDefaultTarget();
  109. }
  110. // make sure we listen for failure
  111. DefaultLogger consoleLogger = new DefaultLogger() {
  112. public void buildFinished(BuildEvent event) {
  113. super.buildFinished(event);
  114. if (event.getException() != null) {
  115. try {
  116. File antout = new File(inTestCase.getSandboxDirectory().getAbsolutePath(), "antout");
  117. if (antout.exists()) {
  118. stdout.append("Forked java command stdout:\n");
  119. System.out.println("Forked java command stdout:");
  120. FileReader fr = new FileReader(antout);
  121. BufferedReader br = new BufferedReader(fr);
  122. String line = br.readLine();
  123. while (line != null) {
  124. stdout.append(line).append("\n");
  125. System.out.println(stdout);
  126. line = br.readLine();
  127. }
  128. fr.close();
  129. }
  130. File anterr = new File(inTestCase.getSandboxDirectory().getAbsolutePath(), "anterr");
  131. if (anterr.exists()) {
  132. stdout.append("Forked java command stderr:\n");
  133. System.out.println("Forked java command stderr:");
  134. FileReader fr = new FileReader(anterr);
  135. BufferedReader br = new BufferedReader(fr);
  136. String line = br.readLine();
  137. while (line != null) {
  138. stdout.append(line).append("\n");
  139. System.out.println(stdout);
  140. line = br.readLine();
  141. }
  142. fr.close();
  143. }
  144. } catch (Exception e) {
  145. String exceptionMessage = "Exception whilst loading forked java task output " + e.getMessage() + "\n";
  146. System.out.println(exceptionMessage);
  147. e.printStackTrace();
  148. stdout.append(exceptionMessage);
  149. }
  150. // AjcTestCase.fail(failMessage + "failure " + event.getException());
  151. AjcTestCase.fail(event.getException() + "\n" + verboseLog + stdout + stderr);
  152. }
  153. }
  154. public void targetFinished(BuildEvent event) {
  155. super.targetFinished(event);
  156. if (event.getException() != null) {
  157. AjcTestCase.fail(failMessage + "failure in '" + event.getTarget() + "' " + event.getException());
  158. }
  159. }
  160. public void messageLogged(BuildEvent event) {
  161. super.messageLogged(event);
  162. Target target = event.getTarget();
  163. if (target != null && m_antTarget.equals(target.getName()) && event.getSource() instanceof Java)
  164. switch (event.getPriority()) {
  165. case Project.MSG_INFO:
  166. stdout.append(event.getMessage()).append('\n');
  167. break;
  168. case Project.MSG_WARN:
  169. stderr.append(event.getMessage()).append('\n');
  170. break;
  171. case Project.MSG_VERBOSE:
  172. verboseLog.append(event.getMessage()).append('\n');
  173. break;
  174. }
  175. }
  176. };
  177. consoleLogger.setErrorPrintStream(System.err);
  178. consoleLogger.setOutputPrintStream(System.out);
  179. consoleLogger.setMessageOutputLevel(m_verbose ? Project.MSG_VERBOSE : Project.MSG_ERR);
  180. p.addBuildListener(consoleLogger);
  181. } catch (Throwable t) {
  182. AjcTestCase.fail(failMessage + "invalid Ant script :" + t.toString());
  183. }
  184. try {
  185. p.setProperty("verbose", "true");
  186. p.fireBuildStarted();
  187. p.executeTarget(m_antTarget);
  188. p.fireBuildFinished(null);
  189. } catch (BuildException e) {
  190. p.fireBuildFinished(e);
  191. } catch (Throwable t) {
  192. AjcTestCase.fail(failMessage + "error when invoking target :" + t.toString());
  193. }
  194. // J12: Line can start with e.g. "OpenJDK 64-Bit Server VM" or "Java HotSpot(TM) 64-Bit Server VM".
  195. // J21: Line can start with e.g. "[0.016s][warning][cds]".
  196. // Therefore, we have to match a substring instead of a whole line.
  197. // Even worse, before J21, the warning appears on stdErr, but in J21+, it appears on stdOut.
  198. final String regexArchivedNonSystemClasses = "[^\n]+( warning:|\\[warning]\\[cds]) " +
  199. "Archived non-system classes are disabled because the java.system.class.loader property is specified " +
  200. ".*org.aspectj.weaver.loadtime.WeavingURLClassLoader[^\n]+\n?";
  201. /* See if stdout/stderr matches test specification */
  202. if (m_stdOutSpec != null) {
  203. String stdout2 = stdout.toString();
  204. stdout2 = stdout2.replaceAll(regexArchivedNonSystemClasses, "");
  205. m_stdOutSpec.matchAgainst(stdout2);
  206. }
  207. if (m_stdErrSpec != null) {
  208. String stderr2 = stderr.toString();
  209. // Working around this ridiculous message that still comes out of Java7 builds:
  210. if (stderr2.contains("Class JavaLaunchHelper is implemented in both") && stderr2.indexOf('\n')!=-1) {
  211. stderr2 = stderr2.replaceAll("objc\\[[0-9]*\\]: Class JavaLaunchHelper is implemented in both [^\n]*\n","");
  212. }
  213. // JDK 11 is complaining about illegal reflective calls - temporary measure ignore these - does that get all tests passing and this is the last problem?
  214. if (stderr2.contains("WARNING: Illegal reflective access using Lookup on org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor")) {
  215. // WARNING: An illegal reflective access operation has occurred
  216. // WARNING: Illegal reflective access using Lookup on org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor (file:/Users/aclement/gits/org.aspectj/loadtime/bin/) to class java.lang.ClassLoader
  217. // WARNING: Please consider reporting this to the maintainers of org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor
  218. // WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
  219. // WARNING: All illegal access operations will be denied in a future release
  220. stderr2 = stderr2.replaceAll("WARNING: An illegal reflective access operation has occurred\n","");
  221. stderr2 = stderr2.replaceAll("WARNING: Illegal reflective access using Lookup on org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor[^\n]*\n","");
  222. stderr2 = stderr2.replaceAll("WARNING: Please consider reporting this to the maintainers of org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor\n","");
  223. stderr2 = stderr2.replaceAll("WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations\n","");
  224. stderr2 = stderr2.replaceAll("WARNING: All illegal access operations will be denied in a future release\n","");
  225. }
  226. stderr2 = stderr2.replaceAll(regexArchivedNonSystemClasses, "");
  227. m_stdErrSpec.matchAgainst(stderr2);
  228. }
  229. }
  230. public void addStdErrSpec(OutputSpec spec) {
  231. if (m_stdErrSpec != null)
  232. throw new UnsupportedOperationException("only one 'stderr' allowed in 'ant'");
  233. m_stdErrSpec = spec;
  234. }
  235. public void addStdOutSpec(OutputSpec spec) {
  236. if (m_stdOutSpec != null)
  237. throw new UnsupportedOperationException("only one 'stdout' allowed in 'ant'");
  238. m_stdOutSpec = spec;
  239. }
  240. public void setVerbose(String verbose) {
  241. if ("true".equalsIgnoreCase(verbose)) {
  242. m_verbose = true;
  243. }
  244. }
  245. public void setFile(String file) {
  246. m_antFile = file;
  247. }
  248. public void setTarget(String target) {
  249. m_antTarget = target;
  250. }
  251. public void addExpectedMessage(ExpectedMessageSpec message) {
  252. throw new UnsupportedOperationException("don't use 'message' in 'ant' specs.");
  253. }
  254. public void setBaseDir(String dir) {
  255. ;
  256. }
  257. public void setTest(AjcTest test) {
  258. m_ajcTest = test;
  259. }
  260. private static void populatePath(Path path, String pathEntries) {
  261. StringTokenizer st = new StringTokenizer(pathEntries, File.pathSeparator);
  262. while (st.hasMoreTokens()) {
  263. path.setPath(new File(st.nextToken()).getAbsolutePath());
  264. }
  265. }
  266. }