1 /* 2 * Copyright (c) 2003, 2015, 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 /* 25 * @test 26 * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689 27 * 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313 28 * 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958 29 * 4947220 7018606 7034570 4244896 5049299 8003488 8054494 30 * @summary Basic tests for Process and Environment Variable code 31 * @run main/othervm/timeout=300 Basic 32 * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=fork Basic 33 * @author Martin Buchholz 34 */ 35 36 import java.lang.ProcessBuilder.Redirect; 37 import static java.lang.ProcessBuilder.Redirect.*; 38 39 import java.io.*; 40 import java.lang.reflect.Field; 41 import java.nio.file.Files; 42 import java.nio.file.Paths; 43 import java.nio.file.StandardCopyOption; 44 import java.util.*; 45 import java.util.concurrent.CountDownLatch; 46 import java.util.concurrent.TimeUnit; 47 import java.security.*; 48 import sun.misc.Unsafe; 49 import java.util.regex.Pattern; 50 import java.util.regex.Matcher; 51 import static java.lang.System.getenv; 52 import static java.lang.System.out; 53 import static java.lang.Boolean.TRUE; 54 import static java.util.AbstractMap.SimpleImmutableEntry; 55 56 public class Basic { 57 58 /* used for Windows only */ 59 static final String systemRoot = System.getenv("SystemRoot"); 60 61 /* used for Mac OS X only */ 62 static final String cfUserTextEncoding = System.getenv("__CF_USER_TEXT_ENCODING"); 63 64 /* used for AIX only */ 65 static final String libpath = System.getenv("LIBPATH"); 66 67 /** 68 * Returns the number of milliseconds since time given by 69 * startNanoTime, which must have been previously returned from a 70 * call to {@link System.nanoTime()}. 71 */ 72 private static long millisElapsedSince(long startNanoTime) { 73 return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime); 74 } 75 76 private static String commandOutput(Reader r) throws Throwable { 77 StringBuilder sb = new StringBuilder(); 78 int c; 79 while ((c = r.read()) > 0) 80 if (c != '\r') 81 sb.append((char) c); 82 return sb.toString(); 83 } 84 85 private static String commandOutput(Process p) throws Throwable { 86 check(p.getInputStream() == p.getInputStream()); 87 check(p.getOutputStream() == p.getOutputStream()); 88 check(p.getErrorStream() == p.getErrorStream()); 89 Reader r = new InputStreamReader(p.getInputStream(),"UTF-8"); 90 String output = commandOutput(r); 91 equal(p.waitFor(), 0); 92 equal(p.exitValue(), 0); 93 // The debug/fastdebug versions of the VM may write some warnings to stdout 94 // (i.e. "Warning: Cannot open log file: hotspot.log" if the VM is started 95 // in a directory without write permissions). These warnings will confuse tests 96 // which match the entire output of the child process so better filter them out. 97 return output.replaceAll("Warning:.*\\n", ""); 98 } 99 100 private static String commandOutput(ProcessBuilder pb) { 101 try { 102 return commandOutput(pb.start()); 103 } catch (Throwable t) { 104 String commandline = ""; 105 for (String arg : pb.command()) 106 commandline += " " + arg; 107 System.out.println("Exception trying to run process: " + commandline); 108 unexpected(t); 109 return ""; 110 } 111 } 112 113 private static String commandOutput(String...command) { 114 try { 115 return commandOutput(Runtime.getRuntime().exec(command)); 116 } catch (Throwable t) { 117 String commandline = ""; 118 for (String arg : command) 119 commandline += " " + arg; 120 System.out.println("Exception trying to run process: " + commandline); 121 unexpected(t); 122 return ""; 123 } 124 } 125 126 private static void checkCommandOutput(ProcessBuilder pb, 127 String expected, 128 String failureMsg) { 129 String got = commandOutput(pb); 130 check(got.equals(expected), 131 failureMsg + "\n" + 132 "Expected: \"" + expected + "\"\n" + 133 "Got: \"" + got + "\""); 134 } 135 136 private static String absolutifyPath(String path) { 137 StringBuilder sb = new StringBuilder(); 138 for (String file : path.split(File.pathSeparator)) { 139 if (sb.length() != 0) 140 sb.append(File.pathSeparator); 141 sb.append(new File(file).getAbsolutePath()); 142 } 143 return sb.toString(); 144 } 145 146 // compare windows-style, by canonicalizing to upper case, 147 // not lower case as String.compareToIgnoreCase does 148 private static class WindowsComparator 149 implements Comparator<String> { 150 public int compare(String x, String y) { 151 return x.toUpperCase(Locale.US) 152 .compareTo(y.toUpperCase(Locale.US)); 153 } 154 } 155 156 private static String sortedLines(String lines) { 157 String[] arr = lines.split("\n"); 158 List<String> ls = new ArrayList<String>(); 159 for (String s : arr) 160 ls.add(s); 161 Collections.sort(ls, new WindowsComparator()); 162 StringBuilder sb = new StringBuilder(); 163 for (String s : ls) 164 sb.append(s + "\n"); 165 return sb.toString(); 166 } 167 168 private static void compareLinesIgnoreCase(String lines1, String lines2) { 169 if (! (sortedLines(lines1).equalsIgnoreCase(sortedLines(lines2)))) { 170 String dashes = 171 "-----------------------------------------------------"; 172 out.println(dashes); 173 out.print(sortedLines(lines1)); 174 out.println(dashes); 175 out.print(sortedLines(lines2)); 176 out.println(dashes); 177 out.println("sizes: " + sortedLines(lines1).length() + 178 " " + sortedLines(lines2).length()); 179 180 fail("Sorted string contents differ"); 181 } 182 } 183 184 private static final Runtime runtime = Runtime.getRuntime(); 185 186 private static final String[] winEnvCommand = {"cmd.exe", "/c", "set"}; 187 188 private static String winEnvFilter(String env) { 189 return env.replaceAll("\r", "") 190 .replaceAll("(?m)^(?:COMSPEC|PROMPT|PATHEXT)=.*\n",""); 191 } 192 193 private static String unixEnvProg() { 194 return new File("/usr/bin/env").canExecute() ? "/usr/bin/env" 195 : "/bin/env"; 196 } 197 198 private static String nativeEnv(String[] env) { 199 try { 200 if (Windows.is()) { 201 return winEnvFilter 202 (commandOutput(runtime.exec(winEnvCommand, env))); 203 } else { 204 return commandOutput(runtime.exec(unixEnvProg(), env)); 205 } 206 } catch (Throwable t) { throw new Error(t); } 207 } 208 209 private static String nativeEnv(ProcessBuilder pb) { 210 try { 211 if (Windows.is()) { 212 pb.command(winEnvCommand); 213 return winEnvFilter(commandOutput(pb)); 214 } else { 215 pb.command(new String[]{unixEnvProg()}); 216 return commandOutput(pb); 217 } 218 } catch (Throwable t) { throw new Error(t); } 219 } 220 221 private static void checkSizes(Map<String,String> environ, int size) { 222 try { 223 equal(size, environ.size()); 224 equal(size, environ.entrySet().size()); 225 equal(size, environ.keySet().size()); 226 equal(size, environ.values().size()); 227 228 boolean isEmpty = (size == 0); 229 equal(isEmpty, environ.isEmpty()); 230 equal(isEmpty, environ.entrySet().isEmpty()); 231 equal(isEmpty, environ.keySet().isEmpty()); 232 equal(isEmpty, environ.values().isEmpty()); 233 } catch (Throwable t) { unexpected(t); } 234 } 235 236 private interface EnvironmentFrobber { 237 void doIt(Map<String,String> environ); 238 } 239 240 private static void testVariableDeleter(EnvironmentFrobber fooDeleter) { 241 try { 242 Map<String,String> environ = new ProcessBuilder().environment(); 243 environ.put("Foo", "BAAR"); 244 fooDeleter.doIt(environ); 245 equal(environ.get("Foo"), null); 246 equal(environ.remove("Foo"), null); 247 } catch (Throwable t) { unexpected(t); } 248 } 249 250 private static void testVariableAdder(EnvironmentFrobber fooAdder) { 251 try { 252 Map<String,String> environ = new ProcessBuilder().environment(); 253 environ.remove("Foo"); 254 fooAdder.doIt(environ); 255 equal(environ.get("Foo"), "Bahrein"); 256 } catch (Throwable t) { unexpected(t); } 257 } 258 259 private static void testVariableModifier(EnvironmentFrobber fooModifier) { 260 try { 261 Map<String,String> environ = new ProcessBuilder().environment(); 262 environ.put("Foo","OldValue"); 263 fooModifier.doIt(environ); 264 equal(environ.get("Foo"), "NewValue"); 265 } catch (Throwable t) { unexpected(t); } 266 } 267 268 private static void printUTF8(String s) throws IOException { 269 out.write(s.getBytes("UTF-8")); 270 } 271 272 private static String getenvAsString(Map<String,String> environment) { 273 StringBuilder sb = new StringBuilder(); 274 environment = new TreeMap<>(environment); 275 for (Map.Entry<String,String> e : environment.entrySet()) 276 // Ignore magic environment variables added by the launcher 277 if (! e.getKey().equals("NLSPATH") && 278 ! e.getKey().equals("XFILESEARCHPATH") && 279 ! e.getKey().equals("LD_LIBRARY_PATH")) 280 sb.append(e.getKey()) 281 .append('=') 282 .append(e.getValue()) 283 .append(','); 284 return sb.toString(); 285 } 286 287 static void print4095(OutputStream s, byte b) throws Throwable { 288 byte[] bytes = new byte[4095]; 289 Arrays.fill(bytes, b); 290 s.write(bytes); // Might hang! 291 } 292 293 static void checkPermissionDenied(ProcessBuilder pb) { 294 try { 295 pb.start(); 296 fail("Expected IOException not thrown"); 297 } catch (IOException e) { 298 String m = e.getMessage(); 299 if (EnglishUnix.is() && 300 ! matches(m, "Permission denied")) 301 unexpected(e); 302 } catch (Throwable t) { unexpected(t); } 303 } 304 305 public static class JavaChild { 306 public static void main(String args[]) throws Throwable { 307 String action = args[0]; 308 if (action.equals("sleep")) { 309 Thread.sleep(10 * 60 * 1000L); 310 } else if (action.equals("testIO")) { 311 String expected = "standard input"; 312 char[] buf = new char[expected.length()+1]; 313 int n = new InputStreamReader(System.in).read(buf,0,buf.length); 314 if (n != expected.length()) 315 System.exit(5); 316 if (! new String(buf,0,n).equals(expected)) 317 System.exit(5); 318 System.err.print("standard error"); 319 System.out.print("standard output"); 320 } else if (action.equals("testInheritIO") 321 || action.equals("testRedirectInherit")) { 322 List<String> childArgs = new ArrayList<String>(javaChildArgs); 323 childArgs.add("testIO"); 324 ProcessBuilder pb = new ProcessBuilder(childArgs); 325 if (action.equals("testInheritIO")) 326 pb.inheritIO(); 327 else 328 redirectIO(pb, INHERIT, INHERIT, INHERIT); 329 ProcessResults r = run(pb); 330 if (! r.out().equals("")) 331 System.exit(7); 332 if (! r.err().equals("")) 333 System.exit(8); 334 if (r.exitValue() != 0) 335 System.exit(9); 336 } else if (action.equals("System.getenv(String)")) { 337 String val = System.getenv(args[1]); 338 printUTF8(val == null ? "null" : val); 339 } else if (action.equals("System.getenv(\\u1234)")) { 340 String val = System.getenv("\u1234"); 341 printUTF8(val == null ? "null" : val); 342 } else if (action.equals("System.getenv()")) { 343 printUTF8(getenvAsString(System.getenv())); 344 } else if (action.equals("ArrayOOME")) { 345 Object dummy; 346 switch(new Random().nextInt(3)) { 347 case 0: dummy = new Integer[Integer.MAX_VALUE]; break; 348 case 1: dummy = new double[Integer.MAX_VALUE]; break; 349 case 2: dummy = new byte[Integer.MAX_VALUE][]; break; 350 default: throw new InternalError(); 351 } 352 } else if (action.equals("pwd")) { 353 printUTF8(new File(System.getProperty("user.dir")) 354 .getCanonicalPath()); 355 } else if (action.equals("print4095")) { 356 print4095(System.out, (byte) '!'); 357 print4095(System.err, (byte) 'E'); 358 System.exit(5); 359 } else if (action.equals("OutErr")) { 360 // You might think the system streams would be 361 // buffered, and in fact they are implemented using 362 // BufferedOutputStream, but each and every print 363 // causes immediate operating system I/O. 364 System.out.print("out"); 365 System.err.print("err"); 366 System.out.print("out"); 367 System.err.print("err"); 368 } else if (action.equals("null PATH")) { 369 equal(System.getenv("PATH"), null); 370 check(new File("/bin/true").exists()); 371 check(new File("/bin/false").exists()); 372 ProcessBuilder pb1 = new ProcessBuilder(); 373 ProcessBuilder pb2 = new ProcessBuilder(); 374 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways"); 375 ProcessResults r; 376 377 for (final ProcessBuilder pb : 378 new ProcessBuilder[] {pb1, pb2}) { 379 pb.command("true"); 380 equal(run(pb).exitValue(), True.exitValue()); 381 382 pb.command("false"); 383 equal(run(pb).exitValue(), False.exitValue()); 384 } 385 386 if (failed != 0) throw new Error("null PATH"); 387 } else if (action.equals("PATH search algorithm")) { 388 equal(System.getenv("PATH"), "dir1:dir2:"); 389 check(new File("/bin/true").exists()); 390 check(new File("/bin/false").exists()); 391 String[] cmd = {"prog"}; 392 ProcessBuilder pb1 = new ProcessBuilder(cmd); 393 ProcessBuilder pb2 = new ProcessBuilder(cmd); 394 ProcessBuilder pb3 = new ProcessBuilder(cmd); 395 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways"); 396 pb3.environment().remove("PATH"); 397 398 for (final ProcessBuilder pb : 399 new ProcessBuilder[] {pb1, pb2, pb3}) { 400 try { 401 // Not on PATH at all; directories don't exist 402 try { 403 pb.start(); 404 fail("Expected IOException not thrown"); 405 } catch (IOException e) { 406 String m = e.getMessage(); 407 if (EnglishUnix.is() && 408 ! matches(m, "No such file")) 409 unexpected(e); 410 } catch (Throwable t) { unexpected(t); } 411 412 // Not on PATH at all; directories exist 413 new File("dir1").mkdirs(); 414 new File("dir2").mkdirs(); 415 try { 416 pb.start(); 417 fail("Expected IOException not thrown"); 418 } catch (IOException e) { 419 String m = e.getMessage(); 420 if (EnglishUnix.is() && 421 ! matches(m, "No such file")) 422 unexpected(e); 423 } catch (Throwable t) { unexpected(t); } 424 425 // Can't execute a directory -- permission denied 426 // Report EACCES errno 427 new File("dir1/prog").mkdirs(); 428 checkPermissionDenied(pb); 429 430 // continue searching if EACCES 431 copy("/bin/true", "dir2/prog"); 432 equal(run(pb).exitValue(), True.exitValue()); 433 new File("dir1/prog").delete(); 434 new File("dir2/prog").delete(); 435 436 new File("dir2/prog").mkdirs(); 437 copy("/bin/true", "dir1/prog"); 438 equal(run(pb).exitValue(), True.exitValue()); 439 440 // Check empty PATH component means current directory. 441 // 442 // While we're here, let's test different kinds of 443 // Unix executables, and PATH vs explicit searching. 444 new File("dir1/prog").delete(); 445 new File("dir2/prog").delete(); 446 for (String[] command : 447 new String[][] { 448 new String[] {"./prog"}, 449 cmd}) { 450 pb.command(command); 451 File prog = new File("./prog"); 452 // "Normal" binaries 453 copy("/bin/true", "./prog"); 454 equal(run(pb).exitValue(), 455 True.exitValue()); 456 copy("/bin/false", "./prog"); 457 equal(run(pb).exitValue(), 458 False.exitValue()); 459 prog.delete(); 460 // Interpreter scripts with #! 461 setFileContents(prog, "#!/bin/true\n"); 462 prog.setExecutable(true); 463 equal(run(pb).exitValue(), 464 True.exitValue()); 465 prog.delete(); 466 setFileContents(prog, "#!/bin/false\n"); 467 prog.setExecutable(true); 468 equal(run(pb).exitValue(), 469 False.exitValue()); 470 // Traditional shell scripts without #! 471 setFileContents(prog, "exec /bin/true\n"); 472 prog.setExecutable(true); 473 equal(run(pb).exitValue(), 474 True.exitValue()); 475 prog.delete(); 476 setFileContents(prog, "exec /bin/false\n"); 477 prog.setExecutable(true); 478 equal(run(pb).exitValue(), 479 False.exitValue()); 480 prog.delete(); 481 } 482 483 // Test Unix interpreter scripts 484 File dir1Prog = new File("dir1/prog"); 485 dir1Prog.delete(); 486 pb.command(new String[] {"prog", "world"}); 487 setFileContents(dir1Prog, "#!/bin/echo hello\n"); 488 checkPermissionDenied(pb); 489 dir1Prog.setExecutable(true); 490 equal(run(pb).out(), "hello dir1/prog world\n"); 491 equal(run(pb).exitValue(), True.exitValue()); 492 dir1Prog.delete(); 493 pb.command(cmd); 494 495 // Test traditional shell scripts without #! 496 setFileContents(dir1Prog, "/bin/echo \"$@\"\n"); 497 pb.command(new String[] {"prog", "hello", "world"}); 498 checkPermissionDenied(pb); 499 dir1Prog.setExecutable(true); 500 equal(run(pb).out(), "hello world\n"); 501 equal(run(pb).exitValue(), True.exitValue()); 502 dir1Prog.delete(); 503 pb.command(cmd); 504 505 // If prog found on both parent and child's PATH, 506 // parent's is used. 507 new File("dir1/prog").delete(); 508 new File("dir2/prog").delete(); 509 new File("prog").delete(); 510 new File("dir3").mkdirs(); 511 copy("/bin/true", "dir1/prog"); 512 copy("/bin/false", "dir3/prog"); 513 pb.environment().put("PATH","dir3"); 514 equal(run(pb).exitValue(), True.exitValue()); 515 copy("/bin/true", "dir3/prog"); 516 copy("/bin/false", "dir1/prog"); 517 equal(run(pb).exitValue(), False.exitValue()); 518 519 } finally { 520 // cleanup 521 new File("dir1/prog").delete(); 522 new File("dir2/prog").delete(); 523 new File("dir3/prog").delete(); 524 new File("dir1").delete(); 525 new File("dir2").delete(); 526 new File("dir3").delete(); 527 new File("prog").delete(); 528 } 529 } 530 531 if (failed != 0) throw new Error("PATH search algorithm"); 532 } 533 else throw new Error("JavaChild invocation error"); 534 } 535 } 536 537 private static void copy(String src, String dst) throws IOException { 538 Files.copy(Paths.get(src), Paths.get(dst), 539 StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); 540 } 541 542 private static String javaChildOutput(ProcessBuilder pb, String...args) { 543 List<String> list = new ArrayList<String>(javaChildArgs); 544 for (String arg : args) 545 list.add(arg); 546 pb.command(list); 547 return commandOutput(pb); 548 } 549 550 private static String getenvInChild(ProcessBuilder pb) { 551 return javaChildOutput(pb, "System.getenv()"); 552 } 553 554 private static String getenvInChild1234(ProcessBuilder pb) { 555 return javaChildOutput(pb, "System.getenv(\\u1234)"); 556 } 557 558 private static String getenvInChild(ProcessBuilder pb, String name) { 559 return javaChildOutput(pb, "System.getenv(String)", name); 560 } 561 562 private static String pwdInChild(ProcessBuilder pb) { 563 return javaChildOutput(pb, "pwd"); 564 } 565 566 private static final String javaExe = 567 System.getProperty("java.home") + 568 File.separator + "bin" + File.separator + "java"; 569 570 private static final String classpath = 571 System.getProperty("java.class.path"); 572 573 private static final List<String> javaChildArgs = 574 Arrays.asList(javaExe, 575 "-XX:+DisplayVMOutputToStderr", 576 "-classpath", absolutifyPath(classpath), 577 "Basic$JavaChild"); 578 579 private static void testEncoding(String encoding, String tested) { 580 try { 581 // If round trip conversion works, should be able to set env vars 582 // correctly in child. 583 if (new String(tested.getBytes()).equals(tested)) { 584 out.println("Testing " + encoding + " environment values"); 585 ProcessBuilder pb = new ProcessBuilder(); 586 pb.environment().put("ASCIINAME",tested); 587 equal(getenvInChild(pb,"ASCIINAME"), tested); 588 } 589 } catch (Throwable t) { unexpected(t); } 590 } 591 592 static class Windows { 593 public static boolean is() { return is; } 594 private static final boolean is = 595 System.getProperty("os.name").startsWith("Windows"); 596 } 597 598 static class AIX { 599 public static boolean is() { return is; } 600 private static final boolean is = 601 System.getProperty("os.name").equals("AIX"); 602 } 603 604 static class Unix { 605 public static boolean is() { return is; } 606 private static final boolean is = 607 (! Windows.is() && 608 new File("/bin/sh").exists() && 609 new File("/bin/true").exists() && 610 new File("/bin/false").exists()); 611 } 612 613 static class UnicodeOS { 614 public static boolean is() { return is; } 615 private static final String osName = System.getProperty("os.name"); 616 private static final boolean is = 617 // MacOS X would probably also qualify 618 osName.startsWith("Windows") && 619 ! osName.startsWith("Windows 9") && 620 ! osName.equals("Windows Me"); 621 } 622 623 static class MacOSX { 624 public static boolean is() { return is; } 625 private static final String osName = System.getProperty("os.name"); 626 private static final boolean is = osName.contains("OS X"); 627 } 628 629 static class True { 630 public static int exitValue() { return 0; } 631 } 632 633 private static class False { 634 public static int exitValue() { return exitValue; } 635 private static final int exitValue = exitValue0(); 636 private static int exitValue0() { 637 // /bin/false returns an *unspecified* non-zero number. 638 try { 639 if (! Unix.is()) 640 return -1; 641 else { 642 int rc = new ProcessBuilder("/bin/false") 643 .start().waitFor(); 644 check(rc != 0); 645 return rc; 646 } 647 } catch (Throwable t) { unexpected(t); return -1; } 648 } 649 } 650 651 static class EnglishUnix { 652 private final static Boolean is = 653 (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL")); 654 655 private static boolean isEnglish(String envvar) { 656 String val = getenv(envvar); 657 return (val == null) || val.matches("en.*") || val.matches("C"); 658 } 659 660 /** Returns true if we can expect English OS error strings */ 661 static boolean is() { return is; } 662 } 663 664 static class DelegatingProcess extends Process { 665 final Process p; 666 667 DelegatingProcess(Process p) { 668 this.p = p; 669 } 670 671 @Override 672 public void destroy() { 673 p.destroy(); 674 } 675 676 @Override 677 public int exitValue() { 678 return p.exitValue(); 679 } 680 681 @Override 682 public int waitFor() throws InterruptedException { 683 return p.waitFor(); 684 } 685 686 @Override 687 public OutputStream getOutputStream() { 688 return p.getOutputStream(); 689 } 690 691 @Override 692 public InputStream getInputStream() { 693 return p.getInputStream(); 694 } 695 696 @Override 697 public InputStream getErrorStream() { 698 return p.getErrorStream(); 699 } 700 } 701 702 private static boolean matches(String str, String regex) { 703 return Pattern.compile(regex).matcher(str).find(); 704 } 705 706 private static String matchAndExtract(String str, String regex) { 707 Matcher matcher = Pattern.compile(regex).matcher(str); 708 if (matcher.find()) { 709 return matcher.group(); 710 } else { 711 return ""; 712 } 713 } 714 715 /* Only used for Mac OS X -- 716 * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty 717 * environment. The environment variable JAVA_MAIN_CLASS_<pid> may also 718 * be set in Mac OS X. 719 * Remove them both from the list of env variables 720 */ 721 private static String removeMacExpectedVars(String vars) { 722 // Check for __CF_USER_TEXT_ENCODING 723 String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING=" 724 +cfUserTextEncoding+",",""); 725 // Check for JAVA_MAIN_CLASS_<pid> 726 String javaMainClassStr 727 = matchAndExtract(cleanedVars, 728 "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,"); 729 return cleanedVars.replace(javaMainClassStr,""); 730 } 731 732 /* Only used for AIX -- 733 * AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment. 734 * Remove it from the list of env variables 735 */ 736 private static String removeAixExpectedVars(String vars) { 737 return vars.replace("AIXTHREAD_GUARDPAGES=0,",""); 738 } 739 740 private static String sortByLinesWindowsly(String text) { 741 String[] lines = text.split("\n"); 742 Arrays.sort(lines, new WindowsComparator()); 743 StringBuilder sb = new StringBuilder(); 744 for (String line : lines) 745 sb.append(line).append("\n"); 746 return sb.toString(); 747 } 748 749 private static void checkMapSanity(Map<String,String> map) { 750 try { 751 Set<String> keySet = map.keySet(); 752 Collection<String> values = map.values(); 753 Set<Map.Entry<String,String>> entrySet = map.entrySet(); 754 755 equal(entrySet.size(), keySet.size()); 756 equal(entrySet.size(), values.size()); 757 758 StringBuilder s1 = new StringBuilder(); 759 for (Map.Entry<String,String> e : entrySet) 760 s1.append(e.getKey() + "=" + e.getValue() + "\n"); 761 762 StringBuilder s2 = new StringBuilder(); 763 for (String var : keySet) 764 s2.append(var + "=" + map.get(var) + "\n"); 765 766 equal(s1.toString(), s2.toString()); 767 768 Iterator<String> kIter = keySet.iterator(); 769 Iterator<String> vIter = values.iterator(); 770 Iterator<Map.Entry<String,String>> eIter = entrySet.iterator(); 771 772 while (eIter.hasNext()) { 773 Map.Entry<String,String> entry = eIter.next(); 774 String key = kIter.next(); 775 String value = vIter.next(); 776 check(entrySet.contains(entry)); 777 check(keySet.contains(key)); 778 check(values.contains(value)); 779 check(map.containsKey(key)); 780 check(map.containsValue(value)); 781 equal(entry.getKey(), key); 782 equal(entry.getValue(), value); 783 } 784 check(! kIter.hasNext() && 785 ! vIter.hasNext()); 786 787 } catch (Throwable t) { unexpected(t); } 788 } 789 790 private static void checkMapEquality(Map<String,String> map1, 791 Map<String,String> map2) { 792 try { 793 equal(map1.size(), map2.size()); 794 equal(map1.isEmpty(), map2.isEmpty()); 795 for (String key : map1.keySet()) { 796 equal(map1.get(key), map2.get(key)); 797 check(map2.keySet().contains(key)); 798 } 799 equal(map1, map2); 800 equal(map2, map1); 801 equal(map1.entrySet(), map2.entrySet()); 802 equal(map2.entrySet(), map1.entrySet()); 803 equal(map1.keySet(), map2.keySet()); 804 equal(map2.keySet(), map1.keySet()); 805 806 equal(map1.hashCode(), map2.hashCode()); 807 equal(map1.entrySet().hashCode(), map2.entrySet().hashCode()); 808 equal(map1.keySet().hashCode(), map2.keySet().hashCode()); 809 } catch (Throwable t) { unexpected(t); } 810 } 811 812 static void checkRedirects(ProcessBuilder pb, 813 Redirect in, Redirect out, Redirect err) { 814 equal(pb.redirectInput(), in); 815 equal(pb.redirectOutput(), out); 816 equal(pb.redirectError(), err); 817 } 818 819 static void redirectIO(ProcessBuilder pb, 820 Redirect in, Redirect out, Redirect err) { 821 pb.redirectInput(in); 822 pb.redirectOutput(out); 823 pb.redirectError(err); 824 } 825 826 static void setFileContents(File file, String contents) { 827 try { 828 Writer w = new FileWriter(file); 829 w.write(contents); 830 w.close(); 831 } catch (Throwable t) { unexpected(t); } 832 } 833 834 static String fileContents(File file) { 835 try { 836 Reader r = new FileReader(file); 837 StringBuilder sb = new StringBuilder(); 838 char[] buffer = new char[1024]; 839 int n; 840 while ((n = r.read(buffer)) != -1) 841 sb.append(buffer,0,n); 842 r.close(); 843 return new String(sb); 844 } catch (Throwable t) { unexpected(t); return ""; } 845 } 846 847 static void testIORedirection() throws Throwable { 848 final File ifile = new File("ifile"); 849 final File ofile = new File("ofile"); 850 final File efile = new File("efile"); 851 ifile.delete(); 852 ofile.delete(); 853 efile.delete(); 854 855 //---------------------------------------------------------------- 856 // Check mutual inequality of different types of Redirect 857 //---------------------------------------------------------------- 858 Redirect[] redirects = 859 { PIPE, 860 INHERIT, 861 Redirect.from(ifile), 862 Redirect.to(ifile), 863 Redirect.appendTo(ifile), 864 Redirect.from(ofile), 865 Redirect.to(ofile), 866 Redirect.appendTo(ofile), 867 }; 868 for (int i = 0; i < redirects.length; i++) 869 for (int j = 0; j < redirects.length; j++) 870 equal(redirects[i].equals(redirects[j]), (i == j)); 871 872 //---------------------------------------------------------------- 873 // Check basic properties of different types of Redirect 874 //---------------------------------------------------------------- 875 equal(PIPE.type(), Redirect.Type.PIPE); 876 equal(PIPE.toString(), "PIPE"); 877 equal(PIPE.file(), null); 878 879 equal(INHERIT.type(), Redirect.Type.INHERIT); 880 equal(INHERIT.toString(), "INHERIT"); 881 equal(INHERIT.file(), null); 882 883 equal(Redirect.from(ifile).type(), Redirect.Type.READ); 884 equal(Redirect.from(ifile).toString(), 885 "redirect to read from file \"ifile\""); 886 equal(Redirect.from(ifile).file(), ifile); 887 equal(Redirect.from(ifile), 888 Redirect.from(ifile)); 889 equal(Redirect.from(ifile).hashCode(), 890 Redirect.from(ifile).hashCode()); 891 892 equal(Redirect.to(ofile).type(), Redirect.Type.WRITE); 893 equal(Redirect.to(ofile).toString(), 894 "redirect to write to file \"ofile\""); 895 equal(Redirect.to(ofile).file(), ofile); 896 equal(Redirect.to(ofile), 897 Redirect.to(ofile)); 898 equal(Redirect.to(ofile).hashCode(), 899 Redirect.to(ofile).hashCode()); 900 901 equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND); 902 equal(Redirect.appendTo(efile).toString(), 903 "redirect to append to file \"efile\""); 904 equal(Redirect.appendTo(efile).file(), efile); 905 equal(Redirect.appendTo(efile), 906 Redirect.appendTo(efile)); 907 equal(Redirect.appendTo(efile).hashCode(), 908 Redirect.appendTo(efile).hashCode()); 909 910 //---------------------------------------------------------------- 911 // Check initial values of redirects 912 //---------------------------------------------------------------- 913 List<String> childArgs = new ArrayList<String>(javaChildArgs); 914 childArgs.add("testIO"); 915 final ProcessBuilder pb = new ProcessBuilder(childArgs); 916 checkRedirects(pb, PIPE, PIPE, PIPE); 917 918 //---------------------------------------------------------------- 919 // Check inheritIO 920 //---------------------------------------------------------------- 921 pb.inheritIO(); 922 checkRedirects(pb, INHERIT, INHERIT, INHERIT); 923 924 //---------------------------------------------------------------- 925 // Check setters and getters agree 926 //---------------------------------------------------------------- 927 pb.redirectInput(ifile); 928 equal(pb.redirectInput().file(), ifile); 929 equal(pb.redirectInput(), Redirect.from(ifile)); 930 931 pb.redirectOutput(ofile); 932 equal(pb.redirectOutput().file(), ofile); 933 equal(pb.redirectOutput(), Redirect.to(ofile)); 934 935 pb.redirectError(efile); 936 equal(pb.redirectError().file(), efile); 937 equal(pb.redirectError(), Redirect.to(efile)); 938 939 THROWS(IllegalArgumentException.class, 940 () -> pb.redirectInput(Redirect.to(ofile)), 941 () -> pb.redirectOutput(Redirect.from(ifile)), 942 () -> pb.redirectError(Redirect.from(ifile))); 943 944 THROWS(IOException.class, 945 // Input file does not exist 946 () -> pb.start()); 947 setFileContents(ifile, "standard input"); 948 949 //---------------------------------------------------------------- 950 // Writing to non-existent files 951 //---------------------------------------------------------------- 952 { 953 ProcessResults r = run(pb); 954 equal(r.exitValue(), 0); 955 equal(fileContents(ofile), "standard output"); 956 equal(fileContents(efile), "standard error"); 957 equal(r.out(), ""); 958 equal(r.err(), ""); 959 ofile.delete(); 960 efile.delete(); 961 } 962 963 //---------------------------------------------------------------- 964 // Both redirectErrorStream + redirectError 965 //---------------------------------------------------------------- 966 { 967 pb.redirectErrorStream(true); 968 ProcessResults r = run(pb); 969 equal(r.exitValue(), 0); 970 equal(fileContents(ofile), 971 "standard error" + "standard output"); 972 equal(fileContents(efile), ""); 973 equal(r.out(), ""); 974 equal(r.err(), ""); 975 ofile.delete(); 976 efile.delete(); 977 } 978 979 //---------------------------------------------------------------- 980 // Appending to existing files 981 //---------------------------------------------------------------- 982 { 983 setFileContents(ofile, "ofile-contents"); 984 setFileContents(efile, "efile-contents"); 985 pb.redirectOutput(Redirect.appendTo(ofile)); 986 pb.redirectError(Redirect.appendTo(efile)); 987 pb.redirectErrorStream(false); 988 ProcessResults r = run(pb); 989 equal(r.exitValue(), 0); 990 equal(fileContents(ofile), 991 "ofile-contents" + "standard output"); 992 equal(fileContents(efile), 993 "efile-contents" + "standard error"); 994 equal(r.out(), ""); 995 equal(r.err(), ""); 996 ofile.delete(); 997 efile.delete(); 998 } 999 1000 //---------------------------------------------------------------- 1001 // Replacing existing files 1002 //---------------------------------------------------------------- 1003 { 1004 setFileContents(ofile, "ofile-contents"); 1005 setFileContents(efile, "efile-contents"); 1006 pb.redirectOutput(ofile); 1007 pb.redirectError(Redirect.to(efile)); 1008 ProcessResults r = run(pb); 1009 equal(r.exitValue(), 0); 1010 equal(fileContents(ofile), "standard output"); 1011 equal(fileContents(efile), "standard error"); 1012 equal(r.out(), ""); 1013 equal(r.err(), ""); 1014 ofile.delete(); 1015 efile.delete(); 1016 } 1017 1018 //---------------------------------------------------------------- 1019 // Appending twice to the same file? 1020 //---------------------------------------------------------------- 1021 { 1022 setFileContents(ofile, "ofile-contents"); 1023 setFileContents(efile, "efile-contents"); 1024 Redirect appender = Redirect.appendTo(ofile); 1025 pb.redirectOutput(appender); 1026 pb.redirectError(appender); 1027 ProcessResults r = run(pb); 1028 equal(r.exitValue(), 0); 1029 equal(fileContents(ofile), 1030 "ofile-contents" + 1031 "standard error" + 1032 "standard output"); 1033 equal(fileContents(efile), "efile-contents"); 1034 equal(r.out(), ""); 1035 equal(r.err(), ""); 1036 ifile.delete(); 1037 ofile.delete(); 1038 efile.delete(); 1039 } 1040 1041 //---------------------------------------------------------------- 1042 // Testing INHERIT is harder. 1043 // Note that this requires __FOUR__ nested JVMs involved in one test, 1044 // if you count the harness JVM. 1045 //---------------------------------------------------------------- 1046 for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) { 1047 redirectIO(pb, PIPE, PIPE, PIPE); 1048 List<String> command = pb.command(); 1049 command.set(command.size() - 1, testName); 1050 Process p = pb.start(); 1051 new PrintStream(p.getOutputStream()).print("standard input"); 1052 p.getOutputStream().close(); 1053 ProcessResults r = run(p); 1054 equal(r.exitValue(), 0); 1055 equal(r.out(), "standard output"); 1056 equal(r.err(), "standard error"); 1057 } 1058 1059 //---------------------------------------------------------------- 1060 // Test security implications of I/O redirection 1061 //---------------------------------------------------------------- 1062 1063 // Read access to current directory is always granted; 1064 // So create a tmpfile for input instead. 1065 final File tmpFile = File.createTempFile("Basic", "tmp"); 1066 setFileContents(tmpFile, "standard input"); 1067 1068 final Policy policy = new Policy(); 1069 Policy.setPolicy(policy); 1070 System.setSecurityManager(new SecurityManager()); 1071 try { 1072 final Permission xPermission 1073 = new FilePermission("<<ALL FILES>>", "execute"); 1074 final Permission rxPermission 1075 = new FilePermission("<<ALL FILES>>", "read,execute"); 1076 final Permission wxPermission 1077 = new FilePermission("<<ALL FILES>>", "write,execute"); 1078 final Permission rwxPermission 1079 = new FilePermission("<<ALL FILES>>", "read,write,execute"); 1080 1081 THROWS(SecurityException.class, 1082 () -> { policy.setPermissions(xPermission); 1083 redirectIO(pb, from(tmpFile), PIPE, PIPE); 1084 pb.start();}, 1085 () -> { policy.setPermissions(rxPermission); 1086 redirectIO(pb, PIPE, to(ofile), PIPE); 1087 pb.start();}, 1088 () -> { policy.setPermissions(rxPermission); 1089 redirectIO(pb, PIPE, PIPE, to(efile)); 1090 pb.start();}); 1091 1092 { 1093 policy.setPermissions(rxPermission); 1094 redirectIO(pb, from(tmpFile), PIPE, PIPE); 1095 ProcessResults r = run(pb); 1096 equal(r.out(), "standard output"); 1097 equal(r.err(), "standard error"); 1098 } 1099 1100 { 1101 policy.setPermissions(wxPermission); 1102 redirectIO(pb, PIPE, to(ofile), to(efile)); 1103 Process p = pb.start(); 1104 new PrintStream(p.getOutputStream()).print("standard input"); 1105 p.getOutputStream().close(); 1106 ProcessResults r = run(p); 1107 policy.setPermissions(rwxPermission); 1108 equal(fileContents(ofile), "standard output"); 1109 equal(fileContents(efile), "standard error"); 1110 } 1111 1112 { 1113 policy.setPermissions(rwxPermission); 1114 redirectIO(pb, from(tmpFile), to(ofile), to(efile)); 1115 ProcessResults r = run(pb); 1116 policy.setPermissions(rwxPermission); 1117 equal(fileContents(ofile), "standard output"); 1118 equal(fileContents(efile), "standard error"); 1119 } 1120 1121 } finally { 1122 policy.setPermissions(new RuntimePermission("setSecurityManager")); 1123 System.setSecurityManager(null); 1124 tmpFile.delete(); 1125 ifile.delete(); 1126 ofile.delete(); 1127 efile.delete(); 1128 } 1129 } 1130 1131 static void checkProcessPid() { 1132 long actualPid = 0; 1133 long expectedPid = -1; 1134 if (Windows.is()) { 1135 String[] argsTasklist = {"tasklist.exe", "/NH", "/FI", "\"IMAGENAME eq tasklist.exe\""}; 1136 ProcessBuilder pb = new ProcessBuilder(argsTasklist); 1137 try { 1138 Process proc = pb.start(); 1139 expectedPid = proc.getPid(); 1140 1141 String output = commandOutput(proc); 1142 String[] splits = output.split("\\s+"); 1143 actualPid = Integer.valueOf(splits[2]); 1144 } catch (Throwable t) { 1145 unexpected(t); 1146 } 1147 } else if (Unix.is() || MacOSX.is()) { 1148 String[] shArgs = {"sh", "-c", "echo $$"}; 1149 ProcessBuilder pb = new ProcessBuilder(shArgs); 1150 try { 1151 Process proc = pb.start(); 1152 expectedPid = proc.getPid(); 1153 1154 String output = commandOutput(proc); 1155 String[] splits = output.split("\\s+"); 1156 actualPid = Integer.valueOf(splits[0]); 1157 } catch (Throwable t) { 1158 unexpected(t); 1159 } 1160 } else { 1161 fail("No test for checkProcessPid on platform: " + System.getProperty("os.name")); 1162 return; 1163 } 1164 1165 equal(actualPid, expectedPid); 1166 1167 // Test the default implementation of Process.getPid 1168 try { 1169 DelegatingProcess p = new DelegatingProcess(null); 1170 p.getPid(); 1171 fail("non-overridden Process.getPid method should throw UOE"); 1172 } catch (UnsupportedOperationException uoe) { 1173 // correct 1174 } 1175 1176 } 1177 1178 private static void realMain(String[] args) throws Throwable { 1179 if (Windows.is()) 1180 System.out.println("This appears to be a Windows system."); 1181 if (Unix.is()) 1182 System.out.println("This appears to be a Unix system."); 1183 if (UnicodeOS.is()) 1184 System.out.println("This appears to be a Unicode-based OS."); 1185 1186 try { testIORedirection(); } 1187 catch (Throwable t) { unexpected(t); } 1188 1189 //---------------------------------------------------------------- 1190 // Basic tests for getPid() 1191 //---------------------------------------------------------------- 1192 checkProcessPid(); 1193 1194 //---------------------------------------------------------------- 1195 // Basic tests for setting, replacing and deleting envvars 1196 //---------------------------------------------------------------- 1197 try { 1198 ProcessBuilder pb = new ProcessBuilder(); 1199 Map<String,String> environ = pb.environment(); 1200 1201 // New env var 1202 environ.put("QUUX", "BAR"); 1203 equal(environ.get("QUUX"), "BAR"); 1204 equal(getenvInChild(pb,"QUUX"), "BAR"); 1205 1206 // Modify env var 1207 environ.put("QUUX","bear"); 1208 equal(environ.get("QUUX"), "bear"); 1209 equal(getenvInChild(pb,"QUUX"), "bear"); 1210 checkMapSanity(environ); 1211 1212 // Remove env var 1213 environ.remove("QUUX"); 1214 equal(environ.get("QUUX"), null); 1215 equal(getenvInChild(pb,"QUUX"), "null"); 1216 checkMapSanity(environ); 1217 1218 // Remove non-existent env var 1219 environ.remove("QUUX"); 1220 equal(environ.get("QUUX"), null); 1221 equal(getenvInChild(pb,"QUUX"), "null"); 1222 checkMapSanity(environ); 1223 } catch (Throwable t) { unexpected(t); } 1224 1225 //---------------------------------------------------------------- 1226 // Pass Empty environment to child 1227 //---------------------------------------------------------------- 1228 try { 1229 ProcessBuilder pb = new ProcessBuilder(); 1230 pb.environment().clear(); 1231 String expected = Windows.is() ? "SystemRoot="+systemRoot+",": ""; 1232 expected = AIX.is() ? "LIBPATH="+libpath+",": expected; 1233 if (Windows.is()) { 1234 pb.environment().put("SystemRoot", systemRoot); 1235 } 1236 if (AIX.is()) { 1237 pb.environment().put("LIBPATH", libpath); 1238 } 1239 String result = getenvInChild(pb); 1240 if (MacOSX.is()) { 1241 result = removeMacExpectedVars(result); 1242 } 1243 if (AIX.is()) { 1244 result = removeAixExpectedVars(result); 1245 } 1246 equal(result, expected); 1247 } catch (Throwable t) { unexpected(t); } 1248 1249 //---------------------------------------------------------------- 1250 // System.getenv() is read-only. 1251 //---------------------------------------------------------------- 1252 THROWS(UnsupportedOperationException.class, 1253 () -> getenv().put("FOO","BAR"), 1254 () -> getenv().remove("PATH"), 1255 () -> getenv().keySet().remove("PATH"), 1256 () -> getenv().values().remove("someValue")); 1257 1258 try { 1259 Collection<Map.Entry<String,String>> c = getenv().entrySet(); 1260 if (! c.isEmpty()) 1261 try { 1262 c.iterator().next().setValue("foo"); 1263 fail("Expected UnsupportedOperationException not thrown"); 1264 } catch (UnsupportedOperationException e) {} // OK 1265 } catch (Throwable t) { unexpected(t); } 1266 1267 //---------------------------------------------------------------- 1268 // System.getenv() always returns the same object in our implementation. 1269 //---------------------------------------------------------------- 1270 try { 1271 check(System.getenv() == System.getenv()); 1272 } catch (Throwable t) { unexpected(t); } 1273 1274 //---------------------------------------------------------------- 1275 // You can't create an env var name containing "=", 1276 // or an env var name or value containing NUL. 1277 //---------------------------------------------------------------- 1278 { 1279 final Map<String,String> m = new ProcessBuilder().environment(); 1280 THROWS(IllegalArgumentException.class, 1281 () -> m.put("FOO=","BAR"), 1282 () -> m.put("FOO\u0000","BAR"), 1283 () -> m.put("FOO","BAR\u0000")); 1284 } 1285 1286 //---------------------------------------------------------------- 1287 // Commands must never be null. 1288 //---------------------------------------------------------------- 1289 THROWS(NullPointerException.class, 1290 () -> new ProcessBuilder((List<String>)null), 1291 () -> new ProcessBuilder().command((List<String>)null)); 1292 1293 //---------------------------------------------------------------- 1294 // Put in a command; get the same one back out. 1295 //---------------------------------------------------------------- 1296 try { 1297 List<String> command = new ArrayList<String>(); 1298 ProcessBuilder pb = new ProcessBuilder(command); 1299 check(pb.command() == command); 1300 List<String> command2 = new ArrayList<String>(2); 1301 command2.add("foo"); 1302 command2.add("bar"); 1303 pb.command(command2); 1304 check(pb.command() == command2); 1305 pb.command("foo", "bar"); 1306 check(pb.command() != command2 && pb.command().equals(command2)); 1307 pb.command(command2); 1308 command2.add("baz"); 1309 equal(pb.command().get(2), "baz"); 1310 } catch (Throwable t) { unexpected(t); } 1311 1312 //---------------------------------------------------------------- 1313 // Commands must contain at least one element. 1314 //---------------------------------------------------------------- 1315 THROWS(IndexOutOfBoundsException.class, 1316 () -> new ProcessBuilder().start(), 1317 () -> new ProcessBuilder(new ArrayList<String>()).start(), 1318 () -> Runtime.getRuntime().exec(new String[]{})); 1319 1320 //---------------------------------------------------------------- 1321 // Commands must not contain null elements at start() time. 1322 //---------------------------------------------------------------- 1323 THROWS(NullPointerException.class, 1324 () -> new ProcessBuilder("foo",null,"bar").start(), 1325 () -> new ProcessBuilder((String)null).start(), 1326 () -> new ProcessBuilder(new String[]{null}).start(), 1327 () -> new ProcessBuilder(new String[]{"foo",null,"bar"}).start()); 1328 1329 //---------------------------------------------------------------- 1330 // Command lists are growable. 1331 //---------------------------------------------------------------- 1332 try { 1333 new ProcessBuilder().command().add("foo"); 1334 new ProcessBuilder("bar").command().add("foo"); 1335 new ProcessBuilder(new String[]{"1","2"}).command().add("3"); 1336 } catch (Throwable t) { unexpected(t); } 1337 1338 //---------------------------------------------------------------- 1339 // Nulls in environment updates generate NullPointerException 1340 //---------------------------------------------------------------- 1341 try { 1342 final Map<String,String> env = new ProcessBuilder().environment(); 1343 THROWS(NullPointerException.class, 1344 () -> env.put("foo",null), 1345 () -> env.put(null,"foo"), 1346 () -> env.remove(null), 1347 () -> { for (Map.Entry<String,String> e : env.entrySet()) 1348 e.setValue(null);}, 1349 () -> Runtime.getRuntime().exec(new String[]{"foo"}, 1350 new String[]{null})); 1351 } catch (Throwable t) { unexpected(t); } 1352 1353 //---------------------------------------------------------------- 1354 // Non-String types in environment updates generate ClassCastException 1355 //---------------------------------------------------------------- 1356 try { 1357 final Map<String,String> env = new ProcessBuilder().environment(); 1358 THROWS(ClassCastException.class, 1359 () -> env.remove(TRUE), 1360 () -> env.keySet().remove(TRUE), 1361 () -> env.values().remove(TRUE), 1362 () -> env.entrySet().remove(TRUE)); 1363 } catch (Throwable t) { unexpected(t); } 1364 1365 //---------------------------------------------------------------- 1366 // Check query operations on environment maps 1367 //---------------------------------------------------------------- 1368 try { 1369 List<Map<String,String>> envs = 1370 new ArrayList<Map<String,String>>(2); 1371 envs.add(System.getenv()); 1372 envs.add(new ProcessBuilder().environment()); 1373 for (final Map<String,String> env : envs) { 1374 //---------------------------------------------------------------- 1375 // Nulls in environment queries are forbidden. 1376 //---------------------------------------------------------------- 1377 THROWS(NullPointerException.class, 1378 () -> getenv(null), 1379 () -> env.get(null), 1380 () -> env.containsKey(null), 1381 () -> env.containsValue(null), 1382 () -> env.keySet().contains(null), 1383 () -> env.values().contains(null)); 1384 1385 //---------------------------------------------------------------- 1386 // Non-String types in environment queries are forbidden. 1387 //---------------------------------------------------------------- 1388 THROWS(ClassCastException.class, 1389 () -> env.get(TRUE), 1390 () -> env.containsKey(TRUE), 1391 () -> env.containsValue(TRUE), 1392 () -> env.keySet().contains(TRUE), 1393 () -> env.values().contains(TRUE)); 1394 1395 //---------------------------------------------------------------- 1396 // Illegal String values in environment queries are (grumble) OK 1397 //---------------------------------------------------------------- 1398 equal(env.get("\u0000"), null); 1399 check(! env.containsKey("\u0000")); 1400 check(! env.containsValue("\u0000")); 1401 check(! env.keySet().contains("\u0000")); 1402 check(! env.values().contains("\u0000")); 1403 } 1404 1405 } catch (Throwable t) { unexpected(t); } 1406 1407 try { 1408 final Set<Map.Entry<String,String>> entrySet = 1409 new ProcessBuilder().environment().entrySet(); 1410 THROWS(NullPointerException.class, 1411 () -> entrySet.contains(null)); 1412 THROWS(ClassCastException.class, 1413 () -> entrySet.contains(TRUE), 1414 () -> entrySet.contains( 1415 new SimpleImmutableEntry<Boolean,String>(TRUE,""))); 1416 1417 check(! entrySet.contains 1418 (new SimpleImmutableEntry<String,String>("", ""))); 1419 } catch (Throwable t) { unexpected(t); } 1420 1421 //---------------------------------------------------------------- 1422 // Put in a directory; get the same one back out. 1423 //---------------------------------------------------------------- 1424 try { 1425 ProcessBuilder pb = new ProcessBuilder(); 1426 File foo = new File("foo"); 1427 equal(pb.directory(), null); 1428 equal(pb.directory(foo).directory(), foo); 1429 equal(pb.directory(null).directory(), null); 1430 } catch (Throwable t) { unexpected(t); } 1431 1432 //---------------------------------------------------------------- 1433 // If round-trip conversion works, check envvar pass-through to child 1434 //---------------------------------------------------------------- 1435 try { 1436 testEncoding("ASCII", "xyzzy"); 1437 testEncoding("Latin1", "\u00f1\u00e1"); 1438 testEncoding("Unicode", "\u22f1\u11e1"); 1439 } catch (Throwable t) { unexpected(t); } 1440 1441 //---------------------------------------------------------------- 1442 // A surprisingly large number of ways to delete an environment var. 1443 //---------------------------------------------------------------- 1444 testVariableDeleter(new EnvironmentFrobber() { 1445 public void doIt(Map<String,String> environ) { 1446 environ.remove("Foo");}}); 1447 1448 testVariableDeleter(new EnvironmentFrobber() { 1449 public void doIt(Map<String,String> environ) { 1450 environ.keySet().remove("Foo");}}); 1451 1452 testVariableDeleter(new EnvironmentFrobber() { 1453 public void doIt(Map<String,String> environ) { 1454 environ.values().remove("BAAR");}}); 1455 1456 testVariableDeleter(new EnvironmentFrobber() { 1457 public void doIt(Map<String,String> environ) { 1458 // Legally fabricate a ProcessEnvironment.StringEntry, 1459 // even though it's private. 1460 Map<String,String> environ2 1461 = new ProcessBuilder().environment(); 1462 environ2.clear(); 1463 environ2.put("Foo","BAAR"); 1464 // Subtlety alert. 1465 Map.Entry<String,String> e 1466 = environ2.entrySet().iterator().next(); 1467 environ.entrySet().remove(e);}}); 1468 1469 testVariableDeleter(new EnvironmentFrobber() { 1470 public void doIt(Map<String,String> environ) { 1471 Map.Entry<String,String> victim = null; 1472 for (Map.Entry<String,String> e : environ.entrySet()) 1473 if (e.getKey().equals("Foo")) 1474 victim = e; 1475 if (victim != null) 1476 environ.entrySet().remove(victim);}}); 1477 1478 testVariableDeleter(new EnvironmentFrobber() { 1479 public void doIt(Map<String,String> environ) { 1480 Iterator<String> it = environ.keySet().iterator(); 1481 while (it.hasNext()) { 1482 String val = it.next(); 1483 if (val.equals("Foo")) 1484 it.remove();}}}); 1485 1486 testVariableDeleter(new EnvironmentFrobber() { 1487 public void doIt(Map<String,String> environ) { 1488 Iterator<Map.Entry<String,String>> it 1489 = environ.entrySet().iterator(); 1490 while (it.hasNext()) { 1491 Map.Entry<String,String> e = it.next(); 1492 if (e.getKey().equals("Foo")) 1493 it.remove();}}}); 1494 1495 testVariableDeleter(new EnvironmentFrobber() { 1496 public void doIt(Map<String,String> environ) { 1497 Iterator<String> it = environ.values().iterator(); 1498 while (it.hasNext()) { 1499 String val = it.next(); 1500 if (val.equals("BAAR")) 1501 it.remove();}}}); 1502 1503 //---------------------------------------------------------------- 1504 // A surprisingly small number of ways to add an environment var. 1505 //---------------------------------------------------------------- 1506 testVariableAdder(new EnvironmentFrobber() { 1507 public void doIt(Map<String,String> environ) { 1508 environ.put("Foo","Bahrein");}}); 1509 1510 //---------------------------------------------------------------- 1511 // A few ways to modify an environment var. 1512 //---------------------------------------------------------------- 1513 testVariableModifier(new EnvironmentFrobber() { 1514 public void doIt(Map<String,String> environ) { 1515 environ.put("Foo","NewValue");}}); 1516 1517 testVariableModifier(new EnvironmentFrobber() { 1518 public void doIt(Map<String,String> environ) { 1519 for (Map.Entry<String,String> e : environ.entrySet()) 1520 if (e.getKey().equals("Foo")) 1521 e.setValue("NewValue");}}); 1522 1523 //---------------------------------------------------------------- 1524 // Fiddle with environment sizes 1525 //---------------------------------------------------------------- 1526 try { 1527 Map<String,String> environ = new ProcessBuilder().environment(); 1528 int size = environ.size(); 1529 checkSizes(environ, size); 1530 1531 environ.put("UnLiKeLYeNVIROmtNam", "someVal"); 1532 checkSizes(environ, size+1); 1533 1534 // Check for environment independence 1535 new ProcessBuilder().environment().clear(); 1536 1537 environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal"); 1538 checkSizes(environ, size+1); 1539 1540 environ.remove("UnLiKeLYeNVIROmtNam"); 1541 checkSizes(environ, size); 1542 1543 environ.clear(); 1544 checkSizes(environ, 0); 1545 1546 environ.clear(); 1547 checkSizes(environ, 0); 1548 1549 environ = new ProcessBuilder().environment(); 1550 environ.keySet().clear(); 1551 checkSizes(environ, 0); 1552 1553 environ = new ProcessBuilder().environment(); 1554 environ.entrySet().clear(); 1555 checkSizes(environ, 0); 1556 1557 environ = new ProcessBuilder().environment(); 1558 environ.values().clear(); 1559 checkSizes(environ, 0); 1560 } catch (Throwable t) { unexpected(t); } 1561 1562 //---------------------------------------------------------------- 1563 // Check that various map invariants hold 1564 //---------------------------------------------------------------- 1565 checkMapSanity(new ProcessBuilder().environment()); 1566 checkMapSanity(System.getenv()); 1567 checkMapEquality(new ProcessBuilder().environment(), 1568 new ProcessBuilder().environment()); 1569 1570 1571 //---------------------------------------------------------------- 1572 // Check effects on external "env" command. 1573 //---------------------------------------------------------------- 1574 try { 1575 Set<String> env1 = new HashSet<String> 1576 (Arrays.asList(nativeEnv((String[])null).split("\n"))); 1577 1578 ProcessBuilder pb = new ProcessBuilder(); 1579 pb.environment().put("QwErTyUiOp","AsDfGhJk"); 1580 1581 Set<String> env2 = new HashSet<String> 1582 (Arrays.asList(nativeEnv(pb).split("\n"))); 1583 1584 check(env2.size() == env1.size() + 1); 1585 env1.add("QwErTyUiOp=AsDfGhJk"); 1586 check(env1.equals(env2)); 1587 } catch (Throwable t) { unexpected(t); } 1588 1589 //---------------------------------------------------------------- 1590 // Test Runtime.exec(...envp...) 1591 // Check for sort order of environment variables on Windows. 1592 //---------------------------------------------------------------- 1593 try { 1594 String systemRoot = "SystemRoot=" + System.getenv("SystemRoot"); 1595 // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~' 1596 String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=", 1597 "+=+", "_=_", "~=~", systemRoot}; 1598 String output = nativeEnv(envp); 1599 String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n"; 1600 // On Windows, Java must keep the environment sorted. 1601 // Order is random on Unix, so this test does the sort. 1602 if (! Windows.is()) 1603 output = sortByLinesWindowsly(output); 1604 equal(output, expected); 1605 } catch (Throwable t) { unexpected(t); } 1606 1607 //---------------------------------------------------------------- 1608 // Test Runtime.exec(...envp...) 1609 // and check SystemRoot gets set automatically on Windows 1610 //---------------------------------------------------------------- 1611 try { 1612 if (Windows.is()) { 1613 String systemRoot = "SystemRoot=" + System.getenv("SystemRoot"); 1614 String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=", 1615 "+=+", "_=_", "~=~"}; 1616 String output = nativeEnv(envp); 1617 String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n"; 1618 equal(output, expected); 1619 } 1620 } catch (Throwable t) { unexpected(t); } 1621 1622 //---------------------------------------------------------------- 1623 // System.getenv() must be consistent with System.getenv(String) 1624 //---------------------------------------------------------------- 1625 try { 1626 for (Map.Entry<String,String> e : getenv().entrySet()) 1627 equal(getenv(e.getKey()), e.getValue()); 1628 } catch (Throwable t) { unexpected(t); } 1629 1630 //---------------------------------------------------------------- 1631 // Fiddle with working directory in child 1632 //---------------------------------------------------------------- 1633 try { 1634 String canonicalUserDir = 1635 new File(System.getProperty("user.dir")).getCanonicalPath(); 1636 String[] sdirs = new String[] 1637 {".", "..", "/", "/bin", 1638 "C:", "c:", "C:/", "c:\\", "\\", "\\bin", 1639 "c:\\windows ", "c:\\Program Files", "c:\\Program Files\\" }; 1640 for (String sdir : sdirs) { 1641 File dir = new File(sdir); 1642 if (! (dir.isDirectory() && dir.exists())) 1643 continue; 1644 out.println("Testing directory " + dir); 1645 //dir = new File(dir.getCanonicalPath()); 1646 1647 ProcessBuilder pb = new ProcessBuilder(); 1648 equal(pb.directory(), null); 1649 equal(pwdInChild(pb), canonicalUserDir); 1650 1651 pb.directory(dir); 1652 equal(pb.directory(), dir); 1653 equal(pwdInChild(pb), dir.getCanonicalPath()); 1654 1655 pb.directory(null); 1656 equal(pb.directory(), null); 1657 equal(pwdInChild(pb), canonicalUserDir); 1658 1659 pb.directory(dir); 1660 } 1661 } catch (Throwable t) { unexpected(t); } 1662 1663 //---------------------------------------------------------------- 1664 // Working directory with Unicode in child 1665 //---------------------------------------------------------------- 1666 try { 1667 if (UnicodeOS.is()) { 1668 File dir = new File(System.getProperty("test.dir", "."), 1669 "ProcessBuilderDir\u4e00\u4e02"); 1670 try { 1671 if (!dir.exists()) 1672 dir.mkdir(); 1673 out.println("Testing Unicode directory:" + dir); 1674 ProcessBuilder pb = new ProcessBuilder(); 1675 pb.directory(dir); 1676 equal(pwdInChild(pb), dir.getCanonicalPath()); 1677 } finally { 1678 if (dir.exists()) 1679 dir.delete(); 1680 } 1681 } 1682 } catch (Throwable t) { unexpected(t); } 1683 1684 //---------------------------------------------------------------- 1685 // OOME in child allocating maximally sized array 1686 // Test for hotspot/jvmti bug 6850957 1687 //---------------------------------------------------------------- 1688 try { 1689 List<String> list = new ArrayList<String>(javaChildArgs); 1690 list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version", 1691 javaExe)); 1692 list.add("ArrayOOME"); 1693 ProcessResults r = run(new ProcessBuilder(list)); 1694 check(r.err().contains("java.lang.OutOfMemoryError:")); 1695 check(r.err().contains(javaExe)); 1696 check(r.err().contains(System.getProperty("java.version"))); 1697 equal(r.exitValue(), 1); 1698 } catch (Throwable t) { unexpected(t); } 1699 1700 //---------------------------------------------------------------- 1701 // Windows has tricky semi-case-insensitive semantics 1702 //---------------------------------------------------------------- 1703 if (Windows.is()) 1704 try { 1705 out.println("Running case insensitve variable tests"); 1706 for (String[] namePair : 1707 new String[][] 1708 { new String[]{"PATH","PaTh"}, 1709 new String[]{"home","HOME"}, 1710 new String[]{"SYSTEMROOT","SystemRoot"}}) { 1711 check((getenv(namePair[0]) == null && 1712 getenv(namePair[1]) == null) 1713 || 1714 getenv(namePair[0]).equals(getenv(namePair[1])), 1715 "Windows environment variables are not case insensitive"); 1716 } 1717 } catch (Throwable t) { unexpected(t); } 1718 1719 //---------------------------------------------------------------- 1720 // Test proper Unicode child environment transfer 1721 //---------------------------------------------------------------- 1722 if (UnicodeOS.is()) 1723 try { 1724 ProcessBuilder pb = new ProcessBuilder(); 1725 pb.environment().put("\u1234","\u5678"); 1726 pb.environment().remove("PATH"); 1727 equal(getenvInChild1234(pb), "\u5678"); 1728 } catch (Throwable t) { unexpected(t); } 1729 1730 1731 //---------------------------------------------------------------- 1732 // Test Runtime.exec(...envp...) with envstrings with initial `=' 1733 //---------------------------------------------------------------- 1734 try { 1735 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1736 childArgs.add("System.getenv()"); 1737 String[] cmdp = childArgs.toArray(new String[childArgs.size()]); 1738 String[] envp; 1739 String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot}; 1740 String[] envpOth = {"=ExitValue=3", "=C:=\\"}; 1741 if (Windows.is()) { 1742 envp = envpWin; 1743 } else { 1744 envp = envpOth; 1745 } 1746 Process p = Runtime.getRuntime().exec(cmdp, envp); 1747 String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,"; 1748 expected = AIX.is() ? expected + "LIBPATH="+libpath+",": expected; 1749 String commandOutput = commandOutput(p); 1750 if (MacOSX.is()) { 1751 commandOutput = removeMacExpectedVars(commandOutput); 1752 } 1753 if (AIX.is()) { 1754 commandOutput = removeAixExpectedVars(commandOutput); 1755 } 1756 equal(commandOutput, expected); 1757 if (Windows.is()) { 1758 ProcessBuilder pb = new ProcessBuilder(childArgs); 1759 pb.environment().clear(); 1760 pb.environment().put("SystemRoot", systemRoot); 1761 pb.environment().put("=ExitValue", "3"); 1762 pb.environment().put("=C:", "\\"); 1763 equal(commandOutput(pb), expected); 1764 } 1765 } catch (Throwable t) { unexpected(t); } 1766 1767 //---------------------------------------------------------------- 1768 // Test Runtime.exec(...envp...) with envstrings without any `=' 1769 //---------------------------------------------------------------- 1770 try { 1771 String[] cmdp = {"echo"}; 1772 String[] envp = {"Hello", "World"}; // Yuck! 1773 Process p = Runtime.getRuntime().exec(cmdp, envp); 1774 equal(commandOutput(p), "\n"); 1775 } catch (Throwable t) { unexpected(t); } 1776 1777 //---------------------------------------------------------------- 1778 // Test Runtime.exec(...envp...) with envstrings containing NULs 1779 //---------------------------------------------------------------- 1780 try { 1781 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1782 childArgs.add("System.getenv()"); 1783 String[] cmdp = childArgs.toArray(new String[childArgs.size()]); 1784 String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck! 1785 "FO\u0000=B\u0000R"}; 1786 String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck! 1787 "FO\u0000=B\u0000R"}; 1788 String[] envp; 1789 if (Windows.is()) { 1790 envp = envpWin; 1791 } else { 1792 envp = envpOth; 1793 } 1794 System.out.println ("cmdp"); 1795 for (int i=0; i<cmdp.length; i++) { 1796 System.out.printf ("cmdp %d: %s\n", i, cmdp[i]); 1797 } 1798 System.out.println ("envp"); 1799 for (int i=0; i<envp.length; i++) { 1800 System.out.printf ("envp %d: %s\n", i, envp[i]); 1801 } 1802 Process p = Runtime.getRuntime().exec(cmdp, envp); 1803 String commandOutput = commandOutput(p); 1804 if (MacOSX.is()) { 1805 commandOutput = removeMacExpectedVars(commandOutput); 1806 } 1807 if (AIX.is()) { 1808 commandOutput = removeAixExpectedVars(commandOutput); 1809 } 1810 check(commandOutput.equals(Windows.is() 1811 ? "LC_ALL=C,SystemRoot="+systemRoot+"," 1812 : AIX.is() 1813 ? "LC_ALL=C,LIBPATH="+libpath+"," 1814 : "LC_ALL=C,"), 1815 "Incorrect handling of envstrings containing NULs"); 1816 } catch (Throwable t) { unexpected(t); } 1817 1818 //---------------------------------------------------------------- 1819 // Test the redirectErrorStream property 1820 //---------------------------------------------------------------- 1821 try { 1822 ProcessBuilder pb = new ProcessBuilder(); 1823 equal(pb.redirectErrorStream(), false); 1824 equal(pb.redirectErrorStream(true), pb); 1825 equal(pb.redirectErrorStream(), true); 1826 equal(pb.redirectErrorStream(false), pb); 1827 equal(pb.redirectErrorStream(), false); 1828 } catch (Throwable t) { unexpected(t); } 1829 1830 try { 1831 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1832 childArgs.add("OutErr"); 1833 ProcessBuilder pb = new ProcessBuilder(childArgs); 1834 { 1835 ProcessResults r = run(pb); 1836 equal(r.out(), "outout"); 1837 equal(r.err(), "errerr"); 1838 } 1839 { 1840 pb.redirectErrorStream(true); 1841 ProcessResults r = run(pb); 1842 equal(r.out(), "outerrouterr"); 1843 equal(r.err(), ""); 1844 } 1845 } catch (Throwable t) { unexpected(t); } 1846 1847 if (Unix.is()) { 1848 //---------------------------------------------------------------- 1849 // We can find true and false when PATH is null 1850 //---------------------------------------------------------------- 1851 try { 1852 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1853 childArgs.add("null PATH"); 1854 ProcessBuilder pb = new ProcessBuilder(childArgs); 1855 pb.environment().remove("PATH"); 1856 ProcessResults r = run(pb); 1857 equal(r.out(), ""); 1858 equal(r.err(), ""); 1859 equal(r.exitValue(), 0); 1860 } catch (Throwable t) { unexpected(t); } 1861 1862 //---------------------------------------------------------------- 1863 // PATH search algorithm on Unix 1864 //---------------------------------------------------------------- 1865 try { 1866 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1867 childArgs.add("PATH search algorithm"); 1868 ProcessBuilder pb = new ProcessBuilder(childArgs); 1869 pb.environment().put("PATH", "dir1:dir2:"); 1870 ProcessResults r = run(pb); 1871 equal(r.out(), ""); 1872 equal(r.err(), ""); 1873 equal(r.exitValue(), True.exitValue()); 1874 } catch (Throwable t) { unexpected(t); } 1875 1876 //---------------------------------------------------------------- 1877 // Parent's, not child's PATH is used 1878 //---------------------------------------------------------------- 1879 try { 1880 new File("suBdiR").mkdirs(); 1881 copy("/bin/true", "suBdiR/unliKely"); 1882 final ProcessBuilder pb = 1883 new ProcessBuilder(new String[]{"unliKely"}); 1884 pb.environment().put("PATH", "suBdiR"); 1885 THROWS(IOException.class, () -> pb.start()); 1886 } catch (Throwable t) { unexpected(t); 1887 } finally { 1888 new File("suBdiR/unliKely").delete(); 1889 new File("suBdiR").delete(); 1890 } 1891 } 1892 1893 //---------------------------------------------------------------- 1894 // Attempt to start bogus program "" 1895 //---------------------------------------------------------------- 1896 try { 1897 new ProcessBuilder("").start(); 1898 fail("Expected IOException not thrown"); 1899 } catch (IOException e) { 1900 String m = e.getMessage(); 1901 if (EnglishUnix.is() && 1902 ! matches(m, "No such file or directory")) 1903 unexpected(e); 1904 } catch (Throwable t) { unexpected(t); } 1905 1906 //---------------------------------------------------------------- 1907 // Check that attempt to execute program name with funny 1908 // characters throws an exception containing those characters. 1909 //---------------------------------------------------------------- 1910 for (String programName : new String[] {"\u00f0", "\u01f0"}) 1911 try { 1912 new ProcessBuilder(programName).start(); 1913 fail("Expected IOException not thrown"); 1914 } catch (IOException e) { 1915 String m = e.getMessage(); 1916 Pattern p = Pattern.compile(programName); 1917 if (! matches(m, programName) 1918 || (EnglishUnix.is() 1919 && ! matches(m, "No such file or directory"))) 1920 unexpected(e); 1921 } catch (Throwable t) { unexpected(t); } 1922 1923 //---------------------------------------------------------------- 1924 // Attempt to start process in nonexistent directory fails. 1925 //---------------------------------------------------------------- 1926 try { 1927 new ProcessBuilder("echo") 1928 .directory(new File("UnLiKeLY")) 1929 .start(); 1930 fail("Expected IOException not thrown"); 1931 } catch (IOException e) { 1932 String m = e.getMessage(); 1933 if (! matches(m, "in directory") 1934 || (EnglishUnix.is() && 1935 ! matches(m, "No such file or directory"))) 1936 unexpected(e); 1937 } catch (Throwable t) { unexpected(t); } 1938 1939 //---------------------------------------------------------------- 1940 // Attempt to write 4095 bytes to the pipe buffer without a 1941 // reader to drain it would deadlock, if not for the fact that 1942 // interprocess pipe buffers are at least 4096 bytes. 1943 // 1944 // Also, check that available reports all the bytes expected 1945 // in the pipe buffer, and that I/O operations do the expected 1946 // things. 1947 //---------------------------------------------------------------- 1948 try { 1949 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1950 childArgs.add("print4095"); 1951 final int SIZE = 4095; 1952 final Process p = new ProcessBuilder(childArgs).start(); 1953 print4095(p.getOutputStream(), (byte) '!'); // Might hang! 1954 p.waitFor(); // Might hang! 1955 equal(SIZE, p.getInputStream().available()); 1956 equal(SIZE, p.getErrorStream().available()); 1957 THROWS(IOException.class, 1958 () -> { p.getOutputStream().write((byte) '!'); 1959 p.getOutputStream().flush();}); 1960 1961 final byte[] bytes = new byte[SIZE + 1]; 1962 equal(SIZE, p.getInputStream().read(bytes)); 1963 for (int i = 0; i < SIZE; i++) 1964 equal((byte) '!', bytes[i]); 1965 equal((byte) 0, bytes[SIZE]); 1966 1967 equal(SIZE, p.getErrorStream().read(bytes)); 1968 for (int i = 0; i < SIZE; i++) 1969 equal((byte) 'E', bytes[i]); 1970 equal((byte) 0, bytes[SIZE]); 1971 1972 equal(0, p.getInputStream().available()); 1973 equal(0, p.getErrorStream().available()); 1974 equal(-1, p.getErrorStream().read()); 1975 equal(-1, p.getInputStream().read()); 1976 1977 equal(p.exitValue(), 5); 1978 1979 p.getInputStream().close(); 1980 p.getErrorStream().close(); 1981 try { p.getOutputStream().close(); } catch (IOException flushFailed) { } 1982 1983 InputStream[] streams = { p.getInputStream(), p.getErrorStream() }; 1984 for (final InputStream in : streams) { 1985 Fun[] ops = { 1986 () -> in.read(), 1987 () -> in.read(bytes), 1988 () -> in.available() 1989 }; 1990 for (Fun op : ops) { 1991 try { 1992 op.f(); 1993 fail(); 1994 } catch (IOException expected) { 1995 check(expected.getMessage() 1996 .matches("[Ss]tream [Cc]losed")); 1997 } 1998 } 1999 } 2000 } catch (Throwable t) { unexpected(t); } 2001 2002 //---------------------------------------------------------------- 2003 // Check that reads which are pending when Process.destroy is 2004 // called, get EOF, not IOException("Stream closed"). 2005 //---------------------------------------------------------------- 2006 try { 2007 final int cases = 4; 2008 for (int i = 0; i < cases; i++) { 2009 final int action = i; 2010 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2011 childArgs.add("sleep"); 2012 final byte[] bytes = new byte[10]; 2013 final Process p = new ProcessBuilder(childArgs).start(); 2014 final CountDownLatch latch = new CountDownLatch(1); 2015 final InputStream s; 2016 switch (action & 0x1) { 2017 case 0: s = p.getInputStream(); break; 2018 case 1: s = p.getErrorStream(); break; 2019 default: throw new Error(); 2020 } 2021 final Thread thread = new Thread() { 2022 public void run() { 2023 try { 2024 int r; 2025 latch.countDown(); 2026 switch (action & 0x2) { 2027 case 0: r = s.read(); break; 2028 case 2: r = s.read(bytes); break; 2029 default: throw new Error(); 2030 } 2031 equal(-1, r); 2032 } catch (Throwable t) { unexpected(t); }}}; 2033 2034 thread.start(); 2035 latch.await(); 2036 Thread.sleep(10); 2037 2038 String os = System.getProperty("os.name"); 2039 if (os.equalsIgnoreCase("Solaris") || 2040 os.equalsIgnoreCase("SunOS")) 2041 { 2042 final Object deferred; 2043 Class<?> c = s.getClass(); 2044 if (c.getName().equals( 2045 "java.lang.UNIXProcess$DeferredCloseInputStream")) 2046 { 2047 deferred = s; 2048 } else { 2049 Field deferredField = p.getClass(). 2050 getDeclaredField("stdout_inner_stream"); 2051 deferredField.setAccessible(true); 2052 deferred = deferredField.get(p); 2053 } 2054 Field useCountField = deferred.getClass(). 2055 getDeclaredField("useCount"); 2056 useCountField.setAccessible(true); 2057 2058 while (useCountField.getInt(deferred) <= 0) { 2059 Thread.yield(); 2060 } 2061 } else if (s instanceof BufferedInputStream) { 2062 // Wait until after the s.read occurs in "thread" by 2063 // checking when the input stream monitor is acquired 2064 // (BufferedInputStream.read is synchronized) 2065 while (!isLocked(s, 10)) { 2066 Thread.sleep(100); 2067 } 2068 } 2069 p.destroy(); 2070 thread.join(); 2071 } 2072 } catch (Throwable t) { unexpected(t); } 2073 2074 //---------------------------------------------------------------- 2075 // Check that subprocesses which create subprocesses of their 2076 // own do not cause parent to hang waiting for file 2077 // descriptors to be closed. 2078 //---------------------------------------------------------------- 2079 try { 2080 if (Unix.is() 2081 && new File("/bin/bash").exists() 2082 && new File("/bin/sleep").exists()) { 2083 // Notice that we only destroy the process created by us (i.e. 2084 // our child) but not our grandchild (i.e. '/bin/sleep'). So 2085 // pay attention that the grandchild doesn't run too long to 2086 // avoid polluting the process space with useless processes. 2087 // Running the grandchild for 60s should be more than enough. 2088 final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" }; 2089 final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 60\")" }; 2090 final ProcessBuilder pb = new ProcessBuilder(cmd); 2091 final Process p = pb.start(); 2092 final InputStream stdout = p.getInputStream(); 2093 final InputStream stderr = p.getErrorStream(); 2094 final OutputStream stdin = p.getOutputStream(); 2095 final Thread reader = new Thread() { 2096 public void run() { 2097 try { stdout.read(); } 2098 catch (IOException e) { 2099 // Check that reader failed because stream was 2100 // asynchronously closed. 2101 // e.printStackTrace(); 2102 if (EnglishUnix.is() && 2103 ! (e.getMessage().matches(".*Bad file.*"))) 2104 unexpected(e); 2105 } 2106 catch (Throwable t) { unexpected(t); }}}; 2107 reader.setDaemon(true); 2108 reader.start(); 2109 Thread.sleep(100); 2110 p.destroy(); 2111 check(p.waitFor() != 0); 2112 check(p.exitValue() != 0); 2113 // Subprocess is now dead, but file descriptors remain open. 2114 // Make sure the test will fail if we don't manage to close 2115 // the open streams within 30 seconds. Notice that this time 2116 // must be shorter than the sleep time of the grandchild. 2117 Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true); 2118 t.schedule(new TimerTask() { 2119 public void run() { 2120 fail("Subprocesses which create subprocesses of " + 2121 "their own caused the parent to hang while " + 2122 "waiting for file descriptors to be closed."); 2123 System.exit(-1); 2124 } 2125 }, 30000); 2126 stdout.close(); 2127 stderr.close(); 2128 stdin.close(); 2129 new ProcessBuilder(cmdkill).start(); 2130 // All streams successfully closed so we can cancel the timer. 2131 t.cancel(); 2132 //---------------------------------------------------------- 2133 // There remain unsolved issues with asynchronous close. 2134 // Here's a highly non-portable experiment to demonstrate: 2135 //---------------------------------------------------------- 2136 if (Boolean.getBoolean("wakeupJeff!")) { 2137 System.out.println("wakeupJeff!"); 2138 // Initialize signal handler for INTERRUPT_SIGNAL. 2139 new FileInputStream("/bin/sleep").getChannel().close(); 2140 // Send INTERRUPT_SIGNAL to every thread in this java. 2141 String[] wakeupJeff = { 2142 "/bin/bash", "-c", 2143 "/bin/ps --noheaders -Lfp $PPID | " + 2144 "/usr/bin/perl -nale 'print $F[3]' | " + 2145 // INTERRUPT_SIGNAL == 62 on my machine du jour. 2146 "/usr/bin/xargs kill -62" 2147 }; 2148 new ProcessBuilder(wakeupJeff).start().waitFor(); 2149 // If wakeupJeff worked, reader probably got EBADF. 2150 reader.join(); 2151 } 2152 } 2153 } catch (Throwable t) { unexpected(t); } 2154 2155 //---------------------------------------------------------------- 2156 // Attempt to start process with insufficient permissions fails. 2157 //---------------------------------------------------------------- 2158 try { 2159 new File("emptyCommand").delete(); 2160 new FileOutputStream("emptyCommand").close(); 2161 new File("emptyCommand").setExecutable(false); 2162 new ProcessBuilder("./emptyCommand").start(); 2163 fail("Expected IOException not thrown"); 2164 } catch (IOException e) { 2165 new File("./emptyCommand").delete(); 2166 String m = e.getMessage(); 2167 if (EnglishUnix.is() && 2168 ! matches(m, "Permission denied")) 2169 unexpected(e); 2170 } catch (Throwable t) { unexpected(t); } 2171 2172 new File("emptyCommand").delete(); 2173 2174 //---------------------------------------------------------------- 2175 // Check for correct security permission behavior 2176 //---------------------------------------------------------------- 2177 final Policy policy = new Policy(); 2178 Policy.setPolicy(policy); 2179 System.setSecurityManager(new SecurityManager()); 2180 2181 try { 2182 // No permissions required to CREATE a ProcessBuilder 2183 policy.setPermissions(/* Nothing */); 2184 new ProcessBuilder("env").directory(null).directory(); 2185 new ProcessBuilder("env").directory(new File("dir")).directory(); 2186 new ProcessBuilder("env").command("??").command(); 2187 } catch (Throwable t) { unexpected(t); } 2188 2189 THROWS(SecurityException.class, 2190 () -> { policy.setPermissions(/* Nothing */); 2191 System.getenv("foo");}, 2192 () -> { policy.setPermissions(/* Nothing */); 2193 System.getenv();}, 2194 () -> { policy.setPermissions(/* Nothing */); 2195 new ProcessBuilder("echo").start();}, 2196 () -> { policy.setPermissions(/* Nothing */); 2197 Runtime.getRuntime().exec("echo");}, 2198 () -> { policy.setPermissions( 2199 new RuntimePermission("getenv.bar")); 2200 System.getenv("foo");}); 2201 2202 try { 2203 policy.setPermissions(new RuntimePermission("getenv.foo")); 2204 System.getenv("foo"); 2205 2206 policy.setPermissions(new RuntimePermission("getenv.*")); 2207 System.getenv("foo"); 2208 System.getenv(); 2209 new ProcessBuilder().environment(); 2210 } catch (Throwable t) { unexpected(t); } 2211 2212 2213 final Permission execPermission 2214 = new FilePermission("<<ALL FILES>>", "execute"); 2215 2216 THROWS(SecurityException.class, 2217 () -> { // environment permission by itself insufficient 2218 policy.setPermissions(new RuntimePermission("getenv.*")); 2219 ProcessBuilder pb = new ProcessBuilder("env"); 2220 pb.environment().put("foo","bar"); 2221 pb.start();}, 2222 () -> { // exec permission by itself insufficient 2223 policy.setPermissions(execPermission); 2224 ProcessBuilder pb = new ProcessBuilder("env"); 2225 pb.environment().put("foo","bar"); 2226 pb.start();}); 2227 2228 try { 2229 // Both permissions? OK. 2230 policy.setPermissions(new RuntimePermission("getenv.*"), 2231 execPermission); 2232 ProcessBuilder pb = new ProcessBuilder("env"); 2233 pb.environment().put("foo","bar"); 2234 Process p = pb.start(); 2235 closeStreams(p); 2236 } catch (IOException e) { // OK 2237 } catch (Throwable t) { unexpected(t); } 2238 2239 try { 2240 // Don't need environment permission unless READING environment 2241 policy.setPermissions(execPermission); 2242 Runtime.getRuntime().exec("env", new String[]{}); 2243 } catch (IOException e) { // OK 2244 } catch (Throwable t) { unexpected(t); } 2245 2246 try { 2247 // Don't need environment permission unless READING environment 2248 policy.setPermissions(execPermission); 2249 new ProcessBuilder("env").start(); 2250 } catch (IOException e) { // OK 2251 } catch (Throwable t) { unexpected(t); } 2252 2253 // Restore "normal" state without a security manager 2254 policy.setPermissions(new RuntimePermission("setSecurityManager")); 2255 System.setSecurityManager(null); 2256 2257 //---------------------------------------------------------------- 2258 // Check that Process.isAlive() & 2259 // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected. 2260 //---------------------------------------------------------------- 2261 try { 2262 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2263 childArgs.add("sleep"); 2264 final Process p = new ProcessBuilder(childArgs).start(); 2265 long start = System.nanoTime(); 2266 if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) { 2267 fail("Test failed: Process exited prematurely"); 2268 } 2269 long end = System.nanoTime(); 2270 // give waitFor(timeout) a wide berth (2s) 2271 System.out.printf(" waitFor process: delta: %d%n",(end - start) ); 2272 2273 if ((end - start) > TimeUnit.SECONDS.toNanos(2)) 2274 fail("Test failed: waitFor took too long (" + (end - start) + "ns)"); 2275 2276 p.destroy(); 2277 p.waitFor(); 2278 2279 if (p.isAlive() || 2280 !p.waitFor(0, TimeUnit.MILLISECONDS)) 2281 { 2282 fail("Test failed: Process still alive - please terminate " + 2283 p.toString() + " manually"); 2284 } 2285 } catch (Throwable t) { unexpected(t); } 2286 2287 //---------------------------------------------------------------- 2288 // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) 2289 // works as expected. 2290 //---------------------------------------------------------------- 2291 try { 2292 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2293 childArgs.add("sleep"); 2294 final Process p = new ProcessBuilder(childArgs).start(); 2295 long start = System.nanoTime(); 2296 2297 p.waitFor(10, TimeUnit.MILLISECONDS); 2298 2299 long end = System.nanoTime(); 2300 if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10)) 2301 fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)"); 2302 2303 p.destroy(); 2304 2305 start = System.nanoTime(); 2306 p.waitFor(8, TimeUnit.SECONDS); 2307 end = System.nanoTime(); 2308 2309 int exitValue = p.exitValue(); 2310 2311 if ((end - start) > TimeUnit.SECONDS.toNanos(7)) 2312 fail("Test failed: waitFor took too long on a dead process. (" + (end - start) + "ns)" 2313 + ", exitValue: " + exitValue); 2314 } catch (Throwable t) { unexpected(t); } 2315 2316 //---------------------------------------------------------------- 2317 // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) 2318 // interrupt works as expected, if interrupted while waiting. 2319 //---------------------------------------------------------------- 2320 try { 2321 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2322 childArgs.add("sleep"); 2323 final Process p = new ProcessBuilder(childArgs).start(); 2324 final long start = System.nanoTime(); 2325 final CountDownLatch aboutToWaitFor = new CountDownLatch(1); 2326 2327 final Thread thread = new Thread() { 2328 public void run() { 2329 try { 2330 aboutToWaitFor.countDown(); 2331 boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS); 2332 fail("waitFor() wasn't interrupted, its return value was: " + result); 2333 } catch (InterruptedException success) { 2334 } catch (Throwable t) { unexpected(t); } 2335 } 2336 }; 2337 2338 thread.start(); 2339 aboutToWaitFor.await(); 2340 Thread.sleep(1000); 2341 thread.interrupt(); 2342 thread.join(10L * 1000L); 2343 check(millisElapsedSince(start) < 10L * 1000L); 2344 check(!thread.isAlive()); 2345 p.destroy(); 2346 } catch (Throwable t) { unexpected(t); } 2347 2348 //---------------------------------------------------------------- 2349 // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) 2350 // interrupt works as expected, if interrupted before waiting. 2351 //---------------------------------------------------------------- 2352 try { 2353 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2354 childArgs.add("sleep"); 2355 final Process p = new ProcessBuilder(childArgs).start(); 2356 final long start = System.nanoTime(); 2357 final CountDownLatch threadStarted = new CountDownLatch(1); 2358 2359 final Thread thread = new Thread() { 2360 public void run() { 2361 try { 2362 threadStarted.countDown(); 2363 do { Thread.yield(); } 2364 while (!Thread.currentThread().isInterrupted()); 2365 boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS); 2366 fail("waitFor() wasn't interrupted, its return value was: " + result); 2367 } catch (InterruptedException success) { 2368 } catch (Throwable t) { unexpected(t); } 2369 } 2370 }; 2371 2372 thread.start(); 2373 threadStarted.await(); 2374 thread.interrupt(); 2375 thread.join(10L * 1000L); 2376 check(millisElapsedSince(start) < 10L * 1000L); 2377 check(!thread.isAlive()); 2378 p.destroy(); 2379 } catch (Throwable t) { unexpected(t); } 2380 2381 //---------------------------------------------------------------- 2382 // Check the default implementation for 2383 // Process.waitFor(long, TimeUnit) 2384 //---------------------------------------------------------------- 2385 try { 2386 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2387 childArgs.add("sleep"); 2388 final Process proc = new ProcessBuilder(childArgs).start(); 2389 DelegatingProcess p = new DelegatingProcess(proc); 2390 long start = System.nanoTime(); 2391 2392 p.waitFor(1000, TimeUnit.MILLISECONDS); 2393 2394 long end = System.nanoTime(); 2395 if ((end - start) < 500000000) 2396 fail("Test failed: waitFor didn't take long enough"); 2397 2398 p.destroy(); 2399 2400 p.waitFor(1000, TimeUnit.MILLISECONDS); 2401 } catch (Throwable t) { unexpected(t); } 2402 } 2403 2404 static void closeStreams(Process p) { 2405 try { 2406 p.getOutputStream().close(); 2407 p.getInputStream().close(); 2408 p.getErrorStream().close(); 2409 } catch (Throwable t) { unexpected(t); } 2410 } 2411 2412 //---------------------------------------------------------------- 2413 // A Policy class designed to make permissions fiddling very easy. 2414 //---------------------------------------------------------------- 2415 private static class Policy extends java.security.Policy { 2416 private Permissions perms; 2417 2418 public void setPermissions(Permission...permissions) { 2419 perms = new Permissions(); 2420 for (Permission permission : permissions) 2421 perms.add(permission); 2422 } 2423 2424 public Policy() { setPermissions(/* Nothing */); } 2425 2426 public PermissionCollection getPermissions(CodeSource cs) { 2427 return perms; 2428 } 2429 2430 public PermissionCollection getPermissions(ProtectionDomain pd) { 2431 return perms; 2432 } 2433 2434 public boolean implies(ProtectionDomain pd, Permission p) { 2435 return perms.implies(p); 2436 } 2437 2438 public void refresh() {} 2439 } 2440 2441 private static class StreamAccumulator extends Thread { 2442 private final InputStream is; 2443 private final StringBuilder sb = new StringBuilder(); 2444 private Throwable throwable = null; 2445 2446 public String result () throws Throwable { 2447 if (throwable != null) 2448 throw throwable; 2449 return sb.toString(); 2450 } 2451 2452 StreamAccumulator (InputStream is) { 2453 this.is = is; 2454 } 2455 2456 public void run() { 2457 try { 2458 Reader r = new InputStreamReader(is); 2459 char[] buf = new char[4096]; 2460 int n; 2461 while ((n = r.read(buf)) > 0) { 2462 sb.append(buf,0,n); 2463 } 2464 } catch (Throwable t) { 2465 throwable = t; 2466 } finally { 2467 try { is.close(); } 2468 catch (Throwable t) { throwable = t; } 2469 } 2470 } 2471 } 2472 2473 static ProcessResults run(ProcessBuilder pb) { 2474 try { 2475 return run(pb.start()); 2476 } catch (Throwable t) { unexpected(t); return null; } 2477 } 2478 2479 private static ProcessResults run(Process p) { 2480 Throwable throwable = null; 2481 int exitValue = -1; 2482 String out = ""; 2483 String err = ""; 2484 2485 StreamAccumulator outAccumulator = 2486 new StreamAccumulator(p.getInputStream()); 2487 StreamAccumulator errAccumulator = 2488 new StreamAccumulator(p.getErrorStream()); 2489 2490 try { 2491 outAccumulator.start(); 2492 errAccumulator.start(); 2493 2494 exitValue = p.waitFor(); 2495 2496 outAccumulator.join(); 2497 errAccumulator.join(); 2498 2499 out = outAccumulator.result(); 2500 err = errAccumulator.result(); 2501 } catch (Throwable t) { 2502 throwable = t; 2503 } 2504 2505 return new ProcessResults(out, err, exitValue, throwable); 2506 } 2507 2508 //---------------------------------------------------------------- 2509 // Results of a command 2510 //---------------------------------------------------------------- 2511 private static class ProcessResults { 2512 private final String out; 2513 private final String err; 2514 private final int exitValue; 2515 private final Throwable throwable; 2516 2517 public ProcessResults(String out, 2518 String err, 2519 int exitValue, 2520 Throwable throwable) { 2521 this.out = out; 2522 this.err = err; 2523 this.exitValue = exitValue; 2524 this.throwable = throwable; 2525 } 2526 2527 public String out() { return out; } 2528 public String err() { return err; } 2529 public int exitValue() { return exitValue; } 2530 public Throwable throwable() { return throwable; } 2531 2532 public String toString() { 2533 StringBuilder sb = new StringBuilder(); 2534 sb.append("<STDOUT>\n" + out() + "</STDOUT>\n") 2535 .append("<STDERR>\n" + err() + "</STDERR>\n") 2536 .append("exitValue = " + exitValue + "\n"); 2537 if (throwable != null) 2538 sb.append(throwable.getStackTrace()); 2539 return sb.toString(); 2540 } 2541 } 2542 2543 //--------------------- Infrastructure --------------------------- 2544 static volatile int passed = 0, failed = 0; 2545 static void pass() {passed++;} 2546 static void fail() {failed++; Thread.dumpStack();} 2547 static void fail(String msg) {System.out.println(msg); fail();} 2548 static void unexpected(Throwable t) {failed++; t.printStackTrace();} 2549 static void check(boolean cond) {if (cond) pass(); else fail();} 2550 static void check(boolean cond, String m) {if (cond) pass(); else fail(m);} 2551 static void equal(Object x, Object y) { 2552 if (x == null ? y == null : x.equals(y)) pass(); 2553 else fail(">'" + x + "'<" + " not equal to " + "'" + y + "'");} 2554 2555 public static void main(String[] args) throws Throwable { 2556 try {realMain(args);} catch (Throwable t) {unexpected(t);} 2557 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); 2558 if (failed > 0) throw new AssertionError("Some tests failed");} 2559 interface Fun {void f() throws Throwable;} 2560 static void THROWS(Class<? extends Throwable> k, Fun... fs) { 2561 for (Fun f : fs) 2562 try { f.f(); fail("Expected " + k.getName() + " not thrown"); } 2563 catch (Throwable t) { 2564 if (k.isAssignableFrom(t.getClass())) pass(); 2565 else unexpected(t);}} 2566 2567 static boolean isLocked(final Object monitor, final long millis) throws InterruptedException { 2568 return new Thread() { 2569 volatile boolean unlocked; 2570 2571 @Override 2572 public void run() { 2573 synchronized (monitor) { unlocked = true; } 2574 } 2575 2576 boolean isLocked() throws InterruptedException { 2577 start(); 2578 join(millis); 2579 return !unlocked; 2580 } 2581 }.isLocked(); 2582 } 2583 }