123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- /*******************************************************************************
- * Copyright (c) 2005 Contributors.
- * All rights reserved.
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Public License v 2.0
- * which accompanies this distribution and is available at
- * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
- *
- * Contributors:
- * Alexandre Vasseur initial implementation
- *******************************************************************************/
- package org.aspectj.testing;
-
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileReader;
- import java.util.StringTokenizer;
-
- import org.apache.tools.ant.BuildEvent;
- import org.apache.tools.ant.BuildException;
- import org.apache.tools.ant.DefaultLogger;
- import org.apache.tools.ant.Project;
- import org.apache.tools.ant.ProjectHelper;
- import org.apache.tools.ant.Target;
- import org.apache.tools.ant.taskdefs.Java;
- import org.apache.tools.ant.types.Path;
- import org.aspectj.tools.ajc.AjcTestCase;
- import org.aspectj.util.LangUtil;
-
- /**
- * Element that allow to run an abritrary Ant target in a sandbox.
- * <p/>
- * Such a spec is used in a {@code <ajc-test><ant file="myAnt.xml" [target="..."] [verbose="true"]/>} XML element. The
- * {@code target} is optional. If not set, default <i>myAnt.xml</i> target is used. The {@code file} is looked up from
- * the {@code <ajc-test dir="..."/>} attribute. If @{code verbose} is set to {@code true}, the {@code ant -v} output is
- * piped, else nothing is reported except errors.
- * <p/>
- * The called Ant target benefits from some implicit variables:
- * <ul>
- * <li>{@code ${aj.sandbox}} points to the test current sandbox folder.</li>
- * <li>
- * {@code ${aj.path}} is an Ant refid on the classpath formed with the sandbox folder + ltw + the AjcTestCase
- * classpath (i.e. usually aspectjrt, junit, and testing infra).
- * </li>
- * <li>
- * For Java 16+, {@code ${aj.addOpensKey}} and {@code ${aj.addOpensValue}} together add {@code --add-opens} and
- * {@code java.base/java.lang=ALL-UNNAMED} as JVM parameters. They have to be used together and consecutively in
- * this order as {@code jvmarg} parameter tags inside the {@code java} Ant task.
- * </li>
- * </ul>
- * <p/>
- * Element {@code <stdout><line text="..">} and {@code <stderr><line text="..">} can be used. For now, a full match is
- * performed on the output of the runned target only (not the whole Ant invocation). This is experimental and you are
- * advised to use a {@code <junit>} task instead or a {@code <java>} whose main throws some exception upon failure.
- *
- * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
- */
- public class AntSpec implements ITestStep {
-
- public static String outputFolders(String... modules) {
- StringBuilder s = new StringBuilder();
- for (String module: modules) {
- s.append(File.pathSeparator + ".." +File.separator + module + File.separator + "target" + File.separator + "classes");
- }
- return s.toString();
- }
-
-
- // ALSO SEE AJC
- private final static String DEFAULT_LTW_CLASSPATH_ENTRIES =
- outputFolders("asm", "bridge", "loadtime", "weaver", "org.aspectj.matcher", "bcel-builder");
-
- private boolean m_verbose = false;
- private AjcTest m_ajcTest;
- private OutputSpec m_stdErrSpec;
- private OutputSpec m_stdOutSpec;
- private String m_antFile;
- private String m_antTarget;
-
- public void execute(final AjcTestCase inTestCase) {
- final String failMessage = "test \"" + m_ajcTest.getTitle() + "\" failed: ";
-
- File buildFile = new File(m_ajcTest.getDir() + File.separatorChar + m_antFile);
- if (!buildFile.exists()) {
- AjcTestCase.fail(failMessage + "no such Ant file " + buildFile.getAbsolutePath());
- }
- Project p = new Project();
- final StringBuffer stdout = new StringBuffer();
- final StringBuffer stderr = new StringBuffer();
- final StringBuffer verboseLog = new StringBuffer();
- try {
- // read the Ant file
- p.init();
- p.setUserProperty("ant.file", buildFile.getAbsolutePath());
- // setup aj.sandbox
- p.setUserProperty("aj.sandbox", inTestCase.getSandboxDirectory().getAbsolutePath());
- // setup aj.dir "modules" folder
- p.setUserProperty("aj.root", new File("..").getAbsolutePath());
-
- // On Java 16+, LTW did not work on AspectJ 1.9.7 to 1.9.21 without this parameter. So, we added the argument here
- // and not in AjcTestCase::run, because without 'useLTW' or 'useFullLTW', there might have been a need for weaver
- // attachment during runtime. See also docs/release/README-1.8.7.adoc.
- //
- // Since AspectJ 1.9.21.1, '--add-opens' is no longer necessary, because we found a workaround for defining
- // classes in arbitrary class loaders. But some tests, e.g. AtAjLTWTests.testLTWUnweavable, still use
- // ClassLoader::defineClass to inject dynamically generated classes into the current class loader. Therefore, we
- // still set the parameters, so they can be used on demand - not for LTW as such, but for class injection. See
- // also tests/java5/ataspectj/ataspectj/UnweavableTest.java.
- //
- // Attention: Ant 1.6.3 under Linux neither likes "" (empty string) nor " " (space), on Windows it would not be
- // a problem. So we use "_dummy" Java system properties, even though they pollute the command line.
- p.setUserProperty(
- "aj.addOpensKey",
- LangUtil.isVMGreaterOrEqual(16) ? "--add-opens" : "-D_dummy"
- );
- p.setUserProperty(
- "aj.addOpensValue",
- LangUtil.isVMGreaterOrEqual(16) ? "java.base/java.lang=ALL-UNNAMED" : "-D_dummy"
- );
-
- // create the test implicit path aj.path that contains the sandbox + regular test infra path
- Path path = new Path(p, inTestCase.getSandboxDirectory().getAbsolutePath());
- populatePath(path, DEFAULT_LTW_CLASSPATH_ENTRIES);
- populatePath(path, AjcTestCase.DEFAULT_CLASSPATH_ENTRIES);
- p.addReference("aj.path", path);
- p.setBasedir(buildFile.getAbsoluteFile().getParent());
- ProjectHelper helper = ProjectHelper.getProjectHelper();
- helper.parse(p, buildFile);
-
- // use default target if no target specified
- if (m_antTarget == null) {
- m_antTarget = p.getDefaultTarget();
- }
-
- // make sure we listen for failure
- DefaultLogger consoleLogger = new DefaultLogger() {
- public void buildFinished(BuildEvent event) {
- super.buildFinished(event);
- if (event.getException() != null) {
-
- try {
- File antout = new File(inTestCase.getSandboxDirectory().getAbsolutePath(), "antout");
- if (antout.exists()) {
- stdout.append("Forked java command stdout:\n");
- System.out.println("Forked java command stdout:");
- FileReader fr = new FileReader(antout);
- BufferedReader br = new BufferedReader(fr);
- String line = br.readLine();
- while (line != null) {
- stdout.append(line).append("\n");
- System.out.println(stdout);
- line = br.readLine();
- }
- fr.close();
- }
-
- File anterr = new File(inTestCase.getSandboxDirectory().getAbsolutePath(), "anterr");
- if (anterr.exists()) {
- stdout.append("Forked java command stderr:\n");
- System.out.println("Forked java command stderr:");
- FileReader fr = new FileReader(anterr);
- BufferedReader br = new BufferedReader(fr);
- String line = br.readLine();
- while (line != null) {
- stdout.append(line).append("\n");
- System.out.println(stdout);
- line = br.readLine();
- }
- fr.close();
- }
- } catch (Exception e) {
- String exceptionMessage = "Exception whilst loading forked java task output " + e.getMessage() + "\n";
- System.out.println(exceptionMessage);
- e.printStackTrace();
- stdout.append(exceptionMessage);
- }
-
- // AjcTestCase.fail(failMessage + "failure " + event.getException());
- AjcTestCase.fail(event.getException() + "\n" + verboseLog + stdout + stderr);
- }
- }
-
- public void targetFinished(BuildEvent event) {
- super.targetFinished(event);
- if (event.getException() != null) {
- AjcTestCase.fail(failMessage + "failure in '" + event.getTarget() + "' " + event.getException());
- }
- }
-
- public void messageLogged(BuildEvent event) {
- super.messageLogged(event);
-
- Target target = event.getTarget();
- if (target != null && m_antTarget.equals(target.getName()) && event.getSource() instanceof Java)
- switch (event.getPriority()) {
- case Project.MSG_INFO:
- stdout.append(event.getMessage()).append('\n');
- break;
- case Project.MSG_WARN:
- stderr.append(event.getMessage()).append('\n');
- break;
- case Project.MSG_VERBOSE:
- verboseLog.append(event.getMessage()).append('\n');
- break;
- }
- }
- };
- consoleLogger.setErrorPrintStream(System.err);
- consoleLogger.setOutputPrintStream(System.out);
- consoleLogger.setMessageOutputLevel(m_verbose ? Project.MSG_VERBOSE : Project.MSG_ERR);
- p.addBuildListener(consoleLogger);
- } catch (Throwable t) {
- AjcTestCase.fail(failMessage + "invalid Ant script :" + t.toString());
- }
- try {
- p.setProperty("verbose", "true");
- p.fireBuildStarted();
- p.executeTarget(m_antTarget);
- p.fireBuildFinished(null);
- } catch (BuildException e) {
- p.fireBuildFinished(e);
- } catch (Throwable t) {
- AjcTestCase.fail(failMessage + "error when invoking target :" + t.toString());
- }
-
- // J12: Line can start with e.g. "OpenJDK 64-Bit Server VM" or "Java HotSpot(TM) 64-Bit Server VM".
- // J21: Line can start with e.g. "[0.016s][warning][cds]".
- // Therefore, we have to match a substring instead of a whole line.
- // Even worse, before J21, the warning appears on stdErr, but in J21+, it appears on stdOut.
- final String regexArchivedNonSystemClasses = "[^\n]+( warning:|\\[warning]\\[cds]) " +
- "Archived non-system classes are disabled because the java.system.class.loader property is specified " +
- ".*org.aspectj.weaver.loadtime.WeavingURLClassLoader[^\n]+\n?";
-
- /* See if stdout/stderr matches test specification */
- if (m_stdOutSpec != null) {
- String stdout2 = stdout.toString();
- stdout2 = stdout2.replaceAll(regexArchivedNonSystemClasses, "");
- m_stdOutSpec.matchAgainst(stdout2);
- }
- if (m_stdErrSpec != null) {
- String stderr2 = stderr.toString();
- // Working around this ridiculous message that still comes out of Java7 builds:
- if (stderr2.contains("Class JavaLaunchHelper is implemented in both") && stderr2.indexOf('\n')!=-1) {
- stderr2 = stderr2.replaceAll("objc\\[[0-9]*\\]: Class JavaLaunchHelper is implemented in both [^\n]*\n","");
- }
- // JDK 11 is complaining about illegal reflective calls - temporary measure ignore these - does that get all tests passing and this is the last problem?
- if (stderr2.contains("WARNING: Illegal reflective access using Lookup on org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor")) {
- // WARNING: An illegal reflective access operation has occurred
- // 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
- // WARNING: Please consider reporting this to the maintainers of org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor
- // WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
- // WARNING: All illegal access operations will be denied in a future release
-
- stderr2 = stderr2.replaceAll("WARNING: An illegal reflective access operation has occurred\n","");
- stderr2 = stderr2.replaceAll("WARNING: Illegal reflective access using Lookup on org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor[^\n]*\n","");
- stderr2 = stderr2.replaceAll("WARNING: Please consider reporting this to the maintainers of org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor\n","");
- stderr2 = stderr2.replaceAll("WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations\n","");
- stderr2 = stderr2.replaceAll("WARNING: All illegal access operations will be denied in a future release\n","");
- }
- stderr2 = stderr2.replaceAll(regexArchivedNonSystemClasses, "");
- m_stdErrSpec.matchAgainst(stderr2);
- }
- }
-
- public void addStdErrSpec(OutputSpec spec) {
- if (m_stdErrSpec != null)
- throw new UnsupportedOperationException("only one 'stderr' allowed in 'ant'");
- m_stdErrSpec = spec;
- }
-
- public void addStdOutSpec(OutputSpec spec) {
- if (m_stdOutSpec != null)
- throw new UnsupportedOperationException("only one 'stdout' allowed in 'ant'");
- m_stdOutSpec = spec;
- }
-
- public void setVerbose(String verbose) {
- if ("true".equalsIgnoreCase(verbose)) {
- m_verbose = true;
- }
- }
-
- public void setFile(String file) {
- m_antFile = file;
- }
-
- public void setTarget(String target) {
- m_antTarget = target;
- }
-
- public void addExpectedMessage(ExpectedMessageSpec message) {
- throw new UnsupportedOperationException("don't use 'message' in 'ant' specs.");
- }
-
- public void setBaseDir(String dir) {
- ;
- }
-
- public void setTest(AjcTest test) {
- m_ajcTest = test;
- }
-
- private static void populatePath(Path path, String pathEntries) {
- StringTokenizer st = new StringTokenizer(pathEntries, File.pathSeparator);
- while (st.hasMoreTokens()) {
- path.setPath(new File(st.nextToken()).getAbsolutePath());
- }
- }
- }
|