Utils.java revision 2727:db3a049ab963
155714Skris/*
255714Skris * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
355714Skris * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
455714Skris *
555714Skris * This code is free software; you can redistribute it and/or modify it
655714Skris * under the terms of the GNU General Public License version 2 only, as
755714Skris * published by the Free Software Foundation.
8296465Sdelphij *
955714Skris * This code is distributed in the hope that it will be useful, but WITHOUT
1055714Skris * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1155714Skris * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1255714Skris * version 2 for more details (a copy is included in the LICENSE file that
1355714Skris * accompanied this code).
1455714Skris *
15296465Sdelphij * You should have received a copy of the GNU General Public License version
1655714Skris * 2 along with this work; if not, write to the Free Software Foundation,
1755714Skris * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1855714Skris *
1955714Skris * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2055714Skris * or visit www.oracle.com if you need additional information or have any
2155714Skris * questions.
22296465Sdelphij */
2355714Skris
2455714Skrispackage jdk.test.lib;
2555714Skris
2655714Skrisimport java.io.File;
2755714Skrisimport java.io.IOException;
2855714Skrisimport java.net.InetAddress;
2955714Skrisimport java.net.MalformedURLException;
3055714Skrisimport java.net.ServerSocket;
3155714Skrisimport java.net.URL;
3255714Skrisimport java.net.URLClassLoader;
3355714Skrisimport java.net.UnknownHostException;
3455714Skrisimport java.nio.file.Files;
3555714Skrisimport java.nio.file.Path;
3655714Skrisimport java.nio.file.Paths;
37296465Sdelphijimport java.util.ArrayList;
3855714Skrisimport java.util.Arrays;
3955714Skrisimport java.util.Collection;
40296465Sdelphijimport java.util.Collections;
4155714Skrisimport java.util.Iterator;
4255714Skrisimport java.util.Map;
4355714Skrisimport java.util.HashMap;
4455714Skrisimport java.util.List;
4555714Skrisimport java.util.Objects;
4655714Skrisimport java.util.Random;
4755714Skrisimport java.util.function.BooleanSupplier;
4855714Skrisimport java.util.concurrent.TimeUnit;
4955714Skrisimport java.util.function.Consumer;
5055714Skrisimport java.util.function.Function;
5155714Skrisimport java.util.regex.Matcher;
52296465Sdelphijimport java.util.regex.Pattern;
5355714Skris
5455714Skrisimport static jdk.test.lib.Asserts.assertTrue;
5555714Skrisimport jdk.test.lib.process.ProcessTools;
5655714Skrisimport jdk.test.lib.process.OutputAnalyzer;
5755714Skris
5855714Skris/**
59296465Sdelphij * Common library for various test helper functions.
60296465Sdelphij */
61296465Sdelphijpublic final class Utils {
62296465Sdelphij
6355714Skris    /**
64109998Smarkm     * Returns the value of 'test.class.path' system property.
65296465Sdelphij     */
6655714Skris    public static final String TEST_CLASS_PATH = System.getProperty("test.class.path", ".");
6755714Skris
6855714Skris    /**
69109998Smarkm     * Returns the sequence used by operating system to separate lines.
70296465Sdelphij     */
71296465Sdelphij    public static final String NEW_LINE = System.getProperty("line.separator");
7255714Skris
73296465Sdelphij    /**
7455714Skris     * Returns the value of 'test.vm.opts' system property.
7555714Skris     */
76160814Ssimon    public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim();
77160814Ssimon
78296465Sdelphij    /**
79160814Ssimon     * Returns the value of 'test.java.opts' system property.
80160814Ssimon     */
8155714Skris    public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim();
82296465Sdelphij
8355714Skris    /**
8455714Skris     * Returns the value of 'test.src' system property.
85296465Sdelphij     */
86296465Sdelphij    public static final String TEST_SRC = System.getProperty("test.src", "").trim();
8755714Skris
8855714Skris    /*
89296465Sdelphij     * Returns the value of 'test.jdk' system property
90296465Sdelphij     */
91296465Sdelphij    public static final String TEST_JDK = System.getProperty("test.jdk");
92296465Sdelphij
93296465Sdelphij    /**
94296465Sdelphij     * Returns the value of 'test.classes' system property
95109998Smarkm     */
96296465Sdelphij    public static final String TEST_CLASSES = System.getProperty("test.classes", ".");
9755714Skris    /**
9855714Skris     * Defines property name for seed value.
9955714Skris     */
100296465Sdelphij    public static final String SEED_PROPERTY_NAME = "jdk.test.lib.random.seed";
10155714Skris
10255714Skris    /* (non-javadoc)
10355714Skris     * Random generator with (or without) predefined seed. Depends on
104296465Sdelphij     * "jdk.test.lib.random.seed" property value.
105296465Sdelphij     */
106296465Sdelphij    private static volatile Random RANDOM_GENERATOR;
10755714Skris
10855714Skris    /**
10955714Skris     * Contains the seed value used for {@link java.util.Random} creation.
11055714Skris     */
11155714Skris    public static final long SEED = Long.getLong(SEED_PROPERTY_NAME, new Random().nextLong());
11255714Skris    /**
11355714Skris    * Returns the value of 'test.timeout.factor' system property
11455714Skris    * converted to {@code double}.
115296465Sdelphij    */
116296465Sdelphij    public static final double TIMEOUT_FACTOR;
117296465Sdelphij    static {
118296465Sdelphij        String toFactor = System.getProperty("test.timeout.factor", "1.0");
11955714Skris        TIMEOUT_FACTOR = Double.parseDouble(toFactor);
12055714Skris    }
12155714Skris
12255714Skris    /**
123296465Sdelphij    * Returns the value of JTREG default test timeout in milliseconds
124296465Sdelphij    * converted to {@code long}.
125296465Sdelphij    */
126296465Sdelphij    public static final long DEFAULT_TEST_TIMEOUT = TimeUnit.SECONDS.toMillis(120);
127296465Sdelphij
128296465Sdelphij    private Utils() {
129296465Sdelphij        // Private constructor to prevent class instantiation
130296465Sdelphij    }
131296465Sdelphij
132296465Sdelphij    /**
133296465Sdelphij     * Returns the list of VM options.
13455714Skris     *
135296465Sdelphij     * @return List of VM options
136296465Sdelphij     */
137296465Sdelphij    public static List<String> getVmOptions() {
138296465Sdelphij        return Arrays.asList(safeSplitString(VM_OPTIONS));
139296465Sdelphij    }
140296465Sdelphij
141296465Sdelphij    /**
142296465Sdelphij     * Returns the list of VM options with -J prefix.
143296465Sdelphij     *
144296465Sdelphij     * @return The list of VM options with -J prefix
145296465Sdelphij     */
146296465Sdelphij    public static List<String> getForwardVmOptions() {
147296465Sdelphij        String[] opts = safeSplitString(VM_OPTIONS);
148296465Sdelphij        for (int i = 0; i < opts.length; i++) {
149296465Sdelphij            opts[i] = "-J" + opts[i];
15055714Skris        }
151296465Sdelphij        return Arrays.asList(opts);
152296465Sdelphij    }
153296465Sdelphij
154296465Sdelphij    /**
155296465Sdelphij     * Returns the default JTReg arguments for a jvm running a test.
156296465Sdelphij     * This is the combination of JTReg arguments test.vm.opts and test.java.opts.
157296465Sdelphij     * @return An array of options, or an empty array if no options.
158296465Sdelphij     */
159296465Sdelphij    public static String[] getTestJavaOpts() {
160296465Sdelphij        List<String> opts = new ArrayList<String>();
161296465Sdelphij        Collections.addAll(opts, safeSplitString(VM_OPTIONS));
162296465Sdelphij        Collections.addAll(opts, safeSplitString(JAVA_OPTIONS));
163296465Sdelphij        return opts.toArray(new String[0]);
164296465Sdelphij    }
165296465Sdelphij
16655714Skris    /**
16755714Skris     * Combines given arguments with default JTReg arguments for a jvm running a test.
16855714Skris     * This is the combination of JTReg arguments test.vm.opts and test.java.opts
16955714Skris     * @return The combination of JTReg test java options and user args.
17055714Skris     */
171296465Sdelphij    public static String[] addTestJavaOpts(String... userArgs) {
172296465Sdelphij        List<String> opts = new ArrayList<String>();
173296465Sdelphij        Collections.addAll(opts, getTestJavaOpts());
174296465Sdelphij        Collections.addAll(opts, userArgs);
175296465Sdelphij        return opts.toArray(new String[0]);
176296465Sdelphij    }
177296465Sdelphij
178296465Sdelphij    /**
179296465Sdelphij     * Removes any options specifying which GC to use, for example "-XX:+UseG1GC".
180296465Sdelphij     * Removes any options matching: -XX:(+/-)Use*GC
181296465Sdelphij     * Used when a test need to set its own GC version. Then any
182296465Sdelphij     * GC specified by the framework must first be removed.
183296465Sdelphij     * @return A copy of given opts with all GC options removed.
184296465Sdelphij     */
185296465Sdelphij    private static final Pattern useGcPattern = Pattern.compile(
18655714Skris            "(?:\\-XX\\:[\\+\\-]Use.+GC)"
187296465Sdelphij            + "|(?:\\-Xconcgc)");
188296465Sdelphij    public static List<String> removeGcOpts(List<String> opts) {
189296465Sdelphij        List<String> optsWithoutGC = new ArrayList<String>();
190296465Sdelphij        for (String opt : opts) {
191296465Sdelphij            if (useGcPattern.matcher(opt).matches()) {
192296465Sdelphij                System.out.println("removeGcOpts: removed " + opt);
193296465Sdelphij            } else {
194296465Sdelphij                optsWithoutGC.add(opt);
195296465Sdelphij            }
196296465Sdelphij        }
197296465Sdelphij        return optsWithoutGC;
198296465Sdelphij    }
199296465Sdelphij
200296465Sdelphij    /**
201296465Sdelphij     * Returns the default JTReg arguments for a jvm running a test without
20255714Skris     * options that matches regular expressions in {@code filters}.
203296465Sdelphij     * This is the combination of JTReg arguments test.vm.opts and test.java.opts.
204296465Sdelphij     * @param filters Regular expressions used to filter out options.
205296465Sdelphij     * @return An array of options, or an empty array if no options.
206296465Sdelphij     */
207296465Sdelphij    public static String[] getFilteredTestJavaOpts(String... filters) {
208296465Sdelphij        String options[] = getTestJavaOpts();
209296465Sdelphij
210296465Sdelphij        if (filters.length == 0) {
211296465Sdelphij            return options;
212296465Sdelphij        }
213296465Sdelphij
214296465Sdelphij        List<String> filteredOptions = new ArrayList<String>(options.length);
215296465Sdelphij        Pattern patterns[] = new Pattern[filters.length];
216296465Sdelphij        for (int i = 0; i < filters.length; i++) {
217296465Sdelphij            patterns[i] = Pattern.compile(filters[i]);
21855714Skris        }
21955714Skris
22055714Skris        for (String option : options) {
22155714Skris            boolean matched = false;
22255714Skris            for (int i = 0; i < patterns.length && !matched; i++) {
223296465Sdelphij                Matcher matcher = patterns[i].matcher(option);
224296465Sdelphij                matched = matcher.find();
225296465Sdelphij            }
226296465Sdelphij            if (!matched) {
227296465Sdelphij                filteredOptions.add(option);
228296465Sdelphij            }
229296465Sdelphij        }
230296465Sdelphij
231296465Sdelphij        return filteredOptions.toArray(new String[filteredOptions.size()]);
232296465Sdelphij    }
233296465Sdelphij
234296465Sdelphij    /**
235296465Sdelphij     * Splits a string by white space.
236296465Sdelphij     * Works like String.split(), but returns an empty array
237296465Sdelphij     * if the string is null or empty.
23855714Skris     */
239296465Sdelphij    private static String[] safeSplitString(String s) {
240296465Sdelphij        if (s == null || s.trim().isEmpty()) {
241296465Sdelphij            return new String[] {};
242296465Sdelphij        }
243296465Sdelphij        return s.trim().split("\\s+");
244296465Sdelphij    }
245296465Sdelphij
246296465Sdelphij    /**
247296465Sdelphij     * @return The full command line for the ProcessBuilder.
248296465Sdelphij     */
249296465Sdelphij    public static String getCommandLine(ProcessBuilder pb) {
250296465Sdelphij        StringBuilder cmd = new StringBuilder();
251296465Sdelphij        for (String s : pb.command()) {
252296465Sdelphij            cmd.append(s).append(" ");
253296465Sdelphij        }
25455714Skris        return cmd.toString();
255296465Sdelphij    }
256296465Sdelphij
257296465Sdelphij    /**
258296465Sdelphij     * Returns the free port on the local host.
259296465Sdelphij     * The function will spin until a valid port number is found.
260296465Sdelphij     *
261296465Sdelphij     * @return The port number
262296465Sdelphij     * @throws InterruptedException if any thread has interrupted the current thread
263296465Sdelphij     * @throws IOException if an I/O error occurs when opening the socket
264296465Sdelphij     */
265296465Sdelphij    public static int getFreePort() throws InterruptedException, IOException {
266296465Sdelphij        int port = -1;
267296465Sdelphij
268296465Sdelphij        while (port <= 0) {
269296465Sdelphij            Thread.sleep(100);
27055714Skris
27155714Skris            ServerSocket serverSocket = null;
27255714Skris            try {
27355714Skris                serverSocket = new ServerSocket(0);
27455714Skris                port = serverSocket.getLocalPort();
275296465Sdelphij            } finally {
276296465Sdelphij                serverSocket.close();
277296465Sdelphij            }
278296465Sdelphij        }
279296465Sdelphij
280296465Sdelphij        return port;
281296465Sdelphij    }
282296465Sdelphij
283296465Sdelphij    /**
284296465Sdelphij     * Returns the name of the local host.
285296465Sdelphij     *
286296465Sdelphij     * @return The host name
287296465Sdelphij     * @throws UnknownHostException if IP address of a host could not be determined
288296465Sdelphij     */
289296465Sdelphij    public static String getHostname() throws UnknownHostException {
29055714Skris        InetAddress inetAddress = InetAddress.getLocalHost();
291296465Sdelphij        String hostName = inetAddress.getHostName();
292296465Sdelphij
293296465Sdelphij        assertTrue((hostName != null && !hostName.isEmpty()),
294296465Sdelphij                "Cannot get hostname");
295296465Sdelphij
296296465Sdelphij        return hostName;
297296465Sdelphij    }
298296465Sdelphij
299296465Sdelphij    /**
300296465Sdelphij     * Uses "jcmd -l" to search for a jvm pid. This function will wait
301296465Sdelphij     * forever (until jtreg timeout) for the pid to be found.
302296465Sdelphij     * @param key Regular expression to search for
303296465Sdelphij     * @return The found pid.
304296465Sdelphij     */
305296465Sdelphij    public static int waitForJvmPid(String key) throws Throwable {
30655714Skris        final long iterationSleepMillis = 250;
307296465Sdelphij        System.out.println("waitForJvmPid: Waiting for key '" + key + "'");
308296465Sdelphij        System.out.flush();
309296465Sdelphij        while (true) {
310296465Sdelphij            int pid = tryFindJvmPid(key);
311296465Sdelphij            if (pid >= 0) {
312296465Sdelphij                return pid;
313296465Sdelphij            }
314296465Sdelphij            Thread.sleep(iterationSleepMillis);
315296465Sdelphij        }
316296465Sdelphij    }
317296465Sdelphij
318296465Sdelphij    /**
319296465Sdelphij     * Searches for a jvm pid in the output from "jcmd -l".
320296465Sdelphij     *
321296465Sdelphij     * Example output from jcmd is:
32255714Skris     * 12498 sun.tools.jcmd.JCmd -l
32355714Skris     * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar
32455714Skris     *
32555714Skris     * @param key A regular expression to search for.
32655714Skris     * @return The found pid, or -1 if not found.
32755714Skris     * @throws Exception If multiple matching jvms are found.
328296465Sdelphij     */
329296465Sdelphij    public static int tryFindJvmPid(String key) throws Throwable {
330296465Sdelphij        OutputAnalyzer output = null;
33155714Skris        try {
33255714Skris            JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd");
333296465Sdelphij            jcmdLauncher.addToolArg("-l");
33455714Skris            output = ProcessTools.executeProcess(jcmdLauncher.getCommand());
33555714Skris            output.shouldHaveExitValue(0);
33655714Skris
33755714Skris            // Search for a line starting with numbers (pid), follwed by the key.
338296465Sdelphij            Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n");
339296465Sdelphij            Matcher matcher = pattern.matcher(output.getStdout());
34055714Skris
34155714Skris            int pid = -1;
34255714Skris            if (matcher.find()) {
343296465Sdelphij                pid = Integer.parseInt(matcher.group(1));
344296465Sdelphij                System.out.println("findJvmPid.pid: " + pid);
345296465Sdelphij                if (matcher.find()) {
346296465Sdelphij                    throw new Exception("Found multiple JVM pids for key: " + key);
347296465Sdelphij                }
34855714Skris            }
34955714Skris            return pid;
35055714Skris        } catch (Throwable t) {
351296465Sdelphij            System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t));
352296465Sdelphij            throw t;
353296465Sdelphij        }
354296465Sdelphij    }
355296465Sdelphij
356296465Sdelphij    /**
357296465Sdelphij     * Adjusts the provided timeout value for the TIMEOUT_FACTOR
35855714Skris     * @param tOut the timeout value to be adjusted
35955714Skris     * @return The timeout value adjusted for the value of "test.timeout.factor"
360296465Sdelphij     *         system property
361296465Sdelphij     */
36255714Skris    public static long adjustTimeout(long tOut) {
36355714Skris        return Math.round(tOut * Utils.TIMEOUT_FACTOR);
364296465Sdelphij    }
365296465Sdelphij
36655714Skris    /**
367296465Sdelphij     * Return the contents of the named file as a single String,
36855714Skris     * or null if not found.
369296465Sdelphij     * @param filename name of the file to read
370296465Sdelphij     * @return String contents of file, or null if file not found.
371296465Sdelphij     * @throws  IOException
372296465Sdelphij     *          if an I/O error occurs reading from the file or a malformed or
373296465Sdelphij     *          unmappable byte sequence is read
374296465Sdelphij     */
375296465Sdelphij    public static String fileAsString(String filename) throws IOException {
376296465Sdelphij        Path filePath = Paths.get(filename);
377296465Sdelphij        if (!Files.exists(filePath)) return null;
378296465Sdelphij        return new String(Files.readAllBytes(filePath));
379296465Sdelphij    }
38055714Skris
381296465Sdelphij    private static final char[] hexArray = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
382296465Sdelphij
383296465Sdelphij    /**
384296465Sdelphij     * Returns hex view of byte array
385296465Sdelphij     *
386296465Sdelphij     * @param bytes byte array to process
387296465Sdelphij     * @return Space separated hexadecimal string representation of bytes
388296465Sdelphij     */
389296465Sdelphij
39055714Skris    public static String toHexString(byte[] bytes) {
391296465Sdelphij        char[] hexView = new char[bytes.length * 3];
39255714Skris        int i = 0;
39355714Skris        for (byte b : bytes) {
394296465Sdelphij            hexView[i++] = hexArray[(b >> 4) & 0x0F];
39555714Skris            hexView[i++] = hexArray[b & 0x0F];
396296465Sdelphij            hexView[i++] = ' ';
39755714Skris        }
398296465Sdelphij        return new String(hexView);
39955714Skris    }
400296465Sdelphij
401296465Sdelphij    /**
402296465Sdelphij     * Returns {@link java.util.Random} generator initialized with particular seed.
403296465Sdelphij     * The seed could be provided via system property {@link Utils#SEED_PROPERTY_NAME}
404296465Sdelphij     * In case no seed is provided, the method uses a random number.
405296465Sdelphij     * The used seed printed to stdout.
406296465Sdelphij     * @return {@link java.util.Random} generator with particular seed.
407296465Sdelphij     */
408296465Sdelphij    public static Random getRandomInstance() {
409296465Sdelphij        if (RANDOM_GENERATOR == null) {
41055714Skris            synchronized (Utils.class) {
41155714Skris                if (RANDOM_GENERATOR == null) {
412296465Sdelphij                    RANDOM_GENERATOR = new Random(SEED);
413296465Sdelphij                    System.out.printf("For random generator using seed: %d%n", SEED);
41455714Skris                    System.out.printf("To re-run test with same seed value please add \"-D%s=%d\" to command line.%n", SEED_PROPERTY_NAME, SEED);
41555714Skris                }
416296465Sdelphij            }
417296465Sdelphij        }
418296465Sdelphij        return RANDOM_GENERATOR;
419296465Sdelphij    }
420296465Sdelphij
421296465Sdelphij    /**
422296465Sdelphij     * Returns random element of non empty collection
423296465Sdelphij     *
424296465Sdelphij     * @param <T> a type of collection element
425296465Sdelphij     * @param collection collection of elements
426296465Sdelphij     * @return random element of collection
427296465Sdelphij     * @throws IllegalArgumentException if collection is empty
428296465Sdelphij     */
429296465Sdelphij    public static <T> T getRandomElement(Collection<T> collection)
43055714Skris            throws IllegalArgumentException {
431296465Sdelphij        if (collection.isEmpty()) {
43255714Skris            throw new IllegalArgumentException("Empty collection");
43355714Skris        }
434296465Sdelphij        Random random = getRandomInstance();
435296465Sdelphij        int elementIndex = 1 + random.nextInt(collection.size() - 1);
436296465Sdelphij        Iterator<T> iterator = collection.iterator();
437296465Sdelphij        while (--elementIndex != 0) {
43855714Skris            iterator.next();
43955714Skris        }
440296465Sdelphij        return iterator.next();
441296465Sdelphij    }
44255714Skris
44355714Skris    /**
444296465Sdelphij     * Returns random element of non empty array
445296465Sdelphij     *
446296465Sdelphij     * @param <T> a type of array element
44755714Skris     * @param array array of elements
44855714Skris     * @return random element of array
449296465Sdelphij     * @throws IllegalArgumentException if array is empty
450296465Sdelphij     */
451296465Sdelphij    public static <T> T getRandomElement(T[] array)
452296465Sdelphij            throws IllegalArgumentException {
453296465Sdelphij        if (array == null || array.length == 0) {
454296465Sdelphij            throw new IllegalArgumentException("Empty or null array");
45555714Skris        }
456296465Sdelphij        Random random = getRandomInstance();
457296465Sdelphij        return array[random.nextInt(array.length)];
458296465Sdelphij    }
459296465Sdelphij
460296465Sdelphij    /**
461296465Sdelphij     * Wait for condition to be true
462296465Sdelphij     *
463296465Sdelphij     * @param condition, a condition to wait for
464296465Sdelphij     */
465296465Sdelphij    public static final void waitForCondition(BooleanSupplier condition) {
46655714Skris        waitForCondition(condition, -1L, 100L);
467296465Sdelphij    }
468296465Sdelphij
469296465Sdelphij    /**
47055714Skris     * Wait until timeout for condition to be true
471296465Sdelphij     *
472296465Sdelphij     * @param condition, a condition to wait for
473296465Sdelphij     * @param timeout a time in milliseconds to wait for condition to be true
474296465Sdelphij     * specifying -1 will wait forever
47555714Skris     * @return condition value, to determine if wait was successful
47655714Skris     */
47755714Skris    public static final boolean waitForCondition(BooleanSupplier condition,
478296465Sdelphij            long timeout) {
479296465Sdelphij        return waitForCondition(condition, timeout, 100L);
480296465Sdelphij    }
481296465Sdelphij
48255714Skris    /**
48355714Skris     * Wait until timeout for condition to be true for specified time
484296465Sdelphij     *
485296465Sdelphij     * @param condition, a condition to wait for
486296465Sdelphij     * @param timeout a time in milliseconds to wait for condition to be true,
487296465Sdelphij     * specifying -1 will wait forever
48855714Skris     * @param sleepTime a time to sleep value in milliseconds
48955714Skris     * @return condition value, to determine if wait was successful
490296465Sdelphij     */
491296465Sdelphij    public static final boolean waitForCondition(BooleanSupplier condition,
492296465Sdelphij            long timeout, long sleepTime) {
493296465Sdelphij        long startTime = System.currentTimeMillis();
49455714Skris        while (!(condition.getAsBoolean() || (timeout != -1L
49555714Skris                && ((System.currentTimeMillis() - startTime) > timeout)))) {
496296465Sdelphij            try {
497296465Sdelphij                Thread.sleep(sleepTime);
498296465Sdelphij            } catch (InterruptedException e) {
499296465Sdelphij                Thread.currentThread().interrupt();
50055714Skris                throw new Error(e);
50155714Skris            }
50255714Skris        }
503296465Sdelphij        return condition.getAsBoolean();
504296465Sdelphij    }
505296465Sdelphij
506296465Sdelphij    /**
507296465Sdelphij     * Interface same as java.lang.Runnable but with
508296465Sdelphij     * method {@code run()} able to throw any Throwable.
509296465Sdelphij     */
510296465Sdelphij    public static interface ThrowingRunnable {
511296465Sdelphij        void run() throws Throwable;
512296465Sdelphij    }
513296465Sdelphij
514296465Sdelphij    /**
515296465Sdelphij     * Filters out an exception that may be thrown by the given
516296465Sdelphij     * test according to the given filter.
517296465Sdelphij     *
518296465Sdelphij     * @param test - method that is invoked and checked for exception.
51955714Skris     * @param filter - function that checks if the thrown exception matches
52055714Skris     *                 criteria given in the filter's implementation.
521296465Sdelphij     * @return - exception that matches the filter if it has been thrown or
522296465Sdelphij     *           {@code null} otherwise.
523296465Sdelphij     * @throws Throwable - if test has thrown an exception that does not
524296465Sdelphij     *                     match the filter.
525296465Sdelphij     */
526296465Sdelphij    public static Throwable filterException(ThrowingRunnable test,
527296465Sdelphij            Function<Throwable, Boolean> filter) throws Throwable {
528296465Sdelphij        try {
529296465Sdelphij            test.run();
530296465Sdelphij        } catch (Throwable t) {
531296465Sdelphij            if (filter.apply(t)) {
532296465Sdelphij                return t;
533296465Sdelphij            } else {
534296465Sdelphij                throw t;
535296465Sdelphij            }
536296465Sdelphij        }
537296465Sdelphij        return null;
538296465Sdelphij    }
53955714Skris
54055714Skris    /**
541296465Sdelphij     * Ensures a requested class is loaded
542296465Sdelphij     * @param aClass class to load
543296465Sdelphij     */
544296465Sdelphij    public static void ensureClassIsLoaded(Class<?> aClass) {
545296465Sdelphij        if (aClass == null) {
546296465Sdelphij            throw new Error("Requested null class");
547296465Sdelphij        }
548296465Sdelphij        try {
549296465Sdelphij            Class.forName(aClass.getName(), /* initialize = */ true,
550296465Sdelphij                    ClassLoader.getSystemClassLoader());
551296465Sdelphij        } catch (ClassNotFoundException e) {
552296465Sdelphij            throw new Error("Class not found", e);
553296465Sdelphij        }
554296465Sdelphij    }
555296465Sdelphij    /**
556296465Sdelphij     * @param parent a class loader to be the parent for the returned one
557296465Sdelphij     * @return an UrlClassLoader with urls made of the 'test.class.path' jtreg
558296465Sdelphij     *         property and with the given parent
55955714Skris     */
56055714Skris    public static URLClassLoader getTestClassPathURLClassLoader(ClassLoader parent) {
561296465Sdelphij        URL[] urls = Arrays.stream(TEST_CLASS_PATH.split(File.pathSeparator))
562296465Sdelphij                .map(Paths::get)
563296465Sdelphij                .map(Path::toUri)
564296465Sdelphij                .map(x -> {
565296465Sdelphij                    try {
566296465Sdelphij                        return x.toURL();
567296465Sdelphij                    } catch (MalformedURLException ex) {
568296465Sdelphij                        throw new Error("Test issue. JTREG property"
569296465Sdelphij                                + " 'test.class.path'"
570296465Sdelphij                                + " is not defined correctly", ex);
571296465Sdelphij                    }
572296465Sdelphij                }).toArray(URL[]::new);
573296465Sdelphij        return new URLClassLoader(urls, parent);
574296465Sdelphij    }
575296465Sdelphij
576296465Sdelphij    /**
577296465Sdelphij     * Runs runnable and checks that it throws expected exception. If exceptionException is null it means
578296465Sdelphij     * that we expect no exception to be thrown.
57955714Skris     * @param runnable what we run
580296465Sdelphij     * @param expectedException expected exception
581296465Sdelphij     */
582296465Sdelphij    public static void runAndCheckException(Runnable runnable, Class<? extends Throwable> expectedException) {
583296465Sdelphij        runAndCheckException(runnable, t -> {
584296465Sdelphij            if (t == null) {
585296465Sdelphij                if (expectedException != null) {
586296465Sdelphij                    throw new AssertionError("Didn't get expected exception " + expectedException.getSimpleName());
587296465Sdelphij                }
588296465Sdelphij            } else {
589296465Sdelphij                String message = "Got unexpected exception " + t.getClass().getSimpleName();
590296465Sdelphij                if (expectedException == null) {
591296465Sdelphij                    throw new AssertionError(message, t);
592296465Sdelphij                } else if (!expectedException.isAssignableFrom(t.getClass())) {
593296465Sdelphij                    message += " instead of " + expectedException.getSimpleName();
594296465Sdelphij                    throw new AssertionError(message, t);
595296465Sdelphij                }
596296465Sdelphij            }
597296465Sdelphij        });
59855714Skris    }
599296465Sdelphij
600296465Sdelphij    /**
601296465Sdelphij     * Runs runnable and makes some checks to ensure that it throws expected exception.
602296465Sdelphij     * @param runnable what we run
603296465Sdelphij     * @param checkException a consumer which checks that we got expected exception and raises a new exception otherwise
604296465Sdelphij     */
605296465Sdelphij    public static void runAndCheckException(Runnable runnable, Consumer<Throwable> checkException) {
606296465Sdelphij        try {
607296465Sdelphij            runnable.run();
608296465Sdelphij            checkException.accept(null);
609296465Sdelphij        } catch (Throwable t) {
610296465Sdelphij            checkException.accept(t);
611296465Sdelphij        }
612296465Sdelphij    }
613296465Sdelphij
614296465Sdelphij    /**
615296465Sdelphij     * Converts to VM type signature
616296465Sdelphij     *
617296465Sdelphij     * @param type Java type to convert
618296465Sdelphij     * @return string representation of VM type
619296465Sdelphij     */
620296465Sdelphij    public static String toJVMTypeSignature(Class<?> type) {
621296465Sdelphij        if (type.isPrimitive()) {
622296465Sdelphij            if (type == boolean.class) {
623296465Sdelphij                return "Z";
624296465Sdelphij            } else if (type == byte.class) {
625296465Sdelphij                return "B";
626296465Sdelphij            } else if (type == char.class) {
627296465Sdelphij                return "C";
628296465Sdelphij            } else if (type == double.class) {
629296465Sdelphij                return "D";
630296465Sdelphij            } else if (type == float.class) {
631296465Sdelphij                return "F";
632296465Sdelphij            } else if (type == int.class) {
633296465Sdelphij                return "I";
634296465Sdelphij            } else if (type == long.class) {
635296465Sdelphij                return "J";
636296465Sdelphij            } else if (type == short.class) {
637296465Sdelphij                return "S";
638109998Smarkm            } else if (type == void.class) {
639296465Sdelphij                return "V";
64055714Skris            } else {
641296465Sdelphij                throw new Error("Unsupported type: " + type);
642            }
643        }
644        String result = type.getName().replaceAll("\\.", "/");
645        if (!type.isArray()) {
646            return "L" + result + ";";
647        }
648        return result;
649    }
650
651    public static Object[] getNullValues(Class<?>... types) {
652        Object[] result = new Object[types.length];
653        int i = 0;
654        for (Class<?> type : types) {
655            result[i++] = NULL_VALUES.get(type);
656        }
657        return result;
658    }
659    private static Map<Class<?>, Object> NULL_VALUES = new HashMap<>();
660    static {
661        NULL_VALUES.put(boolean.class, false);
662        NULL_VALUES.put(byte.class, (byte) 0);
663        NULL_VALUES.put(short.class, (short) 0);
664        NULL_VALUES.put(char.class, '\0');
665        NULL_VALUES.put(int.class, 0);
666        NULL_VALUES.put(long.class, 0L);
667        NULL_VALUES.put(float.class, 0.0f);
668        NULL_VALUES.put(double.class, 0.0d);
669    }
670
671    /**
672     * Returns mandatory property value
673     * @param propName is a name of property to request
674     * @return a String with requested property value
675     */
676    public static String getMandatoryProperty(String propName) {
677        Objects.requireNonNull(propName, "Requested null property");
678        String prop = System.getProperty(propName);
679        Objects.requireNonNull(prop,
680                String.format("A mandatory property '%s' isn't set", propName));
681        return prop;
682    }
683
684    // This method is intended to be called from a jtreg test.
685    // It will identify the name of the test by means of stack walking.
686    // It can handle both jtreg tests and a testng tests wrapped inside jtreg tests.
687    // For jtreg tests the name of the test will be searched by stack-walking
688    // until the method main() is found; the class containing that method is the
689    // main test class and will be returned as the name of the test.
690    // Special handling is used for testng tests.
691    public static String getTestName() {
692        String result = null;
693        // If we are using testng, then we should be able to load the "Test" annotation.
694        Class testClassAnnotation;
695
696        try {
697            testClassAnnotation = Class.forName("org.testng.annotations.Test");
698        } catch (ClassNotFoundException e) {
699            testClassAnnotation = null;
700        }
701
702        StackTraceElement[] elms = (new Throwable()).getStackTrace();
703        for (StackTraceElement n: elms) {
704            String className = n.getClassName();
705
706            // If this is a "main" method, then use its class name, but only
707            // if we are not using testng.
708            if (testClassAnnotation == null && "main".equals(n.getMethodName())) {
709                result = className;
710                break;
711            }
712
713            // If this is a testng test, the test will have no "main" method. We can
714            // detect a testng test class by looking for the org.testng.annotations.Test
715            // annotation. If present, then use the name of this class.
716            if (testClassAnnotation != null) {
717                try {
718                    Class c = Class.forName(className);
719                    if (c.isAnnotationPresent(testClassAnnotation)) {
720                        result = className;
721                        break;
722                    }
723                } catch (ClassNotFoundException e) {
724                    throw new RuntimeException("Unexpected exception: " + e, e);
725                }
726            }
727        }
728
729        if (result == null) {
730            throw new RuntimeException("Couldn't find main test class in stack trace");
731        }
732
733        return result;
734    }
735
736}
737
738