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