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