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