jdk/test/java/lang/ProcessBuilder/Basic.java
changeset 48 dc5744ca15ea
parent 2 90ce3da70b43
child 715 f16baef3a20e
--- a/jdk/test/java/lang/ProcessBuilder/Basic.java	Mon Mar 10 14:32:51 2008 -0700
+++ b/jdk/test/java/lang/ProcessBuilder/Basic.java	Mon Mar 10 14:32:51 2008 -0700
@@ -25,12 +25,15 @@
  * @test
  * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
  *      5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
- *      6464154 6523983 6206031
+ *      6464154 6523983 6206031 4960438 6631352 6631966
  * @summary Basic tests for Process and Environment Variable code
  * @run main/othervm Basic
  * @author Martin Buchholz
  */
 
+import java.lang.ProcessBuilder.Redirect;
+import static java.lang.ProcessBuilder.Redirect.*;
+
 import java.io.*;
 import java.util.*;
 import java.security.*;
@@ -257,7 +260,29 @@
     public static class JavaChild {
         public static void main(String args[]) throws Throwable {
             String action = args[0];
-            if (action.equals("System.getenv(String)")) {
+            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")) {
+                List<String> childArgs = new ArrayList<String>(javaChildArgs);
+                childArgs.add("testIO");
+                ProcessBuilder pb = new ProcessBuilder(childArgs);
+                pb.inheritIO();
+                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)")) {
@@ -599,6 +624,333 @@
         } 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 ""; }
+    }
+
+    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,
+              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(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 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,
+            new Fun(){void f() {
+                pb.redirectInput(Redirect.to(ofile)); }},
+            new Fun(){void f() {
+                pb.redirectInput(Redirect.appendTo(ofile)); }},
+            new Fun(){void f() {
+                pb.redirectOutput(Redirect.from(ifile)); }},
+            new Fun(){void f() {
+                pb.redirectError(Redirect.from(ifile)); }});
+
+        THROWS(IOException.class,
+               // Input file does not exist
+               new Fun(){void f() throws Throwable { 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();
+        }
+
+        //----------------------------------------------------------------
+        // Testing INHERIT is harder.
+        // Note that this requires __FOUR__ nested JVMs involved in one test,
+        // if you count the harness JVM.
+        //----------------------------------------------------------------
+        {
+            redirectIO(pb, PIPE, PIPE, PIPE);
+            List<String> command = pb.command();
+            command.set(command.size() - 1, "testInheritIO");
+            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("<<ALL FILES>>", "execute");
+            final Permission rxPermission
+                = new FilePermission("<<ALL FILES>>", "read,execute");
+            final Permission wxPermission
+                = new FilePermission("<<ALL FILES>>", "write,execute");
+            final Permission rwxPermission
+                = new FilePermission("<<ALL FILES>>", "read,write,execute");
+
+            THROWS(SecurityException.class,
+               new Fun() { void f() throws IOException {
+                   policy.setPermissions(xPermission);
+                   redirectIO(pb, from(tmpFile), PIPE, PIPE);
+                   pb.start();}},
+               new Fun() { void f() throws IOException {
+                   policy.setPermissions(rxPermission);
+                   redirectIO(pb, PIPE, to(ofile), PIPE);
+                   pb.start();}},
+               new Fun() { void f() throws IOException {
+                   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();
+        }
+    }
+
     private static void realMain(String[] args) throws Throwable {
         if (Windows.is())
             System.out.println("This appears to be a Windows system.");
@@ -607,6 +959,9 @@
         if (UnicodeOS.is())
             System.out.println("This appears to be a Unicode-based OS.");
 
+        try { testIORedirection(); }
+        catch (Throwable t) { unexpected(t); }
+
         //----------------------------------------------------------------
         // Basic tests for setting, replacing and deleting envvars
         //----------------------------------------------------------------
@@ -1354,7 +1709,8 @@
                                   execPermission);
             ProcessBuilder pb = new ProcessBuilder("env");
             pb.environment().put("foo","bar");
-            pb.start();
+            Process p = pb.start();
+            closeStreams(p);
         } catch (IOException e) { // OK
         } catch (Throwable t) { unexpected(t); }
 
@@ -1378,6 +1734,14 @@
 
     }
 
+    static void closeStreams(Process p) {
+        try {
+            p.getOutputStream().close();
+            p.getInputStream().close();
+            p.getErrorStream().close();
+        } catch (Throwable t) { unexpected(t); }
+    }
+
     //----------------------------------------------------------------
     // A Policy class designed to make permissions fiddling very easy.
     //----------------------------------------------------------------
@@ -1432,10 +1796,19 @@
                 }
             } catch (Throwable t) {
                 throwable = t;
+            } finally {
+                try { is.close(); }
+                catch (Throwable t) { throwable = t; }
             }
         }
     }
 
+    static ProcessResults run(ProcessBuilder pb) {
+        try {
+            return run(pb.start());
+        } catch (Throwable t) { unexpected(t); return null; }
+    }
+
     private static ProcessResults run(Process p) {
         Throwable throwable = null;
         int exitValue = -1;