View Javadoc
1   /* 
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   *
17   */
18  
19  package org.apache.commons.exec;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertNotNull;
24  import static org.junit.Assert.assertNull;
25  import static org.junit.Assert.assertTrue;
26  import static org.junit.Assert.fail;
27  
28  import java.io.BufferedReader;
29  import java.io.ByteArrayInputStream;
30  import java.io.ByteArrayOutputStream;
31  import java.io.File;
32  import java.io.FileInputStream;
33  import java.io.FileOutputStream;
34  import java.io.FileReader;
35  import java.io.IOException;
36  import java.util.HashMap;
37  import java.util.Map;
38  
39  import org.apache.commons.exec.environment.EnvironmentUtils;
40  import org.junit.After;
41  import org.junit.Before;
42  import org.junit.BeforeClass;
43  import org.junit.Ignore;
44  import org.junit.Test;
45  
46  /**
47   * @version $Id: DefaultExecutorTest.java 1636057 2014-11-01 21:14:00Z ggregory $
48   */
49  public class DefaultExecutorTest {
50  
51      /** Maximum time to wait (15s) */
52      private static final int WAITFOR_TIMEOUT = 15000;
53  
54      private final Executor exec = new DefaultExecutor();
55      private final File testDir = new File("src/test/scripts");
56      private final File foreverOutputFile = new File("./target/forever.txt");
57      private ByteArrayOutputStream baos;
58  
59      private final File testScript = TestUtil.resolveScriptForOS(testDir + "/test");
60      private final File errorTestScript = TestUtil.resolveScriptForOS(testDir + "/error");
61      private final File foreverTestScript = TestUtil.resolveScriptForOS(testDir + "/forever");
62      private final File nonExistingTestScript = TestUtil.resolveScriptForOS(testDir + "/grmpffffff");
63      private final File redirectScript = TestUtil.resolveScriptForOS(testDir + "/redirect");
64      private final File printArgsScript = TestUtil.resolveScriptForOS(testDir + "/printargs");
65  //    private final File acroRd32Script = TestUtil.resolveScriptForOS(testDir + "/acrord32");
66      private final File stdinSript = TestUtil.resolveScriptForOS(testDir + "/stdin");
67      private final File environmentSript = TestUtil.resolveScriptForOS(testDir + "/environment");
68  //    private final File wrapperScript = TestUtil.resolveScriptForOS(testDir + "/wrapper");
69  
70  
71      // Get suitable exit codes for the OS
72      private static int SUCCESS_STATUS; // test script successful exit code
73      private static int ERROR_STATUS;   // test script error exit code
74  
75      @BeforeClass
76      public static void classSetUp() {
77  
78          final int statuses[] = TestUtil.getTestScriptCodesForOS();
79          SUCCESS_STATUS=statuses[0];
80          ERROR_STATUS=statuses[1];
81  
82          // turn on debug mode and throw an exception for each encountered problem
83          System.setProperty("org.apache.commons.exec.lenient", "false");
84          System.setProperty("org.apache.commons.exec.debug", "true");
85      }
86  
87      @Before
88      public void setUp() throws Exception {
89  
90          // delete the marker file
91          this.foreverOutputFile.getParentFile().mkdirs();
92          if (this.foreverOutputFile.exists()) {
93              this.foreverOutputFile.delete();
94          }
95  
96          // prepare a ready to Executor
97          this.baos = new ByteArrayOutputStream();
98          this.exec.setStreamHandler(new PumpStreamHandler(baos, baos));
99      }
100 
101     @After
102     public void tearDown() throws Exception {
103         this.baos.close();
104         foreverOutputFile.delete();
105     }
106 
107     // ======================================================================
108     // Start of regression tests
109     // ======================================================================
110 
111     /**
112      * The simplest possible test - start a script and
113      * check that the output was pumped into our
114      * {@code ByteArrayOutputStream}.
115      *
116      * @throws Exception the test failed
117      */
118     @Test
119     public void testExecute() throws Exception {
120         final CommandLine cl = new CommandLine(testScript);
121         final int exitValue = exec.execute(cl);
122         assertEquals("FOO..", baos.toString().trim());
123         assertFalse(exec.isFailure(exitValue));
124         assertEquals(new File("."), exec.getWorkingDirectory());
125     }
126 
127     @Test
128     public void testExecuteWithWorkingDirectory() throws Exception {
129         final File workingDir = new File("./target");
130         final CommandLine cl = new CommandLine(testScript);
131         exec.setWorkingDirectory(workingDir);
132         final int exitValue = exec.execute(cl);
133         assertEquals("FOO..", baos.toString().trim());
134         assertFalse(exec.isFailure(exitValue));
135         assertEquals(exec.getWorkingDirectory(), workingDir);
136     }
137 
138     @Test(expected = IOException.class)
139     public void testExecuteWithInvalidWorkingDirectory() throws Exception {
140         final File workingDir = new File("/foo/bar");
141         final CommandLine cl = new CommandLine(testScript);
142         exec.setWorkingDirectory(workingDir);
143 
144         exec.execute(cl);
145     }
146 
147     @Test
148     public void testExecuteWithError() throws Exception {
149         final CommandLine cl = new CommandLine(errorTestScript);
150 
151         try{
152             exec.execute(cl);
153             fail("Must throw ExecuteException");
154         } catch (final ExecuteException e) {
155             assertTrue(exec.isFailure(e.getExitValue()));
156         }
157     }
158 
159     @Test
160     public void testExecuteWithArg() throws Exception {
161         final CommandLine cl = new CommandLine(testScript);
162         cl.addArgument("BAR");
163         final int exitValue = exec.execute(cl);
164 
165         assertEquals("FOO..BAR", baos.toString().trim());
166         assertFalse(exec.isFailure(exitValue));
167     }
168 
169     /**
170      * Execute the test script and pass a environment containing
171      * 'TEST_ENV_VAR'.
172      */
173     @Test
174     public void testExecuteWithSingleEnvironmentVariable() throws Exception {
175         final Map<String, String> env = new HashMap<String, String>();
176         env.put("TEST_ENV_VAR", "XYZ");
177 
178         final CommandLine cl = new CommandLine(testScript);
179 
180         final int exitValue = exec.execute(cl, env);
181 
182         assertEquals("FOO.XYZ.", baos.toString().trim());
183         assertFalse(exec.isFailure(exitValue));
184     }
185 
186     /**
187      * Start a asynchronous process which returns an success
188      * exit value.
189      *
190      * @throws Exception the test failed
191      */
192     @Test
193     public void testExecuteAsync() throws Exception {
194         final CommandLine cl = new CommandLine(testScript);
195         final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
196         exec.execute(cl, resultHandler);
197         resultHandler.waitFor(2000);
198         assertTrue(resultHandler.hasResult());
199         assertNull(resultHandler.getException());
200         assertFalse(exec.isFailure(resultHandler.getExitValue()));
201         assertEquals("FOO..", baos.toString().trim());
202     }
203 
204     /**
205      * Start a asynchronous process which returns an error
206      * exit value.
207      *
208      * @throws Exception the test failed
209      */
210     @Test
211     public void testExecuteAsyncWithError() throws Exception {
212         final CommandLine cl = new CommandLine(errorTestScript);
213         final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
214         exec.execute(cl, resultHandler);
215         resultHandler.waitFor(2000);
216         assertTrue(resultHandler.hasResult());
217         assertTrue(exec.isFailure(resultHandler.getExitValue()));
218         assertNotNull(resultHandler.getException());
219         assertEquals("FOO..", baos.toString().trim());
220     }
221 
222     /**
223      * Start a asynchronous process and terminate it manually before the
224      * watchdog timeout occurs.
225      *
226      * @throws Exception the test failed
227      */
228     @Test
229     public void testExecuteAsyncWithTimelyUserTermination() throws Exception {
230         final CommandLine cl = new CommandLine(foreverTestScript);
231         final ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
232         exec.setWatchdog(watchdog);
233         final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
234         exec.execute(cl, handler);
235         // wait for script to run
236         Thread.sleep(2000);
237         assertTrue("Watchdog should watch the process", watchdog.isWatching());
238         // terminate it manually using the watchdog
239         watchdog.destroyProcess();
240         // wait until the result of the process execution is propagated
241         handler.waitFor(WAITFOR_TIMEOUT);
242         assertTrue("Watchdog should have killed the process", watchdog.killedProcess());
243         assertFalse("Watchdog is no longer watching the process", watchdog.isWatching());
244         assertTrue("ResultHandler received a result", handler.hasResult());
245         assertNotNull("ResultHandler received an exception as result", handler.getException());
246     }
247 
248     /**
249      * Start a asynchronous process and try to terminate it manually but
250      * the process was already terminated by the watchdog. This is
251      * basically a race condition between infrastructure and user
252      * code.
253      *
254      * @throws Exception the test failed
255      */
256     @Test
257     public void testExecuteAsyncWithTooLateUserTermination() throws Exception {
258         final CommandLine cl = new CommandLine(foreverTestScript);
259         final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
260         final ExecuteWatchdog watchdog = new ExecuteWatchdog(3000);
261         exec.setWatchdog(watchdog);
262         exec.execute(cl, handler);
263         // wait for script to be terminated by the watchdog
264         Thread.sleep(6000);
265         // try to terminate the already terminated process
266         watchdog.destroyProcess();
267         // wait until the result of the process execution is propagated
268         handler.waitFor(WAITFOR_TIMEOUT);
269         assertTrue("Watchdog should have killed the process already", watchdog.killedProcess());
270         assertFalse("Watchdog is no longer watching the process", watchdog.isWatching());
271         assertTrue("ResultHandler received a result", handler.hasResult());
272         assertNotNull("ResultHandler received an exception as result", handler.getException());
273     }
274 
275     /**
276      * Start a script looping forever (synchronously) and check if the ExecuteWatchdog
277      * kicks in killing the run away process. To make killing a process
278      * more testable the "forever" scripts write each second a '.'
279      * into "./target/forever.txt" (a marker file). After a test run
280      * we should have a few dots in there.
281      *
282      * @throws Exception the test failed
283      */
284     @Test
285     public void testExecuteWatchdogSync() throws Exception {
286 
287         if (OS.isFamilyOpenVms()) {
288             System.out.println("The test 'testExecuteWatchdogSync' currently hangs on the following OS : "
289                     + System.getProperty("os.name"));
290             return;
291         }
292 
293         final long timeout = 10000;
294 
295         final CommandLine cl = new CommandLine(foreverTestScript);
296         final DefaultExecutor executor = new DefaultExecutor();
297         executor.setWorkingDirectory(new File("."));
298         final ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
299         executor.setWatchdog(watchdog);
300 
301         try {
302             executor.execute(cl);
303         }
304         catch (final ExecuteException e) {
305             Thread.sleep(timeout);
306             final int nrOfInvocations = getOccurrences(readFile(this.foreverOutputFile), '.');
307             assertTrue(executor.getWatchdog().killedProcess());
308             assertTrue("killing the subprocess did not work : " + nrOfInvocations, nrOfInvocations > 5
309                     && nrOfInvocations <= 11);
310             return;
311         }
312         catch (final Throwable t) {
313             fail(t.getMessage());
314         }
315 
316         assertTrue("Killed process should be true", executor.getWatchdog().killedProcess() );
317         fail("Process did not create ExecuteException when killed");
318     }
319 
320     /**
321      * Start a script looping forever (asynchronously) and check if the
322      * ExecuteWatchdog kicks in killing the run away process. To make killing
323      * a process more testable the "forever" scripts write each second a '.'
324      * into "./target/forever.txt" (a marker file). After a test run
325      * we should have a few dots in there.
326      *
327      * @throws Exception the test failed
328      */
329     @Test
330     public void testExecuteWatchdogAsync() throws Exception {
331 
332         final long timeout = 10000;
333 
334         final CommandLine cl = new CommandLine(foreverTestScript);
335         final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
336         final DefaultExecutor executor = new DefaultExecutor();
337         executor.setWorkingDirectory(new File("."));
338         executor.setWatchdog(new ExecuteWatchdog(timeout));
339 
340         executor.execute(cl, handler);
341         handler.waitFor(WAITFOR_TIMEOUT);
342 
343         assertTrue("Killed process should be true", executor.getWatchdog().killedProcess() );
344         assertTrue("ResultHandler received a result", handler.hasResult());
345         assertNotNull("ResultHandler received an exception as result", handler.getException());
346 
347         final int nrOfInvocations = getOccurrences(readFile(this.foreverOutputFile), '.');
348         assertTrue("Killing the process did not work : " + nrOfInvocations, nrOfInvocations > 5 && nrOfInvocations <= 11);
349     }
350 
351     /**
352      * [EXEC-68] Synchronously starts a short script with a Watchdog attached with an extremely large timeout. Checks
353      * to see if the script terminated naturally or if it was killed by the Watchdog. Fail if killed by Watchdog.
354      * 
355      * @throws Exception
356      *             the test failed
357      */
358     @Test
359     public void testExecuteWatchdogVeryLongTimeout() throws Exception {
360         final long timeout = Long.MAX_VALUE;
361 
362         final CommandLine cl = new CommandLine(testScript);
363         final DefaultExecutor executor = new DefaultExecutor();
364         executor.setWorkingDirectory(new File("."));
365         final ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
366         executor.setWatchdog(watchdog);
367 
368         try {
369             executor.execute(cl);
370         } catch (final ExecuteException e) {
371             assertFalse("Process should exit normally, not be killed by watchdog", watchdog.killedProcess());
372             // If the Watchdog did not kill it, something else went wrong.
373             throw e;
374         }
375     }
376 
377     /**
378      * Try to start an non-existing application which should result
379      * in an exception.
380      *
381      * @throws Exception the test failed
382      */
383     @Test(expected = IOException.class)
384     public void testExecuteNonExistingApplication() throws Exception {
385         final CommandLine cl = new CommandLine(nonExistingTestScript);
386         final DefaultExecutor executor = new DefaultExecutor();
387 
388         executor.execute(cl);
389     }
390 
391     /**
392      * Try to start an non-existing application asynchronously which should result
393      * in an exception.
394      *
395      * @throws Exception the test failed
396      */
397     @Test
398     public void testExecuteAsyncWithNonExistingApplication() throws Exception {
399         final CommandLine cl = new CommandLine(nonExistingTestScript);
400         final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
401         exec.execute(cl, handler);
402         Thread.sleep(2000);
403         assertNotNull(handler.getException());
404         assertTrue(exec.isFailure(handler.getExitValue()));
405     }
406 
407     /**
408      * Invoke the error script but define that the ERROR_STATUS is a good
409      * exit value and therefore no exception should be thrown.
410      *
411      * @throws Exception the test failed
412      */
413     @Test
414     public void testExecuteWithCustomExitValue1() throws Exception {
415         exec.setExitValue(ERROR_STATUS);
416         final CommandLine cl = new CommandLine(errorTestScript);
417         exec.execute(cl);
418     }
419 
420     /**
421      * Invoke the error script but define that SUCCESS_STATUS is a bad
422      * exit value and therefore an exception should be thrown.
423      *
424      * @throws Exception the test failed
425      */
426     @Test
427     public void testExecuteWithCustomExitValue2() throws Exception {
428         final CommandLine cl = new CommandLine(errorTestScript);
429         exec.setExitValue(SUCCESS_STATUS);
430         try{
431             exec.execute(cl);
432             fail("Must throw ExecuteException");
433         } catch (final ExecuteException e) {
434             assertTrue(exec.isFailure(e.getExitValue()));
435         }
436     }
437 
438     /**
439      * Test the proper handling of ProcessDestroyer for an synchronous process.
440      *
441      * @throws Exception the test failed
442      */
443     @Test
444     public void testExecuteWithProcessDestroyer() throws Exception {
445 
446       final CommandLine cl = new CommandLine(testScript);
447       final ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
448       exec.setProcessDestroyer(processDestroyer);
449 
450       assertTrue(processDestroyer.size() == 0);
451       assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
452 
453       final int exitValue = exec.execute(cl);
454 
455       assertEquals("FOO..", baos.toString().trim());
456       assertFalse(exec.isFailure(exitValue));
457       assertTrue(processDestroyer.size() == 0);
458       assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
459     }
460 
461     /**
462      * Test the proper handling of ProcessDestroyer for an asynchronous process.
463      * Since we do not terminate the process it will be terminated in the
464      * ShutdownHookProcessDestroyer implementation.
465      *
466      * @throws Exception the test failed
467      */
468     @Test
469     public void testExecuteAsyncWithProcessDestroyer() throws Exception {
470 
471       final CommandLine cl = new CommandLine(foreverTestScript);
472       final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
473       final ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
474       final ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
475 
476       assertTrue(exec.getProcessDestroyer() == null);
477       assertTrue(processDestroyer.size() == 0);
478       assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
479 
480       exec.setWatchdog(watchdog);
481       exec.setProcessDestroyer(processDestroyer);
482       exec.execute(cl, handler);
483 
484       // wait for script to start
485       Thread.sleep(2000);
486 
487       // our process destroyer should be initialized now
488       assertNotNull("Process destroyer should exist", exec.getProcessDestroyer());
489       assertEquals("Process destroyer size should be 1", 1, processDestroyer.size());
490       assertTrue("Process destroyer should exist as shutdown hook", processDestroyer.isAddedAsShutdownHook());
491 
492       // terminate it and the process destroyer is detached
493       watchdog.destroyProcess();
494       assertTrue(watchdog.killedProcess());
495       handler.waitFor(WAITFOR_TIMEOUT);
496       assertTrue("ResultHandler received a result", handler.hasResult());
497       assertNotNull(handler.getException());
498       assertEquals("Processor Destroyer size should be 0", 0, processDestroyer.size());
499       assertFalse("Process destroyer should not exist as shutdown hook", processDestroyer.isAddedAsShutdownHook());
500     }
501 
502     /**
503      * Invoke the test using some fancy arguments.
504      *
505      * @throws Exception the test failed
506      */
507     @Test
508     public void testExecuteWithFancyArg() throws Exception {
509         final CommandLine cl = new CommandLine(testScript);
510         cl.addArgument("test $;`(0)[1]{2}");
511         final int exitValue = exec.execute(cl);
512         assertTrue(baos.toString().trim().indexOf("test $;`(0)[1]{2}") > 0);
513         assertFalse(exec.isFailure(exitValue));
514     }
515 
516     /**
517      * Start a process with redirected streams - stdin of the newly
518      * created process is connected to a FileInputStream whereas
519      * the "redirect" script reads all lines from stdin and prints
520      * them on stdout. Furthermore the script prints a status
521      * message on stderr.
522      *
523      * @throws Exception the test failed
524      */
525     @Test
526     public void testExecuteWithRedirectedStreams() throws Exception {
527         if (OS.isFamilyUnix()) {
528             final FileInputStream fis = new FileInputStream("./NOTICE.txt");
529             final CommandLine cl = new CommandLine(redirectScript);
530             final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(baos, baos, fis);
531             final DefaultExecutor executor = new DefaultExecutor();
532             executor.setWorkingDirectory(new File("."));
533             executor.setStreamHandler(pumpStreamHandler);
534             final int exitValue = executor.execute(cl);
535             fis.close();
536             final String result = baos.toString().trim();
537             assertTrue(result, result.indexOf("Finished reading from stdin") > 0);
538             assertFalse("exitValue=" + exitValue, exec.isFailure(exitValue));
539         } else if (OS.isFamilyWindows()) {
540             System.err
541                     .println("The code samples to do that in windows look like a joke ... :-( .., no way I'm doing that");
542             System.err.println("The test 'testExecuteWithRedirectedStreams' does not support the following OS : "
543                     + System.getProperty("os.name"));
544             return;
545         } else {
546             System.err.println("The test 'testExecuteWithRedirectedStreams' does not support the following OS : "
547                     + System.getProperty("os.name"));
548             return;
549         }
550     }
551 
552      /**
553       * Start a process and connect stdout and stderr.
554       *
555       * @throws Exception the test failed
556       */
557      @Test
558     public void testExecuteWithStdOutErr() throws Exception {
559         final CommandLine cl = new CommandLine(testScript);
560         final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(System.out, System.err);
561         final DefaultExecutor executor = new DefaultExecutor();
562         executor.setStreamHandler(pumpStreamHandler);
563         final int exitValue = executor.execute(cl);
564         assertFalse(exec.isFailure(exitValue));
565     }
566 
567     /**
568      * Start a process and connect it to no stream.
569      * 
570      * @throws Exception
571      *             the test failed
572      */
573     @Test
574     public void testExecuteWithNullOutErr() throws Exception {
575         final CommandLine cl = new CommandLine(testScript);
576         final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(null, null);
577         final DefaultExecutor executor = new DefaultExecutor();
578         executor.setStreamHandler(pumpStreamHandler);
579         final int exitValue = executor.execute(cl);
580         assertFalse(exec.isFailure(exitValue));
581     }
582 
583      /**
584       * Start a process and connect out and err to a file.
585       *
586       * @throws Exception the test failed
587       */
588      @Test
589     public void testExecuteWithRedirectOutErr() throws Exception {
590         final File outfile = File.createTempFile("EXEC", ".test");
591         outfile.deleteOnExit();
592         final CommandLine cl = new CommandLine(testScript);
593         final FileOutputStream outAndErr = new FileOutputStream(outfile);
594         try {
595             final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(outAndErr);
596             final DefaultExecutor executor = new DefaultExecutor();
597             executor.setStreamHandler(pumpStreamHandler);
598             final int exitValue = executor.execute(cl);
599             assertFalse(exec.isFailure(exitValue));
600             assertTrue(outfile.exists());
601         } finally {
602             outAndErr.close();
603         }
604     }
605 
606     /**
607      * A generic test case to print the command line arguments to 'printargs' script to solve
608      * even more command line puzzles.
609      *
610      * @throws Exception the test failed
611      */
612     @Test
613     public void testExecuteWithComplexArguments() throws Exception {
614         final CommandLine cl = new CommandLine(printArgsScript);
615         cl.addArgument("gdal_translate");
616         cl.addArgument("HDF5:\"/home/kk/grass/data/4404.he5\"://HDFEOS/GRIDS/OMI_Column_Amount_O3/Data_Fields/ColumnAmountO3/home/kk/4.tif", false);
617         final DefaultExecutor executor = new DefaultExecutor();
618         final int exitValue = executor.execute(cl);
619         assertFalse(exec.isFailure(exitValue));
620      }
621 
622 
623     /**
624      * The test script reads an argument from {@code stdin} and prints
625      * the result to stdout. To make things slightly more interesting
626      * we are using an asynchronous process.
627      *
628      * @throws Exception the test failed
629      */
630     @Test
631     public void testStdInHandling() throws Exception {
632         // newline not needed; causes problems for VMS
633         final ByteArrayInputStream bais = new ByteArrayInputStream("Foo".getBytes());
634         final CommandLine cl = new CommandLine(this.stdinSript);
635         final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(this.baos, System.err, bais);
636         final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
637         final Executor executor = new DefaultExecutor();
638         executor.setStreamHandler(pumpStreamHandler);
639         executor.execute(cl, resultHandler);
640 
641         resultHandler.waitFor(WAITFOR_TIMEOUT);
642         assertTrue("ResultHandler received a result", resultHandler.hasResult());
643 
644         assertFalse(exec.isFailure(resultHandler.getExitValue()));
645         final String result = baos.toString();
646         assertTrue("Result '" + result + "' should contain 'Hello Foo!'", result.indexOf("Hello Foo!") >= 0);
647     }
648 
649     /**
650      * Call a script to dump the environment variables of the subprocess.
651      *
652      * @throws Exception the test failed
653      */
654     @Test
655     public void testEnvironmentVariables() throws Exception {
656         exec.execute(new CommandLine(environmentSript));
657         final String environment = baos.toString().trim();
658         assertTrue("Found no environment variables", environment.length() > 0);
659         assertFalse(environment.indexOf("NEW_VAR") >= 0);
660     }
661 
662     /**
663      * Call a script to dump the environment variables of the subprocess
664      * after adding a custom environment variable.
665      *
666      * @throws Exception the test failed
667      */
668     @Test
669     public void testAddEnvironmentVariables() throws Exception {
670         final Map<String, String> myEnvVars = new HashMap<String, String>();
671         myEnvVars.putAll(EnvironmentUtils.getProcEnvironment());
672         myEnvVars.put("NEW_VAR","NEW_VAL");
673         exec.execute(new CommandLine(environmentSript), myEnvVars);
674         final String environment = baos.toString().trim();
675         assertTrue("Expecting NEW_VAR in "+environment,environment.indexOf("NEW_VAR") >= 0);
676         assertTrue("Expecting NEW_VAL in "+environment,environment.indexOf("NEW_VAL") >= 0);
677     }
678 
679     @Test
680     public void testAddEnvironmentVariableEmbeddedQuote() throws Exception {
681         final Map<String, String> myEnvVars = new HashMap<String, String>();
682         myEnvVars.putAll(EnvironmentUtils.getProcEnvironment());
683         final String name = "NEW_VAR";
684         final String value = "NEW_\"_VAL";
685         myEnvVars.put(name,value);
686         exec.execute(new CommandLine(environmentSript), myEnvVars);
687         final String environment = baos.toString().trim();
688         assertTrue("Expecting "+name+" in "+environment,environment.indexOf(name) >= 0);
689         assertTrue("Expecting "+value+" in "+environment,environment.indexOf(value) >= 0);
690     }
691 
692     // ======================================================================
693     // === Long running tests
694     // ======================================================================
695 
696     /**
697      * Start any processes in a loop to make sure that we do
698      * not leave any handles/resources open.
699      *
700      * @throws Exception the test failed
701      */
702     @Test
703     @Ignore
704     public void _testExecuteStability() throws Exception {
705 
706         // make a plain-vanilla test
707         for (int i=0; i<100; i++) {
708             final Map<String, String> env = new HashMap<String, String>();
709             env.put("TEST_ENV_VAR", Integer.toString(i));
710             final CommandLine cl = new CommandLine(testScript);
711             final int exitValue = exec.execute(cl,env);
712             assertFalse(exec.isFailure(exitValue));
713             assertEquals("FOO." + i + ".", baos.toString().trim());
714             baos.reset();
715         }
716 
717         // now be nasty and use the watchdog to kill out sub-processes
718         for (int i=0; i<100; i++) {
719             final Map<String, String> env = new HashMap<String, String>();
720             env.put("TEST_ENV_VAR", Integer.toString(i));
721             final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
722             final CommandLine cl = new CommandLine(foreverTestScript);
723             final ExecuteWatchdog watchdog = new ExecuteWatchdog(500);
724             exec.setWatchdog(watchdog);
725             exec.execute(cl, env, resultHandler);
726             resultHandler.waitFor(WAITFOR_TIMEOUT);
727             assertTrue("ResultHandler received a result", resultHandler.hasResult());
728             assertNotNull(resultHandler.getException());
729             baos.reset();
730         }
731     }
732 
733 
734     // ======================================================================
735     // === Helper methods
736     // ======================================================================
737 
738     private String readFile(final File file) throws Exception {
739 
740         String text;
741         final StringBuilder contents = new StringBuilder();
742         final BufferedReader reader = new BufferedReader(new FileReader(file));
743 
744         while ((text = reader.readLine()) != null)
745         {
746             contents.append(text)
747                 .append(System.getProperty(
748                     "line.separator"));
749         }
750         reader.close();
751         return contents.toString();
752     }
753 
754     private int getOccurrences(final String data, final char c) {
755 
756         int result = 0;
757 
758         for (int i=0; i<data.length(); i++) {
759             if (data.charAt(i) == c) {
760                 result++;
761             }
762         }
763 
764         return result;
765     }
766 }