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