1 /*
   2  * Copyright (c) 2013, 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.testlibrary;
  25 
  26 import static jdk.testlibrary.Asserts.assertTrue;
  27 
  28 import java.io.BufferedReader;
  29 import java.io.File;
  30 import java.io.FileReader;
  31 import java.io.IOException;
  32 import java.net.InetAddress;
  33 import java.net.ServerSocket;
  34 import java.net.UnknownHostException;
  35 import java.util.ArrayList;
  36 import java.util.List;
  37 import java.util.Arrays;
  38 import java.util.Collections;
  39 import java.util.regex.Pattern;
  40 import java.util.regex.Matcher;
  41 import java.util.concurrent.TimeUnit;
  42 import java.util.function.BooleanSupplier;
  43 
  44 /**
  45  * Common library for various test helper functions.
  46  */
  47 public final class Utils {
  48 
  49     /**
  50      * Returns the sequence used by operating system to separate lines.
  51      */
  52     public static final String NEW_LINE = System.getProperty("line.separator");
  53 
  54     /**
  55      * Returns the value of 'test.vm.opts'system property.
  56      */
  57     public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim();
  58 
  59     /**
  60      * Returns the value of 'test.java.opts'system property.
  61      */
  62     public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim();
  63 
  64     /**
  65     * Returns the value of 'test.timeout.factor' system property
  66     * converted to {@code double}.
  67     */
  68     public static final double TIMEOUT_FACTOR;
  69     static {
  70         String toFactor = System.getProperty("test.timeout.factor", "1.0");
  71        TIMEOUT_FACTOR = Double.parseDouble(toFactor);
  72     }
  73 
  74     /**
  75     * Returns the value of JTREG default test timeout in milliseconds
  76     * converted to {@code long}.
  77     */
  78     public static final long DEFAULT_TEST_TIMEOUT = TimeUnit.SECONDS.toMillis(120);
  79 
  80     private Utils() {
  81         // Private constructor to prevent class instantiation
  82     }
  83 
  84     /**
  85      * Returns the list of VM options.
  86      *
  87      * @return List of VM options
  88      */
  89     public static List<String> getVmOptions() {
  90         return Arrays.asList(safeSplitString(VM_OPTIONS));
  91     }
  92 
  93     /**
  94      * Returns the list of VM options with -J prefix.
  95      *
  96      * @return The list of VM options with -J prefix
  97      */
  98     public static List<String> getForwardVmOptions() {
  99         String[] opts = safeSplitString(VM_OPTIONS);
 100         for (int i = 0; i < opts.length; i++) {
 101             opts[i] = "-J" + opts[i];
 102         }
 103         return Arrays.asList(opts);
 104     }
 105 
 106     /**
 107      * Returns the default JTReg arguments for a jvm running a test.
 108      * This is the combination of JTReg arguments test.vm.opts and test.java.opts.
 109      * @return An array of options, or an empty array if no opptions.
 110      */
 111     public static String[] getTestJavaOpts() {
 112         List<String> opts = new ArrayList<String>();
 113         Collections.addAll(opts, safeSplitString(VM_OPTIONS));
 114         Collections.addAll(opts, safeSplitString(JAVA_OPTIONS));
 115         return opts.toArray(new String[0]);
 116     }
 117 
 118     /**
 119      * Combines given arguments with default JTReg arguments for a jvm running a test.
 120      * This is the combination of JTReg arguments test.vm.opts and test.java.opts
 121      * @return The combination of JTReg test java options and user args.
 122      */
 123     public static String[] addTestJavaOpts(String... userArgs) {
 124         List<String> opts = new ArrayList<String>();
 125         Collections.addAll(opts, getTestJavaOpts());
 126         Collections.addAll(opts, userArgs);
 127         return opts.toArray(new String[0]);
 128     }
 129 
 130     /**
 131      * Removes any options specifying which GC to use, for example "-XX:+UseG1GC".
 132      * Removes any options matching: -XX:(+/-)Use*GC
 133      * Used when a test need to set its own GC version. Then any
 134      * GC specified by the framework must first be removed.
 135      * @return A copy of given opts with all GC options removed.
 136      */
 137     private static final Pattern useGcPattern = Pattern.compile(
 138             "(?:\\-XX\\:[\\+\\-]Use.+GC)"
 139             + "|(?:\\-Xconcgc)");
 140     public static List<String> removeGcOpts(List<String> opts) {
 141         List<String> optsWithoutGC = new ArrayList<String>();
 142         for (String opt : opts) {
 143             if (useGcPattern.matcher(opt).matches()) {
 144                 System.out.println("removeGcOpts: removed " + opt);
 145             } else {
 146                 optsWithoutGC.add(opt);
 147             }
 148         }
 149         return optsWithoutGC;
 150     }
 151 
 152     /**
 153      * Splits a string by white space.
 154      * Works like String.split(), but returns an empty array
 155      * if the string is null or empty.
 156      */
 157     private static String[] safeSplitString(String s) {
 158         if (s == null || s.trim().isEmpty()) {
 159             return new String[] {};
 160         }
 161         return s.trim().split("\\s+");
 162     }
 163 
 164     /**
 165      * @return The full command line for the ProcessBuilder.
 166      */
 167     public static String getCommandLine(ProcessBuilder pb) {
 168         StringBuilder cmd = new StringBuilder();
 169         for (String s : pb.command()) {
 170             cmd.append(s).append(" ");
 171         }
 172         return cmd.toString();
 173     }
 174 
 175     /**
 176      * Returns the free port on the local host.
 177      * The function will spin until a valid port number is found.
 178      *
 179      * @return The port number
 180      * @throws InterruptedException if any thread has interrupted the current thread
 181      * @throws IOException if an I/O error occurs when opening the socket
 182      */
 183     public static int getFreePort() throws InterruptedException, IOException {
 184         int port = -1;
 185 
 186         while (port <= 0) {
 187             Thread.sleep(100);
 188 
 189             ServerSocket serverSocket = null;
 190             try {
 191                 serverSocket = new ServerSocket(0);
 192                 port = serverSocket.getLocalPort();
 193             } finally {
 194                 serverSocket.close();
 195             }
 196         }
 197 
 198         return port;
 199     }
 200 
 201     /**
 202      * Returns the name of the local host.
 203      *
 204      * @return The host name
 205      * @throws UnknownHostException if IP address of a host could not be determined
 206      */
 207     public static String getHostname() throws UnknownHostException {
 208         InetAddress inetAddress = InetAddress.getLocalHost();
 209         String hostName = inetAddress.getHostName();
 210 
 211         assertTrue((hostName != null && !hostName.isEmpty()),
 212                 "Cannot get hostname");
 213 
 214         return hostName;
 215     }
 216 
 217     /**
 218      * Uses "jcmd -l" to search for a jvm pid. This function will wait
 219      * forever (until jtreg timeout) for the pid to be found.
 220      * @param key Regular expression to search for
 221      * @return The found pid.
 222      */
 223     public static int waitForJvmPid(String key) throws Throwable {
 224         final long iterationSleepMillis = 250;
 225         System.out.println("waitForJvmPid: Waiting for key '" + key + "'");
 226         System.out.flush();
 227         while (true) {
 228             int pid = tryFindJvmPid(key);
 229             if (pid >= 0) {
 230                 return pid;
 231             }
 232             Thread.sleep(iterationSleepMillis);
 233         }
 234     }
 235 
 236     /**
 237      * Searches for a jvm pid in the output from "jcmd -l".
 238      *
 239      * Example output from jcmd is:
 240      * 12498 sun.tools.jcmd.JCmd -l
 241      * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar
 242      *
 243      * @param key A regular expression to search for.
 244      * @return The found pid, or -1 if Enot found.
 245      * @throws Exception If multiple matching jvms are found.
 246      */
 247     public static int tryFindJvmPid(String key) throws Throwable {
 248         OutputAnalyzer output = null;
 249         try {
 250             JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd");
 251             jcmdLauncher.addToolArg("-l");
 252             output = ProcessTools.executeProcess(jcmdLauncher.getCommand());
 253             output.shouldHaveExitValue(0);
 254 
 255             // Search for a line starting with numbers (pid), follwed by the key.
 256             Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n");
 257             Matcher matcher = pattern.matcher(output.getStdout());
 258 
 259             int pid = -1;
 260             if (matcher.find()) {
 261                 pid = Integer.parseInt(matcher.group(1));
 262                 System.out.println("findJvmPid.pid: " + pid);
 263                 if (matcher.find()) {
 264                     throw new Exception("Found multiple JVM pids for key: " + key);
 265                 }
 266             }
 267             return pid;
 268         } catch (Throwable t) {
 269             System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t));
 270             throw t;
 271         }
 272     }
 273 
 274     /**
 275      * Returns file content as a list of strings
 276      *
 277      * @param file File to operate on
 278      * @return List of strings
 279      * @throws IOException
 280      */
 281     public static List<String> fileAsList(File file) throws IOException {
 282         assertTrue(file.exists() && file.isFile(),
 283                 file.getAbsolutePath() + " does not exist or not a file");
 284         List<String> output = new ArrayList<>();
 285         try (BufferedReader reader = new BufferedReader(new FileReader(file.getAbsolutePath()))) {
 286             while (reader.ready()) {
 287                 output.add(reader.readLine().replace(NEW_LINE, ""));
 288             }
 289         }
 290         return output;
 291     }
 292 
 293     /**
 294      * Adjusts the provided timeout value for the TIMEOUT_FACTOR
 295      * @param tOut the timeout value to be adjusted
 296      * @return The timeout value adjusted for the value of "test.timeout.factor"
 297      *         system property
 298      */
 299     public static long adjustTimeout(long tOut) {
 300         return Math.round(tOut * Utils.TIMEOUT_FACTOR);
 301     }
 302 
 303     /**
 304      * Wait for condition to be true
 305      *
 306      * @param condition, a condition to wait for
 307      */
 308     public static final void waitForCondition(BooleanSupplier condition) {
 309         waitForCondition(condition, -1L, 100L);
 310     }
 311 
 312     /**
 313      * Wait until timeout for condition to be true
 314      *
 315      * @param condition, a condition to wait for
 316      * @param timeout a time in milliseconds to wait for condition to be true
 317      * specifying -1 will wait forever
 318      * @return condition value, to determine if wait was successfull
 319      */
 320     public static final boolean waitForCondition(BooleanSupplier condition,
 321             long timeout) {
 322         return waitForCondition(condition, timeout, 100L);
 323     }
 324 
 325     /**
 326      * Wait until timeout for condition to be true for specified time
 327      *
 328      * @param condition, a condition to wait for
 329      * @param timeout a time in milliseconds to wait for condition to be true,
 330      * specifying -1 will wait forever
 331      * @param sleepTime a time to sleep value in milliseconds
 332      * @return condition value, to determine if wait was successfull
 333      */
 334     public static final boolean waitForCondition(BooleanSupplier condition,
 335             long timeout, long sleepTime) {
 336         long startTime = System.currentTimeMillis();
 337         while (!(condition.getAsBoolean() || (timeout != -1L
 338                 && ((System.currentTimeMillis() - startTime) > timeout)))) {
 339             try {
 340                 Thread.sleep(sleepTime);
 341             } catch (InterruptedException e) {
 342                 Thread.currentThread().interrupt();
 343                 throw new Error(e);
 344             }
 345         } 
 346         return condition.getAsBoolean();
 347     }
 348 }