test/tools/javap/output/Tester.java

changeset 0
959103a6100f
child 2525
2eb010b6cb22
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/test/tools/javap/output/Tester.java	Wed Apr 27 01:34:52 2016 +0800
     1.3 @@ -0,0 +1,389 @@
     1.4 +/*
     1.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
     1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.7 + *
     1.8 + * This code is free software; you can redistribute it and/or modify it
     1.9 + * under the terms of the GNU General Public License version 2 only, as
    1.10 + * published by the Free Software Foundation.
    1.11 + *
    1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    1.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    1.15 + * version 2 for more details (a copy is included in the LICENSE file that
    1.16 + * accompanied this code).
    1.17 + *
    1.18 + * You should have received a copy of the GNU General Public License version
    1.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    1.21 + *
    1.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    1.23 + * or visit www.oracle.com if you need additional information or have any
    1.24 + * questions.
    1.25 + */
    1.26 +
    1.27 +import java.io.*;
    1.28 +import java.util.*;
    1.29 +import java.lang.annotation.*;
    1.30 +import java.lang.reflect.InvocationTargetException;
    1.31 +
    1.32 +/**
    1.33 + * {@code Tester} is an abstract test-driver that provides the logic
    1.34 + * to execute test-cases, grouped by test classes.
    1.35 + * A test class is a main class extending this class, that instantiate
    1.36 + * itself, and calls the {@link run} method, passing any command line
    1.37 + * arguments.
    1.38 + * <p>
    1.39 + * The {@code run} method, expects arguments to identify test-case classes.
    1.40 + * A test-case class is a class extending the test class, and annotated
    1.41 + * with {@code TestCase}.
    1.42 + * <p>
    1.43 + * If no test-cases are specified, the test class directory is searched for
    1.44 + * co-located test-case classes (i.e. any class extending the test class,
    1.45 + * annotated with  {@code TestCase}).
    1.46 + * <p>
    1.47 + * Besides serving to group test-cases, extending the driver allow
    1.48 + * setting up a test-case template, and possibly overwrite default
    1.49 + * test-driver behaviour.
    1.50 + */
    1.51 +public abstract class Tester {
    1.52 +
    1.53 +    private static boolean debug = false;
    1.54 +    private static final PrintStream out = System.err;
    1.55 +    private static final PrintStream err = System.err;
    1.56 +
    1.57 +
    1.58 +    protected void run(String... args) throws Exception {
    1.59 +
    1.60 +        final File classesdir = new File(System.getProperty("test.classes", "."));
    1.61 +
    1.62 +        String[] classNames = args;
    1.63 +
    1.64 +        // If no test-cases are specified, we regard all co-located classes
    1.65 +        // as potential test-cases.
    1.66 +        if (args.length == 0) {
    1.67 +            final String pattern =  ".*\\.class";
    1.68 +            final File classFiles[] = classesdir.listFiles(new FileFilter() {
    1.69 +                    public boolean accept(File f) {
    1.70 +                        return f.getName().matches(pattern);
    1.71 +                    }
    1.72 +                });
    1.73 +            ArrayList<String> names = new ArrayList<String>(classFiles.length);
    1.74 +            for (File f : classFiles) {
    1.75 +                String fname = f.getName();
    1.76 +                names.add(fname.substring(0, fname.length() -6));
    1.77 +            }
    1.78 +            classNames = names.toArray(new String[names.size()]);
    1.79 +        } else {
    1.80 +            debug = true;
    1.81 +        }
    1.82 +        // Test-cases must extend the driver type, and be marked
    1.83 +        // @TestCase. Other arguments (classes) are ignored.
    1.84 +        // Test-cases are instantiated, and thereby executed.
    1.85 +        for (String clname : classNames) {
    1.86 +            try {
    1.87 +                final Class tclass = Class.forName(clname);
    1.88 +                if  (!getClass().isAssignableFrom(tclass)) continue;
    1.89 +                TestCase anno = (TestCase) tclass.getAnnotation(TestCase.class);
    1.90 +                if (anno == null) continue;
    1.91 +                if (!debug) {
    1.92 +                    ignore i = (ignore) tclass.getAnnotation(ignore.class);
    1.93 +                    if (i != null) {
    1.94 +                        out.println("Ignore: " + clname);
    1.95 +                        ignored++;
    1.96 +                        continue;
    1.97 +                    }
    1.98 +                }
    1.99 +                out.println("TestCase: " + clname);
   1.100 +                cases++;
   1.101 +                Tester tc = (Tester) tclass.getConstructor().newInstance();
   1.102 +                if (tc.errors > 0) {
   1.103 +                    error("" + tc.errors + " test points failed in " + clname);
   1.104 +                    errors += tc.errors - 1;
   1.105 +                    fcases++;
   1.106 +                }
   1.107 +            } catch(ReflectiveOperationException roe) {
   1.108 +                error("Warning: " + clname + " - ReflectiveOperationException");
   1.109 +                roe.printStackTrace(err);
   1.110 +            } catch(Exception unknown) {
   1.111 +                error("Warning: " + clname + " - uncaught exception");
   1.112 +                unknown.printStackTrace(err);
   1.113 +            }
   1.114 +        }
   1.115 +
   1.116 +        String imsg = ignored > 0 ? " (" +  ignored + " ignored)" : "";
   1.117 +        if (errors > 0)
   1.118 +            throw new Error(errors + " error, in " + fcases + " of " + cases + " test-cases" + imsg);
   1.119 +        else
   1.120 +            err.println("" + cases + " test-cases executed" + imsg + ", no errors");
   1.121 +    }
   1.122 +
   1.123 +
   1.124 +    /**
   1.125 +     * Test-cases must be marked with the {@code TestCase} annotation,
   1.126 +     * as well as extend {@code Tester} (or an driver extension
   1.127 +     * specified as the first argument to the {@code main()} method.
   1.128 +     */
   1.129 +    @Retention(RetentionPolicy.RUNTIME)
   1.130 +    @interface TestCase { }
   1.131 +
   1.132 +    /**
   1.133 +     * Individual test-cases failing due to product bugs, may temporarily
   1.134 +     * be excluded by marking them like this, (where "at-" is replaced by "@")
   1.135 +     * at-ignore // 1234567: bug synopsis
   1.136 +     */
   1.137 +    @Retention(RetentionPolicy.RUNTIME)
   1.138 +    @interface ignore { }
   1.139 +
   1.140 +    /**
   1.141 +     * Test-cases are classes extending {@code Tester}, and
   1.142 +     * calling {@link setSrc}, followed by one or more invocations
   1.143 +     * of {@link verify} in the body of the constructor.
   1.144 +     * <p>
   1.145 +     * Sets a default test-case template, which is empty except
   1.146 +     * for a key of {@code "TESTCASE"}.
   1.147 +     * Subclasses will typically call {@code setSrc(TestSource)}
   1.148 +     * to setup a useful test-case template.
   1.149 +     */
   1.150 +    public Tester() {
   1.151 +        this.testCase = this.getClass().getName();
   1.152 +        src = new TestSource("TESTCASE");
   1.153 +    }
   1.154 +
   1.155 +    /**
   1.156 +     * Set the top-level source template.
   1.157 +     */
   1.158 +    protected Tester setSrc(TestSource src) {
   1.159 +        this.src = src;
   1.160 +        return this;
   1.161 +    }
   1.162 +
   1.163 +    /**
   1.164 +     * Convenience method for calling {@code innerSrc("TESTCASE", ...)}.
   1.165 +     */
   1.166 +    protected Tester setSrc(String... lines) {
   1.167 +        return innerSrc("TESTCASE", lines);
   1.168 +    }
   1.169 +
   1.170 +    /**
   1.171 +     * Convenience method for calling {@code innerSrc(key, new TestSource(...))}.
   1.172 +     */
   1.173 +    protected Tester innerSrc(String key, String... lines) {
   1.174 +        return innerSrc(key, new TestSource(lines));
   1.175 +    }
   1.176 +
   1.177 +    /**
   1.178 +     * Specialize the testcase template, setting replacement content
   1.179 +     * for the specified key.
   1.180 +     */
   1.181 +    protected Tester innerSrc(String key, TestSource content) {
   1.182 +        if (src == null) {
   1.183 +            src = new TestSource(key);
   1.184 +        }
   1.185 +        src.setInner(key, content);
   1.186 +        return this;
   1.187 +    }
   1.188 +
   1.189 +    /**
   1.190 +     * On the first invocation, call {@code execute()} to compile
   1.191 +     * the test-case source and process the resulting class(se)
   1.192 +     * into verifiable output.
   1.193 +     * <p>
   1.194 +     * Verify that the output matches each of the regular expressions
   1.195 +     * given as argument.
   1.196 +     * <p>
   1.197 +     * Any failure to match constitutes a test failure, but doesn't
   1.198 +     * abort the test-case.
   1.199 +     * <p>
   1.200 +     * Any exception (e.g. bad regular expression syntax) results in
   1.201 +     * a test failure, and aborts the test-case.
   1.202 +     */
   1.203 +    protected void verify(String... expect) {
   1.204 +        if (!didExecute) {
   1.205 +            try {
   1.206 +                execute();
   1.207 +            } catch(Exception ue) {
   1.208 +                throw new Error(ue);
   1.209 +            } finally {
   1.210 +                didExecute = true;
   1.211 +            }
   1.212 +        }
   1.213 +        if (output == null) {
   1.214 +            error("output is null");
   1.215 +            return;
   1.216 +        }
   1.217 +        for (String e: expect) {
   1.218 +            // Escape regular expressions (to allow input to be literals).
   1.219 +            // Notice, characters to be escaped are themselves identified
   1.220 +            // using regular expressions
   1.221 +            String rc[] = { "(", ")", "[", "]", "{", "}", "$" };
   1.222 +            for (String c : rc) {
   1.223 +                e = e.replace(c, "\\" + c);
   1.224 +            }
   1.225 +            // DEBUG: Uncomment this to test modulo constant pool index.
   1.226 +            // e = e.replaceAll("#[0-9]{2}", "#[0-9]{2}");
   1.227 +            if (!output.matches("(?s).*" + e + ".*")) {
   1.228 +                if (!didPrint) {
   1.229 +                    out.println(output);
   1.230 +                    didPrint = true;
   1.231 +                }
   1.232 +                error("not matched: '" + e + "'");
   1.233 +            } else if(debug) {
   1.234 +                out.println("matched: '" + e + "'");
   1.235 +            }
   1.236 +        }
   1.237 +    }
   1.238 +
   1.239 +    /**
   1.240 +     * Calls {@code writeTestFile()} to write out the test-case source
   1.241 +     * content to a file, then call {@code compileTestFile()} to
   1.242 +     * compile it, and finally run the {@link process} method to produce
   1.243 +     * verifiable output. The default {@code process} method runs javap.
   1.244 +     * <p>
   1.245 +     * If an exception occurs, it results in a test failure, and
   1.246 +     * aborts the test-case.
   1.247 +     */
   1.248 +    protected void execute() throws IOException {
   1.249 +        err.println("TestCase: " + testCase);
   1.250 +        writeTestFile();
   1.251 +        compileTestFile();
   1.252 +        process();
   1.253 +    }
   1.254 +
   1.255 +    /**
   1.256 +     * Generate java source from test-case.
   1.257 +     * TBD: change to use javaFileObject, possibly make
   1.258 +     * this class extend JavaFileObject.
   1.259 +     */
   1.260 +    protected void writeTestFile() throws IOException {
   1.261 +        javaFile = new File("Test.java");
   1.262 +        FileWriter fw = new FileWriter(javaFile);
   1.263 +        BufferedWriter bw = new BufferedWriter(fw);
   1.264 +        PrintWriter pw = new PrintWriter(bw);
   1.265 +        for (String line : src) {
   1.266 +            pw.println(line);
   1.267 +            if (debug) out.println(line);
   1.268 +        }
   1.269 +        pw.close();
   1.270 +    }
   1.271 +
   1.272 +    /**
   1.273 +     * Compile the Java source code.
   1.274 +     */
   1.275 +    protected void compileTestFile() {
   1.276 +        String path = javaFile.getPath();
   1.277 +        String params[] =  { "-source", "1.8", "-g", path };
   1.278 +        int rc = com.sun.tools.javac.Main.compile(params);
   1.279 +        if (rc != 0)
   1.280 +            throw new Error("compilation failed. rc=" + rc);
   1.281 +        classFile = new File(path.substring(0, path.length() - 5) + ".class");
   1.282 +    }
   1.283 +
   1.284 +
   1.285 +    /**
   1.286 +     * Process class file to generate output for verification.
   1.287 +     * The default implementation simply runs javap. This might be
   1.288 +     * overwritten to generate output in a different manner.
   1.289 +     */
   1.290 +    protected void process() {
   1.291 +        String testClasses = "."; //System.getProperty("test.classes", ".");
   1.292 +        StringWriter sw = new StringWriter();
   1.293 +        PrintWriter pw = new PrintWriter(sw);
   1.294 +        String[] args = { "-v", "-classpath", testClasses, "Test" };
   1.295 +        int rc = com.sun.tools.javap.Main.run(args, pw);
   1.296 +        if (rc != 0)
   1.297 +            throw new Error("javap failed. rc=" + rc);
   1.298 +        pw.close();
   1.299 +        output = sw.toString();
   1.300 +        if (debug) {
   1.301 +            out.println(output);
   1.302 +            didPrint = true;
   1.303 +        }
   1.304 +
   1.305 +    }
   1.306 +
   1.307 +
   1.308 +    private String testCase;
   1.309 +    private TestSource src;
   1.310 +    private File javaFile = null;
   1.311 +    private File classFile = null;
   1.312 +    private String output = null;
   1.313 +    private boolean didExecute = false;
   1.314 +    private boolean didPrint = false;
   1.315 +
   1.316 +
   1.317 +    protected void error(String msg) {
   1.318 +        err.println("Error: " + msg);
   1.319 +        errors++;
   1.320 +    }
   1.321 +
   1.322 +    private int cases;
   1.323 +    private int fcases;
   1.324 +    private int errors;
   1.325 +    private int ignored;
   1.326 +
   1.327 +    /**
   1.328 +     * The TestSource class provides a simple container for
   1.329 +     * test cases. It contains an array of source code lines,
   1.330 +     * where zero or more lines may be markers for nested lines.
   1.331 +     * This allows representing templates, with specialization.
   1.332 +     * <P>
   1.333 +     * This may be generalized to support more advance combo
   1.334 +     * tests, but presently it's only used with a static template,
   1.335 +     * and one level of specialization.
   1.336 +     */
   1.337 +    public class TestSource implements Iterable<String> {
   1.338 +
   1.339 +        private String[] lines;
   1.340 +        private Hashtable<String, TestSource> innerSrc;
   1.341 +
   1.342 +        public TestSource(String... lines) {
   1.343 +            this.lines = lines;
   1.344 +            innerSrc = new Hashtable<String, TestSource>();
   1.345 +        }
   1.346 +
   1.347 +        public void setInner(String key, TestSource inner) {
   1.348 +            innerSrc.put(key, inner);
   1.349 +        }
   1.350 +
   1.351 +        public void setInner(String key, String... lines) {
   1.352 +            innerSrc.put(key, new TestSource(lines));
   1.353 +        }
   1.354 +
   1.355 +        public Iterator<String> iterator() {
   1.356 +            return new LineIterator();
   1.357 +        }
   1.358 +
   1.359 +        private class LineIterator implements Iterator<String> {
   1.360 +
   1.361 +            int nextLine = 0;
   1.362 +            Iterator<String> innerIt = null;
   1.363 +
   1.364 +            public  boolean hasNext() {
   1.365 +                return nextLine < lines.length;
   1.366 +            }
   1.367 +
   1.368 +            public String next() {
   1.369 +                if (!hasNext()) throw new NoSuchElementException();
   1.370 +                String str = lines[nextLine];
   1.371 +                TestSource inner = innerSrc.get(str);
   1.372 +                if (inner == null) {
   1.373 +                    nextLine++;
   1.374 +                    return str;
   1.375 +                }
   1.376 +                if (innerIt == null) {
   1.377 +                    innerIt = inner.iterator();
   1.378 +                }
   1.379 +                if (innerIt.hasNext()) {
   1.380 +                    return innerIt.next();
   1.381 +                }
   1.382 +                innerIt = null;
   1.383 +                nextLine++;
   1.384 +                return next();
   1.385 +            }
   1.386 +
   1.387 +            public void remove() {
   1.388 +                throw new UnsupportedOperationException();
   1.389 +            }
   1.390 +        }
   1.391 +    }
   1.392 +}

mercurial