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(new String[]
 572             { javaExe, "-classpath", absolutifyPath(classpath),
 573               "Basic$JavaChild"});
 574 
 575     private static void testEncoding(String encoding, String tested) {
 576         try {
 577             // If round trip conversion works, should be able to set env vars
 578             // correctly in child.
 579             if (new String(tested.getBytes()).equals(tested)) {
 580                 out.println("Testing " + encoding + " environment values");
 581                 ProcessBuilder pb = new ProcessBuilder();
 582                 pb.environment().put("ASCIINAME",tested);
 583                 equal(getenvInChild(pb,"ASCIINAME"), tested);
 584             }
 585         } catch (Throwable t) { unexpected(t); }
 586     }
 587 
 588     static class Windows {
 589         public static boolean is() { return is; }
 590         private static final boolean is =
 591             System.getProperty("os.name").startsWith("Windows");
 592     }
 593 
 594     static class AIX {
 595         public static boolean is() { return is; }
 596         private static final boolean is =
 597             System.getProperty("os.name").equals("AIX");
 598     }
 599 
 600     static class Unix {
 601         public static boolean is() { return is; }
 602         private static final boolean is =
 603             (! Windows.is() &&
 604              new File("/bin/sh").exists() &&
 605              new File("/bin/true").exists() &&
 606              new File("/bin/false").exists());
 607     }
 608 
 609     static class UnicodeOS {
 610         public static boolean is() { return is; }
 611         private static final String osName = System.getProperty("os.name");
 612         private static final boolean is =
 613             // MacOS X would probably also qualify
 614             osName.startsWith("Windows")   &&
 615             ! osName.startsWith("Windows 9") &&
 616             ! osName.equals("Windows Me");
 617     }
 618 
 619     static class MacOSX {
 620         public static boolean is() { return is; }
 621         private static final String osName = System.getProperty("os.name");
 622         private static final boolean is = osName.contains("OS X");
 623     }
 624 
 625     static class True {
 626         public static int exitValue() { return 0; }
 627     }
 628 
 629     private static class False {
 630         public static int exitValue() { return exitValue; }
 631         private static final int exitValue = exitValue0();
 632         private static int exitValue0() {
 633             // /bin/false returns an *unspecified* non-zero number.
 634             try {
 635                 if (! Unix.is())
 636                     return -1;
 637                 else {
 638                     int rc = new ProcessBuilder("/bin/false")
 639                         .start().waitFor();
 640                     check(rc != 0);
 641                     return rc;
 642                 }
 643             } catch (Throwable t) { unexpected(t); return -1; }
 644         }
 645     }
 646 
 647     static class EnglishUnix {
 648         private final static Boolean is =
 649             (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));
 650 
 651         private static boolean isEnglish(String envvar) {
 652             String val = getenv(envvar);
 653             return (val == null) || val.matches("en.*") || val.matches("C");
 654         }
 655 
 656         /** Returns true if we can expect English OS error strings */
 657         static boolean is() { return is; }
 658     }
 659 
 660     static class DelegatingProcess extends Process {
 661         final Process p;
 662 
 663         DelegatingProcess(Process p) {
 664             this.p = p;
 665         }
 666 
 667         @Override
 668         public void destroy() {
 669             p.destroy();
 670         }
 671 
 672         @Override
 673         public int exitValue() {
 674             return p.exitValue();
 675         }
 676 
 677         @Override
 678         public int waitFor() throws InterruptedException {
 679             return p.waitFor();
 680         }
 681 
 682         @Override
 683         public OutputStream getOutputStream() {
 684             return p.getOutputStream();
 685         }
 686 
 687         @Override
 688         public InputStream getInputStream() {
 689             return p.getInputStream();
 690         }
 691 
 692         @Override
 693         public InputStream getErrorStream() {
 694             return p.getErrorStream();
 695         }
 696     }
 697 
 698     private static boolean matches(String str, String regex) {
 699         return Pattern.compile(regex).matcher(str).find();
 700     }
 701 
 702     private static String matchAndExtract(String str, String regex) {
 703         Matcher matcher = Pattern.compile(regex).matcher(str);
 704         if (matcher.find()) {
 705             return matcher.group();
 706         } else {
 707             return "";
 708         }
 709     }
 710 
 711     /* Only used for Mac OS X --
 712      * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty
 713      * environment. The environment variable JAVA_MAIN_CLASS_<pid> may also
 714      * be set in Mac OS X.
 715      * Remove them both from the list of env variables
 716      */
 717     private static String removeMacExpectedVars(String vars) {
 718         // Check for __CF_USER_TEXT_ENCODING
 719         String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="
 720                                             +cfUserTextEncoding+",","");
 721         // Check for JAVA_MAIN_CLASS_<pid>
 722         String javaMainClassStr
 723                 = matchAndExtract(cleanedVars,
 724                                     "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,");
 725         return cleanedVars.replace(javaMainClassStr,"");
 726     }
 727 
 728     /* Only used for AIX --
 729      * AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment.
 730      * Remove it from the list of env variables
 731      */
 732     private static String removeAixExpectedVars(String vars) {
 733         return vars.replace("AIXTHREAD_GUARDPAGES=0,","");
 734     }
 735 
 736     private static String sortByLinesWindowsly(String text) {
 737         String[] lines = text.split("\n");
 738         Arrays.sort(lines, new WindowsComparator());
 739         StringBuilder sb = new StringBuilder();
 740         for (String line : lines)
 741             sb.append(line).append("\n");
 742         return sb.toString();
 743     }
 744 
 745     private static void checkMapSanity(Map<String,String> map) {
 746         try {
 747             Set<String> keySet = map.keySet();
 748             Collection<String> values = map.values();
 749             Set<Map.Entry<String,String>> entrySet = map.entrySet();
 750 
 751             equal(entrySet.size(), keySet.size());
 752             equal(entrySet.size(), values.size());
 753 
 754             StringBuilder s1 = new StringBuilder();
 755             for (Map.Entry<String,String> e : entrySet)
 756                 s1.append(e.getKey() + "=" + e.getValue() + "\n");
 757 
 758             StringBuilder s2 = new StringBuilder();
 759             for (String var : keySet)
 760                 s2.append(var + "=" + map.get(var) + "\n");
 761 
 762             equal(s1.toString(), s2.toString());
 763 
 764             Iterator<String> kIter = keySet.iterator();
 765             Iterator<String> vIter = values.iterator();
 766             Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();
 767 
 768             while (eIter.hasNext()) {
 769                 Map.Entry<String,String> entry = eIter.next();
 770                 String key   = kIter.next();
 771                 String value = vIter.next();
 772                 check(entrySet.contains(entry));
 773                 check(keySet.contains(key));
 774                 check(values.contains(value));
 775                 check(map.containsKey(key));
 776                 check(map.containsValue(value));
 777                 equal(entry.getKey(), key);
 778                 equal(entry.getValue(), value);
 779             }
 780             check(! kIter.hasNext() &&
 781                   ! vIter.hasNext());
 782 
 783         } catch (Throwable t) { unexpected(t); }
 784     }
 785 
 786     private static void checkMapEquality(Map<String,String> map1,
 787                                          Map<String,String> map2) {
 788         try {
 789             equal(map1.size(), map2.size());
 790             equal(map1.isEmpty(), map2.isEmpty());
 791             for (String key : map1.keySet()) {
 792                 equal(map1.get(key), map2.get(key));
 793                 check(map2.keySet().contains(key));
 794             }
 795             equal(map1, map2);
 796             equal(map2, map1);
 797             equal(map1.entrySet(), map2.entrySet());
 798             equal(map2.entrySet(), map1.entrySet());
 799             equal(map1.keySet(), map2.keySet());
 800             equal(map2.keySet(), map1.keySet());
 801 
 802             equal(map1.hashCode(), map2.hashCode());
 803             equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());
 804             equal(map1.keySet().hashCode(), map2.keySet().hashCode());
 805         } catch (Throwable t) { unexpected(t); }
 806     }
 807 
 808     static void checkRedirects(ProcessBuilder pb,
 809                                Redirect in, Redirect out, Redirect err) {
 810         equal(pb.redirectInput(),  in);
 811         equal(pb.redirectOutput(), out);
 812         equal(pb.redirectError(),  err);
 813     }
 814 
 815     static void redirectIO(ProcessBuilder pb,
 816                            Redirect in, Redirect out, Redirect err) {
 817         pb.redirectInput(in);
 818         pb.redirectOutput(out);
 819         pb.redirectError(err);
 820     }
 821 
 822     static void setFileContents(File file, String contents) {
 823         try {
 824             Writer w = new FileWriter(file);
 825             w.write(contents);
 826             w.close();
 827         } catch (Throwable t) { unexpected(t); }
 828     }
 829 
 830     static String fileContents(File file) {
 831         try {
 832             Reader r = new FileReader(file);
 833             StringBuilder sb = new StringBuilder();
 834             char[] buffer = new char[1024];
 835             int n;
 836             while ((n = r.read(buffer)) != -1)
 837                 sb.append(buffer,0,n);
 838             r.close();
 839             return new String(sb);
 840         } catch (Throwable t) { unexpected(t); return ""; }
 841     }
 842 
 843     static void testIORedirection() throws Throwable {
 844         final File ifile = new File("ifile");
 845         final File ofile = new File("ofile");
 846         final File efile = new File("efile");
 847         ifile.delete();
 848         ofile.delete();
 849         efile.delete();
 850 
 851         //----------------------------------------------------------------
 852         // Check mutual inequality of different types of Redirect
 853         //----------------------------------------------------------------
 854         Redirect[] redirects =
 855             { PIPE,
 856               INHERIT,
 857               Redirect.from(ifile),
 858               Redirect.to(ifile),
 859               Redirect.appendTo(ifile),
 860               Redirect.from(ofile),
 861               Redirect.to(ofile),
 862               Redirect.appendTo(ofile),
 863             };
 864         for (int i = 0; i < redirects.length; i++)
 865             for (int j = 0; j < redirects.length; j++)
 866                 equal(redirects[i].equals(redirects[j]), (i == j));
 867 
 868         //----------------------------------------------------------------
 869         // Check basic properties of different types of Redirect
 870         //----------------------------------------------------------------
 871         equal(PIPE.type(), Redirect.Type.PIPE);
 872         equal(PIPE.toString(), "PIPE");
 873         equal(PIPE.file(), null);
 874 
 875         equal(INHERIT.type(), Redirect.Type.INHERIT);
 876         equal(INHERIT.toString(), "INHERIT");
 877         equal(INHERIT.file(), null);
 878 
 879         equal(Redirect.from(ifile).type(), Redirect.Type.READ);
 880         equal(Redirect.from(ifile).toString(),
 881               "redirect to read from file \"ifile\"");
 882         equal(Redirect.from(ifile).file(), ifile);
 883         equal(Redirect.from(ifile),
 884               Redirect.from(ifile));
 885         equal(Redirect.from(ifile).hashCode(),
 886               Redirect.from(ifile).hashCode());
 887 
 888         equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);
 889         equal(Redirect.to(ofile).toString(),
 890               "redirect to write to file \"ofile\"");
 891         equal(Redirect.to(ofile).file(), ofile);
 892         equal(Redirect.to(ofile),
 893               Redirect.to(ofile));
 894         equal(Redirect.to(ofile).hashCode(),
 895               Redirect.to(ofile).hashCode());
 896 
 897         equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);
 898         equal(Redirect.appendTo(efile).toString(),
 899               "redirect to append to file \"efile\"");
 900         equal(Redirect.appendTo(efile).file(), efile);
 901         equal(Redirect.appendTo(efile),
 902               Redirect.appendTo(efile));
 903         equal(Redirect.appendTo(efile).hashCode(),
 904               Redirect.appendTo(efile).hashCode());
 905 
 906         //----------------------------------------------------------------
 907         // Check initial values of redirects
 908         //----------------------------------------------------------------
 909         List<String> childArgs = new ArrayList<String>(javaChildArgs);
 910         childArgs.add("testIO");
 911         final ProcessBuilder pb = new ProcessBuilder(childArgs);
 912         checkRedirects(pb, PIPE, PIPE, PIPE);
 913 
 914         //----------------------------------------------------------------
 915         // Check inheritIO
 916         //----------------------------------------------------------------
 917         pb.inheritIO();
 918         checkRedirects(pb, INHERIT, INHERIT, INHERIT);
 919 
 920         //----------------------------------------------------------------
 921         // Check setters and getters agree
 922         //----------------------------------------------------------------
 923         pb.redirectInput(ifile);
 924         equal(pb.redirectInput().file(), ifile);
 925         equal(pb.redirectInput(), Redirect.from(ifile));
 926 
 927         pb.redirectOutput(ofile);
 928         equal(pb.redirectOutput().file(), ofile);
 929         equal(pb.redirectOutput(), Redirect.to(ofile));
 930 
 931         pb.redirectError(efile);
 932         equal(pb.redirectError().file(), efile);
 933         equal(pb.redirectError(), Redirect.to(efile));
 934 
 935         THROWS(IllegalArgumentException.class,
 936             new Fun(){void f() {
 937                 pb.redirectInput(Redirect.to(ofile)); }},
 938             new Fun(){void f() {
 939                 pb.redirectInput(Redirect.appendTo(ofile)); }},
 940             new Fun(){void f() {
 941                 pb.redirectOutput(Redirect.from(ifile)); }},
 942             new Fun(){void f() {
 943                 pb.redirectError(Redirect.from(ifile)); }});
 944 
 945         THROWS(IOException.class,
 946                // Input file does not exist
 947                new Fun(){void f() throws Throwable { pb.start(); }});
 948         setFileContents(ifile, "standard input");
 949 
 950         //----------------------------------------------------------------
 951         // Writing to non-existent files
 952         //----------------------------------------------------------------
 953         {
 954             ProcessResults r = run(pb);
 955             equal(r.exitValue(), 0);
 956             equal(fileContents(ofile), "standard output");
 957             equal(fileContents(efile), "standard error");
 958             equal(r.out(), "");
 959             equal(r.err(), "");
 960             ofile.delete();
 961             efile.delete();
 962         }
 963 
 964         //----------------------------------------------------------------
 965         // Both redirectErrorStream + redirectError
 966         //----------------------------------------------------------------
 967         {
 968             pb.redirectErrorStream(true);
 969             ProcessResults r = run(pb);
 970             equal(r.exitValue(), 0);
 971             equal(fileContents(ofile),
 972                   "standard error" + "standard output");
 973             equal(fileContents(efile), "");
 974             equal(r.out(), "");
 975             equal(r.err(), "");
 976             ofile.delete();
 977             efile.delete();
 978         }
 979 
 980         //----------------------------------------------------------------
 981         // Appending to existing files
 982         //----------------------------------------------------------------
 983         {
 984             setFileContents(ofile, "ofile-contents");
 985             setFileContents(efile, "efile-contents");
 986             pb.redirectOutput(Redirect.appendTo(ofile));
 987             pb.redirectError(Redirect.appendTo(efile));
 988             pb.redirectErrorStream(false);
 989             ProcessResults r = run(pb);
 990             equal(r.exitValue(), 0);
 991             equal(fileContents(ofile),
 992                   "ofile-contents" + "standard output");
 993             equal(fileContents(efile),
 994                   "efile-contents" + "standard error");
 995             equal(r.out(), "");
 996             equal(r.err(), "");
 997             ofile.delete();
 998             efile.delete();
 999         }
1000 
1001         //----------------------------------------------------------------
1002         // Replacing existing files
1003         //----------------------------------------------------------------
1004         {
1005             setFileContents(ofile, "ofile-contents");
1006             setFileContents(efile, "efile-contents");
1007             pb.redirectOutput(ofile);
1008             pb.redirectError(Redirect.to(efile));
1009             ProcessResults r = run(pb);
1010             equal(r.exitValue(), 0);
1011             equal(fileContents(ofile), "standard output");
1012             equal(fileContents(efile), "standard error");
1013             equal(r.out(), "");
1014             equal(r.err(), "");
1015             ofile.delete();
1016             efile.delete();
1017         }
1018 
1019         //----------------------------------------------------------------
1020         // Appending twice to the same file?
1021         //----------------------------------------------------------------
1022         {
1023             setFileContents(ofile, "ofile-contents");
1024             setFileContents(efile, "efile-contents");
1025             Redirect appender = Redirect.appendTo(ofile);
1026             pb.redirectOutput(appender);
1027             pb.redirectError(appender);
1028             ProcessResults r = run(pb);
1029             equal(r.exitValue(), 0);
1030             equal(fileContents(ofile),
1031                   "ofile-contents" +
1032                   "standard error" +
1033                   "standard output");
1034             equal(fileContents(efile), "efile-contents");
1035             equal(r.out(), "");
1036             equal(r.err(), "");
1037             ifile.delete();
1038             ofile.delete();
1039             efile.delete();
1040         }
1041 
1042         //----------------------------------------------------------------
1043         // Testing INHERIT is harder.
1044         // Note that this requires __FOUR__ nested JVMs involved in one test,
1045         // if you count the harness JVM.
1046         //----------------------------------------------------------------
1047         for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) {
1048             redirectIO(pb, PIPE, PIPE, PIPE);
1049             List<String> command = pb.command();
1050             command.set(command.size() - 1, testName);
1051             Process p = pb.start();
1052             new PrintStream(p.getOutputStream()).print("standard input");
1053             p.getOutputStream().close();
1054             ProcessResults r = run(p);
1055             equal(r.exitValue(), 0);
1056             equal(r.out(), "standard output");
1057             equal(r.err(), "standard error");
1058         }
1059 
1060         //----------------------------------------------------------------
1061         // Test security implications of I/O redirection
1062         //----------------------------------------------------------------
1063 
1064         // Read access to current directory is always granted;
1065         // So create a tmpfile for input instead.
1066         final File tmpFile = File.createTempFile("Basic", "tmp");
1067         setFileContents(tmpFile, "standard input");
1068 
1069         final Policy policy = new Policy();
1070         Policy.setPolicy(policy);
1071         System.setSecurityManager(new SecurityManager());
1072         try {
1073             final Permission xPermission
1074                 = new FilePermission("<<ALL FILES>>", "execute");
1075             final Permission rxPermission
1076                 = new FilePermission("<<ALL FILES>>", "read,execute");
1077             final Permission wxPermission
1078                 = new FilePermission("<<ALL FILES>>", "write,execute");
1079             final Permission rwxPermission
1080                 = new FilePermission("<<ALL FILES>>", "read,write,execute");
1081 
1082             THROWS(SecurityException.class,
1083                new Fun() { void f() throws IOException {
1084                    policy.setPermissions(xPermission);
1085                    redirectIO(pb, from(tmpFile), PIPE, PIPE);
1086                    pb.start();}},
1087                new Fun() { void f() throws IOException {
1088                    policy.setPermissions(rxPermission);
1089                    redirectIO(pb, PIPE, to(ofile), PIPE);
1090                    pb.start();}},
1091                new Fun() { void f() throws IOException {
1092                    policy.setPermissions(rxPermission);
1093                    redirectIO(pb, PIPE, PIPE, to(efile));
1094                    pb.start();}});
1095 
1096             {
1097                 policy.setPermissions(rxPermission);
1098                 redirectIO(pb, from(tmpFile), PIPE, PIPE);
1099                 ProcessResults r = run(pb);
1100                 equal(r.out(), "standard output");
1101                 equal(r.err(), "standard error");
1102             }
1103 
1104             {
1105                 policy.setPermissions(wxPermission);
1106                 redirectIO(pb, PIPE, to(ofile), to(efile));
1107                 Process p = pb.start();
1108                 new PrintStream(p.getOutputStream()).print("standard input");
1109                 p.getOutputStream().close();
1110                 ProcessResults r = run(p);
1111                 policy.setPermissions(rwxPermission);
1112                 equal(fileContents(ofile), "standard output");
1113                 equal(fileContents(efile), "standard error");
1114             }
1115 
1116             {
1117                 policy.setPermissions(rwxPermission);
1118                 redirectIO(pb, from(tmpFile), to(ofile), to(efile));
1119                 ProcessResults r = run(pb);
1120                 policy.setPermissions(rwxPermission);
1121                 equal(fileContents(ofile), "standard output");
1122                 equal(fileContents(efile), "standard error");
1123             }
1124 
1125         } finally {
1126             policy.setPermissions(new RuntimePermission("setSecurityManager"));
1127             System.setSecurityManager(null);
1128             tmpFile.delete();
1129             ifile.delete();
1130             ofile.delete();
1131             efile.delete();
1132         }
1133     }
1134 
1135     private static void realMain(String[] args) throws Throwable {
1136         if (Windows.is())
1137             System.out.println("This appears to be a Windows system.");
1138         if (Unix.is())
1139             System.out.println("This appears to be a Unix system.");
1140         if (UnicodeOS.is())
1141             System.out.println("This appears to be a Unicode-based OS.");
1142 
1143         try { testIORedirection(); }
1144         catch (Throwable t) { unexpected(t); }
1145 
1146         //----------------------------------------------------------------
1147         // Basic tests for setting, replacing and deleting envvars
1148         //----------------------------------------------------------------
1149         try {
1150             ProcessBuilder pb = new ProcessBuilder();
1151             Map<String,String> environ = pb.environment();
1152 
1153             // New env var
1154             environ.put("QUUX", "BAR");
1155             equal(environ.get("QUUX"), "BAR");
1156             equal(getenvInChild(pb,"QUUX"), "BAR");
1157 
1158             // Modify env var
1159             environ.put("QUUX","bear");
1160             equal(environ.get("QUUX"), "bear");
1161             equal(getenvInChild(pb,"QUUX"), "bear");
1162             checkMapSanity(environ);
1163 
1164             // Remove env var
1165             environ.remove("QUUX");
1166             equal(environ.get("QUUX"), null);
1167             equal(getenvInChild(pb,"QUUX"), "null");
1168             checkMapSanity(environ);
1169 
1170             // Remove non-existent env var
1171             environ.remove("QUUX");
1172             equal(environ.get("QUUX"), null);
1173             equal(getenvInChild(pb,"QUUX"), "null");
1174             checkMapSanity(environ);
1175         } catch (Throwable t) { unexpected(t); }
1176 
1177         //----------------------------------------------------------------
1178         // Pass Empty environment to child
1179         //----------------------------------------------------------------
1180         try {
1181             ProcessBuilder pb = new ProcessBuilder();
1182             pb.environment().clear();
1183             String expected = Windows.is() ? "SystemRoot="+systemRoot+",": "";
1184             expected = AIX.is() ? "LIBPATH="+libpath+",": expected;
1185             if (Windows.is()) {
1186                 pb.environment().put("SystemRoot", systemRoot);
1187             }
1188             if (AIX.is()) {
1189                 pb.environment().put("LIBPATH", libpath);
1190             }
1191             String result = getenvInChild(pb);
1192             if (MacOSX.is()) {
1193                 result = removeMacExpectedVars(result);
1194             }
1195             if (AIX.is()) {
1196                 result = removeAixExpectedVars(result);
1197             }
1198             equal(result, expected);
1199         } catch (Throwable t) { unexpected(t); }
1200 
1201         //----------------------------------------------------------------
1202         // System.getenv() is read-only.
1203         //----------------------------------------------------------------
1204         THROWS(UnsupportedOperationException.class,
1205             new Fun(){void f(){ getenv().put("FOO","BAR");}},
1206             new Fun(){void f(){ getenv().remove("PATH");}},
1207             new Fun(){void f(){ getenv().keySet().remove("PATH");}},
1208             new Fun(){void f(){ getenv().values().remove("someValue");}});
1209 
1210         try {
1211             Collection<Map.Entry<String,String>> c = getenv().entrySet();
1212             if (! c.isEmpty())
1213                 try {
1214                     c.iterator().next().setValue("foo");
1215                     fail("Expected UnsupportedOperationException not thrown");
1216                 } catch (UnsupportedOperationException e) {} // OK
1217         } catch (Throwable t) { unexpected(t); }
1218 
1219         //----------------------------------------------------------------
1220         // System.getenv() always returns the same object in our implementation.
1221         //----------------------------------------------------------------
1222         try {
1223             check(System.getenv() == System.getenv());
1224         } catch (Throwable t) { unexpected(t); }
1225 
1226         //----------------------------------------------------------------
1227         // You can't create an env var name containing "=",
1228         // or an env var name or value containing NUL.
1229         //----------------------------------------------------------------
1230         {
1231             final Map<String,String> m = new ProcessBuilder().environment();
1232             THROWS(IllegalArgumentException.class,
1233                 new Fun(){void f(){ m.put("FOO=","BAR");}},
1234                 new Fun(){void f(){ m.put("FOO\u0000","BAR");}},
1235                 new Fun(){void f(){ m.put("FOO","BAR\u0000");}});
1236         }
1237 
1238         //----------------------------------------------------------------
1239         // Commands must never be null.
1240         //----------------------------------------------------------------
1241         THROWS(NullPointerException.class,
1242                new Fun(){void f(){
1243                    new ProcessBuilder((List<String>)null);}},
1244                new Fun(){void f(){
1245                    new ProcessBuilder().command((List<String>)null);}});
1246 
1247         //----------------------------------------------------------------
1248         // Put in a command; get the same one back out.
1249         //----------------------------------------------------------------
1250         try {
1251             List<String> command = new ArrayList<String>();
1252             ProcessBuilder pb = new ProcessBuilder(command);
1253             check(pb.command() == command);
1254             List<String> command2 = new ArrayList<String>(2);
1255             command2.add("foo");
1256             command2.add("bar");
1257             pb.command(command2);
1258             check(pb.command() == command2);
1259             pb.command("foo", "bar");
1260             check(pb.command() != command2 && pb.command().equals(command2));
1261             pb.command(command2);
1262             command2.add("baz");
1263             equal(pb.command().get(2), "baz");
1264         } catch (Throwable t) { unexpected(t); }
1265 
1266         //----------------------------------------------------------------
1267         // Commands must contain at least one element.
1268         //----------------------------------------------------------------
1269         THROWS(IndexOutOfBoundsException.class,
1270             new Fun() { void f() throws IOException {
1271                 new ProcessBuilder().start();}},
1272             new Fun() { void f() throws IOException {
1273                 new ProcessBuilder(new ArrayList<String>()).start();}},
1274             new Fun() { void f() throws IOException {
1275                 Runtime.getRuntime().exec(new String[]{});}});
1276 
1277         //----------------------------------------------------------------
1278         // Commands must not contain null elements at start() time.
1279         //----------------------------------------------------------------
1280         THROWS(NullPointerException.class,
1281             new Fun() { void f() throws IOException {
1282                 new ProcessBuilder("foo",null,"bar").start();}},
1283             new Fun() { void f() throws IOException {
1284                 new ProcessBuilder((String)null).start();}},
1285             new Fun() { void f() throws IOException {
1286                 new ProcessBuilder(new String[]{null}).start();}},
1287             new Fun() { void f() throws IOException {
1288                 new ProcessBuilder(new String[]{"foo",null,"bar"}).start();}});
1289 
1290         //----------------------------------------------------------------
1291         // Command lists are growable.
1292         //----------------------------------------------------------------
1293         try {
1294             new ProcessBuilder().command().add("foo");
1295             new ProcessBuilder("bar").command().add("foo");
1296             new ProcessBuilder(new String[]{"1","2"}).command().add("3");
1297         } catch (Throwable t) { unexpected(t); }
1298 
1299         //----------------------------------------------------------------
1300         // Nulls in environment updates generate NullPointerException
1301         //----------------------------------------------------------------
1302         try {
1303             final Map<String,String> env = new ProcessBuilder().environment();
1304             THROWS(NullPointerException.class,
1305                 new Fun(){void f(){ env.put("foo",null);}},
1306                 new Fun(){void f(){ env.put(null,"foo");}},
1307                 new Fun(){void f(){ env.remove(null);}},
1308                 new Fun(){void f(){
1309                     for (Map.Entry<String,String> e : env.entrySet())
1310                         e.setValue(null);}},
1311                 new Fun() { void f() throws IOException {
1312                     Runtime.getRuntime().exec(new String[]{"foo"},
1313                                               new String[]{null});}});
1314         } catch (Throwable t) { unexpected(t); }
1315 
1316         //----------------------------------------------------------------
1317         // Non-String types in environment updates generate ClassCastException
1318         //----------------------------------------------------------------
1319         try {
1320             final Map<String,String> env = new ProcessBuilder().environment();
1321             THROWS(ClassCastException.class,
1322                 new Fun(){void f(){ env.remove(TRUE);}},
1323                 new Fun(){void f(){ env.keySet().remove(TRUE);}},
1324                 new Fun(){void f(){ env.values().remove(TRUE);}},
1325                 new Fun(){void f(){ env.entrySet().remove(TRUE);}});
1326         } catch (Throwable t) { unexpected(t); }
1327 
1328         //----------------------------------------------------------------
1329         // Check query operations on environment maps
1330         //----------------------------------------------------------------
1331         try {
1332             List<Map<String,String>> envs =
1333                 new ArrayList<Map<String,String>>(2);
1334             envs.add(System.getenv());
1335             envs.add(new ProcessBuilder().environment());
1336             for (final Map<String,String> env : envs) {
1337                 //----------------------------------------------------------------
1338                 // Nulls in environment queries are forbidden.
1339                 //----------------------------------------------------------------
1340                 THROWS(NullPointerException.class,
1341                     new Fun(){void f(){ getenv(null);}},
1342                     new Fun(){void f(){ env.get(null);}},
1343                     new Fun(){void f(){ env.containsKey(null);}},
1344                     new Fun(){void f(){ env.containsValue(null);}},
1345                     new Fun(){void f(){ env.keySet().contains(null);}},
1346                     new Fun(){void f(){ env.values().contains(null);}});
1347 
1348                 //----------------------------------------------------------------
1349                 // Non-String types in environment queries are forbidden.
1350                 //----------------------------------------------------------------
1351                 THROWS(ClassCastException.class,
1352                     new Fun(){void f(){ env.get(TRUE);}},
1353                     new Fun(){void f(){ env.containsKey(TRUE);}},
1354                     new Fun(){void f(){ env.containsValue(TRUE);}},
1355                     new Fun(){void f(){ env.keySet().contains(TRUE);}},
1356                     new Fun(){void f(){ env.values().contains(TRUE);}});
1357 
1358                 //----------------------------------------------------------------
1359                 // Illegal String values in environment queries are (grumble) OK
1360                 //----------------------------------------------------------------
1361                 equal(env.get("\u0000"), null);
1362                 check(! env.containsKey("\u0000"));
1363                 check(! env.containsValue("\u0000"));
1364                 check(! env.keySet().contains("\u0000"));
1365                 check(! env.values().contains("\u0000"));
1366             }
1367 
1368         } catch (Throwable t) { unexpected(t); }
1369 
1370         try {
1371             final Set<Map.Entry<String,String>> entrySet =
1372                 new ProcessBuilder().environment().entrySet();
1373             THROWS(NullPointerException.class,
1374                    new Fun(){void f(){ entrySet.contains(null);}});
1375             THROWS(ClassCastException.class,
1376                 new Fun(){void f(){ entrySet.contains(TRUE);}},
1377                 new Fun(){void f(){
1378                     entrySet.contains(
1379                         new SimpleImmutableEntry<Boolean,String>(TRUE,""));}});
1380 
1381             check(! entrySet.contains
1382                   (new SimpleImmutableEntry<String,String>("", "")));
1383         } catch (Throwable t) { unexpected(t); }
1384 
1385         //----------------------------------------------------------------
1386         // Put in a directory; get the same one back out.
1387         //----------------------------------------------------------------
1388         try {
1389             ProcessBuilder pb = new ProcessBuilder();
1390             File foo = new File("foo");
1391             equal(pb.directory(), null);
1392             equal(pb.directory(foo).directory(), foo);
1393             equal(pb.directory(null).directory(), null);
1394         } catch (Throwable t) { unexpected(t); }
1395 
1396         //----------------------------------------------------------------
1397         // If round-trip conversion works, check envvar pass-through to child
1398         //----------------------------------------------------------------
1399         try {
1400             testEncoding("ASCII",   "xyzzy");
1401             testEncoding("Latin1",  "\u00f1\u00e1");
1402             testEncoding("Unicode", "\u22f1\u11e1");
1403         } catch (Throwable t) { unexpected(t); }
1404 
1405         //----------------------------------------------------------------
1406         // A surprisingly large number of ways to delete an environment var.
1407         //----------------------------------------------------------------
1408         testVariableDeleter(new EnvironmentFrobber() {
1409                 public void doIt(Map<String,String> environ) {
1410                     environ.remove("Foo");}});
1411 
1412         testVariableDeleter(new EnvironmentFrobber() {
1413                 public void doIt(Map<String,String> environ) {
1414                     environ.keySet().remove("Foo");}});
1415 
1416         testVariableDeleter(new EnvironmentFrobber() {
1417                 public void doIt(Map<String,String> environ) {
1418                     environ.values().remove("BAAR");}});
1419 
1420         testVariableDeleter(new EnvironmentFrobber() {
1421                 public void doIt(Map<String,String> environ) {
1422                     // Legally fabricate a ProcessEnvironment.StringEntry,
1423                     // even though it's private.
1424                     Map<String,String> environ2
1425                         = new ProcessBuilder().environment();
1426                     environ2.clear();
1427                     environ2.put("Foo","BAAR");
1428                     // Subtlety alert.
1429                     Map.Entry<String,String> e
1430                         = environ2.entrySet().iterator().next();
1431                     environ.entrySet().remove(e);}});
1432 
1433         testVariableDeleter(new EnvironmentFrobber() {
1434                 public void doIt(Map<String,String> environ) {
1435                     Map.Entry<String,String> victim = null;
1436                     for (Map.Entry<String,String> e : environ.entrySet())
1437                         if (e.getKey().equals("Foo"))
1438                             victim = e;
1439                     if (victim != null)
1440                         environ.entrySet().remove(victim);}});
1441 
1442         testVariableDeleter(new EnvironmentFrobber() {
1443                 public void doIt(Map<String,String> environ) {
1444                     Iterator<String> it = environ.keySet().iterator();
1445                     while (it.hasNext()) {
1446                         String val = it.next();
1447                         if (val.equals("Foo"))
1448                             it.remove();}}});
1449 
1450         testVariableDeleter(new EnvironmentFrobber() {
1451                 public void doIt(Map<String,String> environ) {
1452                     Iterator<Map.Entry<String,String>> it
1453                         = environ.entrySet().iterator();
1454                     while (it.hasNext()) {
1455                         Map.Entry<String,String> e = it.next();
1456                         if (e.getKey().equals("Foo"))
1457                             it.remove();}}});
1458 
1459         testVariableDeleter(new EnvironmentFrobber() {
1460                 public void doIt(Map<String,String> environ) {
1461                     Iterator<String> it = environ.values().iterator();
1462                     while (it.hasNext()) {
1463                         String val = it.next();
1464                         if (val.equals("BAAR"))
1465                             it.remove();}}});
1466 
1467         //----------------------------------------------------------------
1468         // A surprisingly small number of ways to add an environment var.
1469         //----------------------------------------------------------------
1470         testVariableAdder(new EnvironmentFrobber() {
1471                 public void doIt(Map<String,String> environ) {
1472                     environ.put("Foo","Bahrein");}});
1473 
1474         //----------------------------------------------------------------
1475         // A few ways to modify an environment var.
1476         //----------------------------------------------------------------
1477         testVariableModifier(new EnvironmentFrobber() {
1478                 public void doIt(Map<String,String> environ) {
1479                     environ.put("Foo","NewValue");}});
1480 
1481         testVariableModifier(new EnvironmentFrobber() {
1482                 public void doIt(Map<String,String> environ) {
1483                     for (Map.Entry<String,String> e : environ.entrySet())
1484                         if (e.getKey().equals("Foo"))
1485                             e.setValue("NewValue");}});
1486 
1487         //----------------------------------------------------------------
1488         // Fiddle with environment sizes
1489         //----------------------------------------------------------------
1490         try {
1491             Map<String,String> environ = new ProcessBuilder().environment();
1492             int size = environ.size();
1493             checkSizes(environ, size);
1494 
1495             environ.put("UnLiKeLYeNVIROmtNam", "someVal");
1496             checkSizes(environ, size+1);
1497 
1498             // Check for environment independence
1499             new ProcessBuilder().environment().clear();
1500 
1501             environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal");
1502             checkSizes(environ, size+1);
1503 
1504             environ.remove("UnLiKeLYeNVIROmtNam");
1505             checkSizes(environ, size);
1506 
1507             environ.clear();
1508             checkSizes(environ, 0);
1509 
1510             environ.clear();
1511             checkSizes(environ, 0);
1512 
1513             environ = new ProcessBuilder().environment();
1514             environ.keySet().clear();
1515             checkSizes(environ, 0);
1516 
1517             environ = new ProcessBuilder().environment();
1518             environ.entrySet().clear();
1519             checkSizes(environ, 0);
1520 
1521             environ = new ProcessBuilder().environment();
1522             environ.values().clear();
1523             checkSizes(environ, 0);
1524         } catch (Throwable t) { unexpected(t); }
1525 
1526         //----------------------------------------------------------------
1527         // Check that various map invariants hold
1528         //----------------------------------------------------------------
1529         checkMapSanity(new ProcessBuilder().environment());
1530         checkMapSanity(System.getenv());
1531         checkMapEquality(new ProcessBuilder().environment(),
1532                          new ProcessBuilder().environment());
1533 
1534 
1535         //----------------------------------------------------------------
1536         // Check effects on external "env" command.
1537         //----------------------------------------------------------------
1538         try {
1539             Set<String> env1 = new HashSet<String>
1540                 (Arrays.asList(nativeEnv((String[])null).split("\n")));
1541 
1542             ProcessBuilder pb = new ProcessBuilder();
1543             pb.environment().put("QwErTyUiOp","AsDfGhJk");
1544 
1545             Set<String> env2 = new HashSet<String>
1546                 (Arrays.asList(nativeEnv(pb).split("\n")));
1547 
1548             check(env2.size() == env1.size() + 1);
1549             env1.add("QwErTyUiOp=AsDfGhJk");
1550             check(env1.equals(env2));
1551         } catch (Throwable t) { unexpected(t); }
1552 
1553         //----------------------------------------------------------------
1554         // Test Runtime.exec(...envp...)
1555         // Check for sort order of environment variables on Windows.
1556         //----------------------------------------------------------------
1557         try {
1558             String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1559             // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'
1560             String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1561                             "+=+", "_=_", "~=~", systemRoot};
1562             String output = nativeEnv(envp);
1563             String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1564             // On Windows, Java must keep the environment sorted.
1565             // Order is random on Unix, so this test does the sort.
1566             if (! Windows.is())
1567                 output = sortByLinesWindowsly(output);
1568             equal(output, expected);
1569         } catch (Throwable t) { unexpected(t); }
1570 
1571         //----------------------------------------------------------------
1572         // Test Runtime.exec(...envp...)
1573         // and check SystemRoot gets set automatically on Windows
1574         //----------------------------------------------------------------
1575         try {
1576             if (Windows.is()) {
1577                 String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1578                 String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1579                                 "+=+", "_=_", "~=~"};
1580                 String output = nativeEnv(envp);
1581                 String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1582                 equal(output, expected);
1583             }
1584         } catch (Throwable t) { unexpected(t); }
1585 
1586         //----------------------------------------------------------------
1587         // System.getenv() must be consistent with System.getenv(String)
1588         //----------------------------------------------------------------
1589         try {
1590             for (Map.Entry<String,String> e : getenv().entrySet())
1591                 equal(getenv(e.getKey()), e.getValue());
1592         } catch (Throwable t) { unexpected(t); }
1593 
1594         //----------------------------------------------------------------
1595         // Fiddle with working directory in child
1596         //----------------------------------------------------------------
1597         try {
1598             String canonicalUserDir =
1599                 new File(System.getProperty("user.dir")).getCanonicalPath();
1600             String[] sdirs = new String[]
1601                 {".", "..", "/", "/bin",
1602                  "C:", "c:", "C:/", "c:\\", "\\", "\\bin",
1603                  "c:\\windows  ", "c:\\Program Files", "c:\\Program Files\\" };
1604             for (String sdir : sdirs) {
1605                 File dir = new File(sdir);
1606                 if (! (dir.isDirectory() && dir.exists()))
1607                     continue;
1608                 out.println("Testing directory " + dir);
1609                 //dir = new File(dir.getCanonicalPath());
1610 
1611                 ProcessBuilder pb = new ProcessBuilder();
1612                 equal(pb.directory(), null);
1613                 equal(pwdInChild(pb), canonicalUserDir);
1614 
1615                 pb.directory(dir);
1616                 equal(pb.directory(), dir);
1617                 equal(pwdInChild(pb), dir.getCanonicalPath());
1618 
1619                 pb.directory(null);
1620                 equal(pb.directory(), null);
1621                 equal(pwdInChild(pb), canonicalUserDir);
1622 
1623                 pb.directory(dir);
1624             }
1625         } catch (Throwable t) { unexpected(t); }
1626 
1627         //----------------------------------------------------------------
1628         // Working directory with Unicode in child
1629         //----------------------------------------------------------------
1630         try {
1631             if (UnicodeOS.is()) {
1632                 File dir = new File(System.getProperty("test.dir", "."),
1633                                     "ProcessBuilderDir\u4e00\u4e02");
1634                 try {
1635                     if (!dir.exists())
1636                         dir.mkdir();
1637                     out.println("Testing Unicode directory:" + dir);
1638                     ProcessBuilder pb = new ProcessBuilder();
1639                     pb.directory(dir);
1640                     equal(pwdInChild(pb), dir.getCanonicalPath());
1641                 } finally {
1642                     if (dir.exists())
1643                         dir.delete();
1644                 }
1645             }
1646         } catch (Throwable t) { unexpected(t); }
1647 
1648         //----------------------------------------------------------------
1649         // OOME in child allocating maximally sized array
1650         // Test for hotspot/jvmti bug 6850957
1651         //----------------------------------------------------------------
1652         try {
1653             List<String> list = new ArrayList<String>(javaChildArgs);
1654             list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",
1655                                       javaExe));
1656             list.add("ArrayOOME");
1657             ProcessResults r = run(new ProcessBuilder(list));
1658             check(r.out().contains("java.lang.OutOfMemoryError:"));
1659             check(r.out().contains(javaExe));
1660             check(r.err().contains(System.getProperty("java.version")));
1661             equal(r.exitValue(), 1);
1662         } catch (Throwable t) { unexpected(t); }
1663 
1664         //----------------------------------------------------------------
1665         // Windows has tricky semi-case-insensitive semantics
1666         //----------------------------------------------------------------
1667         if (Windows.is())
1668             try {
1669                 out.println("Running case insensitve variable tests");
1670                 for (String[] namePair :
1671                          new String[][]
1672                     { new String[]{"PATH","PaTh"},
1673                       new String[]{"home","HOME"},
1674                       new String[]{"SYSTEMROOT","SystemRoot"}}) {
1675                     check((getenv(namePair[0]) == null &&
1676                            getenv(namePair[1]) == null)
1677                           ||
1678                           getenv(namePair[0]).equals(getenv(namePair[1])),
1679                           "Windows environment variables are not case insensitive");
1680                 }
1681             } catch (Throwable t) { unexpected(t); }
1682 
1683         //----------------------------------------------------------------
1684         // Test proper Unicode child environment transfer
1685         //----------------------------------------------------------------
1686         if (UnicodeOS.is())
1687             try {
1688                 ProcessBuilder pb = new ProcessBuilder();
1689                 pb.environment().put("\u1234","\u5678");
1690                 pb.environment().remove("PATH");
1691                 equal(getenvInChild1234(pb), "\u5678");
1692             } catch (Throwable t) { unexpected(t); }
1693 
1694 
1695         //----------------------------------------------------------------
1696         // Test Runtime.exec(...envp...) with envstrings with initial `='
1697         //----------------------------------------------------------------
1698         try {
1699             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1700             childArgs.add("System.getenv()");
1701             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1702             String[] envp;
1703             String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot};
1704             String[] envpOth = {"=ExitValue=3", "=C:=\\"};
1705             if (Windows.is()) {
1706                 envp = envpWin;
1707             } else {
1708                 envp = envpOth;
1709             }
1710             Process p = Runtime.getRuntime().exec(cmdp, envp);
1711             String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,";
1712             expected = AIX.is() ? expected + "LIBPATH="+libpath+",": expected;
1713             String commandOutput = commandOutput(p);
1714             if (MacOSX.is()) {
1715                 commandOutput = removeMacExpectedVars(commandOutput);
1716             }
1717             if (AIX.is()) {
1718                 commandOutput = removeAixExpectedVars(commandOutput);
1719             }
1720             equal(commandOutput, expected);
1721             if (Windows.is()) {
1722                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1723                 pb.environment().clear();
1724                 pb.environment().put("SystemRoot", systemRoot);
1725                 pb.environment().put("=ExitValue", "3");
1726                 pb.environment().put("=C:", "\\");
1727                 equal(commandOutput(pb), expected);
1728             }
1729         } catch (Throwable t) { unexpected(t); }
1730 
1731         //----------------------------------------------------------------
1732         // Test Runtime.exec(...envp...) with envstrings without any `='
1733         //----------------------------------------------------------------
1734         try {
1735             String[] cmdp = {"echo"};
1736             String[] envp = {"Hello", "World"}; // Yuck!
1737             Process p = Runtime.getRuntime().exec(cmdp, envp);
1738             equal(commandOutput(p), "\n");
1739         } catch (Throwable t) { unexpected(t); }
1740 
1741         //----------------------------------------------------------------
1742         // Test Runtime.exec(...envp...) with envstrings containing NULs
1743         //----------------------------------------------------------------
1744         try {
1745             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1746             childArgs.add("System.getenv()");
1747             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1748             String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck!
1749                              "FO\u0000=B\u0000R"};
1750             String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck!
1751                              "FO\u0000=B\u0000R"};
1752             String[] envp;
1753             if (Windows.is()) {
1754                 envp = envpWin;
1755             } else {
1756                 envp = envpOth;
1757             }
1758             System.out.println ("cmdp");
1759             for (int i=0; i<cmdp.length; i++) {
1760                 System.out.printf ("cmdp %d: %s\n", i, cmdp[i]);
1761             }
1762             System.out.println ("envp");
1763             for (int i=0; i<envp.length; i++) {
1764                 System.out.printf ("envp %d: %s\n", i, envp[i]);
1765             }
1766             Process p = Runtime.getRuntime().exec(cmdp, envp);
1767             String commandOutput = commandOutput(p);
1768             if (MacOSX.is()) {
1769                 commandOutput = removeMacExpectedVars(commandOutput);
1770             }
1771             if (AIX.is()) {
1772                 commandOutput = removeAixExpectedVars(commandOutput);
1773             }
1774             check(commandOutput.equals(Windows.is()
1775                     ? "LC_ALL=C,SystemRoot="+systemRoot+","
1776                     : AIX.is()
1777                             ? "LC_ALL=C,LIBPATH="+libpath+","
1778                             : "LC_ALL=C,"),
1779                   "Incorrect handling of envstrings containing NULs");
1780         } catch (Throwable t) { unexpected(t); }
1781 
1782         //----------------------------------------------------------------
1783         // Test the redirectErrorStream property
1784         //----------------------------------------------------------------
1785         try {
1786             ProcessBuilder pb = new ProcessBuilder();
1787             equal(pb.redirectErrorStream(), false);
1788             equal(pb.redirectErrorStream(true), pb);
1789             equal(pb.redirectErrorStream(), true);
1790             equal(pb.redirectErrorStream(false), pb);
1791             equal(pb.redirectErrorStream(), false);
1792         } catch (Throwable t) { unexpected(t); }
1793 
1794         try {
1795             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1796             childArgs.add("OutErr");
1797             ProcessBuilder pb = new ProcessBuilder(childArgs);
1798             {
1799                 ProcessResults r = run(pb);
1800                 equal(r.out(), "outout");
1801                 equal(r.err(), "errerr");
1802             }
1803             {
1804                 pb.redirectErrorStream(true);
1805                 ProcessResults r = run(pb);
1806                 equal(r.out(), "outerrouterr");
1807                 equal(r.err(), "");
1808             }
1809         } catch (Throwable t) { unexpected(t); }
1810 
1811         if (Unix.is()) {
1812             //----------------------------------------------------------------
1813             // We can find true and false when PATH is null
1814             //----------------------------------------------------------------
1815             try {
1816                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1817                 childArgs.add("null PATH");
1818                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1819                 pb.environment().remove("PATH");
1820                 ProcessResults r = run(pb);
1821                 equal(r.out(), "");
1822                 equal(r.err(), "");
1823                 equal(r.exitValue(), 0);
1824             } catch (Throwable t) { unexpected(t); }
1825 
1826             //----------------------------------------------------------------
1827             // PATH search algorithm on Unix
1828             //----------------------------------------------------------------
1829             try {
1830                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1831                 childArgs.add("PATH search algorithm");
1832                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1833                 pb.environment().put("PATH", "dir1:dir2:");
1834                 ProcessResults r = run(pb);
1835                 equal(r.out(), "");
1836                 equal(r.err(), "");
1837                 equal(r.exitValue(), True.exitValue());
1838             } catch (Throwable t) { unexpected(t); }
1839 
1840             //----------------------------------------------------------------
1841             // Parent's, not child's PATH is used
1842             //----------------------------------------------------------------
1843             try {
1844                 new File("suBdiR").mkdirs();
1845                 copy("/bin/true", "suBdiR/unliKely");
1846                 final ProcessBuilder pb =
1847                     new ProcessBuilder(new String[]{"unliKely"});
1848                 pb.environment().put("PATH", "suBdiR");
1849                 THROWS(IOException.class,
1850                        new Fun() {void f() throws Throwable {pb.start();}});
1851             } catch (Throwable t) { unexpected(t);
1852             } finally {
1853                 new File("suBdiR/unliKely").delete();
1854                 new File("suBdiR").delete();
1855             }
1856         }
1857 
1858         //----------------------------------------------------------------
1859         // Attempt to start bogus program ""
1860         //----------------------------------------------------------------
1861         try {
1862             new ProcessBuilder("").start();
1863             fail("Expected IOException not thrown");
1864         } catch (IOException e) {
1865             String m = e.getMessage();
1866             if (EnglishUnix.is() &&
1867                 ! matches(m, "No such file or directory"))
1868                 unexpected(e);
1869         } catch (Throwable t) { unexpected(t); }
1870 
1871         //----------------------------------------------------------------
1872         // Check that attempt to execute program name with funny
1873         // characters throws an exception containing those characters.
1874         //----------------------------------------------------------------
1875         for (String programName : new String[] {"\u00f0", "\u01f0"})
1876             try {
1877                 new ProcessBuilder(programName).start();
1878                 fail("Expected IOException not thrown");
1879             } catch (IOException e) {
1880                 String m = e.getMessage();
1881                 Pattern p = Pattern.compile(programName);
1882                 if (! matches(m, programName)
1883                     || (EnglishUnix.is()
1884                         && ! matches(m, "No such file or directory")))
1885                     unexpected(e);
1886             } catch (Throwable t) { unexpected(t); }
1887 
1888         //----------------------------------------------------------------
1889         // Attempt to start process in nonexistent directory fails.
1890         //----------------------------------------------------------------
1891         try {
1892             new ProcessBuilder("echo")
1893                 .directory(new File("UnLiKeLY"))
1894                 .start();
1895             fail("Expected IOException not thrown");
1896         } catch (IOException e) {
1897             String m = e.getMessage();
1898             if (! matches(m, "in directory")
1899                 || (EnglishUnix.is() &&
1900                     ! matches(m, "No such file or directory")))
1901                 unexpected(e);
1902         } catch (Throwable t) { unexpected(t); }
1903 
1904         //----------------------------------------------------------------
1905         // Attempt to write 4095 bytes to the pipe buffer without a
1906         // reader to drain it would deadlock, if not for the fact that
1907         // interprocess pipe buffers are at least 4096 bytes.
1908         //
1909         // Also, check that available reports all the bytes expected
1910         // in the pipe buffer, and that I/O operations do the expected
1911         // things.
1912         //----------------------------------------------------------------
1913         try {
1914             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1915             childArgs.add("print4095");
1916             final int SIZE = 4095;
1917             final Process p = new ProcessBuilder(childArgs).start();
1918             print4095(p.getOutputStream(), (byte) '!'); // Might hang!
1919             p.waitFor();                                // Might hang!
1920             equal(SIZE, p.getInputStream().available());
1921             equal(SIZE, p.getErrorStream().available());
1922             THROWS(IOException.class,
1923                    new Fun(){void f() throws IOException {
1924                        p.getOutputStream().write((byte) '!');
1925                        p.getOutputStream().flush();
1926                        }});
1927 
1928             final byte[] bytes = new byte[SIZE + 1];
1929             equal(SIZE, p.getInputStream().read(bytes));
1930             for (int i = 0; i < SIZE; i++)
1931                 equal((byte) '!', bytes[i]);
1932             equal((byte) 0, bytes[SIZE]);
1933 
1934             equal(SIZE, p.getErrorStream().read(bytes));
1935             for (int i = 0; i < SIZE; i++)
1936                 equal((byte) 'E', bytes[i]);
1937             equal((byte) 0, bytes[SIZE]);
1938 
1939             equal(0, p.getInputStream().available());
1940             equal(0, p.getErrorStream().available());
1941             equal(-1, p.getErrorStream().read());
1942             equal(-1, p.getInputStream().read());
1943 
1944             equal(p.exitValue(), 5);
1945 
1946             p.getInputStream().close();
1947             p.getErrorStream().close();
1948             try { p.getOutputStream().close(); } catch (IOException flushFailed) { }
1949 
1950             InputStream[] streams = { p.getInputStream(), p.getErrorStream() };
1951             for (final InputStream in : streams) {
1952                 Fun[] ops = {
1953                     new Fun(){void f() throws IOException {
1954                         in.read(); }},
1955                     new Fun(){void f() throws IOException {
1956                         in.read(bytes); }},
1957                     new Fun(){void f() throws IOException {
1958                         in.available(); }}
1959                 };
1960                 for (Fun op : ops) {
1961                     try {
1962                         op.f();
1963                         fail();
1964                     } catch (IOException expected) {
1965                         check(expected.getMessage()
1966                               .matches("[Ss]tream [Cc]losed"));
1967                     }
1968                 }
1969             }
1970         } catch (Throwable t) { unexpected(t); }
1971 
1972         //----------------------------------------------------------------
1973         // Check that reads which are pending when Process.destroy is
1974         // called, get EOF, not IOException("Stream closed").
1975         //----------------------------------------------------------------
1976         try {
1977             final int cases = 4;
1978             for (int i = 0; i < cases; i++) {
1979                 final int action = i;
1980                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1981                 childArgs.add("sleep");
1982                 final byte[] bytes = new byte[10];
1983                 final Process p = new ProcessBuilder(childArgs).start();
1984                 final CountDownLatch latch = new CountDownLatch(1);
1985                 final InputStream s;
1986                 switch (action & 0x1) {
1987                     case 0: s = p.getInputStream(); break;
1988                     case 1: s = p.getErrorStream(); break;
1989                     default: throw new Error();
1990                 }
1991                 final Thread thread = new Thread() {
1992                     public void run() {
1993                         try {
1994                             int r;
1995                             latch.countDown();
1996                             switch (action & 0x2) {
1997                                 case 0: r = s.read(); break;
1998                                 case 2: r = s.read(bytes); break;
1999                                 default: throw new Error();
2000                             }
2001                             equal(-1, r);
2002                         } catch (Throwable t) { unexpected(t); }}};
2003 
2004                 thread.start();
2005                 latch.await();
2006                 Thread.sleep(10);
2007 
2008                 String os = System.getProperty("os.name");
2009                 if (os.equalsIgnoreCase("Solaris") ||
2010                     os.equalsIgnoreCase("SunOS"))
2011                 {
2012                     final Object deferred;
2013                     Class<?> c = s.getClass();
2014                     if (c.getName().equals(
2015                         "java.lang.UNIXProcess$DeferredCloseInputStream"))
2016                     {
2017                         deferred = s;
2018                     } else {
2019                         Field deferredField = p.getClass().
2020                             getDeclaredField("stdout_inner_stream");
2021                         deferredField.setAccessible(true);
2022                         deferred = deferredField.get(p);
2023                     }
2024                     Field useCountField = deferred.getClass().
2025                         getDeclaredField("useCount");
2026                     useCountField.setAccessible(true);
2027 
2028                     while (useCountField.getInt(deferred) <= 0) {
2029                         Thread.yield();
2030                     }
2031                 } else if (s instanceof BufferedInputStream) {
2032                     Field f = Unsafe.class.getDeclaredField("theUnsafe");
2033                     f.setAccessible(true);
2034                     Unsafe unsafe = (Unsafe)f.get(null);
2035 
2036                     while (unsafe.tryMonitorEnter(s)) {
2037                         unsafe.monitorExit(s);
2038                         Thread.sleep(1);
2039                     }
2040                 }
2041                 p.destroy();
2042                 thread.join();
2043             }
2044         } catch (Throwable t) { unexpected(t); }
2045 
2046         //----------------------------------------------------------------
2047         // Check that subprocesses which create subprocesses of their
2048         // own do not cause parent to hang waiting for file
2049         // descriptors to be closed.
2050         //----------------------------------------------------------------
2051         try {
2052             if (Unix.is()
2053                 && new File("/bin/bash").exists()
2054                 && new File("/bin/sleep").exists()) {
2055                 // Notice that we only destroy the process created by us (i.e.
2056                 // our child) but not our grandchild (i.e. '/bin/sleep'). So
2057                 // pay attention that the grandchild doesn't run too long to
2058                 // avoid polluting the process space with useless processes.
2059                 // Running the grandchild for 60s should be more than enough.
2060                 final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" };
2061                 final ProcessBuilder pb = new ProcessBuilder(cmd);
2062                 final Process p = pb.start();
2063                 final InputStream stdout = p.getInputStream();
2064                 final InputStream stderr = p.getErrorStream();
2065                 final OutputStream stdin = p.getOutputStream();
2066                 final Thread reader = new Thread() {
2067                     public void run() {
2068                         try { stdout.read(); }
2069                         catch (IOException e) {
2070                             // Check that reader failed because stream was
2071                             // asynchronously closed.
2072                             // e.printStackTrace();
2073                             if (EnglishUnix.is() &&
2074                                 ! (e.getMessage().matches(".*Bad file.*")))
2075                                 unexpected(e);
2076                         }
2077                         catch (Throwable t) { unexpected(t); }}};
2078                 reader.setDaemon(true);
2079                 reader.start();
2080                 Thread.sleep(100);
2081                 p.destroy();
2082                 check(p.waitFor() != 0);
2083                 check(p.exitValue() != 0);
2084                 // Subprocess is now dead, but file descriptors remain open.
2085                 // Make sure the test will fail if we don't manage to close
2086                 // the open streams within 30 seconds. Notice that this time
2087                 // must be shorter than the sleep time of the grandchild.
2088                 Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true);
2089                 t.schedule(new TimerTask() {
2090                       public void run() {
2091                           fail("Subprocesses which create subprocesses of " +
2092                                "their own caused the parent to hang while " +
2093                                "waiting for file descriptors to be closed.");
2094                           System.exit(-1);
2095                       }
2096                   }, 30000);
2097                 stdout.close();
2098                 stderr.close();
2099                 stdin.close();
2100                 // All streams successfully closed so we can cancel the timer.
2101                 t.cancel();
2102                 //----------------------------------------------------------
2103                 // There remain unsolved issues with asynchronous close.
2104                 // Here's a highly non-portable experiment to demonstrate:
2105                 //----------------------------------------------------------
2106                 if (Boolean.getBoolean("wakeupJeff!")) {
2107                     System.out.println("wakeupJeff!");
2108                     // Initialize signal handler for INTERRUPT_SIGNAL.
2109                     new FileInputStream("/bin/sleep").getChannel().close();
2110                     // Send INTERRUPT_SIGNAL to every thread in this java.
2111                     String[] wakeupJeff = {
2112                         "/bin/bash", "-c",
2113                         "/bin/ps --noheaders -Lfp $PPID | " +
2114                         "/usr/bin/perl -nale 'print $F[3]' | " +
2115                         // INTERRUPT_SIGNAL == 62 on my machine du jour.
2116                         "/usr/bin/xargs kill -62"
2117                     };
2118                     new ProcessBuilder(wakeupJeff).start().waitFor();
2119                     // If wakeupJeff worked, reader probably got EBADF.
2120                     reader.join();
2121                 }
2122             }
2123         } catch (Throwable t) { unexpected(t); }
2124 
2125         //----------------------------------------------------------------
2126         // Attempt to start process with insufficient permissions fails.
2127         //----------------------------------------------------------------
2128         try {
2129             new File("emptyCommand").delete();
2130             new FileOutputStream("emptyCommand").close();
2131             new File("emptyCommand").setExecutable(false);
2132             new ProcessBuilder("./emptyCommand").start();
2133             fail("Expected IOException not thrown");
2134         } catch (IOException e) {
2135             new File("./emptyCommand").delete();
2136             String m = e.getMessage();
2137             if (EnglishUnix.is() &&
2138                 ! matches(m, "Permission denied"))
2139                 unexpected(e);
2140         } catch (Throwable t) { unexpected(t); }
2141 
2142         new File("emptyCommand").delete();
2143 
2144         //----------------------------------------------------------------
2145         // Check for correct security permission behavior
2146         //----------------------------------------------------------------
2147         final Policy policy = new Policy();
2148         Policy.setPolicy(policy);
2149         System.setSecurityManager(new SecurityManager());
2150 
2151         try {
2152             // No permissions required to CREATE a ProcessBuilder
2153             policy.setPermissions(/* Nothing */);
2154             new ProcessBuilder("env").directory(null).directory();
2155             new ProcessBuilder("env").directory(new File("dir")).directory();
2156             new ProcessBuilder("env").command("??").command();
2157         } catch (Throwable t) { unexpected(t); }
2158 
2159         THROWS(SecurityException.class,
2160             new Fun() { void f() throws IOException {
2161                 policy.setPermissions(/* Nothing */);
2162                 System.getenv("foo");}},
2163             new Fun() { void f() throws IOException {
2164                 policy.setPermissions(/* Nothing */);
2165                 System.getenv();}},
2166             new Fun() { void f() throws IOException {
2167                 policy.setPermissions(/* Nothing */);
2168                 new ProcessBuilder("echo").start();}},
2169             new Fun() { void f() throws IOException {
2170                 policy.setPermissions(/* Nothing */);
2171                 Runtime.getRuntime().exec("echo");}},
2172             new Fun() { void f() throws IOException {
2173                 policy.setPermissions(new RuntimePermission("getenv.bar"));
2174                 System.getenv("foo");}});
2175 
2176         try {
2177             policy.setPermissions(new RuntimePermission("getenv.foo"));
2178             System.getenv("foo");
2179 
2180             policy.setPermissions(new RuntimePermission("getenv.*"));
2181             System.getenv("foo");
2182             System.getenv();
2183             new ProcessBuilder().environment();
2184         } catch (Throwable t) { unexpected(t); }
2185 
2186 
2187         final Permission execPermission
2188             = new FilePermission("<<ALL FILES>>", "execute");
2189 
2190         THROWS(SecurityException.class,
2191             new Fun() { void f() throws IOException {
2192                 // environment permission by itself insufficient
2193                 policy.setPermissions(new RuntimePermission("getenv.*"));
2194                 ProcessBuilder pb = new ProcessBuilder("env");
2195                 pb.environment().put("foo","bar");
2196                 pb.start();}},
2197             new Fun() { void f() throws IOException {
2198                  // exec permission by itself insufficient
2199                  policy.setPermissions(execPermission);
2200                  ProcessBuilder pb = new ProcessBuilder("env");
2201                  pb.environment().put("foo","bar");
2202                  pb.start();}});
2203 
2204         try {
2205             // Both permissions? OK.
2206             policy.setPermissions(new RuntimePermission("getenv.*"),
2207                                   execPermission);
2208             ProcessBuilder pb = new ProcessBuilder("env");
2209             pb.environment().put("foo","bar");
2210             Process p = pb.start();
2211             closeStreams(p);
2212         } catch (IOException e) { // OK
2213         } catch (Throwable t) { unexpected(t); }
2214 
2215         try {
2216             // Don't need environment permission unless READING environment
2217             policy.setPermissions(execPermission);
2218             Runtime.getRuntime().exec("env", new String[]{});
2219         } catch (IOException e) { // OK
2220         } catch (Throwable t) { unexpected(t); }
2221 
2222         try {
2223             // Don't need environment permission unless READING environment
2224             policy.setPermissions(execPermission);
2225             new ProcessBuilder("env").start();
2226         } catch (IOException e) { // OK
2227         } catch (Throwable t) { unexpected(t); }
2228 
2229         // Restore "normal" state without a security manager
2230         policy.setPermissions(new RuntimePermission("setSecurityManager"));
2231         System.setSecurityManager(null);
2232 
2233         //----------------------------------------------------------------
2234         // Check that Process.isAlive() &
2235         // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.
2236         //----------------------------------------------------------------
2237         try {
2238             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2239             childArgs.add("sleep");
2240             final Process p = new ProcessBuilder(childArgs).start();
2241             long start = System.nanoTime();
2242             if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {
2243                 fail("Test failed: Process exited prematurely");
2244             }
2245             long end = System.nanoTime();
2246             // give waitFor(timeout) a wide berth (100ms)
2247             // Old AIX machines my need a little longer.
2248             if ((end - start) > 100000000L * (AIX.is() ? 4 : 1))
2249                 fail("Test failed: waitFor took too long (" + (end - start) + "ns)");
2250 
2251             p.destroy();
2252             p.waitFor();
2253 
2254             if (p.isAlive() ||
2255                 !p.waitFor(0, TimeUnit.MILLISECONDS))
2256             {
2257                 fail("Test failed: Process still alive - please terminate " +
2258                     p.toString() + " manually");
2259             }
2260         } catch (Throwable t) { unexpected(t); }
2261 
2262         //----------------------------------------------------------------
2263         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2264         // works as expected.
2265         //----------------------------------------------------------------
2266         try {
2267             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2268             childArgs.add("sleep");
2269             final Process p = new ProcessBuilder(childArgs).start();
2270             long start = System.nanoTime();
2271 
2272             p.waitFor(1000, TimeUnit.MILLISECONDS);
2273 
2274             long end = System.nanoTime();
2275             if ((end - start) < 500000000)
2276                 fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");
2277 
2278             p.destroy();
2279 
2280             start = System.nanoTime();
2281             p.waitFor(1000, TimeUnit.MILLISECONDS);
2282             end = System.nanoTime();
2283             if ((end - start) > 900000000)
2284                 fail("Test failed: waitFor took too long on a dead process. (" + (end - start) + "ns)");
2285         } catch (Throwable t) { unexpected(t); }
2286 
2287         //----------------------------------------------------------------
2288         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2289         // interrupt works as expected.
2290         //----------------------------------------------------------------
2291         try {
2292             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2293             childArgs.add("sleep");
2294             final Process p = new ProcessBuilder(childArgs).start();
2295             final long start = System.nanoTime();
2296             final CountDownLatch latch = new CountDownLatch(1);
2297 
2298             final Thread thread = new Thread() {
2299                 public void run() {
2300                     try {
2301                         try {
2302                             latch.countDown();
2303                             p.waitFor(10000, TimeUnit.MILLISECONDS);
2304                         } catch (InterruptedException e) {
2305                             return;
2306                         }
2307                         fail("waitFor() wasn't interrupted");
2308                     } catch (Throwable t) { unexpected(t); }}};
2309 
2310             thread.start();
2311             latch.await();
2312             Thread.sleep(1000);
2313             thread.interrupt();
2314             p.destroy();
2315         } catch (Throwable t) { unexpected(t); }
2316 
2317         //----------------------------------------------------------------
2318         // Check the default implementation for
2319         // Process.waitFor(long, TimeUnit)
2320         //----------------------------------------------------------------
2321         try {
2322             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2323             childArgs.add("sleep");
2324             final Process proc = new ProcessBuilder(childArgs).start();
2325             DelegatingProcess p = new DelegatingProcess(proc);
2326             long start = System.nanoTime();
2327 
2328             p.waitFor(1000, TimeUnit.MILLISECONDS);
2329 
2330             long end = System.nanoTime();
2331             if ((end - start) < 500000000)
2332                 fail("Test failed: waitFor didn't take long enough");
2333 
2334             p.destroy();
2335 
2336             p.waitFor(1000, TimeUnit.MILLISECONDS);
2337         } catch (Throwable t) { unexpected(t); }
2338     }
2339 
2340     static void closeStreams(Process p) {
2341         try {
2342             p.getOutputStream().close();
2343             p.getInputStream().close();
2344             p.getErrorStream().close();
2345         } catch (Throwable t) { unexpected(t); }
2346     }
2347 
2348     //----------------------------------------------------------------
2349     // A Policy class designed to make permissions fiddling very easy.
2350     //----------------------------------------------------------------
2351     private static class Policy extends java.security.Policy {
2352         private Permissions perms;
2353 
2354         public void setPermissions(Permission...permissions) {
2355             perms = new Permissions();
2356             for (Permission permission : permissions)
2357                 perms.add(permission);
2358         }
2359 
2360         public Policy() { setPermissions(/* Nothing */); }
2361 
2362         public PermissionCollection getPermissions(CodeSource cs) {
2363             return perms;
2364         }
2365 
2366         public PermissionCollection getPermissions(ProtectionDomain pd) {
2367             return perms;
2368         }
2369 
2370         public boolean implies(ProtectionDomain pd, Permission p) {
2371             return perms.implies(p);
2372         }
2373 
2374         public void refresh() {}
2375     }
2376 
2377     private static class StreamAccumulator extends Thread {
2378         private final InputStream is;
2379         private final StringBuilder sb = new StringBuilder();
2380         private Throwable throwable = null;
2381 
2382         public String result () throws Throwable {
2383             if (throwable != null)
2384                 throw throwable;
2385             return sb.toString();
2386         }
2387 
2388         StreamAccumulator (InputStream is) {
2389             this.is = is;
2390         }
2391 
2392         public void run() {
2393             try {
2394                 Reader r = new InputStreamReader(is);
2395                 char[] buf = new char[4096];
2396                 int n;
2397                 while ((n = r.read(buf)) > 0) {
2398                     sb.append(buf,0,n);
2399                 }
2400             } catch (Throwable t) {
2401                 throwable = t;
2402             } finally {
2403                 try { is.close(); }
2404                 catch (Throwable t) { throwable = t; }
2405             }
2406         }
2407     }
2408 
2409     static ProcessResults run(ProcessBuilder pb) {
2410         try {
2411             return run(pb.start());
2412         } catch (Throwable t) { unexpected(t); return null; }
2413     }
2414 
2415     private static ProcessResults run(Process p) {
2416         Throwable throwable = null;
2417         int exitValue = -1;
2418         String out = "";
2419         String err = "";
2420 
2421         StreamAccumulator outAccumulator =
2422             new StreamAccumulator(p.getInputStream());
2423         StreamAccumulator errAccumulator =
2424             new StreamAccumulator(p.getErrorStream());
2425 
2426         try {
2427             outAccumulator.start();
2428             errAccumulator.start();
2429 
2430             exitValue = p.waitFor();
2431 
2432             outAccumulator.join();
2433             errAccumulator.join();
2434 
2435             out = outAccumulator.result();
2436             err = errAccumulator.result();
2437         } catch (Throwable t) {
2438             throwable = t;
2439         }
2440 
2441         return new ProcessResults(out, err, exitValue, throwable);
2442     }
2443 
2444     //----------------------------------------------------------------
2445     // Results of a command
2446     //----------------------------------------------------------------
2447     private static class ProcessResults {
2448         private final String out;
2449         private final String err;
2450         private final int exitValue;
2451         private final Throwable throwable;
2452 
2453         public ProcessResults(String out,
2454                               String err,
2455                               int exitValue,
2456                               Throwable throwable) {
2457             this.out = out;
2458             this.err = err;
2459             this.exitValue = exitValue;
2460             this.throwable = throwable;
2461         }
2462 
2463         public String out()          { return out; }
2464         public String err()          { return err; }
2465         public int exitValue()       { return exitValue; }
2466         public Throwable throwable() { return throwable; }
2467 
2468         public String toString() {
2469             StringBuilder sb = new StringBuilder();
2470             sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")
2471                 .append("<STDERR>\n" + err() + "</STDERR>\n")
2472                 .append("exitValue = " + exitValue + "\n");
2473             if (throwable != null)
2474                 sb.append(throwable.getStackTrace());
2475             return sb.toString();
2476         }
2477     }
2478 
2479     //--------------------- Infrastructure ---------------------------
2480     static volatile int passed = 0, failed = 0;
2481     static void pass() {passed++;}
2482     static void fail() {failed++; Thread.dumpStack();}
2483     static void fail(String msg) {System.out.println(msg); fail();}
2484     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
2485     static void check(boolean cond) {if (cond) pass(); else fail();}
2486     static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
2487     static void equal(Object x, Object y) {
2488         if (x == null ? y == null : x.equals(y)) pass();
2489         else fail(x + " not equal to " + y);}
2490 
2491     public static void main(String[] args) throws Throwable {
2492         try {realMain(args);} catch (Throwable t) {unexpected(t);}
2493         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
2494         if (failed > 0) throw new AssertionError("Some tests failed");}
2495     private static abstract class Fun {abstract void f() throws Throwable;}
2496     static void THROWS(Class<? extends Throwable> k, Fun... fs) {
2497         for (Fun f : fs)
2498             try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
2499             catch (Throwable t) {
2500                 if (k.isAssignableFrom(t.getClass())) pass();
2501                 else unexpected(t);}}
2502 }