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