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 public static final boolean Java11OrEarlier = GraalServices.Java11OrEarlier; 72 73 protected Method getMethod(String methodName) { 74 return getMethod(getClass(), methodName); 75 } 76 77 protected Method getMethod(Class<?> clazz, String methodName) { 78 Method found = null; 79 for (Method m : clazz.getMethods()) { 80 if (m.getName().equals(methodName)) { 81 Assert.assertNull(found); 82 found = m; 83 } 84 } 85 if (found == null) { 86 /* Now look for non-public methods (but this does not look in superclasses). */ 87 for (Method m : clazz.getDeclaredMethods()) { 88 if (m.getName().equals(methodName)) { 89 Assert.assertNull(found); 90 found = m; 91 } 92 } 93 } 94 if (found != null) { 95 return found; 96 } else { 97 throw new RuntimeException("method not found: " + methodName); 98 } 99 } 100 101 protected Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) { 102 try { 103 return clazz.getMethod(methodName, parameterTypes); 104 } catch (NoSuchMethodException | SecurityException e) { 105 throw new RuntimeException("method not found: " + methodName + "" + Arrays.toString(parameterTypes)); 106 } 107 } 108 109 /** 110 * Compares two given objects for {@linkplain Assert#assertEquals(Object, Object) equality}. 111 * Does a deep copy equality comparison if {@code expected} is an array. 112 */ 113 protected void assertDeepEquals(Object expected, Object actual) { 114 assertDeepEquals(null, expected, actual); 115 } 116 117 /** 118 * Compares two given objects for {@linkplain Assert#assertEquals(Object, Object) equality}. 119 * Does a deep copy equality comparison if {@code expected} is an array. 120 * 121 * @param message the identifying message for the {@link AssertionError} 122 */ 123 protected void assertDeepEquals(String message, Object expected, Object actual) { 124 if (ulpsDelta() > 0) { 125 assertDeepEquals(message, expected, actual, ulpsDelta()); 126 } else { 127 assertDeepEquals(message, expected, actual, equalFloatsOrDoublesDelta()); 128 } 129 } 130 131 /** 132 * Compares two given values for equality, doing a recursive test if both values are arrays of 133 * the same type. 134 * 135 * @param message the identifying message for the {@link AssertionError} 136 * @param delta the maximum delta between two doubles or floats for which both numbers are still 137 * considered equal. 138 */ 139 protected void assertDeepEquals(String message, Object expected, Object actual, double delta) { 140 if (expected != null && actual != null) { 141 Class<?> expectedClass = expected.getClass(); 142 Class<?> actualClass = actual.getClass(); 143 if (expectedClass.isArray()) { 144 Assert.assertEquals(message, expectedClass, actual.getClass()); 145 if (expected instanceof int[]) { 146 Assert.assertArrayEquals(message, (int[]) expected, (int[]) actual); 147 } else if (expected instanceof byte[]) { 148 Assert.assertArrayEquals(message, (byte[]) expected, (byte[]) actual); 149 } else if (expected instanceof char[]) { 150 Assert.assertArrayEquals(message, (char[]) expected, (char[]) actual); 151 } else if (expected instanceof short[]) { 152 Assert.assertArrayEquals(message, (short[]) expected, (short[]) actual); 153 } else if (expected instanceof float[]) { 154 Assert.assertArrayEquals(message, (float[]) expected, (float[]) actual, (float) delta); 155 } else if (expected instanceof long[]) { 156 Assert.assertArrayEquals(message, (long[]) expected, (long[]) actual); 157 } else if (expected instanceof double[]) { 158 Assert.assertArrayEquals(message, (double[]) expected, (double[]) actual, delta); 159 } else if (expected instanceof boolean[]) { 160 new ExactComparisonCriteria().arrayEquals(message, expected, actual); 161 } else if (expected instanceof Object[]) { 162 new ComparisonCriteria() { 163 @Override 164 protected void assertElementsEqual(Object e, Object a) { 165 assertDeepEquals(message, e, a, delta); 166 } 167 }.arrayEquals(message, expected, actual); 168 } else { 169 Assert.fail((message == null ? "" : message) + "non-array value encountered: " + expected); 170 } 171 } else if (expectedClass.equals(double.class) && actualClass.equals(double.class)) { 172 Assert.assertEquals((double) expected, (double) actual, delta); 173 } else if (expectedClass.equals(float.class) && actualClass.equals(float.class)) { 174 Assert.assertEquals((float) expected, (float) actual, delta); 175 } else { 176 Assert.assertEquals(message, expected, actual); 177 } 178 } else { 179 Assert.assertEquals(message, expected, actual); 180 } 181 } 182 183 /** 184 * Compares two given values for equality, doing a recursive test if both values are arrays of 185 * the same type. Uses {@linkplain StrictMath#ulp(float) ULP}s for comparison of floats. 186 * 187 * @param message the identifying message for the {@link AssertionError} 188 * @param ulpsDelta the maximum allowed ulps difference between two doubles or floats for which 189 * both numbers are still considered equal. 190 */ 191 protected void assertDeepEquals(String message, Object expected, Object actual, int ulpsDelta) { 192 ComparisonCriteria doubleUlpsDeltaCriteria = new ComparisonCriteria() { 193 @Override 194 protected void assertElementsEqual(Object e, Object a) { 195 assertTrue(message, e instanceof Double && a instanceof Double); 196 // determine acceptable error based on whether it is a normal number or a NaN/Inf 197 double de = (Double) e; 198 double epsilon = (!Double.isNaN(de) && Double.isFinite(de) ? ulpsDelta * Math.ulp(de) : 0); 199 Assert.assertEquals(message, (Double) e, (Double) a, epsilon); 200 } 201 }; 202 203 ComparisonCriteria floatUlpsDeltaCriteria = new ComparisonCriteria() { 204 @Override 205 protected void assertElementsEqual(Object e, Object a) { 206 assertTrue(message, e instanceof Float && a instanceof Float); 207 // determine acceptable error based on whether it is a normal number or a NaN/Inf 208 float fe = (Float) e; 209 float epsilon = (!Float.isNaN(fe) && Float.isFinite(fe) ? ulpsDelta * Math.ulp(fe) : 0); 210 Assert.assertEquals(message, (Float) e, (Float) a, epsilon); 211 } 212 }; 213 214 if (expected != null && actual != null) { 215 Class<?> expectedClass = expected.getClass(); 216 Class<?> actualClass = actual.getClass(); 217 if (expectedClass.isArray()) { 218 Assert.assertEquals(message, expectedClass, actualClass); 219 if (expected instanceof double[] || expected instanceof Object[]) { 220 doubleUlpsDeltaCriteria.arrayEquals(message, expected, actual); 221 return; 222 } else if (expected instanceof float[] || expected instanceof Object[]) { 223 floatUlpsDeltaCriteria.arrayEquals(message, expected, actual); 224 return; 225 } 226 } else if (expectedClass.equals(double.class) && actualClass.equals(double.class)) { 227 doubleUlpsDeltaCriteria.arrayEquals(message, expected, actual); 228 return; 229 } else if (expectedClass.equals(float.class) && actualClass.equals(float.class)) { 230 floatUlpsDeltaCriteria.arrayEquals(message, expected, actual); 231 return; 232 } 233 } 234 // anything else just use the non-ulps version 235 assertDeepEquals(message, expected, actual, equalFloatsOrDoublesDelta()); 236 } 237 238 /** 239 * Gets the value used by {@link #assertDeepEquals(Object, Object)} and 240 * {@link #assertDeepEquals(String, Object, Object)} for the maximum delta between two doubles 241 * or floats for which both numbers are still considered equal. 242 */ 243 protected double equalFloatsOrDoublesDelta() { 244 return 0.0D; 245 } 246 247 // unless overridden ulpsDelta is not used 248 protected int ulpsDelta() { 249 return 0; 250 } 251 252 @SuppressWarnings("serial") 253 public static class MultiCauseAssertionError extends AssertionError { 254 255 private Throwable[] causes; 256 257 public MultiCauseAssertionError(String message, Throwable... causes) { 258 super(message); 259 this.causes = causes; 260 } 261 262 @Override 263 public void printStackTrace(PrintStream out) { 264 super.printStackTrace(out); 265 int num = 0; 266 for (Throwable cause : causes) { 267 if (cause != null) { 268 out.print("cause " + (num++)); 269 cause.printStackTrace(out); 270 } 271 } 272 } 273 274 @Override 275 public void printStackTrace(PrintWriter out) { 276 super.printStackTrace(out); 277 int num = 0; 278 for (Throwable cause : causes) { 279 if (cause != null) { 280 out.print("cause " + (num++) + ": "); 281 cause.printStackTrace(out); 282 } 283 } 284 } 285 } 286 287 /* 288 * Overrides to the normal JUnit {@link Assert} routines that provide varargs style formatting 289 * and produce an exception stack trace with the assertion frames trimmed out. 290 */ 291 292 /** 293 * Fails a test with the given message. 294 * 295 * @param message the identifying message for the {@link AssertionError} (<code>null</code> 296 * okay) 297 * @see AssertionError 298 */ 299 public static void fail(String message, Object... objects) { 300 AssertionError e; 301 if (message == null) { 302 e = new AssertionError(); 303 } else { 304 e = new AssertionError(String.format(message, objects)); 305 } 306 // Trim the assert frames from the stack trace 307 StackTraceElement[] trace = e.getStackTrace(); 308 int start = 1; // Skip this frame 309 String thisClassName = GraalTest.class.getName(); 310 while (start < trace.length && trace[start].getClassName().equals(thisClassName) && (trace[start].getMethodName().equals("assertTrue") || trace[start].getMethodName().equals("assertFalse"))) { 311 start++; 312 } 313 e.setStackTrace(Arrays.copyOfRange(trace, start, trace.length)); 314 throw e; 315 } 316 317 /** 318 * Asserts that a condition is true. If it isn't it throws an {@link AssertionError} with the 319 * given message. 320 * 321 * @param message the identifying message for the {@link AssertionError} (<code>null</code> 322 * okay) 323 * @param condition condition to be checked 324 */ 325 public static void assertTrue(String message, boolean condition) { 326 assertTrue(condition, message); 327 } 328 329 /** 330 * Asserts that a condition is true. If it isn't it throws an {@link AssertionError} without a 331 * message. 332 * 333 * @param condition condition to be checked 334 */ 335 public static void assertTrue(boolean condition) { 336 assertTrue(condition, null); 337 } 338 339 /** 340 * Asserts that a condition is false. If it isn't it throws an {@link AssertionError} with the 341 * given message. 342 * 343 * @param message the identifying message for the {@link AssertionError} (<code>null</code> 344 * okay) 345 * @param condition condition to be checked 346 */ 347 public static void assertFalse(String message, boolean condition) { 348 assertTrue(!condition, message); 349 } 350 351 /** 352 * Asserts that a condition is false. If it isn't it throws an {@link AssertionError} without a 353 * message. 354 * 355 * @param condition condition to be checked 356 */ 357 public static void assertFalse(boolean condition) { 358 assertTrue(!condition, null); 359 } 360 361 /** 362 * Asserts that a condition is true. If it isn't it throws an {@link AssertionError} with the 363 * given message. 364 * 365 * @param condition condition to be checked 366 * @param message the identifying message for the {@link AssertionError} 367 * @param objects arguments to the format string 368 */ 369 public static void assertTrue(boolean condition, String message, Object... objects) { 370 if (!condition) { 371 fail(message, objects); 372 } 373 } 374 375 /** 376 * Asserts that a condition is false. If it isn't it throws an {@link AssertionError} with the 377 * given message produced by {@link String#format}. 378 * 379 * @param condition condition to be checked 380 * @param message the identifying message for the {@link AssertionError} 381 * @param objects arguments to the format string 382 */ 383 public static void assertFalse(boolean condition, String message, Object... objects) { 384 assertTrue(!condition, message, objects); 385 } 386 387 /** 388 * Gets the {@link DebugHandlersFactory}s available for a {@link DebugContext}. 389 */ 390 protected Collection<DebugHandlersFactory> getDebugHandlersFactories() { 391 return Collections.emptyList(); 392 } 393 394 /** 395 * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if 396 * none currently exists. Debug contexts created by this method will have their 397 * {@link DebugDumpHandler}s closed in {@link #afterTest()}. 398 */ 399 protected DebugContext getDebugContext(OptionValues options) { 400 return getDebugContext(options, null, null); 401 } 402 403 /** 404 * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if 405 * none currently exists. Debug contexts created by this method will have their 406 * {@link DebugDumpHandler}s closed in {@link #afterTest()}. 407 * 408 * @param options currently active options 409 * @param id identification of the compilation or {@code null} 410 * @param method method to use for a proper description of the context or {@code null} 411 * @return configured context for compilation 412 */ 413 protected DebugContext getDebugContext(OptionValues options, String id, ResolvedJavaMethod method) { 414 List<DebugContext> cached = cachedDebugs.get(); 415 if (cached == null) { 416 cached = new ArrayList<>(); 417 cachedDebugs.set(cached); 418 } 419 for (DebugContext debug : cached) { 420 if (debug.getOptions() == options) { 421 return debug; 422 } 423 } 424 final DebugContext.Description descr; 425 if (method == null) { 426 descr = NO_DESCRIPTION; 427 } else { 428 descr = new DebugContext.Description(method, id == null ? method.getName() : id); 429 } 430 DebugContext debug = DebugContext.create(options, descr, globalMetrics, DEFAULT_LOG_STREAM, getDebugHandlersFactories()); 431 cached.add(debug); 432 return debug; 433 } 434 435 private static final GlobalMetrics globalMetrics = new GlobalMetrics(); 436 437 static { 438 Runtime.getRuntime().addShutdownHook(new Thread("GlobalMetricsPrinter") { 439 @Override 440 public void run() { 441 globalMetrics.print(new OptionValues(OptionValues.newOptionMap())); 442 } 443 }); 444 } 445 private final ThreadLocal<List<DebugContext>> cachedDebugs = new ThreadLocal<>(); 446 447 @After 448 public void afterTest() { 449 List<DebugContext> cached = cachedDebugs.get(); 450 if (cached != null) { 451 for (DebugContext debug : cached) { 452 debug.close(); 453 debug.closeDumpHandlers(true); 454 } 455 } 456 } 457 }