Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  Basic.java   Sprache: JAVA

 
/*
 * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


/*
 * @test
 * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
 *      5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
 *      6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958
 *      4947220 7018606 7034570 4244896 5049299 8003488 8054494 8058464
 *      8067796 8224905 8263729 8265173 8272600 8231297 8282219 8285517
 * @key intermittent
 * @summary Basic tests for Process and Environment Variable code
 * @modules java.base/java.lang:open
 *          java.base/java.io:open
 *          java.base/jdk.internal.misc
 * @requires !vm.musl
 * @library /test/lib
 * @run main/othervm/native/timeout=300 -Djava.security.manager=allow Basic
 * @run main/othervm/native/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic
 * @author Martin Buchholz
 */


/*
 * @test
 * @modules java.base/java.lang:open
 *          java.base/java.io:open
 *          java.base/jdk.internal.misc
 * @requires (os.family == "linux" & !vm.musl)
 * @library /test/lib
 * @run main/othervm/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=posix_spawn Basic
 */


import java.lang.ProcessBuilder.Redirect;
import java.lang.ProcessHandle;
import static java.lang.ProcessBuilder.Redirect.*;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.security.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import static java.lang.System.getenv;
import static java.lang.System.out;
import static java.lang.Boolean.TRUE;
import static java.util.AbstractMap.SimpleImmutableEntry;

import jdk.test.lib.Platform;

public class Basic {

    /* used for Windows only */
    static final String systemRoot = System.getenv("SystemRoot");

    /* used for Mac OS X only */
    static final String cfUserTextEncoding = System.getenv("__CF_USER_TEXT_ENCODING");

    /* used for AIX only */
    static final String libpath = System.getenv("LIBPATH");

    /* Used for regex String matching for long error messages */
    static final String PERMISSION_DENIED_ERROR_MSG = "(Permission denied|error=13)";
    static final String NO_SUCH_FILE_ERROR_MSG = "(No such file|error=2)";

    /**
     * Returns the number of milliseconds since time given by
     * startNanoTime, which must have been previously returned from a
     * call to {@link System#nanoTime()}.
     */

    private static long millisElapsedSince(long startNanoTime) {
        return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);
    }

    private static String commandOutput(Reader r) throws Throwable {
        StringBuilder sb = new StringBuilder();
        int c;
        while ((c = r.read()) > 0)
            if (c != '\r')
                sb.append((char) c);
        return sb.toString();
    }

    private static String commandOutput(Process p) throws Throwable {
        check(p.getInputStream()  == p.getInputStream());
        check(p.getOutputStream() == p.getOutputStream());
        check(p.getErrorStream()  == p.getErrorStream());
        Reader r = new InputStreamReader(p.getInputStream(),"UTF-8");
        String output = commandOutput(r);
        equal(p.waitFor(), 0);
        equal(p.exitValue(), 0);
        // The debug/fastdebug versions of the VM may write some warnings to stdout
        // (i.e. "Warning:  Cannot open log file: hotspot.log" if the VM is started
        // in a directory without write permissions). These warnings will confuse tests
        // which match the entire output of the child process so better filter them out.
        return output.replaceAll("Warning:.*\\n""");
    }

    private static String commandOutput(ProcessBuilder pb) {
        try {
            return commandOutput(pb.start());
        } catch (Throwable t) {
            String commandline = "";
            for (String arg : pb.command())
                commandline += " " + arg;
            System.out.println("Exception trying to run process: " + commandline);
            unexpected(t);
            return "";
        }
    }

    private static String commandOutput(String...command) {
        try {
            return commandOutput(Runtime.getRuntime().exec(command));
        } catch (Throwable t) {
            String commandline = "";
            for (String arg : command)
                commandline += " " + arg;
            System.out.println("Exception trying to run process: " + commandline);
            unexpected(t);
            return "";
        }
    }

    private static void checkCommandOutput(ProcessBuilder pb,
                                           String expected,
                                           String failureMsg) {
        String got = commandOutput(pb);
        check(got.equals(expected),
              failureMsg + "\n" +
              "Expected: \"" + expected + "\"\n" +
              "Got: \"" + got + "\"");
    }

    private static String absolutifyPath(String path) {
        StringBuilder sb = new StringBuilder();
        for (String file : path.split(File.pathSeparator)) {
            if (sb.length() != 0)
                sb.append(File.pathSeparator);
            sb.append(new File(file).getAbsolutePath());
        }
        return sb.toString();
    }

    // compare windows-style, by canonicalizing to upper case,
    // not lower case as String.compareToIgnoreCase does
    private static class WindowsComparator
        implements Comparator<String> {
        public int compare(String x, String y) {
            return x.toUpperCase(Locale.US)
                .compareTo(y.toUpperCase(Locale.US));
        }
    }

    private static String sortedLines(String lines) {
        String[] arr = lines.split("\n");
        List<String> ls = new ArrayList<String>();
        for (String s : arr)
            ls.add(s);
        Collections.sort(ls, new WindowsComparator());
        StringBuilder sb = new StringBuilder();
        for (String s : ls)
            sb.append(s + "\n");
        return sb.toString();
    }

    private static void compareLinesIgnoreCase(String lines1, String lines2) {
        if (! (sortedLines(lines1).equalsIgnoreCase(sortedLines(lines2)))) {
            String dashes =
                "-----------------------------------------------------";
            out.println(dashes);
            out.print(sortedLines(lines1));
            out.println(dashes);
            out.print(sortedLines(lines2));
            out.println(dashes);
            out.println("sizes: " + sortedLines(lines1).length() +
                        " " + sortedLines(lines2).length());

            fail("Sorted string contents differ");
        }
    }

    private static final Runtime runtime = Runtime.getRuntime();

    private static final String[] winEnvCommand = {"cmd.exe""/c""set"};

    private static String winEnvFilter(String env) {
        return env.replaceAll("\r""")
            .replaceAll("(?m)^(?:COMSPEC|PROMPT|PATHEXT)=.*\n","");
    }

    private static String unixEnvProg() {
        return new File("/usr/bin/env").canExecute() ? "/usr/bin/env"
            : "/bin/env";
    }

    private static String nativeEnv(String[] env) {
        try {
            if (Windows.is()) {
                return winEnvFilter
                    (commandOutput(runtime.exec(winEnvCommand, env)));
            } else {
                return commandOutput(runtime.exec(unixEnvProg(), env));
            }
        } catch (Throwable t) { throw new Error(t); }
    }

    private static String nativeEnv(ProcessBuilder pb) {
        try {
            if (Windows.is()) {
                pb.command(winEnvCommand);
                return winEnvFilter(commandOutput(pb));
            } else {
                pb.command(new String[]{unixEnvProg()});
                return commandOutput(pb);
            }
        } catch (Throwable t) { throw new Error(t); }
    }

    private static void checkSizes(Map<String,String> environ, int size) {
        try {
            equal(size, environ.size());
            equal(size, environ.entrySet().size());
            equal(size, environ.keySet().size());
            equal(size, environ.values().size());

            boolean isEmpty = (size == 0);
            equal(isEmpty, environ.isEmpty());
            equal(isEmpty, environ.entrySet().isEmpty());
            equal(isEmpty, environ.keySet().isEmpty());
            equal(isEmpty, environ.values().isEmpty());
        } catch (Throwable t) { unexpected(t); }
    }

    private interface EnvironmentFrobber {
        void doIt(Map<String,String> environ);
    }

    private static void testVariableDeleter(EnvironmentFrobber fooDeleter) {
        try {
            Map<String,String> environ = new ProcessBuilder().environment();
            environ.put("Foo""BAAR");
            fooDeleter.doIt(environ);
            equal(environ.get("Foo"), null);
            equal(environ.remove("Foo"), null);
        } catch (Throwable t) { unexpected(t); }
    }

    private static void testVariableAdder(EnvironmentFrobber fooAdder) {
        try {
            Map<String,String> environ = new ProcessBuilder().environment();
            environ.remove("Foo");
            fooAdder.doIt(environ);
            equal(environ.get("Foo"), "Bahrein");
        } catch (Throwable t) { unexpected(t); }
    }

    private static void testVariableModifier(EnvironmentFrobber fooModifier) {
        try {
            Map<String,String> environ = new ProcessBuilder().environment();
            environ.put("Foo","OldValue");
            fooModifier.doIt(environ);
            equal(environ.get("Foo"), "NewValue");
        } catch (Throwable t) { unexpected(t); }
    }

    private static void printUTF8(String s) throws IOException {
        out.write(s.getBytes("UTF-8"));
    }

    private static String getenvAsString(Map<String,String> environment) {
        StringBuilder sb = new StringBuilder();
        environment = new TreeMap<>(environment);
        for (Map.Entry<String,String> e : environment.entrySet())
            // Ignore magic environment variables added by the launcher
            if (! e.getKey().equals("LD_LIBRARY_PATH"))
                sb.append(e.getKey())
                    .append('=')
                    .append(e.getValue())
                    .append(',');
        return sb.toString();
    }

    static void print4095(OutputStream s, byte b) throws Throwable {
        byte[] bytes = new byte[4095];
        Arrays.fill(bytes, b);
        s.write(bytes);         // Might hang!
    }

    static void checkPermissionDenied(ProcessBuilder pb) {
        try {
            pb.start();
            fail("Expected IOException not thrown");
        } catch (IOException e) {
            String m = e.getMessage();
            if (EnglishUnix.is() &&
                ! matches(m, PERMISSION_DENIED_ERROR_MSG))
                unexpected(e);
        } catch (Throwable t) { unexpected(t); }
    }

    public static class JavaChild {
        public static void main(String args[]) throws Throwable {
            String action = args[0];
            if (action.equals("sleep")) {
                Thread.sleep(10 * 60 * 1000L);
            } else if (action.equals("pid")) {
                System.out.println(ProcessHandle.current().pid());
            } else if (action.equals("testIO")) {
                String expected = "standard input";
                char[] buf = new char[expected.length()+1];
                int n = new InputStreamReader(System.in).read(buf,0,buf.length);
                if (n != expected.length())
                    System.exit(5);
                if (! new String(buf,0,n).equals(expected))
                    System.exit(5);
                System.err.print("standard error");
                System.out.print("standard output");
            } else if (action.equals("testInheritIO")
                    || action.equals("testRedirectInherit")) {
                List<String> childArgs = new ArrayList<String>(javaChildArgs);
                childArgs.add("testIO");
                ProcessBuilder pb = new ProcessBuilder(childArgs);
                if (action.equals("testInheritIO"))
                    pb.inheritIO();
                else
                    redirectIO(pb, INHERIT, INHERIT, INHERIT);
                ProcessResults r = run(pb);
                if (! r.out().equals(""))
                    System.exit(7);
                if (! r.err().equals(""))
                    System.exit(8);
                if (r.exitValue() != 0)
                    System.exit(9);
            } else if (action.equals("System.getenv(String)")) {
                String val = System.getenv(args[1]);
                printUTF8(val == null ? "null" : val);
            } else if (action.equals("System.getenv(\\u1234)")) {
                String val = System.getenv("\u1234");
                printUTF8(val == null ? "null" : val);
            } else if (action.equals("System.getenv()")) {
                printUTF8(getenvAsString(System.getenv()));
            } else if (action.equals("ArrayOOME")) {
                Object dummy;
                switch(new Random().nextInt(3)) {
                case 0: dummy = new Integer[Integer.MAX_VALUE]; break;
                case 1: dummy = new double[Integer.MAX_VALUE];  break;
                case 2: dummy = new byte[Integer.MAX_VALUE][];  break;
                defaultthrow new InternalError();
                }
            } else if (action.equals("pwd")) {
                printUTF8(new File(System.getProperty("user.dir"))
                          .getCanonicalPath());
            } else if (action.equals("print4095")) {
                print4095(System.out, (byte'!');
                print4095(System.err, (byte'E');
                System.exit(5);
            } else if (action.equals("OutErr")) {
                // You might think the system streams would be
                // buffered, and in fact they are implemented using
                // BufferedOutputStream, but each and every print
                // causes immediate operating system I/O.
                System.out.print("out");
                System.err.print("err");
                System.out.print("out");
                System.err.print("err");
            } else if (action.equals("null PATH")) {
                equal(System.getenv("PATH"), null);
                check(new File("/bin/true").exists());
                check(new File("/bin/false").exists());
                ProcessBuilder pb1 = new ProcessBuilder();
                ProcessBuilder pb2 = new ProcessBuilder();
                pb2.environment().put("PATH""anyOldPathIgnoredAnyways");
                ProcessResults r;

                for (final ProcessBuilder pb :
                         new ProcessBuilder[] {pb1, pb2}) {
                    pb.command("true");
                    equal(run(pb).exitValue(), True.exitValue());

                    pb.command("false");
                    equal(run(pb).exitValue(), False.exitValue());
                }

                if (failed != 0) throw new Error("null PATH");
            } else if (action.equals("PATH search algorithm")) {
                equal(System.getenv("PATH"), "dir1:dir2:");
                check(new File(TrueExe.path()).exists());
                check(new File(FalseExe.path()).exists());
                String[] cmd = {"prog"};
                ProcessBuilder pb1 = new ProcessBuilder(cmd);
                ProcessBuilder pb2 = new ProcessBuilder(cmd);
                ProcessBuilder pb3 = new ProcessBuilder(cmd);
                pb2.environment().put("PATH""anyOldPathIgnoredAnyways");
                pb3.environment().remove("PATH");

                for (final ProcessBuilder pb :
                         new ProcessBuilder[] {pb1, pb2, pb3}) {
                    try {
                        // Not on PATH at all; directories don't exist
                        try {
                            pb.start();
                            fail("Expected IOException not thrown");
                        } catch (IOException e) {
                            String m = e.getMessage();
                            if (EnglishUnix.is() &&
                                ! matches(m, NO_SUCH_FILE_ERROR_MSG))
                                unexpected(e);
                        } catch (Throwable t) { unexpected(t); }

                        // Not on PATH at all; directories exist
                        new File("dir1").mkdirs();
                        new File("dir2").mkdirs();
                        try {
                            pb.start();
                            fail("Expected IOException not thrown");
                        } catch (IOException e) {
                            String m = e.getMessage();
                            if (EnglishUnix.is() &&
                                ! matches(m, NO_SUCH_FILE_ERROR_MSG))
                                unexpected(e);
                        } catch (Throwable t) { unexpected(t); }

                        // Can't execute a directory -- permission denied
                        // Report EACCES errno
                        new File("dir1/prog").mkdirs();
                        checkPermissionDenied(pb);

                        // continue searching if EACCES
                        copy(TrueExe.path(), "dir2/prog");
                        equal(run(pb).exitValue(), True.exitValue());
                        new File("dir1/prog").delete();
                        new File("dir2/prog").delete();

                        new File("dir2/prog").mkdirs();
                        copy(TrueExe.path(), "dir1/prog");
                        equal(run(pb).exitValue(), True.exitValue());

                        // Check empty PATH component means current directory.
                        //
                        // While we're here, let's test different kinds of
                        // Unix executables, and PATH vs explicit searching.
                        new File("dir1/prog").delete();
                        new File("dir2/prog").delete();
                        for (String[] command :
                                 new String[][] {
                                     new String[] {"./prog"},
                                     cmd}) {
                            pb.command(command);
                            File prog = new File("./prog");
                            // "Normal" binaries
                            copy(TrueExe.path(), "./prog");
                            equal(run(pb).exitValue(),
                                  True.exitValue());
                            copy(FalseExe.path(), "./prog");
                            equal(run(pb).exitValue(),
                                  False.exitValue());
                            prog.delete();
                            // Interpreter scripts with #!
                            setFileContents(prog, "#!/bin/true\n");
                            prog.setExecutable(true);
                            equal(run(pb).exitValue(),
                                  True.exitValue());
                            prog.delete();
                            setFileContents(prog, "#!/bin/false\n");
                            prog.setExecutable(true);
                            equal(run(pb).exitValue(),
                                  False.exitValue());
                            // Traditional shell scripts without #!
                            if (!(Platform.isLinux() && Platform.isMusl())) {
                                setFileContents(prog, "exec /bin/true\n");
                                prog.setExecutable(true);
                                equal(run(pb).exitValue(), True.exitValue());
                                prog.delete();
                                setFileContents(prog, "exec /bin/false\n");
                                prog.setExecutable(true);
                                equal(run(pb).exitValue(), False.exitValue());
                            }
                            prog.delete();
                        }

                        // Test Unix interpreter scripts
                        File dir1Prog = new File("dir1/prog");
                        dir1Prog.delete();
                        pb.command(new String[] {"prog""world"});
                        setFileContents(dir1Prog, "#!/bin/echo hello\n");
                        checkPermissionDenied(pb);
                        dir1Prog.setExecutable(true);
                        equal(run(pb).out(), "hello dir1/prog world\n");
                        equal(run(pb).exitValue(), True.exitValue());
                        dir1Prog.delete();
                        pb.command(cmd);

                        // Test traditional shell scripts without #!
                        if (!(Platform.isLinux() && Platform.isMusl())) {
                            setFileContents(dir1Prog, "/bin/echo \"$@\"\n");
                            pb.command(new String[] {"prog""hello""world"});
                            checkPermissionDenied(pb);
                            dir1Prog.setExecutable(true);
                            equal(run(pb).out(), "hello world\n");
                            equal(run(pb).exitValue(), True.exitValue());
                            dir1Prog.delete();
                            pb.command(cmd);
                        }

                        // If prog found on both parent and child's PATH,
                        // parent's is used.
                        new File("dir1/prog").delete();
                        new File("dir2/prog").delete();
                        new File("prog").delete();
                        new File("dir3").mkdirs();
                        copy(TrueExe.path(), "dir1/prog");
                        copy(FalseExe.path(), "dir3/prog");
                        pb.environment().put("PATH","dir3");
                        equal(run(pb).exitValue(), True.exitValue());
                        copy(TrueExe.path(), "dir3/prog");
                        copy(FalseExe.path(), "dir1/prog");
                        equal(run(pb).exitValue(), False.exitValue());

                    } finally {
                        // cleanup
                        new File("dir1/prog").delete();
                        new File("dir2/prog").delete();
                        new File("dir3/prog").delete();
                        new File("dir1").delete();
                        new File("dir2").delete();
                        new File("dir3").delete();
                        new File("prog").delete();
                    }
                }

                if (failed != 0) throw new Error("PATH search algorithm");
            }
            else throw new Error("JavaChild invocation error");
        }
    }

    private static void copy(String src, String dst) throws IOException {
        Files.copy(Paths.get(src), Paths.get(dst),
                   StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
    }

    private static String javaChildOutput(ProcessBuilder pb, String...args) {
        List<String> list = new ArrayList<String>(javaChildArgs);
        for (String arg : args)
            list.add(arg);
        pb.command(list);
        return commandOutput(pb);
    }

    private static String getenvInChild(ProcessBuilder pb) {
        return javaChildOutput(pb, "System.getenv()");
    }

    private static String getenvInChild1234(ProcessBuilder pb) {
        return javaChildOutput(pb, "System.getenv(\\u1234)");
    }

    private static String getenvInChild(ProcessBuilder pb, String name) {
        return javaChildOutput(pb, "System.getenv(String)", name);
    }

    private static String pwdInChild(ProcessBuilder pb) {
        return javaChildOutput(pb, "pwd");
    }

    private static final String javaExe =
        System.getProperty("java.home") +
        File.separator + "bin" + File.separator + "java";

    private static final String classpath =
        System.getProperty("java.class.path");

    private static final List<String> javaChildArgs =
        Arrays.asList(javaExe,
                      "-XX:+DisplayVMOutputToStderr",
                      "-classpath", absolutifyPath(classpath),
                      "Basic$JavaChild");

    private static void testEncoding(String encoding, String tested) {
        try {
            // If round trip conversion works, should be able to set env vars
            // correctly in child.
            String jnuEncoding = System.getProperty("sun.jnu.encoding");
            Charset cs = jnuEncoding != null
                ? Charset.forName(jnuEncoding, Charset.defaultCharset())
                : Charset.defaultCharset();
            if (new String(tested.getBytes(cs), cs).equals(tested)) {
                out.println("Testing " + encoding + " environment values");
                ProcessBuilder pb = new ProcessBuilder();
                pb.environment().put("ASCIINAME",tested);
                equal(getenvInChild(pb,"ASCIINAME"), tested);
            }
        } catch (Throwable t) { unexpected(t); }
    }

    static class Windows {
        public static boolean is() { return is; }
        private static final boolean is =
            System.getProperty("os.name").startsWith("Windows");
    }

    static class AIX {
        public static boolean is() { return is; }
        private static final boolean is =
            System.getProperty("os.name").equals("AIX");
    }

    static class Unix {
        public static boolean is() { return is; }
        private static final boolean is =
            (! Windows.is() &&
             new File("/bin/sh").exists() &&
             new File("/bin/true").exists() &&
             new File("/bin/false").exists());
    }

    static class UnicodeOS {
        public static boolean is() { return is; }
        private static final String osName = System.getProperty("os.name");
        private static final boolean is =
            // MacOS X would probably also qualify
            osName.startsWith("Windows")   &&
            ! osName.startsWith("Windows 9") &&
            ! osName.equals("Windows Me");
    }

    static class MacOSX {
        public static boolean is() { return is; }
        private static final String osName = System.getProperty("os.name");
        private static final boolean is = osName.contains("OS X");
    }

    static class True {
        public static int exitValue() { return 0; }
    }

    private static class False {
        public static int exitValue() { return exitValue; }
        private static final int exitValue = exitValue0();
        private static int exitValue0() {
            // /bin/false returns an *unspecified* non-zero number.
            try {
                if (! Unix.is())
                    return -1;
                else {
                    int rc = new ProcessBuilder("/bin/false")
                        .start().waitFor();
                    check(rc != 0);
                    return rc;
                }
            } catch (Throwable t) { unexpected(t); return -1; }
        }
    }

    // On Alpine Linux, /bin/true and /bin/false are just links to /bin/busybox.
    // Some tests copy /bin/true and /bin/false to files with a different filename.
    // However, copying the busbox executable into a file with a different name
    // won't result in the expected return codes. As workaround, we create
    // executable files that can be copied and produce the expected return
    // values.

    private static class TrueExe {
        public static String path() { return path; }
        private static final String path = path0();
        private static String path0(){
            if (!Platform.isBusybox("/bin/true")) {
                return "/bin/true";
            } else {
                File trueExe = new File("true");
                setFileContents(trueExe, "#!/bin/true\n");
                trueExe.setExecutable(true);
                return trueExe.getAbsolutePath();
            }
        }
    }

    private static class FalseExe {
        public static String path() { return path; }
        private static final String path = path0();
        private static String path0(){
            if (!Platform.isBusybox("/bin/false")) {
                return "/bin/false";
            } else {
                File falseExe = new File("false");
                setFileContents(falseExe, "#!/bin/false\n");
                falseExe.setExecutable(true);
                return falseExe.getAbsolutePath();
            }
        }
    }

    static class EnglishUnix {
        private static final Boolean is =
            (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));

        private static boolean isEnglish(String envvar) {
            String val = getenv(envvar);
            return (val == null) || val.matches("en.*") || val.matches("C");
        }

        /** Returns true if we can expect English OS error strings */
        static boolean is() { return is; }
    }

    static class DelegatingProcess extends Process {
        final Process p;

        DelegatingProcess(Process p) {
            this.p = p;
        }

        @Override
        public void destroy() {
            p.destroy();
        }

        @Override
        public int exitValue() {
            return p.exitValue();
        }

        @Override
        public int waitFor() throws InterruptedException {
            return p.waitFor();
        }

        @Override
        public OutputStream getOutputStream() {
            return p.getOutputStream();
        }

        @Override
        public InputStream getInputStream() {
            return p.getInputStream();
        }

        @Override
        public InputStream getErrorStream() {
            return p.getErrorStream();
        }
    }

    private static boolean matches(String str, String regex) {
        return Pattern.compile(regex).matcher(str).find();
    }

    private static String matchAndExtract(String str, String regex) {
        Matcher matcher = Pattern.compile(regex).matcher(str);
        if (matcher.find()) {
            return matcher.group();
        } else {
            return "";
        }
    }

    /* Only used for Mac OS X --
     * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty
     * environment. The environment variable JAVA_MAIN_CLASS_<pid> may also
     * be set in Mac OS X.
     * Remove them both from the list of env variables
     */

    private static String removeMacExpectedVars(String vars) {
        // Check for __CF_USER_TEXT_ENCODING
        String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="
                                            +cfUserTextEncoding+",","");
        // Check for JAVA_MAIN_CLASS_<pid>
        String javaMainClassStr
                = matchAndExtract(cleanedVars,
                                    "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,");
        return cleanedVars.replace(javaMainClassStr,"");
    }

    /* Only used for AIX --
     * AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment.
     * Remove it from the list of env variables
     */

    private static String removeAixExpectedVars(String vars) {
        return vars.replace("AIXTHREAD_GUARDPAGES=0,""");
    }

    private static String sortByLinesWindowsly(String text) {
        String[] lines = text.split("\n");
        Arrays.sort(lines, new WindowsComparator());
        StringBuilder sb = new StringBuilder();
        for (String line : lines)
            sb.append(line).append("\n");
        return sb.toString();
    }

    private static void checkMapSanity(Map<String,String> map) {
        try {
            Set<String> keySet = map.keySet();
            Collection<String> values = map.values();
            Set<Map.Entry<String,String>> entrySet = map.entrySet();

            equal(entrySet.size(), keySet.size());
            equal(entrySet.size(), values.size());

            StringBuilder s1 = new StringBuilder();
            for (Map.Entry<String,String> e : entrySet)
                s1.append(e.getKey() + "=" + e.getValue() + "\n");

            StringBuilder s2 = new StringBuilder();
            for (String var : keySet)
                s2.append(var + "=" + map.get(var) + "\n");

            equal(s1.toString(), s2.toString());

            Iterator<String> kIter = keySet.iterator();
            Iterator<String> vIter = values.iterator();
            Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();

            while (eIter.hasNext()) {
                Map.Entry<String,String> entry = eIter.next();
                String key   = kIter.next();
                String value = vIter.next();
                check(entrySet.contains(entry));
                check(keySet.contains(key));
                check(values.contains(value));
                check(map.containsKey(key));
                check(map.containsValue(value));
                equal(entry.getKey(), key);
                equal(entry.getValue(), value);
            }
            check(!kIter.hasNext() &&
                    !vIter.hasNext());

        } catch (Throwable t) { unexpected(t); }
    }

    private static void checkMapEquality(Map<String,String> map1,
                                         Map<String,String> map2) {
        try {
            equal(map1.size(), map2.size());
            equal(map1.isEmpty(), map2.isEmpty());
            for (String key : map1.keySet()) {
                equal(map1.get(key), map2.get(key));
                check(map2.keySet().contains(key));
            }
            equal(map1, map2);
            equal(map2, map1);
            equal(map1.entrySet(), map2.entrySet());
            equal(map2.entrySet(), map1.entrySet());
            equal(map1.keySet(), map2.keySet());
            equal(map2.keySet(), map1.keySet());

            equal(map1.hashCode(), map2.hashCode());
            equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());
            equal(map1.keySet().hashCode(), map2.keySet().hashCode());
        } catch (Throwable t) { unexpected(t); }
    }

    static void checkRedirects(ProcessBuilder pb,
                               Redirect in, Redirect out, Redirect err) {
        equal(pb.redirectInput(), in);
        equal(pb.redirectOutput(), out);
        equal(pb.redirectError(), err);
    }

    static void redirectIO(ProcessBuilder pb,
                           Redirect in, Redirect out, Redirect err) {
        pb.redirectInput(in);
        pb.redirectOutput(out);
        pb.redirectError(err);
    }

    static void setFileContents(File file, String contents) {
        try {
            Writer w = new FileWriter(file);
            w.write(contents);
            w.close();
        } catch (Throwable t) { unexpected(t); }
    }

    static String fileContents(File file) {
        try {
            Reader r = new FileReader(file);
            StringBuilder sb = new StringBuilder();
            char[] buffer = new char[1024];
            int n;
            while ((n = r.read(buffer)) != -1)
                sb.append(buffer,0,n);
            r.close();
            return new String(sb);
        } catch (Throwable t) { unexpected(t); return ""; }
    }

    @SuppressWarnings("removal")
    static void testIORedirection() throws Throwable {
        final File ifile = new File("ifile");
        final File ofile = new File("ofile");
        final File efile = new File("efile");
        ifile.delete();
        ofile.delete();
        efile.delete();

        //----------------------------------------------------------------
        // Check mutual inequality of different types of Redirect
        //----------------------------------------------------------------
        Redirect[] redirects =
            { PIPE,
              INHERIT,
              DISCARD,
              Redirect.from(ifile),
              Redirect.to(ifile),
              Redirect.appendTo(ifile),
              Redirect.from(ofile),
              Redirect.to(ofile),
              Redirect.appendTo(ofile),
            };
        for (int i = 0; i < redirects.length; i++)
            for (int j = 0; j < redirects.length; j++)
                equal(redirects[i].equals(redirects[j]), (i == j));

        //----------------------------------------------------------------
        // Check basic properties of different types of Redirect
        //----------------------------------------------------------------
        equal(PIPE.type(), Redirect.Type.PIPE);
        equal(PIPE.toString(), "PIPE");
        equal(PIPE.file(), null);

        equal(INHERIT.type(), Redirect.Type.INHERIT);
        equal(INHERIT.toString(), "INHERIT");
        equal(INHERIT.file(), null);

        equal(DISCARD.type(), Redirect.Type.WRITE);
        equal(DISCARD.toString(), "WRITE");
        equal(DISCARD.file(), new File((Windows.is() ? "NUL" : "/dev/null")));

        equal(Redirect.from(ifile).type(), Redirect.Type.READ);
        equal(Redirect.from(ifile).toString(),
              "redirect to read from file \"ifile\"");
        equal(Redirect.from(ifile).file(), ifile);
        equal(Redirect.from(ifile),
              Redirect.from(ifile));
        equal(Redirect.from(ifile).hashCode(),
              Redirect.from(ifile).hashCode());

        equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);
        equal(Redirect.to(ofile).toString(),
              "redirect to write to file \"ofile\"");
        equal(Redirect.to(ofile).file(), ofile);
        equal(Redirect.to(ofile),
              Redirect.to(ofile));
        equal(Redirect.to(ofile).hashCode(),
              Redirect.to(ofile).hashCode());

        equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);
        equal(Redirect.appendTo(efile).toString(),
              "redirect to append to file \"efile\"");
        equal(Redirect.appendTo(efile).file(), efile);
        equal(Redirect.appendTo(efile),
              Redirect.appendTo(efile));
        equal(Redirect.appendTo(efile).hashCode(),
              Redirect.appendTo(efile).hashCode());

        //----------------------------------------------------------------
        // Check initial values of redirects
        //----------------------------------------------------------------
        List<String> childArgs = new ArrayList<String>(javaChildArgs);
        childArgs.add("testIO");
        final ProcessBuilder pb = new ProcessBuilder(childArgs);
        checkRedirects(pb, PIPE, PIPE, PIPE);

        //----------------------------------------------------------------
        // Check inheritIO
        //----------------------------------------------------------------
        pb.inheritIO();
        checkRedirects(pb, INHERIT, INHERIT, INHERIT);

        //----------------------------------------------------------------
        // Check DISCARD for stdout,stderr
        //----------------------------------------------------------------
        redirectIO(pb, INHERIT, DISCARD, DISCARD);
        checkRedirects(pb, INHERIT, DISCARD, DISCARD);

        //----------------------------------------------------------------
        // Check setters and getters agree
        //----------------------------------------------------------------
        pb.redirectInput(ifile);
        equal(pb.redirectInput().file(), ifile);
        equal(pb.redirectInput(), Redirect.from(ifile));

        pb.redirectOutput(ofile);
        equal(pb.redirectOutput().file(), ofile);
        equal(pb.redirectOutput(), Redirect.to(ofile));

        pb.redirectError(efile);
        equal(pb.redirectError().file(), efile);
        equal(pb.redirectError(), Redirect.to(efile));

        THROWS(IllegalArgumentException.class,
               () -> pb.redirectInput(Redirect.to(ofile)),
               () -> pb.redirectOutput(Redirect.from(ifile)),
               () -> pb.redirectError(Redirect.from(ifile)),
               () -> pb.redirectInput(DISCARD));

        THROWS(NullPointerException.class,
                () -> pb.redirectInput((File)null),
                () -> pb.redirectOutput((File)null),
                () -> pb.redirectError((File)null),
                () -> pb.redirectInput((Redirect)null),
                () -> pb.redirectOutput((Redirect)null),
                () -> pb.redirectError((Redirect)null));

        THROWS(IOException.class,
               // Input file does not exist
               () -> pb.start());
        setFileContents(ifile, "standard input");

        //----------------------------------------------------------------
        // Writing to non-existent files
        //----------------------------------------------------------------
        {
            ProcessResults r = run(pb);
            equal(r.exitValue(), 0);
            equal(fileContents(ofile), "standard output");
            equal(fileContents(efile), "standard error");
            equal(r.out(), "");
            equal(r.err(), "");
            ofile.delete();
            efile.delete();
        }

        //----------------------------------------------------------------
        // Both redirectErrorStream + redirectError
        //----------------------------------------------------------------
        {
            pb.redirectErrorStream(true);
            ProcessResults r = run(pb);
            equal(r.exitValue(), 0);
            equal(fileContents(ofile),
                    "standard error" + "standard output");
            equal(fileContents(efile), "");
            equal(r.out(), "");
            equal(r.err(), "");
            ofile.delete();
            efile.delete();
        }

        //----------------------------------------------------------------
        // Appending to existing files
        //----------------------------------------------------------------
        {
            setFileContents(ofile, "ofile-contents");
            setFileContents(efile, "efile-contents");
            pb.redirectOutput(Redirect.appendTo(ofile));
            pb.redirectError(Redirect.appendTo(efile));
            pb.redirectErrorStream(false);
            ProcessResults r = run(pb);
            equal(r.exitValue(), 0);
            equal(fileContents(ofile),
                  "ofile-contents" + "standard output");
            equal(fileContents(efile),
                  "efile-contents" + "standard error");
            equal(r.out(), "");
            equal(r.err(), "");
            ofile.delete();
            efile.delete();
        }

        //----------------------------------------------------------------
        // Replacing existing files
        //----------------------------------------------------------------
        {
            setFileContents(ofile, "ofile-contents");
            setFileContents(efile, "efile-contents");
            pb.redirectOutput(ofile);
            pb.redirectError(Redirect.to(efile));
            ProcessResults r = run(pb);
            equal(r.exitValue(), 0);
            equal(fileContents(ofile), "standard output");
            equal(fileContents(efile), "standard error");
            equal(r.out(), "");
            equal(r.err(), "");
            ofile.delete();
            efile.delete();
        }

        //----------------------------------------------------------------
        // Appending twice to the same file?
        //----------------------------------------------------------------
        {
            setFileContents(ofile, "ofile-contents");
            setFileContents(efile, "efile-contents");
            Redirect appender = Redirect.appendTo(ofile);
            pb.redirectOutput(appender);
            pb.redirectError(appender);
            ProcessResults r = run(pb);
            equal(r.exitValue(), 0);
            equal(fileContents(ofile),
                  "ofile-contents" +
                  "standard error" +
                  "standard output");
            equal(fileContents(efile), "efile-contents");
            equal(r.out(), "");
            equal(r.err(), "");
            ifile.delete();
            ofile.delete();
            efile.delete();
        }

        //----------------------------------------------------------------
        // DISCARDing output
        //----------------------------------------------------------------
        {
            setFileContents(ifile, "standard input");
            pb.redirectOutput(DISCARD);
            pb.redirectError(DISCARD);
            ProcessResults r = run(pb);
            equal(r.exitValue(), 0);
            equal(r.out(), "");
            equal(r.err(), "");
        }

        //----------------------------------------------------------------
        // DISCARDing output and redirecting error
        //----------------------------------------------------------------
        {
            setFileContents(ifile, "standard input");
            setFileContents(ofile, "ofile-contents");
            setFileContents(efile, "efile-contents");
            pb.redirectOutput(DISCARD);
            pb.redirectError(efile);
            ProcessResults r = run(pb);
            equal(r.exitValue(), 0);
            equal(fileContents(ofile), "ofile-contents");
            equal(fileContents(efile), "standard error");
            equal(r.out(), "");
            equal(r.err(), "");
            ofile.delete();
            efile.delete();
        }

        //----------------------------------------------------------------
        // DISCARDing error and redirecting output
        //----------------------------------------------------------------
        {
            setFileContents(ifile, "standard input");
            setFileContents(ofile, "ofile-contents");
            setFileContents(efile, "efile-contents");
            pb.redirectOutput(ofile);
            pb.redirectError(DISCARD);
            ProcessResults r = run(pb);
            equal(r.exitValue(), 0);
            equal(fileContents(ofile), "standard output");
            equal(fileContents(efile), "efile-contents");
            equal(r.out(), "");
            equal(r.err(), "");
            ofile.delete();
            efile.delete();
        }

        //----------------------------------------------------------------
        // DISCARDing output and merging error into output
        //----------------------------------------------------------------
        {
            setFileContents(ifile, "standard input");
            setFileContents(ofile, "ofile-contents");
            setFileContents(efile, "efile-contents");
            pb.redirectOutput(DISCARD);
            pb.redirectErrorStream(true);
            pb.redirectError(efile);
            ProcessResults r = run(pb);
            equal(r.exitValue(), 0);
            equal(fileContents(ofile), "ofile-contents");   // untouched
            equal(fileContents(efile), "");                 // empty
            equal(r.out(), "");
            equal(r.err(), "");
            ifile.delete();
            ofile.delete();
            efile.delete();
            pb.redirectErrorStream(false);                  // reset for next test
        }

        //----------------------------------------------------------------
        // Testing INHERIT is harder.
        // Note that this requires __FOUR__ nested JVMs involved in one test,
        // if you count the harness JVM.
        //----------------------------------------------------------------
        for (String testName : new String[] { "testInheritIO""testRedirectInherit" } ) {
            redirectIO(pb, PIPE, PIPE, PIPE);
            List<String> command = pb.command();
            command.set(command.size() - 1, testName);
            Process p = pb.start();
            new PrintStream(p.getOutputStream()).print("standard input");
            p.getOutputStream().close();
            ProcessResults r = run(p);
            equal(r.exitValue(), 0);
            equal(r.out(), "standard output");
            equal(r.err(), "standard error");
        }

        //----------------------------------------------------------------
        // Test security implications of I/O redirection
        //----------------------------------------------------------------

        // Read access to current directory is always granted;
        // So create a tmpfile for input instead.
        final File tmpFile = File.createTempFile("Basic""tmp");
        setFileContents(tmpFile, "standard input");

        final Policy policy = new Policy();
        Policy.setPolicy(policy);
        System.setSecurityManager(new SecurityManager());
        try {
            final Permission xPermission
                = new FilePermission("<>""execute");
            final Permission rxPermission
                = new FilePermission("<>""read,execute");
            final Permission wxPermission
                = new FilePermission("<>""write,execute");
            final Permission rwxPermission
                = new FilePermission("<>""read,write,execute");

            THROWS(SecurityException.class,
                   () -> { policy.setPermissions(xPermission);
                           redirectIO(pb, from(tmpFile), PIPE, PIPE);
                           pb.start();},
                   () -> { policy.setPermissions(rxPermission);
                           redirectIO(pb, PIPE, to(ofile), PIPE);
                           pb.start();},
                   () -> { policy.setPermissions(rxPermission);
                           redirectIO(pb, PIPE, PIPE, to(efile));
                           pb.start();});

            {
                policy.setPermissions(rxPermission);
                redirectIO(pb, from(tmpFile), PIPE, PIPE);
                ProcessResults r = run(pb);
                equal(r.out(), "standard output");
                equal(r.err(), "standard error");
            }

            {
                policy.setPermissions(wxPermission);
                redirectIO(pb, PIPE, to(ofile), to(efile));
                Process p = pb.start();
                new PrintStream(p.getOutputStream()).print("standard input");
                p.getOutputStream().close();
                ProcessResults r = run(p);
                policy.setPermissions(rwxPermission);
                equal(fileContents(ofile), "standard output");
                equal(fileContents(efile), "standard error");
            }

            {
                policy.setPermissions(rwxPermission);
                redirectIO(pb, from(tmpFile), to(ofile), to(efile));
                ProcessResults r = run(pb);
                policy.setPermissions(rwxPermission);
                equal(fileContents(ofile), "standard output");
                equal(fileContents(efile), "standard error");
            }

        } finally {
            policy.setPermissions(new RuntimePermission("setSecurityManager"));
            System.setSecurityManager(null);
            tmpFile.delete();
            ifile.delete();
            ofile.delete();
            efile.delete();
        }
    }

    static void checkProcessPid() {
        ProcessBuilder pb = new ProcessBuilder();
        List<String> list = new ArrayList<String>(javaChildArgs);
        list.add("pid");
        pb.command(list);
        try {
            Process p = pb.start();
            String s = commandOutput(p);
            long actualPid = Long.valueOf(s.trim());
            long expectedPid = p.pid();
            equal(actualPid, expectedPid);
        } catch (Throwable t) {
            unexpected(t);
        }


        // Test the default implementation of Process.getPid
        DelegatingProcess p = new DelegatingProcess(null);
        THROWS(UnsupportedOperationException.class,
                () -> p.pid(),
                () -> p.toHandle(),
                () -> p.supportsNormalTermination(),
                () -> p.children(),
                () -> p.descendants());

    }

    @SuppressWarnings("removal")
    private static void realMain(String[] args) throws Throwable {
        if (Windows.is())
            System.out.println("This appears to be a Windows system.");
        if (Unix.is())
            System.out.println("This appears to be a Unix system.");
        if (UnicodeOS.is())
            System.out.println("This appears to be a Unicode-based OS.");

        try { testIORedirection(); }
        catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // Basic tests for getPid()
        //----------------------------------------------------------------
        checkProcessPid();

        //----------------------------------------------------------------
        // Basic tests for setting, replacing and deleting envvars
        //----------------------------------------------------------------
        try {
            ProcessBuilder pb = new ProcessBuilder();
            Map<String,String> environ = pb.environment();

            // New env var
            environ.put("QUUX""BAR");
            equal(environ.get("QUUX"), "BAR");
            equal(getenvInChild(pb,"QUUX"), "BAR");

            // Modify env var
            environ.put("QUUX","bear");
            equal(environ.get("QUUX"), "bear");
            equal(getenvInChild(pb,"QUUX"), "bear");
            checkMapSanity(environ);

            // Remove env var
            environ.remove("QUUX");
            equal(environ.get("QUUX"), null);
            equal(getenvInChild(pb,"QUUX"), "null");
            checkMapSanity(environ);

            // Remove non-existent env var
            environ.remove("QUUX");
            equal(environ.get("QUUX"), null);
            equal(getenvInChild(pb,"QUUX"), "null");
            checkMapSanity(environ);
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // Pass Empty environment to child
        //----------------------------------------------------------------
        try {
            ProcessBuilder pb = new ProcessBuilder();
            pb.environment().clear();
            String expected = Windows.is() ? "SystemRoot="+systemRoot+",""";
            expected = AIX.is() ? "LIBPATH="+libpath+",": expected;
            if (Windows.is()) {
                pb.environment().put("SystemRoot", systemRoot);
            }
            if (AIX.is()) {
                pb.environment().put("LIBPATH", libpath);
            }
            String result = getenvInChild(pb);
            if (MacOSX.is()) {
                result = removeMacExpectedVars(result);
            }
            if (AIX.is()) {
                result = removeAixExpectedVars(result);
            }
            equal(result, expected);
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // System.getenv() is read-only.
        //----------------------------------------------------------------
        THROWS(UnsupportedOperationException.class,
               () -> getenv().put("FOO","BAR"),
               () -> getenv().remove("PATH"),
               () -> getenv().keySet().remove("PATH"),
               () -> getenv().values().remove("someValue"));

        try {
            Collection<Map.Entry<String,String>> c = getenv().entrySet();
            if (! c.isEmpty())
                try {
                    c.iterator().next().setValue("foo");
                    fail("Expected UnsupportedOperationException not thrown");
                } catch (UnsupportedOperationException e) {} // OK
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // System.getenv() always returns the same object in our implementation.
        //----------------------------------------------------------------
        try {
            check(System.getenv() == System.getenv());
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // You can't create an env var name containing "=",
        // or an env var name or value containing NUL.
        //----------------------------------------------------------------
        {
            final Map<String,String> m = new ProcessBuilder().environment();
            THROWS(IllegalArgumentException.class,
                   () -> m.put("FOO=","BAR"),
                   () -> m.put("FOO\u0000","BAR"),
                   () -> m.put("FOO","BAR\u0000"));
        }

        //----------------------------------------------------------------
        // Commands must never be null.
        //----------------------------------------------------------------
        THROWS(NullPointerException.class,
               () -> new ProcessBuilder((List<String>)null),
               () -> new ProcessBuilder().command((List<String>)null));

        //----------------------------------------------------------------
        // Put in a command; get the same one back out.
        //----------------------------------------------------------------
        try {
            List<String> command = new ArrayList<String>();
            ProcessBuilder pb = new ProcessBuilder(command);
            check(pb.command() == command);
            List<String> command2 = new ArrayList<String>(2);
            command2.add("foo");
            command2.add("bar");
            pb.command(command2);
            check(pb.command() == command2);
            pb.command("foo""bar");
            check(pb.command() != command2 && pb.command().equals(command2));
            pb.command(command2);
            command2.add("baz");
            equal(pb.command().get(2), "baz");
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // Commands must contain at least one element.
        //----------------------------------------------------------------
        THROWS(IndexOutOfBoundsException.class,
               () -> new ProcessBuilder().start(),
               () -> new ProcessBuilder(new ArrayList<String>()).start(),
               () -> Runtime.getRuntime().exec(new String[]{}));

        //----------------------------------------------------------------
        // Commands must not contain null elements at start() time.
        //----------------------------------------------------------------
        THROWS(NullPointerException.class,
               () -> new ProcessBuilder("foo",null,"bar").start(),
               () -> new ProcessBuilder((String)null).start(),
               () -> new ProcessBuilder(new String[]{null}).start(),
               () -> new ProcessBuilder(new String[]{"foo",null,"bar"}).start());

        //----------------------------------------------------------------
        // Command lists are growable.
        //----------------------------------------------------------------
        try {
            new ProcessBuilder().command().add("foo");
            new ProcessBuilder("bar").command().add("foo");
            new ProcessBuilder(new String[]{"1","2"}).command().add("3");
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // Nulls in environment updates generate NullPointerException
        //----------------------------------------------------------------
        try {
            final Map<String,String> env = new ProcessBuilder().environment();
            THROWS(NullPointerException.class,
                   () -> env.put("foo",null),
                   () -> env.put(null,"foo"),
                   () -> env.remove(null),
                   () -> { for (Map.Entry<String,String> e : env.entrySet())
                               e.setValue(null);},
                   () -> Runtime.getRuntime().exec(new String[]{"foo"},
                                                   new String[]{null}));
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // Non-String types in environment updates generate ClassCastException
        //----------------------------------------------------------------
        try {
            final Map<String,String> env = new ProcessBuilder().environment();
            THROWS(ClassCastException.class,
                   () -> env.remove(TRUE),
                   () -> env.keySet().remove(TRUE),
                   () -> env.values().remove(TRUE),
                   () -> env.entrySet().remove(TRUE));
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // Check query operations on environment maps
        //----------------------------------------------------------------
        try {
            List<Map<String,String>> envs =
                new ArrayList<Map<String,String>>(2);
            envs.add(System.getenv());
            envs.add(new ProcessBuilder().environment());
            for (final Map<String,String> env : envs) {
                //----------------------------------------------------------------
                // Nulls in environment queries are forbidden.
                //----------------------------------------------------------------
                THROWS(NullPointerException.class,
                       () -> getenv(null),
                       () -> env.get(null),
                       () -> env.containsKey(null),
                       () -> env.containsValue(null),
                       () -> env.keySet().contains(null),
                       () -> env.values().contains(null));

                //----------------------------------------------------------------
                // Non-String types in environment queries are forbidden.
                //----------------------------------------------------------------
                THROWS(ClassCastException.class,
                       () -> env.get(TRUE),
                       () -> env.containsKey(TRUE),
                       () -> env.containsValue(TRUE),
                       () -> env.keySet().contains(TRUE),
                       () -> env.values().contains(TRUE));

                //----------------------------------------------------------------
                // Illegal String values in environment queries are (grumble) OK
                //----------------------------------------------------------------
                equal(env.get("\u0000"), null);
                check(! env.containsKey("\u0000"));
                check(! env.containsValue("\u0000"));
                check(! env.keySet().contains("\u0000"));
                check(! env.values().contains("\u0000"));
            }

        } catch (Throwable t) { unexpected(t); }

        try {
            final Set<Map.Entry<String,String>> entrySet =
                new ProcessBuilder().environment().entrySet();
            THROWS(NullPointerException.class,
                   () -> entrySet.contains(null));
            THROWS(ClassCastException.class,
                   () -> entrySet.contains(TRUE),
                   () -> entrySet.contains(
                             new SimpleImmutableEntry<Boolean,String>(TRUE,"")));

            check(! entrySet.contains
                  (new SimpleImmutableEntry<String,String>("""")));
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // Put in a directory; get the same one back out.
        //----------------------------------------------------------------
        try {
            ProcessBuilder pb = new ProcessBuilder();
            File foo = new File("foo");
            equal(pb.directory(), null);
            equal(pb.directory(foo).directory(), foo);
            equal(pb.directory(null).directory(), null);
        } catch (Throwable t) { unexpected(t); }

        //----------------------------------------------------------------
        // If round-trip conversion works, check envvar pass-through to child
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=90 G=90

¤ Dauer der Verarbeitung: 0.20 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge