Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/java/lang/ProcessBuilder/Basic.java
47209 views
/*1* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 498668926* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 641331327* 6464154 6523983 6206031 4960438 6631352 6631966 6850957 685095828* 4947220 7018606 7034570 4244896 504929929* 806779630* @summary Basic tests for Process and Environment Variable code31* @run main/othervm/timeout=300 Basic32* @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=fork Basic33* @author Martin Buchholz34*/3536import java.lang.ProcessBuilder.Redirect;37import static java.lang.ProcessBuilder.Redirect.*;3839import java.io.*;40import java.lang.reflect.Field;41import java.util.*;42import java.util.concurrent.CountDownLatch;43import java.util.concurrent.TimeUnit;44import java.security.*;45import sun.misc.Unsafe;46import java.util.regex.Pattern;47import java.util.regex.Matcher;48import static java.lang.System.getenv;49import static java.lang.System.out;50import static java.lang.Boolean.TRUE;51import static java.util.AbstractMap.SimpleImmutableEntry;5253public class Basic {5455/* used for Windows only */56static final String systemRoot = System.getenv("SystemRoot");5758/* used for Mac OS X only */59static final String cfUserTextEncoding = System.getenv("__CF_USER_TEXT_ENCODING");6061/* used for AIX only */62static final String libpath = System.getenv("LIBPATH");6364/**65* Returns the number of milliseconds since time given by66* startNanoTime, which must have been previously returned from a67* call to {@link System.nanoTime()}.68*/69private static long millisElapsedSince(long startNanoTime) {70return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);71}7273private static String commandOutput(Reader r) throws Throwable {74StringBuilder sb = new StringBuilder();75int c;76while ((c = r.read()) > 0)77if (c != '\r')78sb.append((char) c);79return sb.toString();80}8182private static String commandOutput(Process p) throws Throwable {83check(p.getInputStream() == p.getInputStream());84check(p.getOutputStream() == p.getOutputStream());85check(p.getErrorStream() == p.getErrorStream());86Reader r = new InputStreamReader(p.getInputStream(),"UTF-8");87String output = commandOutput(r);88equal(p.waitFor(), 0);89equal(p.exitValue(), 0);90// The debug/fastdebug versions of the VM may write some warnings to stdout91// (i.e. "Warning: Cannot open log file: hotspot.log" if the VM is started92// in a directory without write permissions). These warnings will confuse tests93// which match the entire output of the child process so better filter them out.94return output.replaceAll("Warning:.*\\n", "");95}9697private static String commandOutput(ProcessBuilder pb) {98try {99return commandOutput(pb.start());100} catch (Throwable t) {101String commandline = "";102for (String arg : pb.command())103commandline += " " + arg;104System.out.println("Exception trying to run process: " + commandline);105unexpected(t);106return "";107}108}109110private static String commandOutput(String...command) {111try {112return commandOutput(Runtime.getRuntime().exec(command));113} catch (Throwable t) {114String commandline = "";115for (String arg : command)116commandline += " " + arg;117System.out.println("Exception trying to run process: " + commandline);118unexpected(t);119return "";120}121}122123private static void checkCommandOutput(ProcessBuilder pb,124String expected,125String failureMsg) {126String got = commandOutput(pb);127check(got.equals(expected),128failureMsg + "\n" +129"Expected: \"" + expected + "\"\n" +130"Got: \"" + got + "\"");131}132133private static String absolutifyPath(String path) {134StringBuilder sb = new StringBuilder();135for (String file : path.split(File.pathSeparator)) {136if (sb.length() != 0)137sb.append(File.pathSeparator);138sb.append(new File(file).getAbsolutePath());139}140return sb.toString();141}142143// compare windows-style, by canonicalizing to upper case,144// not lower case as String.compareToIgnoreCase does145private static class WindowsComparator146implements Comparator<String> {147public int compare(String x, String y) {148return x.toUpperCase(Locale.US)149.compareTo(y.toUpperCase(Locale.US));150}151}152153private static String sortedLines(String lines) {154String[] arr = lines.split("\n");155List<String> ls = new ArrayList<String>();156for (String s : arr)157ls.add(s);158Collections.sort(ls, new WindowsComparator());159StringBuilder sb = new StringBuilder();160for (String s : ls)161sb.append(s + "\n");162return sb.toString();163}164165private static void compareLinesIgnoreCase(String lines1, String lines2) {166if (! (sortedLines(lines1).equalsIgnoreCase(sortedLines(lines2)))) {167String dashes =168"-----------------------------------------------------";169out.println(dashes);170out.print(sortedLines(lines1));171out.println(dashes);172out.print(sortedLines(lines2));173out.println(dashes);174out.println("sizes: " + sortedLines(lines1).length() +175" " + sortedLines(lines2).length());176177fail("Sorted string contents differ");178}179}180181private static final Runtime runtime = Runtime.getRuntime();182183private static final String[] winEnvCommand = {"cmd.exe", "/c", "set"};184185private static String winEnvFilter(String env) {186return env.replaceAll("\r", "")187.replaceAll("(?m)^(?:COMSPEC|PROMPT|PATHEXT)=.*\n","");188}189190private static String unixEnvProg() {191return new File("/usr/bin/env").canExecute() ? "/usr/bin/env"192: "/bin/env";193}194195private static String nativeEnv(String[] env) {196try {197if (Windows.is()) {198return winEnvFilter199(commandOutput(runtime.exec(winEnvCommand, env)));200} else {201return commandOutput(runtime.exec(unixEnvProg(), env));202}203} catch (Throwable t) { throw new Error(t); }204}205206private static String nativeEnv(ProcessBuilder pb) {207try {208if (Windows.is()) {209pb.command(winEnvCommand);210return winEnvFilter(commandOutput(pb));211} else {212pb.command(new String[]{unixEnvProg()});213return commandOutput(pb);214}215} catch (Throwable t) { throw new Error(t); }216}217218private static void checkSizes(Map<String,String> environ, int size) {219try {220equal(size, environ.size());221equal(size, environ.entrySet().size());222equal(size, environ.keySet().size());223equal(size, environ.values().size());224225boolean isEmpty = (size == 0);226equal(isEmpty, environ.isEmpty());227equal(isEmpty, environ.entrySet().isEmpty());228equal(isEmpty, environ.keySet().isEmpty());229equal(isEmpty, environ.values().isEmpty());230} catch (Throwable t) { unexpected(t); }231}232233private interface EnvironmentFrobber {234void doIt(Map<String,String> environ);235}236237private static void testVariableDeleter(EnvironmentFrobber fooDeleter) {238try {239Map<String,String> environ = new ProcessBuilder().environment();240environ.put("Foo", "BAAR");241fooDeleter.doIt(environ);242equal(environ.get("Foo"), null);243equal(environ.remove("Foo"), null);244} catch (Throwable t) { unexpected(t); }245}246247private static void testVariableAdder(EnvironmentFrobber fooAdder) {248try {249Map<String,String> environ = new ProcessBuilder().environment();250environ.remove("Foo");251fooAdder.doIt(environ);252equal(environ.get("Foo"), "Bahrein");253} catch (Throwable t) { unexpected(t); }254}255256private static void testVariableModifier(EnvironmentFrobber fooModifier) {257try {258Map<String,String> environ = new ProcessBuilder().environment();259environ.put("Foo","OldValue");260fooModifier.doIt(environ);261equal(environ.get("Foo"), "NewValue");262} catch (Throwable t) { unexpected(t); }263}264265private static void printUTF8(String s) throws IOException {266out.write(s.getBytes("UTF-8"));267}268269private static String getenvAsString(Map<String,String> environment) {270StringBuilder sb = new StringBuilder();271environment = new TreeMap<>(environment);272for (Map.Entry<String,String> e : environment.entrySet())273// Ignore magic environment variables added by the launcher274if (! e.getKey().equals("NLSPATH") &&275! e.getKey().equals("XFILESEARCHPATH") &&276! e.getKey().equals("LD_LIBRARY_PATH"))277sb.append(e.getKey())278.append('=')279.append(e.getValue())280.append(',');281return sb.toString();282}283284static void print4095(OutputStream s, byte b) throws Throwable {285byte[] bytes = new byte[4095];286Arrays.fill(bytes, b);287s.write(bytes); // Might hang!288}289290static void checkPermissionDenied(ProcessBuilder pb) {291try {292pb.start();293fail("Expected IOException not thrown");294} catch (IOException e) {295String m = e.getMessage();296if (EnglishUnix.is() &&297! matches(m, "Permission denied"))298unexpected(e);299} catch (Throwable t) { unexpected(t); }300}301302public static class JavaChild {303public static void main(String args[]) throws Throwable {304String action = args[0];305if (action.equals("sleep")) {306Thread.sleep(10 * 60 * 1000L);307} else if (action.equals("testIO")) {308String expected = "standard input";309char[] buf = new char[expected.length()+1];310int n = new InputStreamReader(System.in).read(buf,0,buf.length);311if (n != expected.length())312System.exit(5);313if (! new String(buf,0,n).equals(expected))314System.exit(5);315System.err.print("standard error");316System.out.print("standard output");317} else if (action.equals("testInheritIO")318|| action.equals("testRedirectInherit")) {319List<String> childArgs = new ArrayList<String>(javaChildArgs);320childArgs.add("testIO");321ProcessBuilder pb = new ProcessBuilder(childArgs);322if (action.equals("testInheritIO"))323pb.inheritIO();324else325redirectIO(pb, INHERIT, INHERIT, INHERIT);326ProcessResults r = run(pb);327if (! r.out().equals(""))328System.exit(7);329if (! r.err().equals(""))330System.exit(8);331if (r.exitValue() != 0)332System.exit(9);333} else if (action.equals("System.getenv(String)")) {334String val = System.getenv(args[1]);335printUTF8(val == null ? "null" : val);336} else if (action.equals("System.getenv(\\u1234)")) {337String val = System.getenv("\u1234");338printUTF8(val == null ? "null" : val);339} else if (action.equals("System.getenv()")) {340printUTF8(getenvAsString(System.getenv()));341} else if (action.equals("ArrayOOME")) {342Object dummy;343switch(new Random().nextInt(3)) {344case 0: dummy = new Integer[Integer.MAX_VALUE]; break;345case 1: dummy = new double[Integer.MAX_VALUE]; break;346case 2: dummy = new byte[Integer.MAX_VALUE][]; break;347default: throw new InternalError();348}349} else if (action.equals("pwd")) {350printUTF8(new File(System.getProperty("user.dir"))351.getCanonicalPath());352} else if (action.equals("print4095")) {353print4095(System.out, (byte) '!');354print4095(System.err, (byte) 'E');355System.exit(5);356} else if (action.equals("OutErr")) {357// You might think the system streams would be358// buffered, and in fact they are implemented using359// BufferedOutputStream, but each and every print360// causes immediate operating system I/O.361System.out.print("out");362System.err.print("err");363System.out.print("out");364System.err.print("err");365} else if (action.equals("null PATH")) {366equal(System.getenv("PATH"), null);367check(new File("/bin/true").exists());368check(new File("/bin/false").exists());369ProcessBuilder pb1 = new ProcessBuilder();370ProcessBuilder pb2 = new ProcessBuilder();371pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");372ProcessResults r;373374for (final ProcessBuilder pb :375new ProcessBuilder[] {pb1, pb2}) {376pb.command("true");377equal(run(pb).exitValue(), True.exitValue());378379pb.command("false");380equal(run(pb).exitValue(), False.exitValue());381}382383if (failed != 0) throw new Error("null PATH");384} else if (action.equals("PATH search algorithm")) {385equal(System.getenv("PATH"), "dir1:dir2:");386check(new File("/bin/true").exists());387check(new File("/bin/false").exists());388String[] cmd = {"prog"};389ProcessBuilder pb1 = new ProcessBuilder(cmd);390ProcessBuilder pb2 = new ProcessBuilder(cmd);391ProcessBuilder pb3 = new ProcessBuilder(cmd);392pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");393pb3.environment().remove("PATH");394395for (final ProcessBuilder pb :396new ProcessBuilder[] {pb1, pb2, pb3}) {397try {398// Not on PATH at all; directories don't exist399try {400pb.start();401fail("Expected IOException not thrown");402} catch (IOException e) {403String m = e.getMessage();404if (EnglishUnix.is() &&405! matches(m, "No such file"))406unexpected(e);407} catch (Throwable t) { unexpected(t); }408409// Not on PATH at all; directories exist410new File("dir1").mkdirs();411new File("dir2").mkdirs();412try {413pb.start();414fail("Expected IOException not thrown");415} catch (IOException e) {416String m = e.getMessage();417if (EnglishUnix.is() &&418! matches(m, "No such file"))419unexpected(e);420} catch (Throwable t) { unexpected(t); }421422// Can't execute a directory -- permission denied423// Report EACCES errno424new File("dir1/prog").mkdirs();425checkPermissionDenied(pb);426427// continue searching if EACCES428copy("/bin/true", "dir2/prog");429equal(run(pb).exitValue(), True.exitValue());430new File("dir1/prog").delete();431new File("dir2/prog").delete();432433new File("dir2/prog").mkdirs();434copy("/bin/true", "dir1/prog");435equal(run(pb).exitValue(), True.exitValue());436437// Check empty PATH component means current directory.438//439// While we're here, let's test different kinds of440// Unix executables, and PATH vs explicit searching.441new File("dir1/prog").delete();442new File("dir2/prog").delete();443for (String[] command :444new String[][] {445new String[] {"./prog"},446cmd}) {447pb.command(command);448File prog = new File("./prog");449// "Normal" binaries450copy("/bin/true", "./prog");451equal(run(pb).exitValue(),452True.exitValue());453copy("/bin/false", "./prog");454equal(run(pb).exitValue(),455False.exitValue());456prog.delete();457// Interpreter scripts with #!458setFileContents(prog, "#!/bin/true\n");459prog.setExecutable(true);460equal(run(pb).exitValue(),461True.exitValue());462prog.delete();463setFileContents(prog, "#!/bin/false\n");464prog.setExecutable(true);465equal(run(pb).exitValue(),466False.exitValue());467// Traditional shell scripts without #!468setFileContents(prog, "exec /bin/true\n");469prog.setExecutable(true);470equal(run(pb).exitValue(),471True.exitValue());472prog.delete();473setFileContents(prog, "exec /bin/false\n");474prog.setExecutable(true);475equal(run(pb).exitValue(),476False.exitValue());477prog.delete();478}479480// Test Unix interpreter scripts481File dir1Prog = new File("dir1/prog");482dir1Prog.delete();483pb.command(new String[] {"prog", "world"});484setFileContents(dir1Prog, "#!/bin/echo hello\n");485checkPermissionDenied(pb);486dir1Prog.setExecutable(true);487equal(run(pb).out(), "hello dir1/prog world\n");488equal(run(pb).exitValue(), True.exitValue());489dir1Prog.delete();490pb.command(cmd);491492// Test traditional shell scripts without #!493setFileContents(dir1Prog, "/bin/echo \"$@\"\n");494pb.command(new String[] {"prog", "hello", "world"});495checkPermissionDenied(pb);496dir1Prog.setExecutable(true);497equal(run(pb).out(), "hello world\n");498equal(run(pb).exitValue(), True.exitValue());499dir1Prog.delete();500pb.command(cmd);501502// If prog found on both parent and child's PATH,503// parent's is used.504new File("dir1/prog").delete();505new File("dir2/prog").delete();506new File("prog").delete();507new File("dir3").mkdirs();508copy("/bin/true", "dir1/prog");509copy("/bin/false", "dir3/prog");510pb.environment().put("PATH","dir3");511equal(run(pb).exitValue(), True.exitValue());512copy("/bin/true", "dir3/prog");513copy("/bin/false", "dir1/prog");514equal(run(pb).exitValue(), False.exitValue());515516} finally {517// cleanup518new File("dir1/prog").delete();519new File("dir2/prog").delete();520new File("dir3/prog").delete();521new File("dir1").delete();522new File("dir2").delete();523new File("dir3").delete();524new File("prog").delete();525}526}527528if (failed != 0) throw new Error("PATH search algorithm");529}530else throw new Error("JavaChild invocation error");531}532}533534private static void copy(String src, String dst) {535system("/bin/cp", "-fp", src, dst);536}537538private static void system(String... command) {539try {540ProcessBuilder pb = new ProcessBuilder(command);541ProcessResults r = run(pb.start());542equal(r.exitValue(), 0);543equal(r.out(), "");544equal(r.err(), "");545} catch (Throwable t) { unexpected(t); }546}547548private static String javaChildOutput(ProcessBuilder pb, String...args) {549List<String> list = new ArrayList<String>(javaChildArgs);550for (String arg : args)551list.add(arg);552pb.command(list);553return commandOutput(pb);554}555556private static String getenvInChild(ProcessBuilder pb) {557return javaChildOutput(pb, "System.getenv()");558}559560private static String getenvInChild1234(ProcessBuilder pb) {561return javaChildOutput(pb, "System.getenv(\\u1234)");562}563564private static String getenvInChild(ProcessBuilder pb, String name) {565return javaChildOutput(pb, "System.getenv(String)", name);566}567568private static String pwdInChild(ProcessBuilder pb) {569return javaChildOutput(pb, "pwd");570}571572private static final String javaExe =573System.getProperty("java.home") +574File.separator + "bin" + File.separator + "java";575576private static final String classpath =577System.getProperty("java.class.path");578579private static final List<String> javaChildArgs =580Arrays.asList(javaExe,581"-XX:+DisplayVMOutputToStderr",582"-classpath", absolutifyPath(classpath),583"Basic$JavaChild");584585private static void testEncoding(String encoding, String tested) {586try {587// If round trip conversion works, should be able to set env vars588// correctly in child.589if (new String(tested.getBytes()).equals(tested)) {590out.println("Testing " + encoding + " environment values");591ProcessBuilder pb = new ProcessBuilder();592pb.environment().put("ASCIINAME",tested);593equal(getenvInChild(pb,"ASCIINAME"), tested);594}595} catch (Throwable t) { unexpected(t); }596}597598static class Windows {599public static boolean is() { return is; }600private static final boolean is =601System.getProperty("os.name").startsWith("Windows");602}603604static class AIX {605public static boolean is() { return is; }606private static final boolean is =607System.getProperty("os.name").equals("AIX");608}609610static class Unix {611public static boolean is() { return is; }612private static final boolean is =613(! Windows.is() &&614new File("/bin/sh").exists() &&615new File("/bin/true").exists() &&616new File("/bin/false").exists());617}618619static class UnicodeOS {620public static boolean is() { return is; }621private static final String osName = System.getProperty("os.name");622private static final boolean is =623// MacOS X would probably also qualify624osName.startsWith("Windows") &&625! osName.startsWith("Windows 9") &&626! osName.equals("Windows Me");627}628629static class MacOSX {630public static boolean is() { return is; }631private static final String osName = System.getProperty("os.name");632private static final boolean is = osName.contains("OS X");633}634635static class True {636public static int exitValue() { return 0; }637}638639private static class False {640public static int exitValue() { return exitValue; }641private static final int exitValue = exitValue0();642private static int exitValue0() {643// /bin/false returns an *unspecified* non-zero number.644try {645if (! Unix.is())646return -1;647else {648int rc = new ProcessBuilder("/bin/false")649.start().waitFor();650check(rc != 0);651return rc;652}653} catch (Throwable t) { unexpected(t); return -1; }654}655}656657static class EnglishUnix {658private final static Boolean is =659(! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));660661private static boolean isEnglish(String envvar) {662String val = getenv(envvar);663return (val == null) || val.matches("en.*") || val.matches("C");664}665666/** Returns true if we can expect English OS error strings */667static boolean is() { return is; }668}669670static class DelegatingProcess extends Process {671final Process p;672673DelegatingProcess(Process p) {674this.p = p;675}676677@Override678public void destroy() {679p.destroy();680}681682@Override683public int exitValue() {684return p.exitValue();685}686687@Override688public int waitFor() throws InterruptedException {689return p.waitFor();690}691692@Override693public OutputStream getOutputStream() {694return p.getOutputStream();695}696697@Override698public InputStream getInputStream() {699return p.getInputStream();700}701702@Override703public InputStream getErrorStream() {704return p.getErrorStream();705}706}707708private static boolean matches(String str, String regex) {709return Pattern.compile(regex).matcher(str).find();710}711712private static String matchAndExtract(String str, String regex) {713Matcher matcher = Pattern.compile(regex).matcher(str);714if (matcher.find()) {715return matcher.group();716} else {717return "";718}719}720721/* Only used for Mac OS X --722* Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty723* environment. The environment variable JAVA_MAIN_CLASS_<pid> may also724* be set in Mac OS X.725* Remove them both from the list of env variables726*/727private static String removeMacExpectedVars(String vars) {728// Check for __CF_USER_TEXT_ENCODING729String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="730+cfUserTextEncoding+",","");731// Check for JAVA_MAIN_CLASS_<pid>732String javaMainClassStr733= matchAndExtract(cleanedVars,734"JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,");735return cleanedVars.replace(javaMainClassStr,"");736}737738/* Only used for AIX --739* AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment.740* Remove it from the list of env variables741*/742private static String removeAixExpectedVars(String vars) {743return vars.replace("AIXTHREAD_GUARDPAGES=0,","");744}745746private static String sortByLinesWindowsly(String text) {747String[] lines = text.split("\n");748Arrays.sort(lines, new WindowsComparator());749StringBuilder sb = new StringBuilder();750for (String line : lines)751sb.append(line).append("\n");752return sb.toString();753}754755private static void checkMapSanity(Map<String,String> map) {756try {757Set<String> keySet = map.keySet();758Collection<String> values = map.values();759Set<Map.Entry<String,String>> entrySet = map.entrySet();760761equal(entrySet.size(), keySet.size());762equal(entrySet.size(), values.size());763764StringBuilder s1 = new StringBuilder();765for (Map.Entry<String,String> e : entrySet)766s1.append(e.getKey() + "=" + e.getValue() + "\n");767768StringBuilder s2 = new StringBuilder();769for (String var : keySet)770s2.append(var + "=" + map.get(var) + "\n");771772equal(s1.toString(), s2.toString());773774Iterator<String> kIter = keySet.iterator();775Iterator<String> vIter = values.iterator();776Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();777778while (eIter.hasNext()) {779Map.Entry<String,String> entry = eIter.next();780String key = kIter.next();781String value = vIter.next();782check(entrySet.contains(entry));783check(keySet.contains(key));784check(values.contains(value));785check(map.containsKey(key));786check(map.containsValue(value));787equal(entry.getKey(), key);788equal(entry.getValue(), value);789}790check(! kIter.hasNext() &&791! vIter.hasNext());792793} catch (Throwable t) { unexpected(t); }794}795796private static void checkMapEquality(Map<String,String> map1,797Map<String,String> map2) {798try {799equal(map1.size(), map2.size());800equal(map1.isEmpty(), map2.isEmpty());801for (String key : map1.keySet()) {802equal(map1.get(key), map2.get(key));803check(map2.keySet().contains(key));804}805equal(map1, map2);806equal(map2, map1);807equal(map1.entrySet(), map2.entrySet());808equal(map2.entrySet(), map1.entrySet());809equal(map1.keySet(), map2.keySet());810equal(map2.keySet(), map1.keySet());811812equal(map1.hashCode(), map2.hashCode());813equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());814equal(map1.keySet().hashCode(), map2.keySet().hashCode());815} catch (Throwable t) { unexpected(t); }816}817818static void checkRedirects(ProcessBuilder pb,819Redirect in, Redirect out, Redirect err) {820equal(pb.redirectInput(), in);821equal(pb.redirectOutput(), out);822equal(pb.redirectError(), err);823}824825static void redirectIO(ProcessBuilder pb,826Redirect in, Redirect out, Redirect err) {827pb.redirectInput(in);828pb.redirectOutput(out);829pb.redirectError(err);830}831832static void setFileContents(File file, String contents) {833try {834Writer w = new FileWriter(file);835w.write(contents);836w.close();837} catch (Throwable t) { unexpected(t); }838}839840static String fileContents(File file) {841try {842Reader r = new FileReader(file);843StringBuilder sb = new StringBuilder();844char[] buffer = new char[1024];845int n;846while ((n = r.read(buffer)) != -1)847sb.append(buffer,0,n);848r.close();849return new String(sb);850} catch (Throwable t) { unexpected(t); return ""; }851}852853static void testIORedirection() throws Throwable {854final File ifile = new File("ifile");855final File ofile = new File("ofile");856final File efile = new File("efile");857ifile.delete();858ofile.delete();859efile.delete();860861//----------------------------------------------------------------862// Check mutual inequality of different types of Redirect863//----------------------------------------------------------------864Redirect[] redirects =865{ PIPE,866INHERIT,867Redirect.from(ifile),868Redirect.to(ifile),869Redirect.appendTo(ifile),870Redirect.from(ofile),871Redirect.to(ofile),872Redirect.appendTo(ofile),873};874for (int i = 0; i < redirects.length; i++)875for (int j = 0; j < redirects.length; j++)876equal(redirects[i].equals(redirects[j]), (i == j));877878//----------------------------------------------------------------879// Check basic properties of different types of Redirect880//----------------------------------------------------------------881equal(PIPE.type(), Redirect.Type.PIPE);882equal(PIPE.toString(), "PIPE");883equal(PIPE.file(), null);884885equal(INHERIT.type(), Redirect.Type.INHERIT);886equal(INHERIT.toString(), "INHERIT");887equal(INHERIT.file(), null);888889equal(Redirect.from(ifile).type(), Redirect.Type.READ);890equal(Redirect.from(ifile).toString(),891"redirect to read from file \"ifile\"");892equal(Redirect.from(ifile).file(), ifile);893equal(Redirect.from(ifile),894Redirect.from(ifile));895equal(Redirect.from(ifile).hashCode(),896Redirect.from(ifile).hashCode());897898equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);899equal(Redirect.to(ofile).toString(),900"redirect to write to file \"ofile\"");901equal(Redirect.to(ofile).file(), ofile);902equal(Redirect.to(ofile),903Redirect.to(ofile));904equal(Redirect.to(ofile).hashCode(),905Redirect.to(ofile).hashCode());906907equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);908equal(Redirect.appendTo(efile).toString(),909"redirect to append to file \"efile\"");910equal(Redirect.appendTo(efile).file(), efile);911equal(Redirect.appendTo(efile),912Redirect.appendTo(efile));913equal(Redirect.appendTo(efile).hashCode(),914Redirect.appendTo(efile).hashCode());915916//----------------------------------------------------------------917// Check initial values of redirects918//----------------------------------------------------------------919List<String> childArgs = new ArrayList<String>(javaChildArgs);920childArgs.add("testIO");921final ProcessBuilder pb = new ProcessBuilder(childArgs);922checkRedirects(pb, PIPE, PIPE, PIPE);923924//----------------------------------------------------------------925// Check inheritIO926//----------------------------------------------------------------927pb.inheritIO();928checkRedirects(pb, INHERIT, INHERIT, INHERIT);929930//----------------------------------------------------------------931// Check setters and getters agree932//----------------------------------------------------------------933pb.redirectInput(ifile);934equal(pb.redirectInput().file(), ifile);935equal(pb.redirectInput(), Redirect.from(ifile));936937pb.redirectOutput(ofile);938equal(pb.redirectOutput().file(), ofile);939equal(pb.redirectOutput(), Redirect.to(ofile));940941pb.redirectError(efile);942equal(pb.redirectError().file(), efile);943equal(pb.redirectError(), Redirect.to(efile));944945THROWS(IllegalArgumentException.class,946() -> pb.redirectInput(Redirect.to(ofile)),947() -> pb.redirectOutput(Redirect.from(ifile)),948() -> pb.redirectError(Redirect.from(ifile)));949950THROWS(IOException.class,951// Input file does not exist952() -> pb.start());953setFileContents(ifile, "standard input");954955//----------------------------------------------------------------956// Writing to non-existent files957//----------------------------------------------------------------958{959ProcessResults r = run(pb);960equal(r.exitValue(), 0);961equal(fileContents(ofile), "standard output");962equal(fileContents(efile), "standard error");963equal(r.out(), "");964equal(r.err(), "");965ofile.delete();966efile.delete();967}968969//----------------------------------------------------------------970// Both redirectErrorStream + redirectError971//----------------------------------------------------------------972{973pb.redirectErrorStream(true);974ProcessResults r = run(pb);975equal(r.exitValue(), 0);976equal(fileContents(ofile),977"standard error" + "standard output");978equal(fileContents(efile), "");979equal(r.out(), "");980equal(r.err(), "");981ofile.delete();982efile.delete();983}984985//----------------------------------------------------------------986// Appending to existing files987//----------------------------------------------------------------988{989setFileContents(ofile, "ofile-contents");990setFileContents(efile, "efile-contents");991pb.redirectOutput(Redirect.appendTo(ofile));992pb.redirectError(Redirect.appendTo(efile));993pb.redirectErrorStream(false);994ProcessResults r = run(pb);995equal(r.exitValue(), 0);996equal(fileContents(ofile),997"ofile-contents" + "standard output");998equal(fileContents(efile),999"efile-contents" + "standard error");1000equal(r.out(), "");1001equal(r.err(), "");1002ofile.delete();1003efile.delete();1004}10051006//----------------------------------------------------------------1007// Replacing existing files1008//----------------------------------------------------------------1009{1010setFileContents(ofile, "ofile-contents");1011setFileContents(efile, "efile-contents");1012pb.redirectOutput(ofile);1013pb.redirectError(Redirect.to(efile));1014ProcessResults r = run(pb);1015equal(r.exitValue(), 0);1016equal(fileContents(ofile), "standard output");1017equal(fileContents(efile), "standard error");1018equal(r.out(), "");1019equal(r.err(), "");1020ofile.delete();1021efile.delete();1022}10231024//----------------------------------------------------------------1025// Appending twice to the same file?1026//----------------------------------------------------------------1027{1028setFileContents(ofile, "ofile-contents");1029setFileContents(efile, "efile-contents");1030Redirect appender = Redirect.appendTo(ofile);1031pb.redirectOutput(appender);1032pb.redirectError(appender);1033ProcessResults r = run(pb);1034equal(r.exitValue(), 0);1035equal(fileContents(ofile),1036"ofile-contents" +1037"standard error" +1038"standard output");1039equal(fileContents(efile), "efile-contents");1040equal(r.out(), "");1041equal(r.err(), "");1042ifile.delete();1043ofile.delete();1044efile.delete();1045}10461047//----------------------------------------------------------------1048// Testing INHERIT is harder.1049// Note that this requires __FOUR__ nested JVMs involved in one test,1050// if you count the harness JVM.1051//----------------------------------------------------------------1052for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) {1053redirectIO(pb, PIPE, PIPE, PIPE);1054List<String> command = pb.command();1055command.set(command.size() - 1, testName);1056Process p = pb.start();1057new PrintStream(p.getOutputStream()).print("standard input");1058p.getOutputStream().close();1059ProcessResults r = run(p);1060equal(r.exitValue(), 0);1061equal(r.out(), "standard output");1062equal(r.err(), "standard error");1063}10641065//----------------------------------------------------------------1066// Test security implications of I/O redirection1067//----------------------------------------------------------------10681069// Read access to current directory is always granted;1070// So create a tmpfile for input instead.1071final File tmpFile = File.createTempFile("Basic", "tmp");1072setFileContents(tmpFile, "standard input");10731074final Policy policy = new Policy();1075Policy.setPolicy(policy);1076System.setSecurityManager(new SecurityManager());1077try {1078final Permission xPermission1079= new FilePermission("<<ALL FILES>>", "execute");1080final Permission rxPermission1081= new FilePermission("<<ALL FILES>>", "read,execute");1082final Permission wxPermission1083= new FilePermission("<<ALL FILES>>", "write,execute");1084final Permission rwxPermission1085= new FilePermission("<<ALL FILES>>", "read,write,execute");10861087THROWS(SecurityException.class,1088() -> { policy.setPermissions(xPermission);1089redirectIO(pb, from(tmpFile), PIPE, PIPE);1090pb.start();},1091() -> { policy.setPermissions(rxPermission);1092redirectIO(pb, PIPE, to(ofile), PIPE);1093pb.start();},1094() -> { policy.setPermissions(rxPermission);1095redirectIO(pb, PIPE, PIPE, to(efile));1096pb.start();});10971098{1099policy.setPermissions(rxPermission);1100redirectIO(pb, from(tmpFile), PIPE, PIPE);1101ProcessResults r = run(pb);1102equal(r.out(), "standard output");1103equal(r.err(), "standard error");1104}11051106{1107policy.setPermissions(wxPermission);1108redirectIO(pb, PIPE, to(ofile), to(efile));1109Process p = pb.start();1110new PrintStream(p.getOutputStream()).print("standard input");1111p.getOutputStream().close();1112ProcessResults r = run(p);1113policy.setPermissions(rwxPermission);1114equal(fileContents(ofile), "standard output");1115equal(fileContents(efile), "standard error");1116}11171118{1119policy.setPermissions(rwxPermission);1120redirectIO(pb, from(tmpFile), to(ofile), to(efile));1121ProcessResults r = run(pb);1122policy.setPermissions(rwxPermission);1123equal(fileContents(ofile), "standard output");1124equal(fileContents(efile), "standard error");1125}11261127} finally {1128policy.setPermissions(new RuntimePermission("setSecurityManager"));1129System.setSecurityManager(null);1130tmpFile.delete();1131ifile.delete();1132ofile.delete();1133efile.delete();1134}1135}11361137private static void realMain(String[] args) throws Throwable {1138if (Windows.is())1139System.out.println("This appears to be a Windows system.");1140if (Unix.is())1141System.out.println("This appears to be a Unix system.");1142if (UnicodeOS.is())1143System.out.println("This appears to be a Unicode-based OS.");11441145try { testIORedirection(); }1146catch (Throwable t) { unexpected(t); }11471148//----------------------------------------------------------------1149// Basic tests for setting, replacing and deleting envvars1150//----------------------------------------------------------------1151try {1152ProcessBuilder pb = new ProcessBuilder();1153Map<String,String> environ = pb.environment();11541155// New env var1156environ.put("QUUX", "BAR");1157equal(environ.get("QUUX"), "BAR");1158equal(getenvInChild(pb,"QUUX"), "BAR");11591160// Modify env var1161environ.put("QUUX","bear");1162equal(environ.get("QUUX"), "bear");1163equal(getenvInChild(pb,"QUUX"), "bear");1164checkMapSanity(environ);11651166// Remove env var1167environ.remove("QUUX");1168equal(environ.get("QUUX"), null);1169equal(getenvInChild(pb,"QUUX"), "null");1170checkMapSanity(environ);11711172// Remove non-existent env var1173environ.remove("QUUX");1174equal(environ.get("QUUX"), null);1175equal(getenvInChild(pb,"QUUX"), "null");1176checkMapSanity(environ);1177} catch (Throwable t) { unexpected(t); }11781179//----------------------------------------------------------------1180// Pass Empty environment to child1181//----------------------------------------------------------------1182try {1183ProcessBuilder pb = new ProcessBuilder();1184pb.environment().clear();1185String expected = Windows.is() ? "SystemRoot="+systemRoot+",": "";1186expected = AIX.is() ? "LIBPATH="+libpath+",": expected;1187if (Windows.is()) {1188pb.environment().put("SystemRoot", systemRoot);1189}1190if (AIX.is()) {1191pb.environment().put("LIBPATH", libpath);1192}1193String result = getenvInChild(pb);1194if (MacOSX.is()) {1195result = removeMacExpectedVars(result);1196}1197if (AIX.is()) {1198result = removeAixExpectedVars(result);1199}1200equal(result, expected);1201} catch (Throwable t) { unexpected(t); }12021203//----------------------------------------------------------------1204// System.getenv() is read-only.1205//----------------------------------------------------------------1206THROWS(UnsupportedOperationException.class,1207() -> getenv().put("FOO","BAR"),1208() -> getenv().remove("PATH"),1209() -> getenv().keySet().remove("PATH"),1210() -> getenv().values().remove("someValue"));12111212try {1213Collection<Map.Entry<String,String>> c = getenv().entrySet();1214if (! c.isEmpty())1215try {1216c.iterator().next().setValue("foo");1217fail("Expected UnsupportedOperationException not thrown");1218} catch (UnsupportedOperationException e) {} // OK1219} catch (Throwable t) { unexpected(t); }12201221//----------------------------------------------------------------1222// System.getenv() always returns the same object in our implementation.1223//----------------------------------------------------------------1224try {1225check(System.getenv() == System.getenv());1226} catch (Throwable t) { unexpected(t); }12271228//----------------------------------------------------------------1229// You can't create an env var name containing "=",1230// or an env var name or value containing NUL.1231//----------------------------------------------------------------1232{1233final Map<String,String> m = new ProcessBuilder().environment();1234THROWS(IllegalArgumentException.class,1235() -> m.put("FOO=","BAR"),1236() -> m.put("FOO\u0000","BAR"),1237() -> m.put("FOO","BAR\u0000"));1238}12391240//----------------------------------------------------------------1241// Commands must never be null.1242//----------------------------------------------------------------1243THROWS(NullPointerException.class,1244() -> new ProcessBuilder((List<String>)null),1245() -> new ProcessBuilder().command((List<String>)null));12461247//----------------------------------------------------------------1248// Put in a command; get the same one back out.1249//----------------------------------------------------------------1250try {1251List<String> command = new ArrayList<String>();1252ProcessBuilder pb = new ProcessBuilder(command);1253check(pb.command() == command);1254List<String> command2 = new ArrayList<String>(2);1255command2.add("foo");1256command2.add("bar");1257pb.command(command2);1258check(pb.command() == command2);1259pb.command("foo", "bar");1260check(pb.command() != command2 && pb.command().equals(command2));1261pb.command(command2);1262command2.add("baz");1263equal(pb.command().get(2), "baz");1264} catch (Throwable t) { unexpected(t); }12651266//----------------------------------------------------------------1267// Commands must contain at least one element.1268//----------------------------------------------------------------1269THROWS(IndexOutOfBoundsException.class,1270() -> new ProcessBuilder().start(),1271() -> new ProcessBuilder(new ArrayList<String>()).start(),1272() -> Runtime.getRuntime().exec(new String[]{}));12731274//----------------------------------------------------------------1275// Commands must not contain null elements at start() time.1276//----------------------------------------------------------------1277THROWS(NullPointerException.class,1278() -> new ProcessBuilder("foo",null,"bar").start(),1279() -> new ProcessBuilder((String)null).start(),1280() -> new ProcessBuilder(new String[]{null}).start(),1281() -> new ProcessBuilder(new String[]{"foo",null,"bar"}).start());12821283//----------------------------------------------------------------1284// Command lists are growable.1285//----------------------------------------------------------------1286try {1287new ProcessBuilder().command().add("foo");1288new ProcessBuilder("bar").command().add("foo");1289new ProcessBuilder(new String[]{"1","2"}).command().add("3");1290} catch (Throwable t) { unexpected(t); }12911292//----------------------------------------------------------------1293// Nulls in environment updates generate NullPointerException1294//----------------------------------------------------------------1295try {1296final Map<String,String> env = new ProcessBuilder().environment();1297THROWS(NullPointerException.class,1298() -> env.put("foo",null),1299() -> env.put(null,"foo"),1300() -> env.remove(null),1301() -> { for (Map.Entry<String,String> e : env.entrySet())1302e.setValue(null);},1303() -> Runtime.getRuntime().exec(new String[]{"foo"},1304new String[]{null}));1305} catch (Throwable t) { unexpected(t); }13061307//----------------------------------------------------------------1308// Non-String types in environment updates generate ClassCastException1309//----------------------------------------------------------------1310try {1311final Map<String,String> env = new ProcessBuilder().environment();1312THROWS(ClassCastException.class,1313() -> env.remove(TRUE),1314() -> env.keySet().remove(TRUE),1315() -> env.values().remove(TRUE),1316() -> env.entrySet().remove(TRUE));1317} catch (Throwable t) { unexpected(t); }13181319//----------------------------------------------------------------1320// Check query operations on environment maps1321//----------------------------------------------------------------1322try {1323List<Map<String,String>> envs =1324new ArrayList<Map<String,String>>(2);1325envs.add(System.getenv());1326envs.add(new ProcessBuilder().environment());1327for (final Map<String,String> env : envs) {1328//----------------------------------------------------------------1329// Nulls in environment queries are forbidden.1330//----------------------------------------------------------------1331THROWS(NullPointerException.class,1332() -> getenv(null),1333() -> env.get(null),1334() -> env.containsKey(null),1335() -> env.containsValue(null),1336() -> env.keySet().contains(null),1337() -> env.values().contains(null));13381339//----------------------------------------------------------------1340// Non-String types in environment queries are forbidden.1341//----------------------------------------------------------------1342THROWS(ClassCastException.class,1343() -> env.get(TRUE),1344() -> env.containsKey(TRUE),1345() -> env.containsValue(TRUE),1346() -> env.keySet().contains(TRUE),1347() -> env.values().contains(TRUE));13481349//----------------------------------------------------------------1350// Illegal String values in environment queries are (grumble) OK1351//----------------------------------------------------------------1352equal(env.get("\u0000"), null);1353check(! env.containsKey("\u0000"));1354check(! env.containsValue("\u0000"));1355check(! env.keySet().contains("\u0000"));1356check(! env.values().contains("\u0000"));1357}13581359} catch (Throwable t) { unexpected(t); }13601361try {1362final Set<Map.Entry<String,String>> entrySet =1363new ProcessBuilder().environment().entrySet();1364THROWS(NullPointerException.class,1365() -> entrySet.contains(null));1366THROWS(ClassCastException.class,1367() -> entrySet.contains(TRUE),1368() -> entrySet.contains(1369new SimpleImmutableEntry<Boolean,String>(TRUE,"")));13701371check(! entrySet.contains1372(new SimpleImmutableEntry<String,String>("", "")));1373} catch (Throwable t) { unexpected(t); }13741375//----------------------------------------------------------------1376// Put in a directory; get the same one back out.1377//----------------------------------------------------------------1378try {1379ProcessBuilder pb = new ProcessBuilder();1380File foo = new File("foo");1381equal(pb.directory(), null);1382equal(pb.directory(foo).directory(), foo);1383equal(pb.directory(null).directory(), null);1384} catch (Throwable t) { unexpected(t); }13851386//----------------------------------------------------------------1387// If round-trip conversion works, check envvar pass-through to child1388//----------------------------------------------------------------1389try {1390testEncoding("ASCII", "xyzzy");1391testEncoding("Latin1", "\u00f1\u00e1");1392testEncoding("Unicode", "\u22f1\u11e1");1393} catch (Throwable t) { unexpected(t); }13941395//----------------------------------------------------------------1396// A surprisingly large number of ways to delete an environment var.1397//----------------------------------------------------------------1398testVariableDeleter(new EnvironmentFrobber() {1399public void doIt(Map<String,String> environ) {1400environ.remove("Foo");}});14011402testVariableDeleter(new EnvironmentFrobber() {1403public void doIt(Map<String,String> environ) {1404environ.keySet().remove("Foo");}});14051406testVariableDeleter(new EnvironmentFrobber() {1407public void doIt(Map<String,String> environ) {1408environ.values().remove("BAAR");}});14091410testVariableDeleter(new EnvironmentFrobber() {1411public void doIt(Map<String,String> environ) {1412// Legally fabricate a ProcessEnvironment.StringEntry,1413// even though it's private.1414Map<String,String> environ21415= new ProcessBuilder().environment();1416environ2.clear();1417environ2.put("Foo","BAAR");1418// Subtlety alert.1419Map.Entry<String,String> e1420= environ2.entrySet().iterator().next();1421environ.entrySet().remove(e);}});14221423testVariableDeleter(new EnvironmentFrobber() {1424public void doIt(Map<String,String> environ) {1425Map.Entry<String,String> victim = null;1426for (Map.Entry<String,String> e : environ.entrySet())1427if (e.getKey().equals("Foo"))1428victim = e;1429if (victim != null)1430environ.entrySet().remove(victim);}});14311432testVariableDeleter(new EnvironmentFrobber() {1433public void doIt(Map<String,String> environ) {1434Iterator<String> it = environ.keySet().iterator();1435while (it.hasNext()) {1436String val = it.next();1437if (val.equals("Foo"))1438it.remove();}}});14391440testVariableDeleter(new EnvironmentFrobber() {1441public void doIt(Map<String,String> environ) {1442Iterator<Map.Entry<String,String>> it1443= environ.entrySet().iterator();1444while (it.hasNext()) {1445Map.Entry<String,String> e = it.next();1446if (e.getKey().equals("Foo"))1447it.remove();}}});14481449testVariableDeleter(new EnvironmentFrobber() {1450public void doIt(Map<String,String> environ) {1451Iterator<String> it = environ.values().iterator();1452while (it.hasNext()) {1453String val = it.next();1454if (val.equals("BAAR"))1455it.remove();}}});14561457//----------------------------------------------------------------1458// A surprisingly small number of ways to add an environment var.1459//----------------------------------------------------------------1460testVariableAdder(new EnvironmentFrobber() {1461public void doIt(Map<String,String> environ) {1462environ.put("Foo","Bahrein");}});14631464//----------------------------------------------------------------1465// A few ways to modify an environment var.1466//----------------------------------------------------------------1467testVariableModifier(new EnvironmentFrobber() {1468public void doIt(Map<String,String> environ) {1469environ.put("Foo","NewValue");}});14701471testVariableModifier(new EnvironmentFrobber() {1472public void doIt(Map<String,String> environ) {1473for (Map.Entry<String,String> e : environ.entrySet())1474if (e.getKey().equals("Foo"))1475e.setValue("NewValue");}});14761477//----------------------------------------------------------------1478// Fiddle with environment sizes1479//----------------------------------------------------------------1480try {1481Map<String,String> environ = new ProcessBuilder().environment();1482int size = environ.size();1483checkSizes(environ, size);14841485environ.put("UnLiKeLYeNVIROmtNam", "someVal");1486checkSizes(environ, size+1);14871488// Check for environment independence1489new ProcessBuilder().environment().clear();14901491environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal");1492checkSizes(environ, size+1);14931494environ.remove("UnLiKeLYeNVIROmtNam");1495checkSizes(environ, size);14961497environ.clear();1498checkSizes(environ, 0);14991500environ.clear();1501checkSizes(environ, 0);15021503environ = new ProcessBuilder().environment();1504environ.keySet().clear();1505checkSizes(environ, 0);15061507environ = new ProcessBuilder().environment();1508environ.entrySet().clear();1509checkSizes(environ, 0);15101511environ = new ProcessBuilder().environment();1512environ.values().clear();1513checkSizes(environ, 0);1514} catch (Throwable t) { unexpected(t); }15151516//----------------------------------------------------------------1517// Check that various map invariants hold1518//----------------------------------------------------------------1519checkMapSanity(new ProcessBuilder().environment());1520checkMapSanity(System.getenv());1521checkMapEquality(new ProcessBuilder().environment(),1522new ProcessBuilder().environment());152315241525//----------------------------------------------------------------1526// Check effects on external "env" command.1527//----------------------------------------------------------------1528try {1529Set<String> env1 = new HashSet<String>1530(Arrays.asList(nativeEnv((String[])null).split("\n")));15311532ProcessBuilder pb = new ProcessBuilder();1533pb.environment().put("QwErTyUiOp","AsDfGhJk");15341535Set<String> env2 = new HashSet<String>1536(Arrays.asList(nativeEnv(pb).split("\n")));15371538check(env2.size() == env1.size() + 1);1539env1.add("QwErTyUiOp=AsDfGhJk");1540check(env1.equals(env2));1541} catch (Throwable t) { unexpected(t); }15421543//----------------------------------------------------------------1544// Test Runtime.exec(...envp...)1545// Check for sort order of environment variables on Windows.1546//----------------------------------------------------------------1547try {1548String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");1549// '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'1550String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",1551"+=+", "_=_", "~=~", systemRoot};1552String output = nativeEnv(envp);1553String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";1554// On Windows, Java must keep the environment sorted.1555// Order is random on Unix, so this test does the sort.1556if (! Windows.is())1557output = sortByLinesWindowsly(output);1558equal(output, expected);1559} catch (Throwable t) { unexpected(t); }15601561//----------------------------------------------------------------1562// Test Runtime.exec(...envp...)1563// and check SystemRoot gets set automatically on Windows1564//----------------------------------------------------------------1565try {1566if (Windows.is()) {1567String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");1568String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",1569"+=+", "_=_", "~=~"};1570String output = nativeEnv(envp);1571String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";1572equal(output, expected);1573}1574} catch (Throwable t) { unexpected(t); }15751576//----------------------------------------------------------------1577// System.getenv() must be consistent with System.getenv(String)1578//----------------------------------------------------------------1579try {1580for (Map.Entry<String,String> e : getenv().entrySet())1581equal(getenv(e.getKey()), e.getValue());1582} catch (Throwable t) { unexpected(t); }15831584//----------------------------------------------------------------1585// Fiddle with working directory in child1586//----------------------------------------------------------------1587try {1588String canonicalUserDir =1589new File(System.getProperty("user.dir")).getCanonicalPath();1590String[] sdirs = new String[]1591{".", "..", "/", "/bin",1592"C:", "c:", "C:/", "c:\\", "\\", "\\bin",1593"c:\\windows ", "c:\\Program Files", "c:\\Program Files\\" };1594for (String sdir : sdirs) {1595File dir = new File(sdir);1596if (! (dir.isDirectory() && dir.exists()))1597continue;1598out.println("Testing directory " + dir);1599//dir = new File(dir.getCanonicalPath());16001601ProcessBuilder pb = new ProcessBuilder();1602equal(pb.directory(), null);1603equal(pwdInChild(pb), canonicalUserDir);16041605pb.directory(dir);1606equal(pb.directory(), dir);1607equal(pwdInChild(pb), dir.getCanonicalPath());16081609pb.directory(null);1610equal(pb.directory(), null);1611equal(pwdInChild(pb), canonicalUserDir);16121613pb.directory(dir);1614}1615} catch (Throwable t) { unexpected(t); }16161617//----------------------------------------------------------------1618// Working directory with Unicode in child1619//----------------------------------------------------------------1620try {1621if (UnicodeOS.is()) {1622File dir = new File(System.getProperty("test.dir", "."),1623"ProcessBuilderDir\u4e00\u4e02");1624try {1625if (!dir.exists())1626dir.mkdir();1627out.println("Testing Unicode directory:" + dir);1628ProcessBuilder pb = new ProcessBuilder();1629pb.directory(dir);1630equal(pwdInChild(pb), dir.getCanonicalPath());1631} finally {1632if (dir.exists())1633dir.delete();1634}1635}1636} catch (Throwable t) { unexpected(t); }16371638//----------------------------------------------------------------1639// OOME in child allocating maximally sized array1640// Test for hotspot/jvmti bug 68509571641//----------------------------------------------------------------1642try {1643List<String> list = new ArrayList<String>(javaChildArgs);1644list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",1645javaExe));1646list.add("ArrayOOME");1647ProcessResults r = run(new ProcessBuilder(list));1648check(r.err().contains("java.lang.OutOfMemoryError:"));1649check(r.err().contains(javaExe));1650check(r.err().contains(System.getProperty("java.version")));1651equal(r.exitValue(), 1);1652} catch (Throwable t) { unexpected(t); }16531654//----------------------------------------------------------------1655// Windows has tricky semi-case-insensitive semantics1656//----------------------------------------------------------------1657if (Windows.is())1658try {1659out.println("Running case insensitve variable tests");1660for (String[] namePair :1661new String[][]1662{ new String[]{"PATH","PaTh"},1663new String[]{"home","HOME"},1664new String[]{"SYSTEMROOT","SystemRoot"}}) {1665check((getenv(namePair[0]) == null &&1666getenv(namePair[1]) == null)1667||1668getenv(namePair[0]).equals(getenv(namePair[1])),1669"Windows environment variables are not case insensitive");1670}1671} catch (Throwable t) { unexpected(t); }16721673//----------------------------------------------------------------1674// Test proper Unicode child environment transfer1675//----------------------------------------------------------------1676if (UnicodeOS.is())1677try {1678ProcessBuilder pb = new ProcessBuilder();1679pb.environment().put("\u1234","\u5678");1680pb.environment().remove("PATH");1681equal(getenvInChild1234(pb), "\u5678");1682} catch (Throwable t) { unexpected(t); }168316841685//----------------------------------------------------------------1686// Test Runtime.exec(...envp...) with envstrings with initial `='1687//----------------------------------------------------------------1688try {1689List<String> childArgs = new ArrayList<String>(javaChildArgs);1690childArgs.add("System.getenv()");1691String[] cmdp = childArgs.toArray(new String[childArgs.size()]);1692String[] envp;1693String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot};1694String[] envpOth = {"=ExitValue=3", "=C:=\\"};1695if (Windows.is()) {1696envp = envpWin;1697} else {1698envp = envpOth;1699}1700Process p = Runtime.getRuntime().exec(cmdp, envp);1701String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,";1702expected = AIX.is() ? expected + "LIBPATH="+libpath+",": expected;1703String commandOutput = commandOutput(p);1704if (MacOSX.is()) {1705commandOutput = removeMacExpectedVars(commandOutput);1706}1707if (AIX.is()) {1708commandOutput = removeAixExpectedVars(commandOutput);1709}1710equal(commandOutput, expected);1711if (Windows.is()) {1712ProcessBuilder pb = new ProcessBuilder(childArgs);1713pb.environment().clear();1714pb.environment().put("SystemRoot", systemRoot);1715pb.environment().put("=ExitValue", "3");1716pb.environment().put("=C:", "\\");1717equal(commandOutput(pb), expected);1718}1719} catch (Throwable t) { unexpected(t); }17201721//----------------------------------------------------------------1722// Test Runtime.exec(...envp...) with envstrings without any `='1723//----------------------------------------------------------------1724try {1725String[] cmdp = {"echo"};1726String[] envp = {"Hello", "World"}; // Yuck!1727Process p = Runtime.getRuntime().exec(cmdp, envp);1728equal(commandOutput(p), "\n");1729} catch (Throwable t) { unexpected(t); }17301731//----------------------------------------------------------------1732// Test Runtime.exec(...envp...) with envstrings containing NULs1733//----------------------------------------------------------------1734try {1735List<String> childArgs = new ArrayList<String>(javaChildArgs);1736childArgs.add("System.getenv()");1737String[] cmdp = childArgs.toArray(new String[childArgs.size()]);1738String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck!1739"FO\u0000=B\u0000R"};1740String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck!1741"FO\u0000=B\u0000R"};1742String[] envp;1743if (Windows.is()) {1744envp = envpWin;1745} else {1746envp = envpOth;1747}1748System.out.println ("cmdp");1749for (int i=0; i<cmdp.length; i++) {1750System.out.printf ("cmdp %d: %s\n", i, cmdp[i]);1751}1752System.out.println ("envp");1753for (int i=0; i<envp.length; i++) {1754System.out.printf ("envp %d: %s\n", i, envp[i]);1755}1756Process p = Runtime.getRuntime().exec(cmdp, envp);1757String commandOutput = commandOutput(p);1758if (MacOSX.is()) {1759commandOutput = removeMacExpectedVars(commandOutput);1760}1761if (AIX.is()) {1762commandOutput = removeAixExpectedVars(commandOutput);1763}1764check(commandOutput.equals(Windows.is()1765? "LC_ALL=C,SystemRoot="+systemRoot+","1766: AIX.is()1767? "LC_ALL=C,LIBPATH="+libpath+","1768: "LC_ALL=C,"),1769"Incorrect handling of envstrings containing NULs");1770} catch (Throwable t) { unexpected(t); }17711772//----------------------------------------------------------------1773// Test the redirectErrorStream property1774//----------------------------------------------------------------1775try {1776ProcessBuilder pb = new ProcessBuilder();1777equal(pb.redirectErrorStream(), false);1778equal(pb.redirectErrorStream(true), pb);1779equal(pb.redirectErrorStream(), true);1780equal(pb.redirectErrorStream(false), pb);1781equal(pb.redirectErrorStream(), false);1782} catch (Throwable t) { unexpected(t); }17831784try {1785List<String> childArgs = new ArrayList<String>(javaChildArgs);1786childArgs.add("OutErr");1787ProcessBuilder pb = new ProcessBuilder(childArgs);1788{1789ProcessResults r = run(pb);1790equal(r.out(), "outout");1791equal(r.err(), "errerr");1792}1793{1794pb.redirectErrorStream(true);1795ProcessResults r = run(pb);1796equal(r.out(), "outerrouterr");1797equal(r.err(), "");1798}1799} catch (Throwable t) { unexpected(t); }18001801if (Unix.is()) {1802//----------------------------------------------------------------1803// We can find true and false when PATH is null1804//----------------------------------------------------------------1805try {1806List<String> childArgs = new ArrayList<String>(javaChildArgs);1807childArgs.add("null PATH");1808ProcessBuilder pb = new ProcessBuilder(childArgs);1809pb.environment().remove("PATH");1810ProcessResults r = run(pb);1811equal(r.out(), "");1812equal(r.err(), "");1813equal(r.exitValue(), 0);1814} catch (Throwable t) { unexpected(t); }18151816//----------------------------------------------------------------1817// PATH search algorithm on Unix1818//----------------------------------------------------------------1819try {1820List<String> childArgs = new ArrayList<String>(javaChildArgs);1821childArgs.add("PATH search algorithm");1822ProcessBuilder pb = new ProcessBuilder(childArgs);1823pb.environment().put("PATH", "dir1:dir2:");1824ProcessResults r = run(pb);1825equal(r.out(), "");1826equal(r.err(), "");1827equal(r.exitValue(), True.exitValue());1828} catch (Throwable t) { unexpected(t); }18291830//----------------------------------------------------------------1831// Parent's, not child's PATH is used1832//----------------------------------------------------------------1833try {1834new File("suBdiR").mkdirs();1835copy("/bin/true", "suBdiR/unliKely");1836final ProcessBuilder pb =1837new ProcessBuilder(new String[]{"unliKely"});1838pb.environment().put("PATH", "suBdiR");1839THROWS(IOException.class, () -> pb.start());1840} catch (Throwable t) { unexpected(t);1841} finally {1842new File("suBdiR/unliKely").delete();1843new File("suBdiR").delete();1844}1845}18461847//----------------------------------------------------------------1848// Attempt to start bogus program ""1849//----------------------------------------------------------------1850try {1851new ProcessBuilder("").start();1852fail("Expected IOException not thrown");1853} catch (IOException e) {1854String m = e.getMessage();1855if (EnglishUnix.is() &&1856! matches(m, "No such file or directory"))1857unexpected(e);1858} catch (Throwable t) { unexpected(t); }18591860//----------------------------------------------------------------1861// Check that attempt to execute program name with funny1862// characters throws an exception containing those characters.1863//----------------------------------------------------------------1864for (String programName : new String[] {"\u00f0", "\u01f0"})1865try {1866new ProcessBuilder(programName).start();1867fail("Expected IOException not thrown");1868} catch (IOException e) {1869String m = e.getMessage();1870Pattern p = Pattern.compile(programName);1871if (! matches(m, programName)1872|| (EnglishUnix.is()1873&& ! matches(m, "No such file or directory")))1874unexpected(e);1875} catch (Throwable t) { unexpected(t); }18761877//----------------------------------------------------------------1878// Attempt to start process in nonexistent directory fails.1879//----------------------------------------------------------------1880try {1881new ProcessBuilder("echo")1882.directory(new File("UnLiKeLY"))1883.start();1884fail("Expected IOException not thrown");1885} catch (IOException e) {1886String m = e.getMessage();1887if (! matches(m, "in directory")1888|| (EnglishUnix.is() &&1889! matches(m, "No such file or directory")))1890unexpected(e);1891} catch (Throwable t) { unexpected(t); }18921893//----------------------------------------------------------------1894// Attempt to write 4095 bytes to the pipe buffer without a1895// reader to drain it would deadlock, if not for the fact that1896// interprocess pipe buffers are at least 4096 bytes.1897//1898// Also, check that available reports all the bytes expected1899// in the pipe buffer, and that I/O operations do the expected1900// things.1901//----------------------------------------------------------------1902try {1903List<String> childArgs = new ArrayList<String>(javaChildArgs);1904childArgs.add("print4095");1905final int SIZE = 4095;1906final Process p = new ProcessBuilder(childArgs).start();1907print4095(p.getOutputStream(), (byte) '!'); // Might hang!1908p.waitFor(); // Might hang!1909equal(SIZE, p.getInputStream().available());1910equal(SIZE, p.getErrorStream().available());1911THROWS(IOException.class,1912() -> { p.getOutputStream().write((byte) '!');1913p.getOutputStream().flush();});19141915final byte[] bytes = new byte[SIZE + 1];1916equal(SIZE, p.getInputStream().read(bytes));1917for (int i = 0; i < SIZE; i++)1918equal((byte) '!', bytes[i]);1919equal((byte) 0, bytes[SIZE]);19201921equal(SIZE, p.getErrorStream().read(bytes));1922for (int i = 0; i < SIZE; i++)1923equal((byte) 'E', bytes[i]);1924equal((byte) 0, bytes[SIZE]);19251926equal(0, p.getInputStream().available());1927equal(0, p.getErrorStream().available());1928equal(-1, p.getErrorStream().read());1929equal(-1, p.getInputStream().read());19301931equal(p.exitValue(), 5);19321933p.getInputStream().close();1934p.getErrorStream().close();1935try { p.getOutputStream().close(); } catch (IOException flushFailed) { }19361937InputStream[] streams = { p.getInputStream(), p.getErrorStream() };1938for (final InputStream in : streams) {1939Fun[] ops = {1940() -> in.read(),1941() -> in.read(bytes),1942() -> in.available()1943};1944for (Fun op : ops) {1945try {1946op.f();1947fail();1948} catch (IOException expected) {1949check(expected.getMessage()1950.matches("[Ss]tream [Cc]losed"));1951}1952}1953}1954} catch (Throwable t) { unexpected(t); }19551956//----------------------------------------------------------------1957// Check that reads which are pending when Process.destroy is1958// called, get EOF, not IOException("Stream closed").1959//----------------------------------------------------------------1960try {1961final int cases = 4;1962for (int i = 0; i < cases; i++) {1963final int action = i;1964List<String> childArgs = new ArrayList<String>(javaChildArgs);1965childArgs.add("sleep");1966final byte[] bytes = new byte[10];1967final Process p = new ProcessBuilder(childArgs).start();1968final CountDownLatch latch = new CountDownLatch(1);1969final InputStream s;1970switch (action & 0x1) {1971case 0: s = p.getInputStream(); break;1972case 1: s = p.getErrorStream(); break;1973default: throw new Error();1974}1975final Thread thread = new Thread() {1976public void run() {1977try {1978int r;1979latch.countDown();1980switch (action & 0x2) {1981case 0: r = s.read(); break;1982case 2: r = s.read(bytes); break;1983default: throw new Error();1984}1985equal(-1, r);1986} catch (Throwable t) { unexpected(t); }}};19871988thread.start();1989latch.await();1990Thread.sleep(10);19911992String os = System.getProperty("os.name");1993if (os.equalsIgnoreCase("Solaris") ||1994os.equalsIgnoreCase("SunOS"))1995{1996final Object deferred;1997Class<?> c = s.getClass();1998if (c.getName().equals(1999"java.lang.UNIXProcess$DeferredCloseInputStream"))2000{2001deferred = s;2002} else {2003Field deferredField = p.getClass().2004getDeclaredField("stdout_inner_stream");2005deferredField.setAccessible(true);2006deferred = deferredField.get(p);2007}2008Field useCountField = deferred.getClass().2009getDeclaredField("useCount");2010useCountField.setAccessible(true);20112012while (useCountField.getInt(deferred) <= 0) {2013Thread.yield();2014}2015} else if (s instanceof BufferedInputStream) {2016Field f = Unsafe.class.getDeclaredField("theUnsafe");2017f.setAccessible(true);2018Unsafe unsafe = (Unsafe)f.get(null);20192020while (unsafe.tryMonitorEnter(s)) {2021unsafe.monitorExit(s);2022Thread.sleep(1);2023}2024}2025p.destroy();2026thread.join();2027}2028} catch (Throwable t) { unexpected(t); }20292030//----------------------------------------------------------------2031// Check that subprocesses which create subprocesses of their2032// own do not cause parent to hang waiting for file2033// descriptors to be closed.2034//----------------------------------------------------------------2035try {2036if (Unix.is()2037&& new File("/bin/bash").exists()2038&& new File("/bin/sleep").exists()) {2039// Notice that we only destroy the process created by us (i.e.2040// our child) but not our grandchild (i.e. '/bin/sleep'). So2041// pay attention that the grandchild doesn't run too long to2042// avoid polluting the process space with useless processes.2043// Running the grandchild for 60s should be more than enough.2044final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" };2045final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 60\")" };2046final ProcessBuilder pb = new ProcessBuilder(cmd);2047final Process p = pb.start();2048final InputStream stdout = p.getInputStream();2049final InputStream stderr = p.getErrorStream();2050final OutputStream stdin = p.getOutputStream();2051final Thread reader = new Thread() {2052public void run() {2053try { stdout.read(); }2054catch (IOException e) {2055// Check that reader failed because stream was2056// asynchronously closed.2057// e.printStackTrace();2058if (EnglishUnix.is() &&2059! (e.getMessage().matches(".*Bad file.*")))2060unexpected(e);2061}2062catch (Throwable t) { unexpected(t); }}};2063reader.setDaemon(true);2064reader.start();2065Thread.sleep(100);2066p.destroy();2067check(p.waitFor() != 0);2068check(p.exitValue() != 0);2069// Subprocess is now dead, but file descriptors remain open.2070// Make sure the test will fail if we don't manage to close2071// the open streams within 30 seconds. Notice that this time2072// must be shorter than the sleep time of the grandchild.2073Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true);2074t.schedule(new TimerTask() {2075public void run() {2076fail("Subprocesses which create subprocesses of " +2077"their own caused the parent to hang while " +2078"waiting for file descriptors to be closed.");2079System.exit(-1);2080}2081}, 30000);2082stdout.close();2083stderr.close();2084stdin.close();2085new ProcessBuilder(cmdkill).start();2086// All streams successfully closed so we can cancel the timer.2087t.cancel();2088//----------------------------------------------------------2089// There remain unsolved issues with asynchronous close.2090// Here's a highly non-portable experiment to demonstrate:2091//----------------------------------------------------------2092if (Boolean.getBoolean("wakeupJeff!")) {2093System.out.println("wakeupJeff!");2094// Initialize signal handler for INTERRUPT_SIGNAL.2095new FileInputStream("/bin/sleep").getChannel().close();2096// Send INTERRUPT_SIGNAL to every thread in this java.2097String[] wakeupJeff = {2098"/bin/bash", "-c",2099"/bin/ps --noheaders -Lfp $PPID | " +2100"/usr/bin/perl -nale 'print $F[3]' | " +2101// INTERRUPT_SIGNAL == 62 on my machine du jour.2102"/usr/bin/xargs kill -62"2103};2104new ProcessBuilder(wakeupJeff).start().waitFor();2105// If wakeupJeff worked, reader probably got EBADF.2106reader.join();2107}2108}2109} catch (Throwable t) { unexpected(t); }21102111//----------------------------------------------------------------2112// Attempt to start process with insufficient permissions fails.2113//----------------------------------------------------------------2114try {2115new File("emptyCommand").delete();2116new FileOutputStream("emptyCommand").close();2117new File("emptyCommand").setExecutable(false);2118new ProcessBuilder("./emptyCommand").start();2119fail("Expected IOException not thrown");2120} catch (IOException e) {2121new File("./emptyCommand").delete();2122String m = e.getMessage();2123if (EnglishUnix.is() &&2124! matches(m, "Permission denied"))2125unexpected(e);2126} catch (Throwable t) { unexpected(t); }21272128new File("emptyCommand").delete();21292130//----------------------------------------------------------------2131// Check for correct security permission behavior2132//----------------------------------------------------------------2133final Policy policy = new Policy();2134Policy.setPolicy(policy);2135System.setSecurityManager(new SecurityManager());21362137try {2138// No permissions required to CREATE a ProcessBuilder2139policy.setPermissions(/* Nothing */);2140new ProcessBuilder("env").directory(null).directory();2141new ProcessBuilder("env").directory(new File("dir")).directory();2142new ProcessBuilder("env").command("??").command();2143} catch (Throwable t) { unexpected(t); }21442145THROWS(SecurityException.class,2146() -> { policy.setPermissions(/* Nothing */);2147System.getenv("foo");},2148() -> { policy.setPermissions(/* Nothing */);2149System.getenv();},2150() -> { policy.setPermissions(/* Nothing */);2151new ProcessBuilder("echo").start();},2152() -> { policy.setPermissions(/* Nothing */);2153Runtime.getRuntime().exec("echo");},2154() -> { policy.setPermissions(2155new RuntimePermission("getenv.bar"));2156System.getenv("foo");});21572158try {2159policy.setPermissions(new RuntimePermission("getenv.foo"));2160System.getenv("foo");21612162policy.setPermissions(new RuntimePermission("getenv.*"));2163System.getenv("foo");2164System.getenv();2165new ProcessBuilder().environment();2166} catch (Throwable t) { unexpected(t); }216721682169final Permission execPermission2170= new FilePermission("<<ALL FILES>>", "execute");21712172THROWS(SecurityException.class,2173() -> { // environment permission by itself insufficient2174policy.setPermissions(new RuntimePermission("getenv.*"));2175ProcessBuilder pb = new ProcessBuilder("env");2176pb.environment().put("foo","bar");2177pb.start();},2178() -> { // exec permission by itself insufficient2179policy.setPermissions(execPermission);2180ProcessBuilder pb = new ProcessBuilder("env");2181pb.environment().put("foo","bar");2182pb.start();});21832184try {2185// Both permissions? OK.2186policy.setPermissions(new RuntimePermission("getenv.*"),2187execPermission);2188ProcessBuilder pb = new ProcessBuilder("env");2189pb.environment().put("foo","bar");2190Process p = pb.start();2191closeStreams(p);2192} catch (IOException e) { // OK2193} catch (Throwable t) { unexpected(t); }21942195try {2196// Don't need environment permission unless READING environment2197policy.setPermissions(execPermission);2198Runtime.getRuntime().exec("env", new String[]{});2199} catch (IOException e) { // OK2200} catch (Throwable t) { unexpected(t); }22012202try {2203// Don't need environment permission unless READING environment2204policy.setPermissions(execPermission);2205new ProcessBuilder("env").start();2206} catch (IOException e) { // OK2207} catch (Throwable t) { unexpected(t); }22082209// Restore "normal" state without a security manager2210policy.setPermissions(new RuntimePermission("setSecurityManager"));2211System.setSecurityManager(null);22122213//----------------------------------------------------------------2214// Check that Process.isAlive() &2215// Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.2216//----------------------------------------------------------------2217try {2218List<String> childArgs = new ArrayList<String>(javaChildArgs);2219childArgs.add("sleep");2220final Process p = new ProcessBuilder(childArgs).start();2221long start = System.nanoTime();2222if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {2223fail("Test failed: Process exited prematurely");2224}2225long end = System.nanoTime();2226// give waitFor(timeout) a wide berth (2s)2227System.out.printf(" waitFor process: delta: %d%n",(end - start) );22282229if ((end - start) > TimeUnit.SECONDS.toNanos(2))2230fail("Test failed: waitFor took too long (" + (end - start) + "ns)");22312232p.destroy();2233p.waitFor();22342235if (p.isAlive() ||2236!p.waitFor(0, TimeUnit.MILLISECONDS))2237{2238fail("Test failed: Process still alive - please terminate " +2239p.toString() + " manually");2240}2241} catch (Throwable t) { unexpected(t); }22422243//----------------------------------------------------------------2244// Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)2245// works as expected.2246//----------------------------------------------------------------2247try {2248List<String> childArgs = new ArrayList<String>(javaChildArgs);2249childArgs.add("sleep");2250final Process p = new ProcessBuilder(childArgs).start();2251long start = System.nanoTime();22522253p.waitFor(10, TimeUnit.MILLISECONDS);22542255long end = System.nanoTime();2256if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10))2257fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");22582259p.destroy();2260} catch (Throwable t) { unexpected(t); }22612262//----------------------------------------------------------------2263// Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)2264// interrupt works as expected, if interrupted while waiting.2265//----------------------------------------------------------------2266try {2267List<String> childArgs = new ArrayList<String>(javaChildArgs);2268childArgs.add("sleep");2269final Process p = new ProcessBuilder(childArgs).start();2270final long start = System.nanoTime();2271final CountDownLatch aboutToWaitFor = new CountDownLatch(1);22722273final Thread thread = new Thread() {2274public void run() {2275try {2276aboutToWaitFor.countDown();2277Thread.currentThread().interrupt();2278boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);2279fail("waitFor() wasn't interrupted, its return value was: " + result);2280} catch (InterruptedException success) {2281} catch (Throwable t) { unexpected(t); }2282}2283};22842285thread.start();2286aboutToWaitFor.await();2287thread.interrupt();2288thread.join(10L * 1000L);2289check(millisElapsedSince(start) < 10L * 1000L);2290check(!thread.isAlive());2291p.destroy();2292} catch (Throwable t) { unexpected(t); }22932294//----------------------------------------------------------------2295// Check that Process.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS)2296// interrupt works as expected, if interrupted while waiting.2297//----------------------------------------------------------------2298try {2299List<String> childArgs = new ArrayList<String>(javaChildArgs);2300childArgs.add("sleep");2301final Process p = new ProcessBuilder(childArgs).start();2302final long start = System.nanoTime();2303final CountDownLatch aboutToWaitFor = new CountDownLatch(1);23042305final Thread thread = new Thread() {2306public void run() {2307try {2308aboutToWaitFor.countDown();2309Thread.currentThread().interrupt();2310boolean result = p.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS);2311fail("waitFor() wasn't interrupted, its return value was: " + result);2312} catch (InterruptedException success) {2313} catch (Throwable t) { unexpected(t); }2314}2315};23162317thread.start();2318aboutToWaitFor.await();2319thread.interrupt();2320thread.join(10L * 1000L);2321check(millisElapsedSince(start) < 10L * 1000L);2322check(!thread.isAlive());2323p.destroy();2324} catch (Throwable t) { unexpected(t); }23252326//----------------------------------------------------------------2327// Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)2328// interrupt works as expected, if interrupted before waiting.2329//----------------------------------------------------------------2330try {2331List<String> childArgs = new ArrayList<String>(javaChildArgs);2332childArgs.add("sleep");2333final Process p = new ProcessBuilder(childArgs).start();2334final long start = System.nanoTime();2335final CountDownLatch threadStarted = new CountDownLatch(1);23362337final Thread thread = new Thread() {2338public void run() {2339try {2340threadStarted.countDown();2341do { Thread.yield(); }2342while (!Thread.currentThread().isInterrupted());2343boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);2344fail("waitFor() wasn't interrupted, its return value was: " + result);2345} catch (InterruptedException success) {2346} catch (Throwable t) { unexpected(t); }2347}2348};23492350thread.start();2351threadStarted.await();2352thread.interrupt();2353thread.join(10L * 1000L);2354check(millisElapsedSince(start) < 10L * 1000L);2355check(!thread.isAlive());2356p.destroy();2357} catch (Throwable t) { unexpected(t); }23582359//----------------------------------------------------------------2360// Check that Process.waitFor(timeout, null) throws NPE.2361//----------------------------------------------------------------2362try {2363List<String> childArgs = new ArrayList<String>(javaChildArgs);2364childArgs.add("sleep");2365final Process p = new ProcessBuilder(childArgs).start();2366THROWS(NullPointerException.class,2367() -> p.waitFor(10L, null));2368THROWS(NullPointerException.class,2369() -> p.waitFor(0L, null));2370THROWS(NullPointerException.class,2371() -> p.waitFor(-1L, null));2372// Terminate process and recheck after it exits2373p.destroy();2374p.waitFor();2375THROWS(NullPointerException.class,2376() -> p.waitFor(10L, null));2377THROWS(NullPointerException.class,2378() -> p.waitFor(0L, null));2379THROWS(NullPointerException.class,2380() -> p.waitFor(-1L, null));2381} catch (Throwable t) { unexpected(t); }23822383//----------------------------------------------------------------2384// Check that default implementation of Process.waitFor(timeout, null) throws NPE.2385//----------------------------------------------------------------2386try {2387List<String> childArgs = new ArrayList<String>(javaChildArgs);2388childArgs.add("sleep");2389final Process proc = new ProcessBuilder(childArgs).start();2390final DelegatingProcess p = new DelegatingProcess(proc);23912392THROWS(NullPointerException.class,2393() -> p.waitFor(10L, null));2394THROWS(NullPointerException.class,2395() -> p.waitFor(0L, null));2396THROWS(NullPointerException.class,2397() -> p.waitFor(-1L, null));2398// Terminate process and recheck after it exits2399p.destroy();2400p.waitFor();2401THROWS(NullPointerException.class,2402() -> p.waitFor(10L, null));2403THROWS(NullPointerException.class,2404() -> p.waitFor(0L, null));2405THROWS(NullPointerException.class,2406() -> p.waitFor(-1L, null));2407} catch (Throwable t) { unexpected(t); }24082409//----------------------------------------------------------------2410// Check the default implementation for2411// Process.waitFor(long, TimeUnit)2412//----------------------------------------------------------------2413try {2414List<String> childArgs = new ArrayList<String>(javaChildArgs);2415childArgs.add("sleep");2416final Process proc = new ProcessBuilder(childArgs).start();2417DelegatingProcess p = new DelegatingProcess(proc);2418long start = System.nanoTime();24192420p.waitFor(1000, TimeUnit.MILLISECONDS);24212422long end = System.nanoTime();2423if ((end - start) < 500000000)2424fail("Test failed: waitFor didn't take long enough");24252426p.destroy();24272428p.waitFor(1000, TimeUnit.MILLISECONDS);2429} catch (Throwable t) { unexpected(t); }2430}24312432static void closeStreams(Process p) {2433try {2434p.getOutputStream().close();2435p.getInputStream().close();2436p.getErrorStream().close();2437} catch (Throwable t) { unexpected(t); }2438}24392440//----------------------------------------------------------------2441// A Policy class designed to make permissions fiddling very easy.2442//----------------------------------------------------------------2443private static class Policy extends java.security.Policy {2444private Permissions perms;24452446public void setPermissions(Permission...permissions) {2447perms = new Permissions();2448for (Permission permission : permissions)2449perms.add(permission);2450}24512452public Policy() { setPermissions(/* Nothing */); }24532454public PermissionCollection getPermissions(CodeSource cs) {2455return perms;2456}24572458public PermissionCollection getPermissions(ProtectionDomain pd) {2459return perms;2460}24612462public boolean implies(ProtectionDomain pd, Permission p) {2463return perms.implies(p);2464}24652466public void refresh() {}2467}24682469private static class StreamAccumulator extends Thread {2470private final InputStream is;2471private final StringBuilder sb = new StringBuilder();2472private Throwable throwable = null;24732474public String result () throws Throwable {2475if (throwable != null)2476throw throwable;2477return sb.toString();2478}24792480StreamAccumulator (InputStream is) {2481this.is = is;2482}24832484public void run() {2485try {2486Reader r = new InputStreamReader(is);2487char[] buf = new char[4096];2488int n;2489while ((n = r.read(buf)) > 0) {2490sb.append(buf,0,n);2491}2492} catch (Throwable t) {2493throwable = t;2494} finally {2495try { is.close(); }2496catch (Throwable t) { throwable = t; }2497}2498}2499}25002501static ProcessResults run(ProcessBuilder pb) {2502try {2503return run(pb.start());2504} catch (Throwable t) { unexpected(t); return null; }2505}25062507private static ProcessResults run(Process p) {2508Throwable throwable = null;2509int exitValue = -1;2510String out = "";2511String err = "";25122513StreamAccumulator outAccumulator =2514new StreamAccumulator(p.getInputStream());2515StreamAccumulator errAccumulator =2516new StreamAccumulator(p.getErrorStream());25172518try {2519outAccumulator.start();2520errAccumulator.start();25212522exitValue = p.waitFor();25232524outAccumulator.join();2525errAccumulator.join();25262527out = outAccumulator.result();2528err = errAccumulator.result();2529} catch (Throwable t) {2530throwable = t;2531}25322533return new ProcessResults(out, err, exitValue, throwable);2534}25352536//----------------------------------------------------------------2537// Results of a command2538//----------------------------------------------------------------2539private static class ProcessResults {2540private final String out;2541private final String err;2542private final int exitValue;2543private final Throwable throwable;25442545public ProcessResults(String out,2546String err,2547int exitValue,2548Throwable throwable) {2549this.out = out;2550this.err = err;2551this.exitValue = exitValue;2552this.throwable = throwable;2553}25542555public String out() { return out; }2556public String err() { return err; }2557public int exitValue() { return exitValue; }2558public Throwable throwable() { return throwable; }25592560public String toString() {2561StringBuilder sb = new StringBuilder();2562sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")2563.append("<STDERR>\n" + err() + "</STDERR>\n")2564.append("exitValue = " + exitValue + "\n");2565if (throwable != null)2566sb.append(throwable.getStackTrace());2567return sb.toString();2568}2569}25702571//--------------------- Infrastructure ---------------------------2572static volatile int passed = 0, failed = 0;2573static void pass() {passed++;}2574static void fail() {failed++; Thread.dumpStack();}2575static void fail(String msg) {System.out.println(msg); fail();}2576static void unexpected(Throwable t) {failed++; t.printStackTrace();}2577static void check(boolean cond) {if (cond) pass(); else fail();}2578static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}2579static void equal(Object x, Object y) {2580if (x == null ? y == null : x.equals(y)) pass();2581else fail(x + " not equal to " + y);}25822583public static void main(String[] args) throws Throwable {2584try {realMain(args);} catch (Throwable t) {unexpected(t);}2585System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);2586if (failed > 0) throw new AssertionError("Some tests failed");}2587interface Fun {void f() throws Throwable;}2588static void THROWS(Class<? extends Throwable> k, Fun... fs) {2589for (Fun f : fs)2590try { f.f(); fail("Expected " + k.getName() + " not thrown"); }2591catch (Throwable t) {2592if (k.isAssignableFrom(t.getClass())) pass();2593else unexpected(t);}}2594}259525962597