1 /* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.test; 26 27 import static org.graalvm.compiler.debug.DebugContext.DEFAULT_LOG_STREAM; 28 import static org.graalvm.compiler.debug.DebugContext.NO_DESCRIPTION; 29 30 import java.io.PrintStream; 31 import java.io.PrintWriter; 32 import java.lang.reflect.Field; 33 import java.lang.reflect.Method; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Collection; 37 import java.util.Collections; 38 import java.util.List; 39 40 import org.graalvm.compiler.debug.DebugContext; 41 import org.graalvm.compiler.debug.DebugDumpHandler; 42 import org.graalvm.compiler.debug.DebugHandlersFactory; 43 import org.graalvm.compiler.debug.GlobalMetrics; 44 import org.graalvm.compiler.options.OptionValues; 45 import org.graalvm.compiler.serviceprovider.GraalServices; 46 import org.junit.After; 47 import org.junit.Assert; 48 import org.junit.internal.ComparisonCriteria; 49 import org.junit.internal.ExactComparisonCriteria; 50 51 import jdk.vm.ci.meta.ResolvedJavaMethod; 52 import sun.misc.Unsafe; 53 54 /** 55 * Base class that contains common utility methods and classes useful in unit tests. 56 */ 57 public class GraalTest { 58 59 public static final Unsafe UNSAFE; 60 static { 61 try { 62 Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); 63 theUnsafe.setAccessible(true); 64 UNSAFE = (Unsafe) theUnsafe.get(Unsafe.class); 65 } catch (Exception e) { 66 throw new RuntimeException("exception while trying to get Unsafe", e); 67 } 68 } 69 70 public static final boolean Java8OrEarlier = GraalServices.Java8OrEarlier; 71 72 protected Method getMethod(String methodName) { 73 return getMethod(getClass(), methodName); 74 } 75 76 protected Method getMethod(Class<?> clazz, String methodName) { 77 Method found = null; 78 for (Method m : clazz.getMethods()) { 79 if (m.getName().equals(methodName)) { 80 Assert.assertNull(found); 81 found = m; 82 } 83 } 84 if (found == null) { 85 /* Now look for non-public methods (but this does not look in superclasses). */ 86 for (Method m : clazz.getDeclaredMethods()) { 87 if (m.getName().equals(methodName)) { 88 Assert.assertNull(found); 89 found = m; 90 } 91 } 92 } 93 if (found != null) { 94 return found; 95 } else { 96 throw new RuntimeException("method not found: " + methodName); 97 } 98 } 99 100 protected Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) { 101 try { 102 return clazz.getMethod(methodName, parameterTypes); 103 } catch (NoSuchMethodException | SecurityException e) { 104 throw new RuntimeException("method not found: " + methodName + "" + Arrays.toString(parameterTypes)); 105 } 106 } 107 108 /** 109 * Compares two given objects for {@linkplain Assert#assertEquals(Object, Object) equality}. 110 * Does a deep copy equality comparison if {@code expected} is an array. 111 */ 112 protected void assertDeepEquals(Object expected, Object actual) { 113 assertDeepEquals(null, expected, actual); 114 } 115 116 /** 117 * Compares two given objects for {@linkplain Assert#assertEquals(Object, Object) equality}. 118 * Does a deep copy equality comparison if {@code expected} is an array. 119 * 120 * @param message the identifying message for the {@link AssertionError} 121 */ 122 protected void assertDeepEquals(String message, Object expected, Object actual) { 123 if (ulpsDelta() > 0) { 124 assertDeepEquals(message, expected, actual, ulpsDelta()); 125 } else { 126 assertDeepEquals(message, expected, actual, equalFloatsOrDoublesDelta()); 127 } 128 } 129 130 /** 131 * Compares two given values for equality, doing a recursive test if both values are arrays of 132 * the same type. 133 * 134 * @param message the identifying message for the {@link AssertionError} 135 * @param delta the maximum delta between two doubles or floats for which both numbers are still 136 * considered equal. 137 */ 138 protected void assertDeepEquals(String message, Object expected, Object actual, double delta) { 139 if (expected != null && actual != null) { 140 Class<?> expectedClass = expected.getClass(); 141 Class<?> actualClass = actual.getClass(); 142 if (expectedClass.isArray()) { 143 Assert.assertEquals(message, expectedClass, actual.getClass()); 144 if (expected instanceof int[]) { 145 Assert.assertArrayEquals(message, (int[]) expected, (int[]) actual); 146 } else if (expected instanceof byte[]) { 147 Assert.assertArrayEquals(message, (byte[]) expected, (byte[]) actual); 148 } else if (expected instanceof char[]) { 149 Assert.assertArrayEquals(message, (char[]) expected, (char[]) actual); 150 } else if (expected instanceof short[]) { 151 Assert.assertArrayEquals(message, (short[]) expected, (short[]) actual); 152 } else if (expected instanceof float[]) { 153 Assert.assertArrayEquals(message, (float[]) expected, (float[]) actual, (float) delta); 154 } else if (expected instanceof long[]) { 155 Assert.assertArrayEquals(message, (long[]) expected, (long[]) actual); 156 } else if (expected instanceof double[]) { 157 Assert.assertArrayEquals(message, (double[]) expected, (double[]) actual, delta); 158 } else if (expected instanceof boolean[]) { 159 new ExactComparisonCriteria().arrayEquals(message, expected, actual); 160 } else if (expected instanceof Object[]) { 161 new ComparisonCriteria() { 162 @Override 163 protected void assertElementsEqual(Object e, Object a) { 164 assertDeepEquals(message, e, a, delta); 165 } 166 }.arrayEquals(message, expected, actual); 167 } else { 168 Assert.fail((message == null ? "" : message) + "non-array value encountered: " + expected); 169 } 170 } else if (expectedClass.equals(double.class) && actualClass.equals(double.class)) { 171 Assert.assertEquals((double) expected, (double) actual, delta); 172 } else if (expectedClass.equals(float.class) && actualClass.equals(float.class)) { 173 Assert.assertEquals((float) expected, (float) actual, delta); 174 } else { 175 Assert.assertEquals(message, expected, actual); 176 } 177 } else { 178 Assert.assertEquals(message, expected, actual); 179 } 180 } 181 182 /** 183 * Compares two given values for equality, doing a recursive test if both values are arrays of 184 * the same type. Uses {@linkplain StrictMath#ulp(float) ULP}s for comparison of floats. 185 * 186 * @param message the identifying message for the {@link AssertionError} 187 * @param ulpsDelta the maximum allowed ulps difference between two doubles or floats for which 188 * both numbers are still considered equal. 189 */ 190 protected void assertDeepEquals(String message, Object expected, Object actual, int ulpsDelta) { 191 ComparisonCriteria doubleUlpsDeltaCriteria = new ComparisonCriteria() { 192 @Override 193 protected void assertElementsEqual(Object e, Object a) { 194 assertTrue(message, e instanceof Double && a instanceof Double); 195 // determine acceptable error based on whether it is a normal number or a NaN/Inf 196 double de = (Double) e; 197 double epsilon = (!Double.isNaN(de) && Double.isFinite(de) ? ulpsDelta * Math.ulp(de) : 0); 198 Assert.assertEquals(message, (Double) e, (Double) a, epsilon); 199 } 200 }; 201 202 ComparisonCriteria floatUlpsDeltaCriteria = new ComparisonCriteria() { 203 @Override 204 protected void assertElementsEqual(Object e, Object a) { 205 assertTrue(message, e instanceof Float && a instanceof Float); 206 // determine acceptable error based on whether it is a normal number or a NaN/Inf 207 float fe = (Float) e; 208 float epsilon = (!Float.isNaN(fe) && Float.isFinite(fe) ? ulpsDelta * Math.ulp(fe) : 0); 209 Assert.assertEquals(message, (Float) e, (Float) a, epsilon); 210 } 211 }; 212 213 if (expected != null && actual != null) { 214 Class<?> expectedClass = expected.getClass(); 215 Class<?> actualClass = actual.getClass(); 216 if (expectedClass.isArray()) { 217 Assert.assertEquals(message, expectedClass, actualClass); 218 if (expected instanceof double[] || expected instanceof Object[]) { 219 doubleUlpsDeltaCriteria.arrayEquals(message, expected, actual); 220 return; 221 } else if (expected instanceof float[] || expected instanceof Object[]) { 222 floatUlpsDeltaCriteria.arrayEquals(message, expected, actual); 223 return; 224 } 225 } else if (expectedClass.equals(double.class) && actualClass.equals(double.class)) { 226 doubleUlpsDeltaCriteria.arrayEquals(message, expected, actual); 227 return; 228 } else if (expectedClass.equals(float.class) && actualClass.equals(float.class)) { 229 floatUlpsDeltaCriteria.arrayEquals(message, expected, actual); 230 return; 231 } 232 } 233 // anything else just use the non-ulps version 234 assertDeepEquals(message, expected, actual, equalFloatsOrDoublesDelta()); 235 } 236 237 /** 238 * Gets the value used by {@link #assertDeepEquals(Object, Object)} and 239 * {@link #assertDeepEquals(String, Object, Object)} for the maximum delta between two doubles 240 * or floats for which both numbers are still considered equal. 241 */ 242 protected double equalFloatsOrDoublesDelta() { 243 return 0.0D; 244 } 245 246 // unless overridden ulpsDelta is not used 247 protected int ulpsDelta() { 248 return 0; 249 } 250 251 @SuppressWarnings("serial") 252 public static class MultiCauseAssertionError extends AssertionError { 253 254 private Throwable[] causes; 255 256 public MultiCauseAssertionError(String message, Throwable... causes) { 257 super(message); 258 this.causes = causes; 259 } 260 261 @Override 262 public void printStackTrace(PrintStream out) { 263 super.printStackTrace(out); 264 int num = 0; 265 for (Throwable cause : causes) { 266 if (cause != null) { 267 out.print("cause " + (num++)); 268 cause.printStackTrace(out); 269 } 270 } 271 } 272 273 @Override 274 public void printStackTrace(PrintWriter out) { 275 super.printStackTrace(out); 276 int num = 0; 277 for (Throwable cause : causes) { 278 if (cause != null) { 279 out.print("cause " + (num++) + ": "); 280 cause.printStackTrace(out); 281 } 282 } 283 } 284 } 285 286 /* 287 * Overrides to the normal JUnit {@link Assert} routines that provide varargs style formatting 288 * and produce an exception stack trace with the assertion frames trimmed out. 289 */ 290 291 /** 292 * Fails a test with the given message. 293 * 294 * @param message the identifying message for the {@link AssertionError} (<code>null</code> 295 * okay) 296 * @see AssertionError 297 */ 298 public static void fail(String message, Object... objects) { 299 AssertionError e; 300 if (message == null) { 301 e = new AssertionError(); 302 } else { 303 e = new AssertionError(String.format(message, objects)); 304 } 305 // Trim the assert frames from the stack trace 306 StackTraceElement[] trace = e.getStackTrace(); 307 int start = 1; // Skip this frame 308 String thisClassName = GraalTest.class.getName(); 309 while (start < trace.length && trace[start].getClassName().equals(thisClassName) && (trace[start].getMethodName().equals("assertTrue") || trace[start].getMethodName().equals("assertFalse"))) { 310 start++; 311 } 312 e.setStackTrace(Arrays.copyOfRange(trace, start, trace.length)); 313 throw e; 314 } 315 316 /** 317 * Asserts that a condition is true. If it isn't it throws an {@link AssertionError} with the 318 * given message. 319 * 320 * @param message the identifying message for the {@link AssertionError} (<code>null</code> 321 * okay) 322 * @param condition condition to be checked 323 */ 324 public static void assertTrue(String message, boolean condition) { 325 assertTrue(condition, message); 326 } 327 328 /** 329 * Asserts that a condition is true. If it isn't it throws an {@link AssertionError} without a 330 * message. 331 * 332 * @param condition condition to be checked 333 */ 334 public static void assertTrue(boolean condition) { 335 assertTrue(condition, null); 336 } 337 338 /** 339 * Asserts that a condition is false. If it isn't it throws an {@link AssertionError} with the 340 * given message. 341 * 342 * @param message the identifying message for the {@link AssertionError} (<code>null</code> 343 * okay) 344 * @param condition condition to be checked 345 */ 346 public static void assertFalse(String message, boolean condition) { 347 assertTrue(!condition, message); 348 } 349 350 /** 351 * Asserts that a condition is false. If it isn't it throws an {@link AssertionError} without a 352 * message. 353 * 354 * @param condition condition to be checked 355 */ 356 public static void assertFalse(boolean condition) { 357 assertTrue(!condition, null); 358 } 359 360 /** 361 * Asserts that a condition is true. If it isn't it throws an {@link AssertionError} with the 362 * given message. 363 * 364 * @param condition condition to be checked 365 * @param message the identifying message for the {@link AssertionError} 366 * @param objects arguments to the format string 367 */ 368 public static void assertTrue(boolean condition, String message, Object... objects) { 369 if (!condition) { 370 fail(message, objects); 371 } 372 } 373 374 /** 375 * Asserts that a condition is false. If it isn't it throws an {@link AssertionError} with the 376 * given message produced by {@link String#format}. 377 * 378 * @param condition condition to be checked 379 * @param message the identifying message for the {@link AssertionError} 380 * @param objects arguments to the format string 381 */ 382 public static void assertFalse(boolean condition, String message, Object... objects) { 383 assertTrue(!condition, message, objects); 384 } 385 386 /** 387 * Gets the {@link DebugHandlersFactory}s available for a {@link DebugContext}. 388 */ 389 protected Collection<DebugHandlersFactory> getDebugHandlersFactories() { 390 return Collections.emptyList(); 391 } 392 393 /** 394 * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if 395 * none currently exists. Debug contexts created by this method will have their 396 * {@link DebugDumpHandler}s closed in {@link #afterTest()}. 397 */ 398 protected DebugContext getDebugContext(OptionValues options) { 399 return getDebugContext(options, null, null); 400 } 401 402 /** 403 * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if 404 * none currently exists. Debug contexts created by this method will have their 405 * {@link DebugDumpHandler}s closed in {@link #afterTest()}. 406 * 407 * @param options currently active options 408 * @param id identification of the compilation or {@code null} 409 * @param method method to use for a proper description of the context or {@code null} 410 * @return configured context for compilation 411 */ 412 protected DebugContext getDebugContext(OptionValues options, String id, ResolvedJavaMethod method) { 413 List<DebugContext> cached = cachedDebugs.get(); 414 if (cached == null) { 415 cached = new ArrayList<>(); 416 cachedDebugs.set(cached); 417 } 418 for (DebugContext debug : cached) { 419 if (debug.getOptions() == options) { 420 return debug; 421 } 422 } 423 final DebugContext.Description descr; 424 if (method == null) { 425 descr = NO_DESCRIPTION; 426 } else { 427 descr = new DebugContext.Description(method, id == null ? method.getName() : id); 428 } 429 DebugContext debug = DebugContext.create(options, descr, globalMetrics, DEFAULT_LOG_STREAM, getDebugHandlersFactories()); 430 cached.add(debug); 431 return debug; 432 } 433 434 private static final GlobalMetrics globalMetrics = new GlobalMetrics(); 435 436 static { 437 Runtime.getRuntime().addShutdownHook(new Thread("GlobalMetricsPrinter") { 438 @Override 439 public void run() { 440 globalMetrics.print(new OptionValues(OptionValues.newOptionMap())); 441 } 442 }); 443 } 444 private final ThreadLocal<List<DebugContext>> cachedDebugs = new ThreadLocal<>(); 445 446 @After 447 public void afterTest() { 448 List<DebugContext> cached = cachedDebugs.get(); 449 if (cached != null) { 450 for (DebugContext debug : cached) { 451 debug.close(); 452 debug.closeDumpHandlers(true); 453 } 454 } 455 } 456 }