1 /* 2 * Copyright (c) 2013, 2016, 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.io.InputStream; 33 import java.io.OutputStream; 34 import java.net.InetAddress; 35 import java.net.ServerSocket; 36 import java.net.UnknownHostException; 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.Arrays; 40 import java.util.Collections; 41 import java.util.Objects; 42 import java.util.regex.Pattern; 43 import java.util.regex.Matcher; 44 import java.util.concurrent.TimeUnit; 45 import java.util.function.BooleanSupplier; 46 import java.util.function.Function; 47 48 /** 49 * Common library for various test helper functions. 50 */ 51 public final class Utils { 52 53 /** 54 * Returns the sequence used by operating system to separate lines. 55 */ 56 public static final String NEW_LINE = System.getProperty("line.separator"); 57 58 /** 59 * Returns the value of 'test.vm.opts'system property. 60 */ 61 public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim(); 62 63 /** 64 * Returns the value of 'test.java.opts'system property. 65 */ 66 public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim(); 67 68 69 /** 70 * Returns the value of 'test.timeout.factor' system property 71 * converted to {@code double}. 72 */ 73 public static final double TIMEOUT_FACTOR; 74 static { 75 String toFactor = System.getProperty("test.timeout.factor", "1.0"); 76 TIMEOUT_FACTOR = Double.parseDouble(toFactor); 77 } 78 79 /** 80 * Returns the value of JTREG default test timeout in milliseconds 81 * converted to {@code long}. 82 */ 83 public static final long DEFAULT_TEST_TIMEOUT = TimeUnit.SECONDS.toMillis(120); 84 85 private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; 86 private static final int DEFAULT_BUFFER_SIZE = 8192; 87 88 private Utils() { 89 // Private constructor to prevent class instantiation 90 } 91 92 /** 93 * Returns the list of VM options. 94 * 95 * @return List of VM options 96 */ 97 public static List<String> getVmOptions() { 98 return Arrays.asList(safeSplitString(VM_OPTIONS)); 99 } 100 101 /** 102 * Returns the list of VM options with -J prefix. 103 * 104 * @return The list of VM options with -J prefix 105 */ 106 public static List<String> getForwardVmOptions() { 107 String[] opts = safeSplitString(VM_OPTIONS); 108 for (int i = 0; i < opts.length; i++) { 109 opts[i] = "-J" + opts[i]; 110 } 111 return Arrays.asList(opts); 112 } 113 114 /** 115 * Returns the default JTReg arguments for a jvm running a test. 116 * This is the combination of JTReg arguments test.vm.opts and test.java.opts. 117 * @return An array of options, or an empty array if no opptions. 118 */ 119 public static String[] getTestJavaOpts() { 120 List<String> opts = new ArrayList<String>(); 121 Collections.addAll(opts, safeSplitString(VM_OPTIONS)); 122 Collections.addAll(opts, safeSplitString(JAVA_OPTIONS)); 123 return opts.toArray(new String[0]); 124 } 125 126 /** 127 * Combines given arguments with default JTReg arguments for a jvm running a test. 128 * This is the combination of JTReg arguments test.vm.opts and test.java.opts 129 * @return The combination of JTReg test java options and user args. 130 */ 131 public static String[] addTestJavaOpts(String... userArgs) { 132 List<String> opts = new ArrayList<String>(); 133 Collections.addAll(opts, getTestJavaOpts()); 134 Collections.addAll(opts, userArgs); 135 return opts.toArray(new String[0]); 136 } 137 138 /** 139 * Removes any options specifying which GC to use, for example "-XX:+UseG1GC". 140 * Removes any options matching: -XX:(+/-)Use*GC 141 * Used when a test need to set its own GC version. Then any 142 * GC specified by the framework must first be removed. 143 * @return A copy of given opts with all GC options removed. 144 */ 145 private static final Pattern useGcPattern = Pattern.compile("\\-XX\\:[\\+\\-]Use.+GC"); 146 public static List<String> removeGcOpts(List<String> opts) { 147 List<String> optsWithoutGC = new ArrayList<String>(); 148 for (String opt : opts) { 149 if (useGcPattern.matcher(opt).matches()) { 150 System.out.println("removeGcOpts: removed " + opt); 151 } else { 152 optsWithoutGC.add(opt); 153 } 154 } 155 return optsWithoutGC; 156 } 157 158 /** 159 * Splits a string by white space. 160 * Works like String.split(), but returns an empty array 161 * if the string is null or empty. 162 */ 163 private static String[] safeSplitString(String s) { 164 if (s == null || s.trim().isEmpty()) { 165 return new String[] {}; 166 } 167 return s.trim().split("\\s+"); 168 } 169 170 /** 171 * @return The full command line for the ProcessBuilder. 172 */ 173 public static String getCommandLine(ProcessBuilder pb) { 174 StringBuilder cmd = new StringBuilder(); 175 for (String s : pb.command()) { 176 cmd.append(s).append(" "); 177 } 178 return cmd.toString(); 179 } 180 181 /** 182 * Returns the free port on the local host. 183 * The function will spin until a valid port number is found. 184 * 185 * @return The port number 186 * @throws InterruptedException if any thread has interrupted the current thread 187 * @throws IOException if an I/O error occurs when opening the socket 188 */ 189 public static int getFreePort() throws InterruptedException, IOException { 190 int port = -1; 191 192 while (port <= 0) { 193 Thread.sleep(100); 194 195 ServerSocket serverSocket = null; 196 try { 197 serverSocket = new ServerSocket(0); 198 port = serverSocket.getLocalPort(); 199 } finally { 200 serverSocket.close(); 201 } 202 } 203 204 return port; 205 } 206 207 /** 208 * Returns the name of the local host. 209 * 210 * @return The host name 211 * @throws UnknownHostException if IP address of a host could not be determined 212 */ 213 public static String getHostname() throws UnknownHostException { 214 InetAddress inetAddress = InetAddress.getLocalHost(); 215 String hostName = inetAddress.getHostName(); 216 217 assertTrue((hostName != null && !hostName.isEmpty()), 218 "Cannot get hostname"); 219 220 return hostName; 221 } 222 223 /** 224 * Uses "jcmd -l" to search for a jvm pid. This function will wait 225 * forever (until jtreg timeout) for the pid to be found. 226 * @param key Regular expression to search for 227 * @return The found pid. 228 */ 229 public static int waitForJvmPid(String key) throws Throwable { 230 final long iterationSleepMillis = 250; 231 System.out.println("waitForJvmPid: Waiting for key '" + key + "'"); 232 System.out.flush(); 233 while (true) { 234 int pid = tryFindJvmPid(key); 235 if (pid >= 0) { 236 return pid; 237 } 238 Thread.sleep(iterationSleepMillis); 239 } 240 } 241 242 /** 243 * Searches for a jvm pid in the output from "jcmd -l". 244 * 245 * Example output from jcmd is: 246 * 12498 sun.tools.jcmd.JCmd -l 247 * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar 248 * 249 * @param key A regular expression to search for. 250 * @return The found pid, or -1 if Enot found. 251 * @throws Exception If multiple matching jvms are found. 252 */ 253 public static int tryFindJvmPid(String key) throws Throwable { 254 OutputAnalyzer output = null; 255 try { 256 JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd"); 257 jcmdLauncher.addToolArg("-l"); 258 output = ProcessTools.executeProcess(jcmdLauncher.getCommand()); 259 output.shouldHaveExitValue(0); 260 261 // Search for a line starting with numbers (pid), follwed by the key. 262 Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n"); 263 Matcher matcher = pattern.matcher(output.getStdout()); 264 265 int pid = -1; 266 if (matcher.find()) { 267 pid = Integer.parseInt(matcher.group(1)); 268 System.out.println("findJvmPid.pid: " + pid); 269 if (matcher.find()) { 270 throw new Exception("Found multiple JVM pids for key: " + key); 271 } 272 } 273 return pid; 274 } catch (Throwable t) { 275 System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t)); 276 throw t; 277 } 278 } 279 280 /** 281 * Returns file content as a list of strings 282 * 283 * @param file File to operate on 284 * @return List of strings 285 * @throws IOException 286 */ 287 public static List<String> fileAsList(File file) throws IOException { 288 assertTrue(file.exists() && file.isFile(), 289 file.getAbsolutePath() + " does not exist or not a file"); 290 List<String> output = new ArrayList<>(); 291 try (BufferedReader reader = new BufferedReader(new FileReader(file.getAbsolutePath()))) { 292 while (reader.ready()) { 293 output.add(reader.readLine().replace(NEW_LINE, "")); 294 } 295 } 296 return output; 297 } 298 299 /** 300 * Helper method to read all bytes from InputStream 301 * 302 * @param is InputStream to read from 303 * @return array of bytes 304 * @throws IOException 305 */ 306 public static byte[] readAllBytes(InputStream is) throws IOException { 307 byte[] buf = new byte[DEFAULT_BUFFER_SIZE]; 308 int capacity = buf.length; 309 int nread = 0; 310 int n; 311 for (;;) { 312 // read to EOF which may read more or less than initial buffer size 313 while ((n = is.read(buf, nread, capacity - nread)) > 0) 314 nread += n; 315 316 // if the last call to read returned -1, then we're done 317 if (n < 0) 318 break; 319 320 // need to allocate a larger buffer 321 if (capacity <= MAX_BUFFER_SIZE - capacity) { 322 capacity = capacity << 1; 323 } else { 324 if (capacity == MAX_BUFFER_SIZE) 325 throw new OutOfMemoryError("Required array size too large"); 326 capacity = MAX_BUFFER_SIZE; 327 } 328 buf = Arrays.copyOf(buf, capacity); 329 } 330 return (capacity == nread) ? buf : Arrays.copyOf(buf, nread); 331 } 332 333 /** 334 * Adjusts the provided timeout value for the TIMEOUT_FACTOR 335 * @param tOut the timeout value to be adjusted 336 * @return The timeout value adjusted for the value of "test.timeout.factor" 337 * system property 338 */ 339 public static long adjustTimeout(long tOut) { 340 return Math.round(tOut * Utils.TIMEOUT_FACTOR); 341 } 342 343 /** 344 * Interface same as java.lang.Runnable but with 345 * method {@code run()} able to throw any Throwable. 346 */ 347 public static interface ThrowingRunnable { 348 void run() throws Throwable; 349 } 350 351 /** 352 * Filters out an exception that may be thrown by the given 353 * test according to the given filter. 354 * 355 * @param test - method that is invoked and checked for exception. 356 * @param filter - function that checks if the thrown exception matches 357 * criteria given in the filter's implementation. 358 * @return - exception that matches the filter if it has been thrown or 359 * {@code null} otherwise. 360 * @throws Throwable - if test has thrown an exception that does not 361 * match the filter. 362 */ 363 public static Throwable filterException(ThrowingRunnable test, 364 Function<Throwable, Boolean> filter) throws Throwable { 365 try { 366 test.run(); 367 } catch (Throwable t) { 368 if (filter.apply(t)) { 369 return t; 370 } else { 371 throw t; 372 } 373 } 374 return null; 375 } 376 377 private static final int BUFFER_SIZE = 1024; 378 379 /** 380 * Reads all bytes from the input stream and writes the bytes to the 381 * given output stream in the order that they are read. On return, the 382 * input stream will be at end of stream. This method does not close either 383 * stream. 384 * <p> 385 * This method may block indefinitely reading from the input stream, or 386 * writing to the output stream. The behavior for the case where the input 387 * and/or output stream is <i>asynchronously closed</i>, or the thread 388 * interrupted during the transfer, is highly input and output stream 389 * specific, and therefore not specified. 390 * <p> 391 * If an I/O error occurs reading from the input stream or writing to the 392 * output stream, then it may do so after some bytes have been read or 393 * written. Consequently the input stream may not be at end of stream and 394 * one, or both, streams may be in an inconsistent state. It is strongly 395 * recommended that both streams be promptly closed if an I/O error occurs. 396 * 397 * @param in the input stream, non-null 398 * @param out the output stream, non-null 399 * @return the number of bytes transferred 400 * @throws IOException if an I/O error occurs when reading or writing 401 * @throws NullPointerException if {@code in} or {@code out} is {@code null} 402 * 403 */ 404 public static long transferTo(InputStream in, OutputStream out) 405 throws IOException { 406 long transferred = 0; 407 byte[] buffer = new byte[BUFFER_SIZE]; 408 int read; 409 while ((read = in.read(buffer, 0, BUFFER_SIZE)) >= 0) { 410 out.write(buffer, 0, read); 411 transferred += read; 412 } 413 return transferred; 414 } 415 416 /** 417 * Wait for condition to be true 418 * 419 * @param condition, a condition to wait for 420 */ 421 public static final void waitForCondition(BooleanSupplier condition) { 422 waitForCondition(condition, -1L, 100L); 423 } 424 425 /** 426 * Wait until timeout for condition to be true 427 * 428 * @param condition, a condition to wait for 429 * @param timeout a time in milliseconds to wait for condition to be true 430 * specifying -1 will wait forever 431 * @return condition value, to determine if wait was successful 432 */ 433 public static final boolean waitForCondition(BooleanSupplier condition, 434 long timeout) { 435 return waitForCondition(condition, timeout, 100L); 436 } 437 438 /** 439 * Wait until timeout for condition to be true for specified time 440 * 441 * @param condition, a condition to wait for 442 * @param timeout a time in milliseconds to wait for condition to be true, 443 * specifying -1 will wait forever 444 * @param sleepTime a time to sleep value in milliseconds 445 * @return condition value, to determine if wait was successful 446 */ 447 public static final boolean waitForCondition(BooleanSupplier condition, 448 long timeout, long sleepTime) { 449 long startTime = System.currentTimeMillis(); 450 while (!(condition.getAsBoolean() || (timeout != -1L 451 && ((System.currentTimeMillis() - startTime) > timeout)))) { 452 try { 453 Thread.sleep(sleepTime); 454 } catch (InterruptedException e) { 455 Thread.currentThread().interrupt(); 456 throw new Error(e); 457 } 458 } 459 return condition.getAsBoolean(); 460 } 461 }