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