Utils.java revision 2224:2a8815d86b93
1/* 2 * Copyright (c) 2013, 2016, 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 24package jdk.test.lib; 25 26import java.io.File; 27import java.io.IOException; 28import java.lang.reflect.Field; 29import java.net.InetAddress; 30import java.net.MalformedURLException; 31import java.net.ServerSocket; 32import java.net.URL; 33import java.net.URLClassLoader; 34import java.net.UnknownHostException; 35import java.nio.file.Files; 36import java.nio.file.Path; 37import java.nio.file.Paths; 38import java.util.ArrayList; 39import java.util.Arrays; 40import java.util.Collection; 41import java.util.Collections; 42import java.util.Iterator; 43import java.util.Map; 44import java.util.HashMap; 45import java.util.List; 46import java.util.Objects; 47import java.util.Random; 48import java.util.function.BooleanSupplier; 49import java.util.concurrent.TimeUnit; 50import java.util.function.Consumer; 51import java.util.function.Function; 52import java.util.regex.Matcher; 53import java.util.regex.Pattern; 54import jdk.internal.misc.Unsafe; 55 56import static jdk.test.lib.Asserts.assertTrue; 57import jdk.test.lib.process.ProcessTools; 58import jdk.test.lib.process.OutputAnalyzer; 59 60/** 61 * Common library for various test helper functions. 62 */ 63public final class Utils { 64 65 /** 66 * Returns the value of 'test.class.path' system property. 67 */ 68 public static final String TEST_CLASS_PATH = System.getProperty("test.class.path", "."); 69 70 /** 71 * Returns the sequence used by operating system to separate lines. 72 */ 73 public static final String NEW_LINE = System.getProperty("line.separator"); 74 75 /** 76 * Returns the value of 'test.vm.opts' system property. 77 */ 78 public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim(); 79 80 /** 81 * Returns the value of 'test.java.opts' system property. 82 */ 83 public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim(); 84 85 /** 86 * Returns the value of 'test.src' system property. 87 */ 88 public static final String TEST_SRC = System.getProperty("test.src", "").trim(); 89 90 private static Unsafe unsafe = null; 91 92 /** 93 * Defines property name for seed value. 94 */ 95 public static final String SEED_PROPERTY_NAME = "jdk.test.lib.random.seed"; 96 97 /* (non-javadoc) 98 * Random generator with (or without) predefined seed. Depends on 99 * "jdk.test.lib.random.seed" property value. 100 */ 101 private static volatile Random RANDOM_GENERATOR; 102 103 /** 104 * Contains the seed value used for {@link java.util.Random} creation. 105 */ 106 public static final long SEED = Long.getLong(SEED_PROPERTY_NAME, new Random().nextLong()); 107 /** 108 * Returns the value of 'test.timeout.factor' system property 109 * converted to {@code double}. 110 */ 111 public static final double TIMEOUT_FACTOR; 112 static { 113 String toFactor = System.getProperty("test.timeout.factor", "1.0"); 114 TIMEOUT_FACTOR = Double.parseDouble(toFactor); 115 } 116 117 /** 118 * Returns the value of JTREG default test timeout in milliseconds 119 * converted to {@code long}. 120 */ 121 public static final long DEFAULT_TEST_TIMEOUT = TimeUnit.SECONDS.toMillis(120); 122 123 private Utils() { 124 // Private constructor to prevent class instantiation 125 } 126 127 /** 128 * Returns the list of VM options. 129 * 130 * @return List of VM options 131 */ 132 public static List<String> getVmOptions() { 133 return Arrays.asList(safeSplitString(VM_OPTIONS)); 134 } 135 136 /** 137 * Returns the list of VM options with -J prefix. 138 * 139 * @return The list of VM options with -J prefix 140 */ 141 public static List<String> getForwardVmOptions() { 142 String[] opts = safeSplitString(VM_OPTIONS); 143 for (int i = 0; i < opts.length; i++) { 144 opts[i] = "-J" + opts[i]; 145 } 146 return Arrays.asList(opts); 147 } 148 149 /** 150 * Returns the default JTReg arguments for a jvm running a test. 151 * This is the combination of JTReg arguments test.vm.opts and test.java.opts. 152 * @return An array of options, or an empty array if no options. 153 */ 154 public static String[] getTestJavaOpts() { 155 List<String> opts = new ArrayList<String>(); 156 Collections.addAll(opts, safeSplitString(VM_OPTIONS)); 157 Collections.addAll(opts, safeSplitString(JAVA_OPTIONS)); 158 return opts.toArray(new String[0]); 159 } 160 161 /** 162 * Combines given arguments with default JTReg arguments for a jvm running a test. 163 * This is the combination of JTReg arguments test.vm.opts and test.java.opts 164 * @return The combination of JTReg test java options and user args. 165 */ 166 public static String[] addTestJavaOpts(String... userArgs) { 167 List<String> opts = new ArrayList<String>(); 168 Collections.addAll(opts, getTestJavaOpts()); 169 Collections.addAll(opts, userArgs); 170 return opts.toArray(new String[0]); 171 } 172 173 /** 174 * Removes any options specifying which GC to use, for example "-XX:+UseG1GC". 175 * Removes any options matching: -XX:(+/-)Use*GC 176 * Used when a test need to set its own GC version. Then any 177 * GC specified by the framework must first be removed. 178 * @return A copy of given opts with all GC options removed. 179 */ 180 private static final Pattern useGcPattern = Pattern.compile( 181 "(?:\\-XX\\:[\\+\\-]Use.+GC)" 182 + "|(?:\\-Xconcgc)"); 183 public static List<String> removeGcOpts(List<String> opts) { 184 List<String> optsWithoutGC = new ArrayList<String>(); 185 for (String opt : opts) { 186 if (useGcPattern.matcher(opt).matches()) { 187 System.out.println("removeGcOpts: removed " + opt); 188 } else { 189 optsWithoutGC.add(opt); 190 } 191 } 192 return optsWithoutGC; 193 } 194 195 /** 196 * Returns the default JTReg arguments for a jvm running a test without 197 * options that matches regular expressions in {@code filters}. 198 * This is the combination of JTReg arguments test.vm.opts and test.java.opts. 199 * @param filters Regular expressions used to filter out options. 200 * @return An array of options, or an empty array if no options. 201 */ 202 public static String[] getFilteredTestJavaOpts(String... filters) { 203 String options[] = getTestJavaOpts(); 204 205 if (filters.length == 0) { 206 return options; 207 } 208 209 List<String> filteredOptions = new ArrayList<String>(options.length); 210 Pattern patterns[] = new Pattern[filters.length]; 211 for (int i = 0; i < filters.length; i++) { 212 patterns[i] = Pattern.compile(filters[i]); 213 } 214 215 for (String option : options) { 216 boolean matched = false; 217 for (int i = 0; i < patterns.length && !matched; i++) { 218 Matcher matcher = patterns[i].matcher(option); 219 matched = matcher.find(); 220 } 221 if (!matched) { 222 filteredOptions.add(option); 223 } 224 } 225 226 return filteredOptions.toArray(new String[filteredOptions.size()]); 227 } 228 229 /** 230 * Splits a string by white space. 231 * Works like String.split(), but returns an empty array 232 * if the string is null or empty. 233 */ 234 private static String[] safeSplitString(String s) { 235 if (s == null || s.trim().isEmpty()) { 236 return new String[] {}; 237 } 238 return s.trim().split("\\s+"); 239 } 240 241 /** 242 * @return The full command line for the ProcessBuilder. 243 */ 244 public static String getCommandLine(ProcessBuilder pb) { 245 StringBuilder cmd = new StringBuilder(); 246 for (String s : pb.command()) { 247 cmd.append(s).append(" "); 248 } 249 return cmd.toString(); 250 } 251 252 /** 253 * Returns the free port on the local host. 254 * The function will spin until a valid port number is found. 255 * 256 * @return The port number 257 * @throws InterruptedException if any thread has interrupted the current thread 258 * @throws IOException if an I/O error occurs when opening the socket 259 */ 260 public static int getFreePort() throws InterruptedException, IOException { 261 int port = -1; 262 263 while (port <= 0) { 264 Thread.sleep(100); 265 266 ServerSocket serverSocket = null; 267 try { 268 serverSocket = new ServerSocket(0); 269 port = serverSocket.getLocalPort(); 270 } finally { 271 serverSocket.close(); 272 } 273 } 274 275 return port; 276 } 277 278 /** 279 * Returns the name of the local host. 280 * 281 * @return The host name 282 * @throws UnknownHostException if IP address of a host could not be determined 283 */ 284 public static String getHostname() throws UnknownHostException { 285 InetAddress inetAddress = InetAddress.getLocalHost(); 286 String hostName = inetAddress.getHostName(); 287 288 assertTrue((hostName != null && !hostName.isEmpty()), 289 "Cannot get hostname"); 290 291 return hostName; 292 } 293 294 /** 295 * Uses "jcmd -l" to search for a jvm pid. This function will wait 296 * forever (until jtreg timeout) for the pid to be found. 297 * @param key Regular expression to search for 298 * @return The found pid. 299 */ 300 public static int waitForJvmPid(String key) throws Throwable { 301 final long iterationSleepMillis = 250; 302 System.out.println("waitForJvmPid: Waiting for key '" + key + "'"); 303 System.out.flush(); 304 while (true) { 305 int pid = tryFindJvmPid(key); 306 if (pid >= 0) { 307 return pid; 308 } 309 Thread.sleep(iterationSleepMillis); 310 } 311 } 312 313 /** 314 * Searches for a jvm pid in the output from "jcmd -l". 315 * 316 * Example output from jcmd is: 317 * 12498 sun.tools.jcmd.JCmd -l 318 * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar 319 * 320 * @param key A regular expression to search for. 321 * @return The found pid, or -1 if not found. 322 * @throws Exception If multiple matching jvms are found. 323 */ 324 public static int tryFindJvmPid(String key) throws Throwable { 325 OutputAnalyzer output = null; 326 try { 327 JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd"); 328 jcmdLauncher.addToolArg("-l"); 329 output = ProcessTools.executeProcess(jcmdLauncher.getCommand()); 330 output.shouldHaveExitValue(0); 331 332 // Search for a line starting with numbers (pid), follwed by the key. 333 Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n"); 334 Matcher matcher = pattern.matcher(output.getStdout()); 335 336 int pid = -1; 337 if (matcher.find()) { 338 pid = Integer.parseInt(matcher.group(1)); 339 System.out.println("findJvmPid.pid: " + pid); 340 if (matcher.find()) { 341 throw new Exception("Found multiple JVM pids for key: " + key); 342 } 343 } 344 return pid; 345 } catch (Throwable t) { 346 System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t)); 347 throw t; 348 } 349 } 350 351 /** 352 * Adjusts the provided timeout value for the TIMEOUT_FACTOR 353 * @param tOut the timeout value to be adjusted 354 * @return The timeout value adjusted for the value of "test.timeout.factor" 355 * system property 356 */ 357 public static long adjustTimeout(long tOut) { 358 return Math.round(tOut * Utils.TIMEOUT_FACTOR); 359 } 360 361 /** 362 * Return the contents of the named file as a single String, 363 * or null if not found. 364 * @param filename name of the file to read 365 * @return String contents of file, or null if file not found. 366 * @throws IOException 367 * if an I/O error occurs reading from the file or a malformed or 368 * unmappable byte sequence is read 369 */ 370 public static String fileAsString(String filename) throws IOException { 371 Path filePath = Paths.get(filename); 372 if (!Files.exists(filePath)) return null; 373 return new String(Files.readAllBytes(filePath)); 374 } 375 376 /** 377 * @return Unsafe instance. 378 */ 379 public static synchronized Unsafe getUnsafe() { 380 if (unsafe == null) { 381 try { 382 Field f = Unsafe.class.getDeclaredField("theUnsafe"); 383 f.setAccessible(true); 384 unsafe = (Unsafe) f.get(null); 385 } catch (NoSuchFieldException | IllegalAccessException e) { 386 throw new RuntimeException("Unable to get Unsafe instance.", e); 387 } 388 } 389 return unsafe; 390 } 391 private static final char[] hexArray = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 392 393 /** 394 * Returns hex view of byte array 395 * 396 * @param bytes byte array to process 397 * @return Space separated hexadecimal string representation of bytes 398 */ 399 400 public static String toHexString(byte[] bytes) { 401 char[] hexView = new char[bytes.length * 3]; 402 int i = 0; 403 for (byte b : bytes) { 404 hexView[i++] = hexArray[(b >> 4) & 0x0F]; 405 hexView[i++] = hexArray[b & 0x0F]; 406 hexView[i++] = ' '; 407 } 408 return new String(hexView); 409 } 410 411 /** 412 * Returns {@link java.util.Random} generator initialized with particular seed. 413 * The seed could be provided via system property {@link Utils#SEED_PROPERTY_NAME} 414 * In case no seed is provided, the method uses a random number. 415 * The used seed printed to stdout. 416 * @return {@link java.util.Random} generator with particular seed. 417 */ 418 public static Random getRandomInstance() { 419 if (RANDOM_GENERATOR == null) { 420 synchronized (Utils.class) { 421 if (RANDOM_GENERATOR == null) { 422 RANDOM_GENERATOR = new Random(SEED); 423 System.out.printf("For random generator using seed: %d%n", SEED); 424 System.out.printf("To re-run test with same seed value please add \"-D%s=%d\" to command line.%n", SEED_PROPERTY_NAME, SEED); 425 } 426 } 427 } 428 return RANDOM_GENERATOR; 429 } 430 431 /** 432 * Returns random element of non empty collection 433 * 434 * @param <T> a type of collection element 435 * @param collection collection of elements 436 * @return random element of collection 437 * @throws IllegalArgumentException if collection is empty 438 */ 439 public static <T> T getRandomElement(Collection<T> collection) 440 throws IllegalArgumentException { 441 if (collection.isEmpty()) { 442 throw new IllegalArgumentException("Empty collection"); 443 } 444 Random random = getRandomInstance(); 445 int elementIndex = 1 + random.nextInt(collection.size() - 1); 446 Iterator<T> iterator = collection.iterator(); 447 while (--elementIndex != 0) { 448 iterator.next(); 449 } 450 return iterator.next(); 451 } 452 453 /** 454 * Returns random element of non empty array 455 * 456 * @param <T> a type of array element 457 * @param array array of elements 458 * @return random element of array 459 * @throws IllegalArgumentException if array is empty 460 */ 461 public static <T> T getRandomElement(T[] array) 462 throws IllegalArgumentException { 463 if (array == null || array.length == 0) { 464 throw new IllegalArgumentException("Empty or null array"); 465 } 466 Random random = getRandomInstance(); 467 return array[random.nextInt(array.length)]; 468 } 469 470 /** 471 * Wait for condition to be true 472 * 473 * @param condition, a condition to wait for 474 */ 475 public static final void waitForCondition(BooleanSupplier condition) { 476 waitForCondition(condition, -1L, 100L); 477 } 478 479 /** 480 * Wait until timeout for condition to be true 481 * 482 * @param condition, a condition to wait for 483 * @param timeout a time in milliseconds to wait for condition to be true 484 * specifying -1 will wait forever 485 * @return condition value, to determine if wait was successful 486 */ 487 public static final boolean waitForCondition(BooleanSupplier condition, 488 long timeout) { 489 return waitForCondition(condition, timeout, 100L); 490 } 491 492 /** 493 * Wait until timeout for condition to be true for specified time 494 * 495 * @param condition, a condition to wait for 496 * @param timeout a time in milliseconds to wait for condition to be true, 497 * specifying -1 will wait forever 498 * @param sleepTime a time to sleep value in milliseconds 499 * @return condition value, to determine if wait was successful 500 */ 501 public static final boolean waitForCondition(BooleanSupplier condition, 502 long timeout, long sleepTime) { 503 long startTime = System.currentTimeMillis(); 504 while (!(condition.getAsBoolean() || (timeout != -1L 505 && ((System.currentTimeMillis() - startTime) > timeout)))) { 506 try { 507 Thread.sleep(sleepTime); 508 } catch (InterruptedException e) { 509 Thread.currentThread().interrupt(); 510 throw new Error(e); 511 } 512 } 513 return condition.getAsBoolean(); 514 } 515 516 /** 517 * Interface same as java.lang.Runnable but with 518 * method {@code run()} able to throw any Throwable. 519 */ 520 public static interface ThrowingRunnable { 521 void run() throws Throwable; 522 } 523 524 /** 525 * Filters out an exception that may be thrown by the given 526 * test according to the given filter. 527 * 528 * @param test - method that is invoked and checked for exception. 529 * @param filter - function that checks if the thrown exception matches 530 * criteria given in the filter's implementation. 531 * @return - exception that matches the filter if it has been thrown or 532 * {@code null} otherwise. 533 * @throws Throwable - if test has thrown an exception that does not 534 * match the filter. 535 */ 536 public static Throwable filterException(ThrowingRunnable test, 537 Function<Throwable, Boolean> filter) throws Throwable { 538 try { 539 test.run(); 540 } catch (Throwable t) { 541 if (filter.apply(t)) { 542 return t; 543 } else { 544 throw t; 545 } 546 } 547 return null; 548 } 549 550 /** 551 * Ensures a requested class is loaded 552 * @param aClass class to load 553 */ 554 public static void ensureClassIsLoaded(Class<?> aClass) { 555 if (aClass == null) { 556 throw new Error("Requested null class"); 557 } 558 try { 559 Class.forName(aClass.getName(), /* initialize = */ true, 560 ClassLoader.getSystemClassLoader()); 561 } catch (ClassNotFoundException e) { 562 throw new Error("Class not found", e); 563 } 564 } 565 /** 566 * @param parent a class loader to be the parent for the returned one 567 * @return an UrlClassLoader with urls made of the 'test.class.path' jtreg 568 * property and with the given parent 569 */ 570 public static URLClassLoader getTestClassPathURLClassLoader(ClassLoader parent) { 571 URL[] urls = Arrays.stream(TEST_CLASS_PATH.split(File.pathSeparator)) 572 .map(Paths::get) 573 .map(Path::toUri) 574 .map(x -> { 575 try { 576 return x.toURL(); 577 } catch (MalformedURLException ex) { 578 throw new Error("Test issue. JTREG property" 579 + " 'test.class.path'" 580 + " is not defined correctly", ex); 581 } 582 }).toArray(URL[]::new); 583 return new URLClassLoader(urls, parent); 584 } 585 586 /** 587 * Runs runnable and checks that it throws expected exception. If exceptionException is null it means 588 * that we expect no exception to be thrown. 589 * @param runnable what we run 590 * @param expectedException expected exception 591 */ 592 public static void runAndCheckException(Runnable runnable, Class<? extends Throwable> expectedException) { 593 runAndCheckException(runnable, t -> { 594 if (t == null) { 595 if (expectedException != null) { 596 throw new AssertionError("Didn't get expected exception " + expectedException.getSimpleName()); 597 } 598 } else { 599 String message = "Got unexpected exception " + t.getClass().getSimpleName(); 600 if (expectedException == null) { 601 throw new AssertionError(message, t); 602 } else if (!expectedException.isAssignableFrom(t.getClass())) { 603 message += " instead of " + expectedException.getSimpleName(); 604 throw new AssertionError(message, t); 605 } 606 } 607 }); 608 } 609 610 /** 611 * Runs runnable and makes some checks to ensure that it throws expected exception. 612 * @param runnable what we run 613 * @param checkException a consumer which checks that we got expected exception and raises a new exception otherwise 614 */ 615 public static void runAndCheckException(Runnable runnable, Consumer<Throwable> checkException) { 616 try { 617 runnable.run(); 618 checkException.accept(null); 619 } catch (Throwable t) { 620 checkException.accept(t); 621 } 622 } 623 624 /** 625 * Converts to VM type signature 626 * 627 * @param type Java type to convert 628 * @return string representation of VM type 629 */ 630 public static String toJVMTypeSignature(Class<?> type) { 631 if (type.isPrimitive()) { 632 if (type == boolean.class) { 633 return "Z"; 634 } else if (type == byte.class) { 635 return "B"; 636 } else if (type == char.class) { 637 return "C"; 638 } else if (type == double.class) { 639 return "D"; 640 } else if (type == float.class) { 641 return "F"; 642 } else if (type == int.class) { 643 return "I"; 644 } else if (type == long.class) { 645 return "J"; 646 } else if (type == short.class) { 647 return "S"; 648 } else if (type == void.class) { 649 return "V"; 650 } else { 651 throw new Error("Unsupported type: " + type); 652 } 653 } 654 String result = type.getName().replaceAll("\\.", "/"); 655 if (!type.isArray()) { 656 return "L" + result + ";"; 657 } 658 return result; 659 } 660 661 public static Object[] getNullValues(Class<?>... types) { 662 Object[] result = new Object[types.length]; 663 int i = 0; 664 for (Class<?> type : types) { 665 result[i++] = NULL_VALUES.get(type); 666 } 667 return result; 668 } 669 private static Map<Class<?>, Object> NULL_VALUES = new HashMap<>(); 670 static { 671 NULL_VALUES.put(boolean.class, false); 672 NULL_VALUES.put(byte.class, (byte) 0); 673 NULL_VALUES.put(short.class, (short) 0); 674 NULL_VALUES.put(char.class, '\0'); 675 NULL_VALUES.put(int.class, 0); 676 NULL_VALUES.put(long.class, 0L); 677 NULL_VALUES.put(float.class, 0.0f); 678 NULL_VALUES.put(double.class, 0.0d); 679 } 680 681 /** 682 * Returns mandatory property value 683 * @param propName is a name of property to request 684 * @return a String with requested property value 685 */ 686 public static String getMandatoryProperty(String propName) { 687 Objects.requireNonNull(propName, "Requested null property"); 688 String prop = System.getProperty(propName); 689 Objects.requireNonNull(prop, 690 String.format("A mandatory property '%s' isn't set", propName)); 691 return prop; 692 } 693} 694 695