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 1564600 2014-02-05 01:17:47Z 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</code>.
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 PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(new FileOutputStream(outfile));
594          final DefaultExecutor executor = new DefaultExecutor();
595         executor.setStreamHandler(pumpStreamHandler);
596          final int exitValue = executor.execute(cl);
597          assertFalse(exec.isFailure(exitValue));
598          assertTrue(outfile.exists());
599      }
600 
601     /**
602      * A generic test case to print the command line arguments to 'printargs' script to solve
603      * even more command line puzzles.
604      *
605      * @throws Exception the test failed
606      */
607     @Test
608     public void testExecuteWithComplexArguments() throws Exception {
609         final CommandLine cl = new CommandLine(printArgsScript);
610         cl.addArgument("gdal_translate");
611         cl.addArgument("HDF5:\"/home/kk/grass/data/4404.he5\"://HDFEOS/GRIDS/OMI_Column_Amount_O3/Data_Fields/ColumnAmountO3/home/kk/4.tif", false);
612         final DefaultExecutor executor = new DefaultExecutor();
613         final int exitValue = executor.execute(cl);
614         assertFalse(exec.isFailure(exitValue));
615      }
616 
617 
618     /**
619      * The test script reads an argument from <code>stdin<code> and prints
620      * the result to stdout. To make things slightly more interesting
621      * we are using an asynchronous process.
622      *
623      * @throws Exception the test failed
624      */
625     @Test
626     public void testStdInHandling() throws Exception {
627         // newline not needed; causes problems for VMS
628         final ByteArrayInputStream bais = new ByteArrayInputStream("Foo".getBytes());
629         final CommandLine cl = new CommandLine(this.stdinSript);
630         final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(this.baos, System.err, bais);
631         final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
632         final Executor executor = new DefaultExecutor();
633         executor.setStreamHandler(pumpStreamHandler);
634         executor.execute(cl, resultHandler);
635 
636         resultHandler.waitFor(WAITFOR_TIMEOUT);
637         assertTrue("ResultHandler received a result", resultHandler.hasResult());
638 
639         assertFalse(exec.isFailure(resultHandler.getExitValue()));
640         final String result = baos.toString();
641         assertTrue("Result '" + result + "' should contain 'Hello Foo!'", result.indexOf("Hello Foo!") >= 0);
642     }
643 
644     /**
645      * Call a script to dump the environment variables of the subprocess.
646      *
647      * @throws Exception the test failed
648      */
649     @Test
650     public void testEnvironmentVariables() throws Exception {
651         exec.execute(new CommandLine(environmentSript));
652         final String environment = baos.toString().trim();
653         assertTrue("Found no environment variables", environment.length() > 0);
654         assertFalse(environment.indexOf("NEW_VAR") >= 0);
655     }
656 
657     /**
658      * Call a script to dump the environment variables of the subprocess
659      * after adding a custom environment variable.
660      *
661      * @throws Exception the test failed
662      */
663     @Test
664     public void testAddEnvironmentVariables() throws Exception {
665         final Map<String, String> myEnvVars = new HashMap<String, String>();
666         myEnvVars.putAll(EnvironmentUtils.getProcEnvironment());
667         myEnvVars.put("NEW_VAR","NEW_VAL");
668         exec.execute(new CommandLine(environmentSript), myEnvVars);
669         final String environment = baos.toString().trim();
670         assertTrue("Expecting NEW_VAR in "+environment,environment.indexOf("NEW_VAR") >= 0);
671         assertTrue("Expecting NEW_VAL in "+environment,environment.indexOf("NEW_VAL") >= 0);
672     }
673 
674     @Test
675     public void testAddEnvironmentVariableEmbeddedQuote() throws Exception {
676         final Map<String, String> myEnvVars = new HashMap<String, String>();
677         myEnvVars.putAll(EnvironmentUtils.getProcEnvironment());
678         final String name = "NEW_VAR";
679         final String value = "NEW_\"_VAL";
680         myEnvVars.put(name,value);
681         exec.execute(new CommandLine(environmentSript), myEnvVars);
682         final String environment = baos.toString().trim();
683         assertTrue("Expecting "+name+" in "+environment,environment.indexOf(name) >= 0);
684         assertTrue("Expecting "+value+" in "+environment,environment.indexOf(value) >= 0);
685     }
686 
687     // ======================================================================
688     // === Long running tests
689     // ======================================================================
690 
691     /**
692      * Start any processes in a loop to make sure that we do
693      * not leave any handles/resources open.
694      *
695      * @throws Exception the test failed
696      */
697     @Test
698     @Ignore
699     public void _testExecuteStability() throws Exception {
700 
701         // make a plain-vanilla test
702         for (int i=0; i<100; i++) {
703             final Map<String, String> env = new HashMap<String, String>();
704             env.put("TEST_ENV_VAR", Integer.toString(i));
705             final CommandLine cl = new CommandLine(testScript);
706             final int exitValue = exec.execute(cl,env);
707             assertFalse(exec.isFailure(exitValue));
708             assertEquals("FOO." + i + ".", baos.toString().trim());
709             baos.reset();
710         }
711 
712         // now be nasty and use the watchdog to kill out sub-processes
713         for (int i=0; i<100; i++) {
714             final Map<String, String> env = new HashMap<String, String>();
715             env.put("TEST_ENV_VAR", Integer.toString(i));
716             final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
717             final CommandLine cl = new CommandLine(foreverTestScript);
718             final ExecuteWatchdog watchdog = new ExecuteWatchdog(500);
719             exec.setWatchdog(watchdog);
720             exec.execute(cl, env, resultHandler);
721             resultHandler.waitFor(WAITFOR_TIMEOUT);
722             assertTrue("ResultHandler received a result", resultHandler.hasResult());
723             assertNotNull(resultHandler.getException());
724             baos.reset();
725         }
726     }
727 
728 
729     // ======================================================================
730     // === Helper methods
731     // ======================================================================
732 
733     private String readFile(final File file) throws Exception {
734 
735         String text;
736         final StringBuilder contents = new StringBuilder();
737         final BufferedReader reader = new BufferedReader(new FileReader(file));
738 
739         while ((text = reader.readLine()) != null)
740         {
741             contents.append(text)
742                 .append(System.getProperty(
743                     "line.separator"));
744         }
745         reader.close();
746         return contents.toString();
747     }
748 
749     private int getOccurrences(final String data, final char c) {
750 
751         int result = 0;
752 
753         for (int i=0; i<data.length(); i++) {
754             if (data.charAt(i) == c) {
755                 result++;
756             }
757         }
758 
759         return result;
760     }
761 }