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