Tue, 24 Dec 2013 09:17:37 -0800
8029230: Update copyright year to match last edit in jdk8 langtools repository for 2013
Reviewed-by: ksrini
Contributed-by: steve.sides@oracle.com
rfield@1422 | 1 | /* |
ksrini@2227 | 2 | * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. |
rfield@1422 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
rfield@1422 | 4 | * |
rfield@1422 | 5 | * This code is free software; you can redistribute it and/or modify it |
rfield@1422 | 6 | * under the terms of the GNU General Public License version 2 only, as |
rfield@1422 | 7 | * published by the Free Software Foundation. Oracle designates this |
rfield@1422 | 8 | * particular file as subject to the "Classpath" exception as provided |
rfield@1422 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
rfield@1422 | 10 | * |
rfield@1422 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
rfield@1422 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
rfield@1422 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
rfield@1422 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
rfield@1422 | 15 | * accompanied this code). |
rfield@1422 | 16 | * |
rfield@1422 | 17 | * You should have received a copy of the GNU General Public License version |
rfield@1422 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
rfield@1422 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
rfield@1422 | 20 | * |
rfield@1422 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
rfield@1422 | 22 | * or visit www.oracle.com if you need additional information or have any |
rfield@1422 | 23 | * questions. |
rfield@1422 | 24 | */ |
rfield@1422 | 25 | |
rfield@1422 | 26 | package org.openjdk.tests.separate; |
rfield@1422 | 27 | |
rfield@1422 | 28 | import org.testng.ITestResult; |
rfield@1422 | 29 | import org.testng.annotations.AfterMethod; |
rfield@1422 | 30 | |
rfield@1422 | 31 | import java.lang.reflect.InvocationTargetException; |
rfield@1422 | 32 | import java.util.Arrays; |
rfield@1422 | 33 | import java.util.HashSet; |
rfield@1422 | 34 | import java.util.List; |
rfield@1422 | 35 | |
rfield@1422 | 36 | import static org.openjdk.tests.separate.SourceModel.Class; |
rfield@1422 | 37 | import static org.openjdk.tests.separate.SourceModel.*; |
rfield@1422 | 38 | import static org.testng.Assert.*; |
rfield@1422 | 39 | |
rfield@1422 | 40 | public class TestHarness { |
rfield@1422 | 41 | |
rfield@1422 | 42 | /** |
rfield@1422 | 43 | * Creates a per-thread persistent compiler object to allow as much |
rfield@1422 | 44 | * sharing as possible, but still allows for parallel execution of tests. |
rfield@1422 | 45 | */ |
rfield@1422 | 46 | protected ThreadLocal<Compiler> compilerLocal = new ThreadLocal<Compiler>(){ |
rfield@1422 | 47 | protected synchronized Compiler initialValue() { |
rfield@1422 | 48 | return new Compiler(); |
rfield@1422 | 49 | } |
rfield@1422 | 50 | }; |
rfield@1422 | 51 | |
rfield@1422 | 52 | protected ThreadLocal<Boolean> verboseLocal = new ThreadLocal<Boolean>() { |
rfield@1422 | 53 | protected synchronized Boolean initialValue() { |
rfield@1422 | 54 | return Boolean.FALSE; |
rfield@1422 | 55 | } |
rfield@1422 | 56 | }; |
rfield@1422 | 57 | |
rfield@1422 | 58 | protected boolean verbose; |
rfield@1422 | 59 | protected boolean canUseCompilerCache; |
rfield@1422 | 60 | public static final String stdMethodName = SourceModel.stdMethodName; |
rfield@1422 | 61 | |
rfield@1422 | 62 | private TestHarness() { |
rfield@1422 | 63 | } |
rfield@1422 | 64 | |
rfield@1422 | 65 | protected TestHarness(boolean verbose, boolean canUseCompilerCache) { |
rfield@1422 | 66 | this.verbose = verbose; |
rfield@1422 | 67 | this.canUseCompilerCache = canUseCompilerCache; |
rfield@1422 | 68 | } |
rfield@1422 | 69 | |
rfield@1422 | 70 | public void setTestVerbose() { |
rfield@1422 | 71 | verboseLocal.set(Boolean.TRUE); |
rfield@1422 | 72 | } |
rfield@1422 | 73 | |
rfield@1422 | 74 | @AfterMethod |
rfield@1422 | 75 | public void reset() { |
rfield@1422 | 76 | if (!this.verbose) { |
rfield@1422 | 77 | verboseLocal.set(Boolean.FALSE); |
rfield@1422 | 78 | } |
rfield@1422 | 79 | } |
rfield@1422 | 80 | |
rfield@1422 | 81 | public Compiler.Flags[] compilerFlags() { |
rfield@1422 | 82 | HashSet<Compiler.Flags> flags = new HashSet<>(); |
rfield@1422 | 83 | if (verboseLocal.get() == Boolean.TRUE) { |
rfield@1422 | 84 | flags.add(Compiler.Flags.VERBOSE); |
rfield@1422 | 85 | } |
rfield@1422 | 86 | if (this.canUseCompilerCache) { |
rfield@1422 | 87 | flags.add(Compiler.Flags.USECACHE); |
rfield@1422 | 88 | } |
rfield@1422 | 89 | return flags.toArray(new Compiler.Flags[0]); |
rfield@1422 | 90 | } |
rfield@1422 | 91 | |
rfield@1422 | 92 | @AfterMethod |
rfield@1422 | 93 | public void printError(ITestResult result) { |
rfield@1422 | 94 | if (result.getStatus() == ITestResult.FAILURE) { |
rfield@1422 | 95 | String clsName = result.getTestClass().getName(); |
rfield@1422 | 96 | clsName = clsName.substring(clsName.lastIndexOf(".") + 1); |
rfield@1422 | 97 | System.out.println("Test " + clsName + "." + |
rfield@1422 | 98 | result.getName() + " FAILED"); |
rfield@1422 | 99 | } |
rfield@1422 | 100 | } |
rfield@1422 | 101 | |
rfield@1422 | 102 | private static final ConcreteMethod stdCM = ConcreteMethod.std("-1"); |
rfield@1422 | 103 | private static final AbstractMethod stdAM = |
rfield@1422 | 104 | new AbstractMethod("int", stdMethodName); |
rfield@1422 | 105 | |
rfield@1422 | 106 | /** |
rfield@1422 | 107 | * Returns a class which has a static method with the same name as |
rfield@1422 | 108 | * 'method', whose body creates an new instance of 'specimen' and invokes |
rfield@1422 | 109 | * 'method' upon it via an invokevirtual instruction with 'args' as |
rfield@1422 | 110 | * function call parameters. |
rfield@1422 | 111 | * |
rfield@1422 | 112 | * 'returns' is a dummy return value that need only match 'methods' |
rfield@1422 | 113 | * return type (it is only used in the dummy class when compiling IV). |
rfield@1422 | 114 | */ |
rfield@1422 | 115 | private Class invokeVirtualHarness( |
rfield@1422 | 116 | Class specimen, ConcreteMethod method, |
rfield@1422 | 117 | String returns, String ... args) { |
rfield@1422 | 118 | Method cm = new ConcreteMethod( |
rfield@1422 | 119 | method.getReturnType(), method.getName(), |
rfield@1422 | 120 | "return " + returns + ";", method.getElements()); |
rfield@1422 | 121 | Class stub = new Class(specimen.getName(), cm); |
rfield@1422 | 122 | |
rfield@1422 | 123 | String params = toJoinedString(args, ", "); |
rfield@1422 | 124 | |
rfield@1422 | 125 | ConcreteMethod sm = new ConcreteMethod( |
rfield@1422 | 126 | method.getReturnType(), method.getName(), |
rfield@1422 | 127 | String.format("return (new %s()).%s(%s);", |
rfield@1422 | 128 | specimen.getName(), method.getName(), params), |
rfield@1422 | 129 | new AccessFlag("public"), new AccessFlag("static")); |
rfield@1422 | 130 | |
rfield@1422 | 131 | Class iv = new Class("IV_" + specimen.getName(), sm); |
rfield@1422 | 132 | |
rfield@1422 | 133 | iv.addCompilationDependency(stub); |
rfield@1422 | 134 | iv.addCompilationDependency(cm); |
rfield@1422 | 135 | |
rfield@1422 | 136 | return iv; |
rfield@1422 | 137 | } |
rfield@1422 | 138 | |
rfield@1422 | 139 | /** |
rfield@1422 | 140 | * Returns a class which has a static method with the same name as |
rfield@1422 | 141 | * 'method', whose body creates an new instance of 'specimen', casts it |
rfield@1422 | 142 | * to 'iface' (including the type parameters) and invokes |
rfield@1422 | 143 | * 'method' upon it via an invokeinterface instruction with 'args' as |
rfield@1422 | 144 | * function call parameters. |
rfield@1422 | 145 | */ |
rfield@1422 | 146 | private Class invokeInterfaceHarness(Class specimen, Extends iface, |
rfield@1422 | 147 | AbstractMethod method, String ... args) { |
rfield@1422 | 148 | Interface istub = new Interface( |
rfield@1422 | 149 | iface.getType().getName(), iface.getType().getAccessFlags(), |
rfield@1422 | 150 | iface.getType().getParameters(), |
rfield@1422 | 151 | null, Arrays.asList((Method)method)); |
rfield@1422 | 152 | Class cstub = new Class(specimen.getName()); |
rfield@1422 | 153 | |
rfield@1422 | 154 | String params = toJoinedString(args, ", "); |
rfield@1422 | 155 | |
rfield@1422 | 156 | ConcreteMethod sm = new ConcreteMethod( |
rfield@1422 | 157 | "int", SourceModel.stdMethodName, |
rfield@1422 | 158 | String.format("return ((%s)(new %s())).%s(%s);", iface.toString(), |
rfield@1422 | 159 | specimen.getName(), method.getName(), params), |
rfield@1422 | 160 | new AccessFlag("public"), new AccessFlag("static")); |
rfield@1422 | 161 | sm.suppressWarnings(); |
rfield@1422 | 162 | |
rfield@1422 | 163 | Class ii = new Class("II_" + specimen.getName() + "_" + |
rfield@1422 | 164 | iface.getType().getName(), sm); |
rfield@1422 | 165 | ii.addCompilationDependency(istub); |
rfield@1422 | 166 | ii.addCompilationDependency(cstub); |
rfield@1422 | 167 | ii.addCompilationDependency(method); |
rfield@1422 | 168 | return ii; |
rfield@1422 | 169 | } |
rfield@1422 | 170 | |
rfield@1422 | 171 | |
rfield@1422 | 172 | /** |
rfield@1422 | 173 | * Uses 'loader' to load class 'clzz', and calls the static method |
rfield@1422 | 174 | * 'method'. If the return value does not equal 'value' (or if an |
rfield@1422 | 175 | * exception is thrown), then a test failure is indicated. |
rfield@1422 | 176 | * |
rfield@1422 | 177 | * If 'value' is null, then no equality check is performed -- the assertion |
rfield@1422 | 178 | * fails only if an exception is thrown. |
rfield@1422 | 179 | */ |
rfield@1422 | 180 | protected void assertStaticCallEquals( |
rfield@1422 | 181 | ClassLoader loader, Class clzz, String method, Object value) { |
rfield@1422 | 182 | java.lang.Class<?> cls = null; |
rfield@1422 | 183 | try { |
rfield@1422 | 184 | cls = java.lang.Class.forName(clzz.getName(), true, loader); |
rfield@1422 | 185 | } catch (ClassNotFoundException e) {} |
rfield@1422 | 186 | assertNotNull(cls); |
rfield@1422 | 187 | |
rfield@1422 | 188 | java.lang.reflect.Method m = null; |
rfield@1422 | 189 | try { |
rfield@1422 | 190 | m = cls.getMethod(method); |
rfield@1422 | 191 | } catch (NoSuchMethodException e) {} |
rfield@1422 | 192 | assertNotNull(m); |
rfield@1422 | 193 | |
rfield@1422 | 194 | try { |
rfield@1422 | 195 | Object res = m.invoke(null); |
rfield@1422 | 196 | assertNotNull(res); |
rfield@1422 | 197 | if (value != null) { |
rfield@1422 | 198 | assertEquals(res, value); |
rfield@1422 | 199 | } |
rfield@1422 | 200 | } catch (InvocationTargetException | IllegalAccessException e) { |
briangoetz@2174 | 201 | fail("Unexpected exception thrown: " + e.getCause(), e.getCause()); |
rfield@1422 | 202 | } |
rfield@1422 | 203 | } |
rfield@1422 | 204 | |
rfield@1422 | 205 | /** |
rfield@1422 | 206 | * Creates a class which calls target::method(args) via invokevirtual, |
rfield@1422 | 207 | * compiles and loads both the new class and 'target', and then invokes |
rfield@1422 | 208 | * the method. If the returned value does not match 'value' then a |
rfield@1422 | 209 | * test failure is indicated. |
rfield@1422 | 210 | */ |
rfield@1422 | 211 | public void assertInvokeVirtualEquals( |
rfield@1422 | 212 | Object value, Class target, ConcreteMethod method, |
rfield@1422 | 213 | String returns, String ... args) { |
rfield@1422 | 214 | |
rfield@1422 | 215 | Compiler compiler = compilerLocal.get(); |
rfield@1422 | 216 | compiler.setFlags(compilerFlags()); |
rfield@1422 | 217 | |
rfield@1422 | 218 | Class iv = invokeVirtualHarness(target, method, returns, args); |
rfield@1422 | 219 | ClassLoader loader = compiler.compile(iv, target); |
rfield@1422 | 220 | |
rfield@1422 | 221 | assertStaticCallEquals(loader, iv, method.getName(), value); |
rfield@1422 | 222 | compiler.cleanup(); |
rfield@1422 | 223 | } |
rfield@1422 | 224 | |
rfield@1422 | 225 | /** |
rfield@1422 | 226 | * Convenience method for above, which assumes stdMethodName, |
rfield@1422 | 227 | * a return type of 'int', and no arguments. |
rfield@1422 | 228 | */ |
rfield@1422 | 229 | public void assertInvokeVirtualEquals(int value, Class target) { |
briangoetz@2174 | 230 | assertInvokeVirtualEquals(value, target, stdCM, "-1"); |
rfield@1422 | 231 | } |
rfield@1422 | 232 | |
rfield@1422 | 233 | /** |
rfield@1422 | 234 | * Creates a class which calls target::method(args) via invokeinterface |
rfield@1422 | 235 | * through 'iface', compiles and loads both it and 'target', and |
rfield@1422 | 236 | * then invokes the method. If the returned value does not match |
rfield@1422 | 237 | * 'value' then a test failure is indicated. |
rfield@1422 | 238 | */ |
rfield@1422 | 239 | public void assertInvokeInterfaceEquals(Object value, Class target, |
rfield@1422 | 240 | Extends iface, AbstractMethod method, String ... args) { |
rfield@1422 | 241 | |
rfield@1422 | 242 | Compiler compiler = compilerLocal.get(); |
rfield@1422 | 243 | compiler.setFlags(compilerFlags()); |
rfield@1422 | 244 | |
rfield@1422 | 245 | Class ii = invokeInterfaceHarness(target, iface, method, args); |
rfield@1422 | 246 | ClassLoader loader = compiler.compile(ii, target); |
rfield@1422 | 247 | |
rfield@1422 | 248 | assertStaticCallEquals(loader, ii, method.getName(), value); |
rfield@1422 | 249 | compiler.cleanup(); |
rfield@1422 | 250 | } |
rfield@1422 | 251 | |
rfield@1422 | 252 | /** |
rfield@1422 | 253 | * Convenience method for above, which assumes stdMethodName, |
rfield@1422 | 254 | * a return type of 'int', and no arguments. |
rfield@1422 | 255 | */ |
rfield@1422 | 256 | public void assertInvokeInterfaceEquals( |
rfield@1422 | 257 | int value, Class target, Interface iface) { |
rfield@1422 | 258 | |
rfield@1422 | 259 | Compiler compiler = compilerLocal.get(); |
rfield@1422 | 260 | compiler.setFlags(compilerFlags()); |
rfield@1422 | 261 | |
briangoetz@2174 | 262 | assertInvokeInterfaceEquals(value, target, new Extends(iface), stdAM); |
rfield@1422 | 263 | |
rfield@1422 | 264 | compiler.cleanup(); |
rfield@1422 | 265 | } |
rfield@1422 | 266 | |
briangoetz@2174 | 267 | protected void assertInvokeInterfaceThrows(java.lang.Class<? extends Throwable> errorClass, |
briangoetz@2174 | 268 | Class target, Extends iface, AbstractMethod method, |
briangoetz@2174 | 269 | String... args) { |
briangoetz@2174 | 270 | try { |
briangoetz@2174 | 271 | assertInvokeInterfaceEquals(0, target, iface, method, args); |
briangoetz@2174 | 272 | fail("Expected exception: " + errorClass); |
briangoetz@2174 | 273 | } |
briangoetz@2174 | 274 | catch (AssertionError e) { |
briangoetz@2174 | 275 | Throwable cause = e.getCause(); |
briangoetz@2174 | 276 | if (cause == null) |
briangoetz@2174 | 277 | throw e; |
briangoetz@2174 | 278 | else if ((errorClass.isAssignableFrom(cause.getClass()))) { |
briangoetz@2174 | 279 | // this is success |
briangoetz@2174 | 280 | return; |
briangoetz@2174 | 281 | } |
briangoetz@2174 | 282 | else |
briangoetz@2174 | 283 | throw e; |
briangoetz@2174 | 284 | } |
briangoetz@2174 | 285 | } |
briangoetz@2174 | 286 | |
rfield@1422 | 287 | /** |
rfield@1422 | 288 | * Creates a class which calls target::method(args) via invokevirtual, |
rfield@1422 | 289 | * compiles and loads both the new class and 'target', and then invokes |
rfield@1422 | 290 | * the method. If an exception of type 'exceptionType' is not thrown, |
rfield@1422 | 291 | * then a test failure is indicated. |
rfield@1422 | 292 | */ |
rfield@1422 | 293 | public void assertThrows(java.lang.Class<?> exceptionType, Class target, |
rfield@1422 | 294 | ConcreteMethod method, String returns, String ... args) { |
rfield@1422 | 295 | |
rfield@1422 | 296 | Compiler compiler = compilerLocal.get(); |
rfield@1422 | 297 | compiler.setFlags(compilerFlags()); |
rfield@1422 | 298 | |
rfield@1422 | 299 | Class iv = invokeVirtualHarness(target, method, returns, args); |
rfield@1422 | 300 | ClassLoader loader = compiler.compile(iv, target); |
rfield@1422 | 301 | |
rfield@1422 | 302 | java.lang.Class<?> cls = null; |
rfield@1422 | 303 | try { |
rfield@1422 | 304 | cls = java.lang.Class.forName(iv.getName(), true, loader); |
rfield@1422 | 305 | } catch (ClassNotFoundException e) {} |
rfield@1422 | 306 | assertNotNull(cls); |
rfield@1422 | 307 | |
rfield@1422 | 308 | java.lang.reflect.Method m = null; |
rfield@1422 | 309 | try { |
rfield@1422 | 310 | m = cls.getMethod(method.getName()); |
rfield@1422 | 311 | } catch (NoSuchMethodException e) {} |
rfield@1422 | 312 | assertNotNull(m); |
rfield@1422 | 313 | |
rfield@1422 | 314 | try { |
rfield@1422 | 315 | m.invoke(null); |
rfield@1422 | 316 | fail("Exception should have been thrown"); |
rfield@1422 | 317 | } catch (InvocationTargetException | IllegalAccessException e) { |
rfield@1422 | 318 | if (verboseLocal.get() == Boolean.TRUE) { |
rfield@1422 | 319 | System.out.println(e.getCause()); |
rfield@1422 | 320 | } |
rfield@2170 | 321 | assertTrue(exceptionType.isAssignableFrom(e.getCause().getClass())); |
rfield@1422 | 322 | } |
rfield@1422 | 323 | compiler.cleanup(); |
rfield@1422 | 324 | } |
rfield@1422 | 325 | |
rfield@1422 | 326 | /** |
rfield@1422 | 327 | * Convenience method for above, which assumes stdMethodName, |
rfield@1422 | 328 | * a return type of 'int', and no arguments. |
rfield@1422 | 329 | */ |
rfield@1422 | 330 | public void assertThrows(java.lang.Class<?> exceptionType, Class target) { |
rfield@1422 | 331 | assertThrows(exceptionType, target, stdCM, "-1"); |
rfield@1422 | 332 | } |
rfield@1422 | 333 | |
rfield@1422 | 334 | private static <T> String toJoinedString(T[] a, String... p) { |
rfield@1422 | 335 | return toJoinedString(Arrays.asList(a), p); |
rfield@1422 | 336 | } |
rfield@1422 | 337 | |
rfield@1422 | 338 | private static <T> String toJoinedString(List<T> list, String... p) { |
rfield@1422 | 339 | StringBuilder sb = new StringBuilder(); |
rfield@1422 | 340 | String sep = ""; |
rfield@1422 | 341 | String init = ""; |
rfield@1422 | 342 | String end = ""; |
rfield@1422 | 343 | String empty = null; |
rfield@1422 | 344 | switch (p.length) { |
rfield@1422 | 345 | case 4: |
rfield@1422 | 346 | empty = p[3]; |
rfield@1422 | 347 | /*fall-through*/ |
rfield@1422 | 348 | case 3: |
rfield@1422 | 349 | end = p[2]; |
rfield@1422 | 350 | /*fall-through*/ |
rfield@1422 | 351 | case 2: |
rfield@1422 | 352 | init = p[1]; |
rfield@1422 | 353 | /*fall-through*/ |
rfield@1422 | 354 | case 1: |
rfield@1422 | 355 | sep = p[0]; |
rfield@1422 | 356 | break; |
rfield@1422 | 357 | } |
rfield@1422 | 358 | if (empty != null && list.isEmpty()) { |
rfield@1422 | 359 | return empty; |
rfield@1422 | 360 | } else { |
rfield@1422 | 361 | sb.append(init); |
rfield@1422 | 362 | for (T x : list) { |
rfield@1422 | 363 | if (sb.length() != init.length()) { |
rfield@1422 | 364 | sb.append(sep); |
rfield@1422 | 365 | } |
rfield@1422 | 366 | sb.append(x.toString()); |
rfield@1422 | 367 | } |
rfield@1422 | 368 | sb.append(end); |
rfield@1422 | 369 | return sb.toString(); |
rfield@1422 | 370 | } |
rfield@1422 | 371 | } |
rfield@1422 | 372 | } |