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