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