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