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 +}