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