1 /* 2 * Copyright (c) 2013, 2018, 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.IOException; 31 import java.io.PrintStream; 32 import java.io.PrintWriter; 33 import java.lang.reflect.Field; 34 import java.lang.reflect.Method; 35 import java.nio.file.FileVisitResult; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.nio.file.SimpleFileVisitor; 39 import java.nio.file.attribute.BasicFileAttributes; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collection; 43 import java.util.Collections; 44 import java.util.List; 45 import java.util.concurrent.TimeUnit; 46 47 import org.graalvm.compiler.debug.DebugContext; 48 import org.graalvm.compiler.debug.DebugDumpHandler; 49 import org.graalvm.compiler.debug.DebugHandlersFactory; 50 import org.graalvm.compiler.debug.GlobalMetrics; 51 import org.graalvm.compiler.options.OptionValues; 52 import org.graalvm.compiler.serviceprovider.GraalServices; 53 import org.junit.After; 54 import org.junit.Assert; 55 import org.junit.AssumptionViolatedException; 56 import org.junit.internal.ComparisonCriteria; 57 import org.junit.internal.ExactComparisonCriteria; 58 import org.junit.rules.DisableOnDebug; 59 import org.junit.rules.TestRule; 60 import org.junit.rules.Timeout; 61 62 import jdk.vm.ci.meta.ResolvedJavaMethod; 63 import sun.misc.Unsafe; 64 65 /** 66 * Base class that contains common utility methods and classes useful in unit tests. 67 */ 68 public class GraalTest { 69 70 public static final Unsafe UNSAFE; 71 static { 72 try { 73 Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); 74 theUnsafe.setAccessible(true); 75 UNSAFE = (Unsafe) theUnsafe.get(Unsafe.class); 76 } catch (Exception e) { 77 throw new RuntimeException("exception while trying to get Unsafe", e); 78 } 79 } 80 81 protected Method getMethod(String methodName) { 82 return getMethod(getClass(), methodName); 83 } 84 85 protected Method getMethod(Class<?> clazz, String methodName) { 86 Method found = null; 87 for (Method m : clazz.getMethods()) { 88 if (m.getName().equals(methodName)) { 89 Assert.assertNull(found); 90 found = m; 91 } 92 } 93 if (found == null) { 94 /* Now look for non-public methods (but this does not look in superclasses). */ 95 for (Method m : clazz.getDeclaredMethods()) { 96 if (m.getName().equals(methodName)) { 97 Assert.assertNull(found); 98 found = m; 99 } 100 } 101 } 102 if (found != null) { 103 return found; 104 } else { 105 throw new RuntimeException("method not found: " + methodName); 106 } 107 } 108 109 protected Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) { 110 try { 111 return clazz.getMethod(methodName, parameterTypes); 112 } catch (NoSuchMethodException | SecurityException e) { 113 throw new RuntimeException("method not found: " + methodName + "" + Arrays.toString(parameterTypes)); 114 } 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 protected void assertDeepEquals(Object expected, Object actual) { 122 assertDeepEquals(null, expected, actual); 123 } 124 125 /** 126 * Compares two given objects for {@linkplain Assert#assertEquals(Object, Object) equality}. 127 * Does a deep copy equality comparison if {@code expected} is an array. 128 * 129 * @param message the identifying message for the {@link AssertionError} 130 */ 131 protected void assertDeepEquals(String message, Object expected, Object actual) { 132 if (ulpsDelta() > 0) { 133 assertDeepEquals(message, expected, actual, ulpsDelta()); 134 } else { 135 assertDeepEquals(message, expected, actual, equalFloatsOrDoublesDelta()); 136 } 137 } 138 139 /** 140 * Compares two given values for equality, doing a recursive test if both values are arrays of 141 * the same type. 142 * 143 * @param message the identifying message for the {@link AssertionError} 144 * @param delta the maximum delta between two doubles or floats for which both numbers are still 145 * considered equal. 146 */ 147 protected void assertDeepEquals(String message, Object expected, Object actual, double delta) { 148 if (expected != null && actual != null) { 149 Class<?> expectedClass = expected.getClass(); 150 Class<?> actualClass = actual.getClass(); 151 if (expectedClass.isArray()) { 152 Assert.assertEquals(message, expectedClass, actual.getClass()); 153 if (expected instanceof int[]) { 154 Assert.assertArrayEquals(message, (int[]) expected, (int[]) actual); 155 } else if (expected instanceof byte[]) { 156 Assert.assertArrayEquals(message, (byte[]) expected, (byte[]) actual); 157 } else if (expected instanceof char[]) { 158 Assert.assertArrayEquals(message, (char[]) expected, (char[]) actual); 159 } else if (expected instanceof short[]) { 160 Assert.assertArrayEquals(message, (short[]) expected, (short[]) actual); 161 } else if (expected instanceof float[]) { 162 Assert.assertArrayEquals(message, (float[]) expected, (float[]) actual, (float) delta); 163 } else if (expected instanceof long[]) { 164 Assert.assertArrayEquals(message, (long[]) expected, (long[]) actual); 165 } else if (expected instanceof double[]) { 166 Assert.assertArrayEquals(message, (double[]) expected, (double[]) actual, delta); 167 } else if (expected instanceof boolean[]) { 168 new ExactComparisonCriteria().arrayEquals(message, expected, actual); 169 } else if (expected instanceof Object[]) { 170 new ComparisonCriteria() { 171 @Override 172 protected void assertElementsEqual(Object e, Object a) { 173 assertDeepEquals(message, e, a, delta); 174 } 175 }.arrayEquals(message, expected, actual); 176 } else { 177 Assert.fail((message == null ? "" : message) + "non-array value encountered: " + expected); 178 } 179 } else if (expectedClass.equals(double.class) && actualClass.equals(double.class)) { 180 Assert.assertEquals((double) expected, (double) actual, delta); 181 } else if (expectedClass.equals(float.class) && actualClass.equals(float.class)) { 182 Assert.assertEquals((float) expected, (float) actual, delta); 183 } else { 184 Assert.assertEquals(message, expected, actual); 185 } 186 } else { 187 Assert.assertEquals(message, expected, actual); 188 } 189 } 190 191 /** 192 * Compares two given values for equality, doing a recursive test if both values are arrays of 193 * the same type. Uses {@linkplain StrictMath#ulp(float) ULP}s for comparison of floats. 194 * 195 * @param message the identifying message for the {@link AssertionError} 196 * @param ulpsDelta the maximum allowed ulps difference between two doubles or floats for which 197 * both numbers are still considered equal. 198 */ 199 protected void assertDeepEquals(String message, Object expected, Object actual, int ulpsDelta) { 200 ComparisonCriteria doubleUlpsDeltaCriteria = new ComparisonCriteria() { 201 @Override 202 protected void assertElementsEqual(Object e, Object a) { 203 assertTrue(message, e instanceof Double && a instanceof Double); 204 // determine acceptable error based on whether it is a normal number or a NaN/Inf 205 double de = (Double) e; 206 double epsilon = (!Double.isNaN(de) && Double.isFinite(de) ? ulpsDelta * Math.ulp(de) : 0); 207 Assert.assertEquals(message, (Double) e, (Double) a, epsilon); 208 } 209 }; 210 211 ComparisonCriteria floatUlpsDeltaCriteria = new ComparisonCriteria() { 212 @Override 213 protected void assertElementsEqual(Object e, Object a) { 214 assertTrue(message, e instanceof Float && a instanceof Float); 215 // determine acceptable error based on whether it is a normal number or a NaN/Inf 216 float fe = (Float) e; 217 float epsilon = (!Float.isNaN(fe) && Float.isFinite(fe) ? ulpsDelta * Math.ulp(fe) : 0); 218 Assert.assertEquals(message, (Float) e, (Float) a, epsilon); 219 } 220 }; 221 222 if (expected != null && actual != null) { 223 Class<?> expectedClass = expected.getClass(); 224 Class<?> actualClass = actual.getClass(); 225 if (expectedClass.isArray()) { 226 Assert.assertEquals(message, expectedClass, actualClass); 227 if (expected instanceof double[] || expected instanceof Object[]) { 228 doubleUlpsDeltaCriteria.arrayEquals(message, expected, actual); 229 return; 230 } else if (expected instanceof float[] || expected instanceof Object[]) { 231 floatUlpsDeltaCriteria.arrayEquals(message, expected, actual); 232 return; 233 } 234 } else if (expectedClass.equals(double.class) && actualClass.equals(double.class)) { 235 doubleUlpsDeltaCriteria.arrayEquals(message, expected, actual); 236 return; 237 } else if (expectedClass.equals(float.class) && actualClass.equals(float.class)) { 238 floatUlpsDeltaCriteria.arrayEquals(message, expected, actual); 239 return; 240 } 241 } 242 // anything else just use the non-ulps version 243 assertDeepEquals(message, expected, actual, equalFloatsOrDoublesDelta()); 244 } 245 246 /** 247 * @see "https://bugs.openjdk.java.net/browse/JDK-8076557" 248 */ 249 public static void assumeManagementLibraryIsLoadable() { 250 try { 251 /* Trigger loading of the management library using the bootstrap class loader. */ 252 GraalServices.getCurrentThreadAllocatedBytes(); 253 } catch (UnsatisfiedLinkError | NoClassDefFoundError | UnsupportedOperationException e) { 254 throw new AssumptionViolatedException("Management interface is unavailable: " + e); 255 } 256 } 257 258 /** 259 * Gets the value used by {@link #assertDeepEquals(Object, Object)} and 260 * {@link #assertDeepEquals(String, Object, Object)} for the maximum delta between two doubles 261 * or floats for which both numbers are still considered equal. 262 */ 263 protected double equalFloatsOrDoublesDelta() { 264 return 0.0D; 265 } 266 267 // unless overridden ulpsDelta is not used 268 protected int ulpsDelta() { 269 return 0; 270 } 271 272 @SuppressWarnings("serial") 273 public static class MultiCauseAssertionError extends AssertionError { 274 275 private Throwable[] causes; 276 277 public MultiCauseAssertionError(String message, Throwable... causes) { 278 super(message); 279 this.causes = causes; 280 } 281 282 @Override 283 public void printStackTrace(PrintStream out) { 284 super.printStackTrace(out); 285 int num = 0; 286 for (Throwable cause : causes) { 287 if (cause != null) { 288 out.print("cause " + (num++)); 289 cause.printStackTrace(out); 290 } 291 } 292 } 293 294 @Override 295 public void printStackTrace(PrintWriter out) { 296 super.printStackTrace(out); 297 int num = 0; 298 for (Throwable cause : causes) { 299 if (cause != null) { 300 out.print("cause " + (num++) + ": "); 301 cause.printStackTrace(out); 302 } 303 } 304 } 305 } 306 307 /* 308 * Overrides to the normal JUnit {@link Assert} routines that provide varargs style formatting 309 * and produce an exception stack trace with the assertion frames trimmed out. 310 */ 311 312 /** 313 * Fails a test with the given message. 314 * 315 * @param message the identifying message for the {@link AssertionError} (<code>null</code> 316 * okay) 317 * @see AssertionError 318 */ 319 public static void fail(String message, Object... objects) { 320 AssertionError e; 321 if (message == null) { 322 e = new AssertionError(); 323 } else { 324 e = new AssertionError(String.format(message, objects)); 325 } 326 // Trim the assert frames from the stack trace 327 StackTraceElement[] trace = e.getStackTrace(); 328 int start = 1; // Skip this frame 329 String thisClassName = GraalTest.class.getName(); 330 while (start < trace.length && trace[start].getClassName().equals(thisClassName) && (trace[start].getMethodName().equals("assertTrue") || trace[start].getMethodName().equals("assertFalse"))) { 331 start++; 332 } 333 e.setStackTrace(Arrays.copyOfRange(trace, start, trace.length)); 334 throw e; 335 } 336 337 /** 338 * Asserts that a condition is true. If it isn't it throws an {@link AssertionError} with the 339 * given message. 340 * 341 * @param message the identifying message for the {@link AssertionError} (<code>null</code> 342 * okay) 343 * @param condition condition to be checked 344 */ 345 public static void assertTrue(String message, boolean condition) { 346 assertTrue(condition, message); 347 } 348 349 /** 350 * Asserts that a condition is true. If it isn't it throws an {@link AssertionError} without a 351 * message. 352 * 353 * @param condition condition to be checked 354 */ 355 public static void assertTrue(boolean condition) { 356 assertTrue(condition, null); 357 } 358 359 /** 360 * Asserts that a condition is false. If it isn't it throws an {@link AssertionError} with the 361 * given message. 362 * 363 * @param message the identifying message for the {@link AssertionError} (<code>null</code> 364 * okay) 365 * @param condition condition to be checked 366 */ 367 public static void assertFalse(String message, boolean condition) { 368 assertTrue(!condition, message); 369 } 370 371 /** 372 * Asserts that a condition is false. If it isn't it throws an {@link AssertionError} without a 373 * message. 374 * 375 * @param condition condition to be checked 376 */ 377 public static void assertFalse(boolean condition) { 378 assertTrue(!condition, null); 379 } 380 381 /** 382 * Asserts that a condition is true. If it isn't it throws an {@link AssertionError} with the 383 * given message. 384 * 385 * @param condition condition to be checked 386 * @param message the identifying message for the {@link AssertionError} 387 * @param objects arguments to the format string 388 */ 389 public static void assertTrue(boolean condition, String message, Object... objects) { 390 if (!condition) { 391 fail(message, objects); 392 } 393 } 394 395 /** 396 * Asserts that a condition is false. If it isn't it throws an {@link AssertionError} with the 397 * given message produced by {@link String#format}. 398 * 399 * @param condition condition to be checked 400 * @param message the identifying message for the {@link AssertionError} 401 * @param objects arguments to the format string 402 */ 403 public static void assertFalse(boolean condition, String message, Object... objects) { 404 assertTrue(!condition, message, objects); 405 } 406 407 /** 408 * Gets the {@link DebugHandlersFactory}s available for a {@link DebugContext}. 409 */ 410 protected Collection<DebugHandlersFactory> getDebugHandlersFactories() { 411 return Collections.emptyList(); 412 } 413 414 /** 415 * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if 416 * none currently exists. Debug contexts created by this method will have their 417 * {@link DebugDumpHandler}s closed in {@link #afterTest()}. 418 */ 419 protected DebugContext getDebugContext(OptionValues options) { 420 return getDebugContext(options, null, null); 421 } 422 423 /** 424 * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if 425 * none currently exists. Debug contexts created by this method will have their 426 * {@link DebugDumpHandler}s closed in {@link #afterTest()}. 427 * 428 * @param options currently active options 429 * @param id identification of the compilation or {@code null} 430 * @param method method to use for a proper description of the context or {@code null} 431 * @return configured context for compilation 432 */ 433 protected DebugContext getDebugContext(OptionValues options, String id, ResolvedJavaMethod method) { 434 List<DebugContext> cached = cachedDebugs.get(); 435 if (cached == null) { 436 cached = new ArrayList<>(); 437 cachedDebugs.set(cached); 438 } 439 for (DebugContext debug : cached) { 440 if (debug.getOptions() == options) { 441 return debug; 442 } 443 } 444 final DebugContext.Description descr; 445 if (method == null) { 446 descr = NO_DESCRIPTION; 447 } else { 448 descr = new DebugContext.Description(method, id == null ? method.getName() : id); 449 } 450 DebugContext debug = DebugContext.create(options, descr, globalMetrics, DEFAULT_LOG_STREAM, getDebugHandlersFactories()); 451 cached.add(debug); 452 return debug; 453 } 454 455 private static final GlobalMetrics globalMetrics = new GlobalMetrics(); 456 457 static { 458 Runtime.getRuntime().addShutdownHook(new Thread("GlobalMetricsPrinter") { 459 @Override 460 public void run() { 461 globalMetrics.print(new OptionValues(OptionValues.newOptionMap())); 462 } 463 }); 464 } 465 private final ThreadLocal<List<DebugContext>> cachedDebugs = new ThreadLocal<>(); 466 467 @After 468 public void afterTest() { 469 List<DebugContext> cached = cachedDebugs.get(); 470 if (cached != null) { 471 for (DebugContext debug : cached) { 472 debug.close(); 473 debug.closeDumpHandlers(true); 474 } 475 } 476 } 477 478 private static final double TIMEOUT_SCALING_FACTOR = Double.parseDouble(System.getProperty("graaltest.timeout.factor", "1.0")); 479 480 /** 481 * Creates a {@link TestRule} that applies a given timeout. 482 * 483 * A test harness can scale {@code length} with a factor specified by the 484 * {@code graaltest.timeout.factor} system property. 485 */ 486 public static TestRule createTimeout(long length, TimeUnit timeUnit) { 487 Timeout timeout = new Timeout((long) (length * TIMEOUT_SCALING_FACTOR), timeUnit); 488 try { 489 return new DisableOnDebug(timeout); 490 } catch (LinkageError ex) { 491 return timeout; 492 } 493 } 494 495 /** 496 * @see #createTimeout 497 */ 498 public static TestRule createTimeoutSeconds(int seconds) { 499 return createTimeout(seconds, TimeUnit.SECONDS); 500 } 501 502 /** 503 * @see #createTimeout 504 */ 505 public static TestRule createTimeoutMillis(long milliseconds) { 506 return createTimeout(milliseconds, TimeUnit.MILLISECONDS); 507 } 508 509 /** 510 * Tries to recursively remove {@code directory}. If it fails with an {@link IOException}, the 511 * exception's {@code toString()} is printed to {@link System#err} and the exception is 512 * returned. 513 */ 514 public static IOException removeDirectory(Path directory) { 515 try { 516 Files.walkFileTree(directory, new SimpleFileVisitor<Path>() { 517 @Override 518 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 519 Files.delete(file); 520 return FileVisitResult.CONTINUE; 521 } 522 523 @Override 524 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 525 Files.delete(dir); 526 return FileVisitResult.CONTINUE; 527 } 528 }); 529 } catch (IOException e) { 530 System.err.println(e); 531 return e; 532 } 533 return null; 534 } 535 }