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