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