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 junit.framework.TestCase;
22  import org.apache.commons.exec.environment.EnvironmentUtils;
23  
24  import java.io.BufferedReader;
25  import java.io.ByteArrayInputStream;
26  import java.io.ByteArrayOutputStream;
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.io.FileOutputStream;
30  import java.io.FileReader;
31  import java.io.IOException;
32  import java.io.PipedInputStream;
33  import java.io.PipedOutputStream;
34  import java.util.HashMap;
35  import java.util.Map;
36  
37  public class DefaultExecutorTest extends TestCase {
38  
39      /** Maximum time to wait (15s) */
40      private static final int WAITFOR_TIMEOUT = 15000;
41  
42      private Executor exec = new DefaultExecutor();
43      private File testDir = new File("src/test/scripts");
44      private File foreverOutputFile = new File("./target/forever.txt");
45      private ByteArrayOutputStream baos;
46  
47      private File testScript = TestUtil.resolveScriptForOS(testDir + "/test");
48      private File errorTestScript = TestUtil.resolveScriptForOS(testDir + "/error");
49      private File foreverTestScript = TestUtil.resolveScriptForOS(testDir + "/forever");
50      private File nonExistingTestScript = TestUtil.resolveScriptForOS(testDir + "/grmpffffff");
51      private File redirectScript = TestUtil.resolveScriptForOS(testDir + "/redirect");
52      private File pingScript = TestUtil.resolveScriptForOS(testDir + "/ping");
53      private File printArgsScript = TestUtil.resolveScriptForOS(testDir + "/printargs");
54      private File acroRd32Script = TestUtil.resolveScriptForOS(testDir + "/acrord32");
55      private File stdinSript = TestUtil.resolveScriptForOS(testDir + "/stdin");
56      private File environmentSript = TestUtil.resolveScriptForOS(testDir + "/environment");
57      private File wrapperScript = TestUtil.resolveScriptForOS(testDir + "/wrapper");
58  
59  
60      // Get suitable exit codes for the OS
61      private static final int SUCCESS_STATUS; // test script successful exit code
62      private static final int ERROR_STATUS;   // test script error exit code
63  
64      static{
65  
66          int statuses[] = TestUtil.getTestScriptCodesForOS();
67          SUCCESS_STATUS=statuses[0];
68          ERROR_STATUS=statuses[1];
69  
70          // turn on debug mode and throw an exception for each encountered problem
71          System.setProperty("org.apache.commons.exec.lenient", "false");
72          System.setProperty("org.apache.commons.exec.debug", "true");
73      }
74  
75      protected void setUp() throws Exception {
76  
77          // delete the marker file
78          this.foreverOutputFile.getParentFile().mkdirs();
79          if(this.foreverOutputFile.exists()) {
80              this.foreverOutputFile.delete();
81          }
82  
83          // prepare a ready to Executor
84          this.baos = new ByteArrayOutputStream();
85          this.exec.setStreamHandler(new PumpStreamHandler(baos, baos));
86      }
87  
88      protected void tearDown() throws Exception {
89          this.baos.close();
90          foreverOutputFile.delete();
91      }
92  
93      // ======================================================================
94      // Start of regression tests
95      // ======================================================================
96  
97      /**
98       * The simplest possible test - start a script and
99       * check that the output was pumped into our
100      * <code>ByteArrayOutputStream</code>.
101      *
102      * @throws Exception the test failed
103      */
104     public void testExecute() throws Exception {
105         CommandLine cl = new CommandLine(testScript);
106         int exitValue = exec.execute(cl);
107         assertEquals("FOO..", baos.toString().trim());
108         assertFalse(exec.isFailure(exitValue));
109         assertEquals(new File("."), exec.getWorkingDirectory());
110     }
111 
112     public void testExecuteWithWorkingDirectory() throws Exception {
113         File workingDir = new File("./target");
114         CommandLine cl = new CommandLine(testScript);
115         exec.setWorkingDirectory(workingDir);
116         int exitValue = exec.execute(cl);
117         assertEquals("FOO..", baos.toString().trim());
118         assertFalse(exec.isFailure(exitValue));
119         assertEquals(exec.getWorkingDirectory(), workingDir);
120     }
121 
122     public void testExecuteWithInvalidWorkingDirectory() throws Exception {
123         File workingDir = new File("/foo/bar");
124         CommandLine cl = new CommandLine(testScript);
125         exec.setWorkingDirectory(workingDir);
126         try {
127             exec.execute(cl);
128             fail("Expected exception due to invalid working directory");
129         }
130         catch(IOException e) {
131             return;
132         }
133     }
134 
135     public void testExecuteWithError() throws Exception {
136         CommandLine cl = new CommandLine(errorTestScript);
137 
138         try{
139             exec.execute(cl);
140             fail("Must throw ExecuteException");
141         } catch(ExecuteException e) {
142             assertTrue(exec.isFailure(e.getExitValue()));
143         }
144     }
145 
146     public void testExecuteWithArg() throws Exception {
147         CommandLine cl = new CommandLine(testScript);
148         cl.addArgument("BAR");
149         int exitValue = exec.execute(cl);
150 
151         assertEquals("FOO..BAR", baos.toString().trim());
152         assertFalse(exec.isFailure(exitValue));
153     }
154 
155     /**
156      * Execute the test script and pass a environment containing
157      * 'TEST_ENV_VAR'.
158      */
159     public void testExecuteWithSingleEnvironmentVariable() throws Exception {
160     	Map env = new HashMap();
161         env.put("TEST_ENV_VAR", "XYZ");
162 
163         CommandLine cl = new CommandLine(testScript);
164 
165         int exitValue = exec.execute(cl, env);
166 
167         assertEquals("FOO.XYZ.", baos.toString().trim());
168         assertFalse(exec.isFailure(exitValue));
169     }
170 
171     /**
172      * Start a asynchronous process which returns an success
173      * exit value.
174      *
175      * @throws Exception the test failed
176      */
177     public void testExecuteAsync() throws Exception {
178         CommandLine cl = new CommandLine(testScript);
179         DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
180         exec.execute(cl, resultHandler);
181         resultHandler.waitFor(2000);
182         assertTrue(resultHandler.hasResult());
183         assertNull(resultHandler.getException());
184         assertFalse(exec.isFailure(resultHandler.getExitValue()));
185         assertEquals("FOO..", baos.toString().trim());
186     }
187 
188     /**
189      * Start a asynchronous process which returns an error
190      * exit value.
191      *
192      * @throws Exception the test failed
193      */
194     public void testExecuteAsyncWithError() throws Exception {
195         CommandLine cl = new CommandLine(errorTestScript);
196         DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
197         exec.execute(cl, resultHandler);
198         resultHandler.waitFor(2000);
199         assertTrue(resultHandler.hasResult());
200         assertTrue(exec.isFailure(resultHandler.getExitValue()));
201         assertNotNull(resultHandler.getException());
202         assertEquals("FOO..", baos.toString().trim());
203     }
204 
205     /**
206      * Start a asynchronous process and terminate it manually before the
207      * watchdog timeout occurs.
208      *
209      * @throws Exception the test failed
210      */
211     public void testExecuteAsyncWithTimelyUserTermination() throws Exception {
212         CommandLine cl = new CommandLine(foreverTestScript);
213         ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
214         exec.setWatchdog(watchdog);
215         DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
216         exec.execute(cl, handler);
217         // wait for script to run
218         Thread.sleep(2000);
219         assertTrue("Watchdog should watch the process", watchdog.isWatching());
220         // terminate it manually using the watchdog
221         watchdog.destroyProcess();
222         // wait until the result of the process execution is propagated
223         handler.waitFor(WAITFOR_TIMEOUT);
224         assertTrue("Watchdog should have killed the process", watchdog.killedProcess());
225         assertFalse("Watchdog is no longer watching the process", watchdog.isWatching());
226         assertTrue("ResultHandler received a result", handler.hasResult());
227         assertNotNull("ResultHandler received an exception as result", handler.getException());
228     }
229 
230     /**
231      * Start a asynchronous process and try to terminate it manually but
232      * the process was already terminated by the watchdog. This is
233      * basically a race condition between infrastructure and user
234      * code.
235      *
236      * @throws Exception the test failed
237      */
238     public void testExecuteAsyncWithTooLateUserTermination() throws Exception {
239         CommandLine cl = new CommandLine(foreverTestScript);
240         DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
241         ExecuteWatchdog watchdog = new ExecuteWatchdog(3000);
242         exec.setWatchdog(watchdog);
243         exec.execute(cl, handler);
244         // wait for script to be terminated by the watchdog
245         Thread.sleep(6000);
246         // try to terminate the already terminated process
247         watchdog.destroyProcess();
248         // wait until the result of the process execution is propagated
249         handler.waitFor(WAITFOR_TIMEOUT);
250         assertTrue("Watchdog should have killed the process already", watchdog.killedProcess());
251         assertFalse("Watchdog is no longer watching the process", watchdog.isWatching());
252         assertTrue("ResultHandler received a result", handler.hasResult());
253         assertNotNull("ResultHandler received an exception as result", handler.getException());
254     }
255 
256     /**
257      * Start a script looping forever (synchronously) and check if the ExecuteWatchdog
258      * kicks in killing the run away process. To make killing a process
259      * more testable the "forever" scripts write each second a '.'
260      * into "./target/forever.txt" (a marker file). After a test run
261      * we should have a few dots in there.
262      *
263      * @throws Exception the test failed
264      */
265     public void testExecuteWatchdogSync() throws Exception {
266 
267         if (OS.isFamilyOpenVms()){
268             System.out.println("The test 'testExecuteWatchdogSync' currently hangs on the following OS : "
269                     + System.getProperty("os.name"));
270             return;
271         }
272 
273         long timeout = 10000;
274 
275         CommandLine cl = new CommandLine(foreverTestScript);
276         DefaultExecutor executor = new DefaultExecutor();
277         executor.setWorkingDirectory(new File("."));
278         ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
279         executor.setWatchdog(watchdog);
280 
281         try {
282             executor.execute(cl);
283         }
284         catch(ExecuteException e) {
285             Thread.sleep(timeout);
286             int nrOfInvocations = getOccurrences(readFile(this.foreverOutputFile), '.');
287             assertTrue( executor.getWatchdog().killedProcess() );
288             assertTrue("killing the subprocess did not work : " + nrOfInvocations, nrOfInvocations > 5 && nrOfInvocations <= 11);
289             return;
290         }
291         catch(Throwable t) {
292             fail(t.getMessage());
293         }
294 
295         assertTrue("Killed process should be true", executor.getWatchdog().killedProcess() );
296         fail("Process did not create ExecuteException when killed");
297     }
298 
299     /**
300      * Start a script looping forever (asynchronously) and check if the
301      * ExecuteWatchdog kicks in killing the run away process. To make killing
302      * a process more testable the "forever" scripts write each second a '.'
303      * into "./target/forever.txt" (a marker file). After a test run
304      * we should have a few dots in there.
305      *
306      * @throws Exception the test failed
307      */
308     public void testExecuteWatchdogAsync() throws Exception {
309 
310         long timeout = 10000;
311 
312         CommandLine cl = new CommandLine(foreverTestScript);
313         DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
314         DefaultExecutor executor = new DefaultExecutor();
315         executor.setWorkingDirectory(new File("."));
316         executor.setWatchdog(new ExecuteWatchdog(timeout));
317 
318         executor.execute(cl, handler);
319         handler.waitFor(WAITFOR_TIMEOUT);
320 
321         assertTrue("Killed process should be true", executor.getWatchdog().killedProcess() );
322         assertTrue("ResultHandler received a result", handler.hasResult());
323         assertNotNull("ResultHandler received an exception as result", handler.getException());
324 
325         int nrOfInvocations = getOccurrences(readFile(this.foreverOutputFile), '.');
326         assertTrue("Killing the process did not work : " + nrOfInvocations, nrOfInvocations > 5 && nrOfInvocations <= 11);
327     }
328 
329     /**
330      * [EXEC-68] Synchronously starts a short script with a Watchdog attached with an extremely large timeout. Checks
331      * to see if the script terminated naturally or if it was killed by the Watchdog. Fail if killed by Watchdog.
332      * 
333      * @throws Exception
334      *             the test failed
335      */
336     public void testExecuteWatchdogVeryLongTimeout() throws Exception {
337         long timeout = Long.MAX_VALUE;
338 
339         CommandLine cl = new CommandLine(testScript);
340         DefaultExecutor executor = new DefaultExecutor();
341         executor.setWorkingDirectory(new File("."));
342         ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
343         executor.setWatchdog(watchdog);
344 
345         try {
346             executor.execute(cl);
347         } catch (ExecuteException e) {
348             assertFalse("Process should exit normally, not be killed by watchdog", watchdog.killedProcess());
349             // If the Watchdog did not kill it, something else went wrong.
350             throw e;
351         }
352     }
353 
354     /**
355      * Try to start an non-existing application which should result
356      * in an exception.
357      *
358      * @throws Exception the test failed
359      */
360     public void testExecuteNonExistingApplication() throws Exception {
361         CommandLine cl = new CommandLine(nonExistingTestScript);
362         DefaultExecutor executor = new DefaultExecutor();
363         try {
364             executor.execute(cl);
365         }
366         catch( IOException e) {
367             // expected
368             return;
369         }
370         fail("Got no exception when executing an non-existing application");
371     }
372 
373     /**
374      * Try to start an non-existing application asynchronously which should result
375      * in an exception.
376      *
377      * @throws Exception the test failed
378      */
379     public void testExecuteAsyncWithNonExistingApplication() throws Exception {
380         CommandLine cl = new CommandLine(nonExistingTestScript);
381         DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
382         exec.execute(cl, handler);
383         Thread.sleep(2000);
384         assertNotNull(handler.getException());
385         assertTrue(exec.isFailure(handler.getExitValue()));
386     }
387 
388     /**
389      * Invoke the error script but define that the ERROR_STATUS is a good
390      * exit value and therefore no exception should be thrown.
391      *
392      * @throws Exception the test failed
393      */
394     public void testExecuteWithCustomExitValue1() throws Exception {
395         exec.setExitValue(ERROR_STATUS);
396         CommandLine cl = new CommandLine(errorTestScript);
397         exec.execute(cl);
398     }
399 
400     /**
401      * Invoke the error script but define that SUCCESS_STATUS is a bad
402      * exit value and therefore an exception should be thrown.
403      *
404      * @throws Exception the test failed
405      */
406     public void testExecuteWithCustomExitValue2() throws Exception {
407         CommandLine cl = new CommandLine(errorTestScript);
408         exec.setExitValue(SUCCESS_STATUS);
409         try{
410             exec.execute(cl);
411             fail("Must throw ExecuteException");
412         } catch(ExecuteException e) {
413             assertTrue(exec.isFailure(e.getExitValue()));
414             return;
415         }
416     }
417 
418     /**
419      * Test the proper handling of ProcessDestroyer for an synchronous process.
420      *
421      * @throws Exception the test failed
422      */
423     public void testExecuteWithProcessDestroyer() throws Exception {
424 
425       CommandLine cl = new CommandLine(testScript);
426       ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
427       exec.setProcessDestroyer(processDestroyer);
428 
429       assertTrue(processDestroyer.size() == 0);
430       assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
431 
432       int exitValue = exec.execute(cl);
433 
434       assertEquals("FOO..", baos.toString().trim());
435       assertFalse(exec.isFailure(exitValue));
436       assertTrue(processDestroyer.size() == 0);
437       assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
438     }
439 
440     /**
441      * Test the proper handling of ProcessDestroyer for an asynchronous process.
442      * Since we do not terminate the process it will be terminated in the
443      * ShutdownHookProcessDestroyer implementation.
444      *
445      * @throws Exception the test failed
446      */
447     public void testExecuteAsyncWithProcessDestroyer() throws Exception {
448 
449       CommandLine cl = new CommandLine(foreverTestScript);
450       DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
451       ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
452       ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
453 
454       assertTrue(exec.getProcessDestroyer() == null);
455       assertTrue(processDestroyer.size() == 0);
456       assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
457 
458       exec.setWatchdog(watchdog);
459       exec.setProcessDestroyer(processDestroyer);
460       exec.execute(cl, handler);
461 
462       // wait for script to start
463       Thread.sleep(2000);
464 
465       // our process destroyer should be initialized now
466       assertNotNull("Process destroyer should exist", exec.getProcessDestroyer());
467       assertEquals("Process destroyer size should be 1", 1, processDestroyer.size());
468       assertTrue("Process destroyer should exist as shutdown hook", processDestroyer.isAddedAsShutdownHook());
469 
470       // terminate it and the process destroyer is detached
471       watchdog.destroyProcess();
472       assertTrue(watchdog.killedProcess());
473       handler.waitFor(WAITFOR_TIMEOUT);
474       assertTrue("ResultHandler received a result", handler.hasResult());
475       assertNotNull(handler.getException());
476       assertEquals("Processor Destroyer size should be 0", 0, processDestroyer.size());
477       assertFalse("Process destroyer should not exist as shutdown hook", processDestroyer.isAddedAsShutdownHook());
478     }
479 
480     /**
481      * Invoke the test using some fancy arguments.
482      *
483      * @throws Exception the test failed
484      */
485     public void testExecuteWithFancyArg() throws Exception {
486         CommandLine cl = new CommandLine(testScript);
487         cl.addArgument("test $;`(0)[1]{2}");
488         int exitValue = exec.execute(cl);
489         assertTrue(baos.toString().trim().indexOf("test $;`(0)[1]{2}") > 0);
490         assertFalse(exec.isFailure(exitValue));
491     }
492 
493     /**
494      * Start a process with redirected streams - stdin of the newly
495      * created process is connected to a FileInputStream whereas
496      * the "redirect" script reads all lines from stdin and prints
497      * them on stdout. Furthermore the script prints a status
498      * message on stderr.
499      *
500      * @throws Exception the test failed
501      */
502     public void testExecuteWithRedirectedStreams() throws Exception
503     {
504         if(OS.isFamilyUnix())
505         {
506             FileInputStream fis = new FileInputStream("./NOTICE.txt");
507             CommandLine cl = new CommandLine(redirectScript);
508             PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( baos, baos, fis );
509             DefaultExecutor executor = new DefaultExecutor();
510             executor.setWorkingDirectory(new File("."));
511             executor.setStreamHandler( pumpStreamHandler );
512             int exitValue = executor.execute(cl);
513             fis.close();
514             String result = baos.toString().trim();
515             assertTrue(result, result.indexOf("Finished reading from stdin") > 0);
516             assertFalse("exitValue=" + exitValue, exec.isFailure(exitValue));
517         }
518         else if(OS.isFamilyWindows()) {
519             System.err.println("The code samples to do that in windows look like a joke ... :-( .., no way I'm doing that");
520             System.err.println("The test 'testExecuteWithRedirectedStreams' does not support the following OS : " + System.getProperty("os.name"));
521             return;
522         }
523         else {
524             System.err.println("The test 'testExecuteWithRedirectedStreams' does not support the following OS : " + System.getProperty("os.name"));
525             return;
526         }
527     }
528 
529      /**
530       * Start a process and connect stdout and stderr.
531       *
532       * @throws Exception the test failed
533       */
534      public void testExecuteWithStdOutErr() throws Exception
535      {
536          CommandLine cl = new CommandLine(testScript);
537          PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( System.out, System.err );
538          DefaultExecutor executor = new DefaultExecutor();
539          executor.setStreamHandler( pumpStreamHandler );
540          int exitValue = executor.execute(cl);
541          assertFalse(exec.isFailure(exitValue));
542      }
543 
544      /**
545       * Start a process and connect it to no stream.
546       *
547       * @throws Exception the test failed
548       */
549      public void testExecuteWithNullOutErr() throws Exception
550      {
551          CommandLine cl = new CommandLine(testScript);
552          PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( null, null );
553          DefaultExecutor executor = new DefaultExecutor();
554          executor.setStreamHandler( pumpStreamHandler );
555          int exitValue = executor.execute(cl);
556          assertFalse(exec.isFailure(exitValue));
557      }
558 
559      /**
560       * Start a process and connect out and err to a file.
561       *
562       * @throws Exception the test failed
563       */
564      public void testExecuteWithRedirectOutErr() throws Exception
565      {
566          File outfile = File.createTempFile("EXEC", ".test");
567          outfile.deleteOnExit();
568          CommandLine cl = new CommandLine(testScript);
569          PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( new FileOutputStream(outfile) );
570          DefaultExecutor executor = new DefaultExecutor();
571          executor.setStreamHandler( pumpStreamHandler );
572          int exitValue = executor.execute(cl);
573          assertFalse(exec.isFailure(exitValue));
574          assertTrue(outfile.exists());
575      }
576 
577     /**
578      * A generic test case to print the command line arguments to 'printargs' script to solve
579      * even more command line puzzles.
580      *
581      * @throws Exception the test failed
582      */
583     public void testExecuteWithComplexArguments() throws Exception {
584         CommandLine cl = new CommandLine(printArgsScript);
585         cl.addArgument("gdal_translate");
586         cl.addArgument("HDF5:\"/home/kk/grass/data/4404.he5\"://HDFEOS/GRIDS/OMI_Column_Amount_O3/Data_Fields/ColumnAmountO3/home/kk/4.tif", false);
587         DefaultExecutor executor = new DefaultExecutor();
588         int exitValue = executor.execute(cl);
589         assertFalse(exec.isFailure(exitValue));
590      }
591 
592 
593     /**
594      * The test script reads an argument from <code>stdin<code> and prints
595      * the result to stdout. To make things slightly more interesting
596      * we are using an asynchronous process.
597      *
598      * @throws Exception the test failed
599      */
600     public void testStdInHandling() throws Exception {
601 
602         ByteArrayInputStream bais = new ByteArrayInputStream("Foo".getBytes()); // newline not needed; causes problems for VMS
603         CommandLine cl = new CommandLine(this.stdinSript);
604         PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( this.baos, System.err, bais);
605         DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
606         Executor executor = new DefaultExecutor();
607         executor.setStreamHandler(pumpStreamHandler);
608         executor.execute(cl, resultHandler);
609 
610         resultHandler.waitFor(WAITFOR_TIMEOUT);
611         assertTrue("ResultHandler received a result", resultHandler.hasResult());
612 
613         assertFalse(exec.isFailure(resultHandler.getExitValue()));
614         String result = baos.toString();
615         assertTrue("Result '"+result+"' should contain 'Hello Foo!'", result.indexOf("Hello Foo!") >= 0);
616     }
617 
618     /**
619      * Call a script to dump the environment variables of the subprocess.
620      *
621      * @throws Exception the test failed
622      */
623     public void testEnvironmentVariables() throws Exception {
624         exec.execute(new CommandLine(environmentSript));
625         String environment = baos.toString().trim();
626         assertTrue("Found no environment variables", environment.length() > 0);
627         assertFalse(environment.indexOf("NEW_VAR") >= 0);
628     }
629 
630     /**
631      * Call a script to dump the environment variables of the subprocess
632      * after adding a custom environment variable.
633      *
634      * @throws Exception the test failed
635      */
636     public void testAddEnvironmentVariables() throws Exception {
637         Map myEnvVars = new HashMap();
638         myEnvVars.putAll(EnvironmentUtils.getProcEnvironment());
639         myEnvVars.put("NEW_VAR","NEW_VAL");
640         exec.execute(new CommandLine(environmentSript), myEnvVars);
641         String environment = baos.toString().trim();
642         assertTrue("Expecting NEW_VAR in "+environment,environment.indexOf("NEW_VAR") >= 0);
643         assertTrue("Expecting NEW_VAL in "+environment,environment.indexOf("NEW_VAL") >= 0);
644     }
645 
646     public void testAddEnvironmentVariableEmbeddedQuote() throws Exception {
647         Map myEnvVars = new HashMap();
648         myEnvVars.putAll(EnvironmentUtils.getProcEnvironment());
649         String name = "NEW_VAR";
650         String value = "NEW_\"_VAL";
651         myEnvVars.put(name,value);
652         exec.execute(new CommandLine(environmentSript), myEnvVars);
653         String environment = baos.toString().trim();
654         assertTrue("Expecting "+name+" in "+environment,environment.indexOf(name) >= 0);
655         assertTrue("Expecting "+value+" in "+environment,environment.indexOf(value) >= 0);
656     }
657 
658     // ======================================================================
659     // === Testing bug fixes
660     // ======================================================================
661 
662     /**
663      * Test the patch for EXEC-33 (https://issues.apache.org/jira/browse/EXEC-33)
664      *
665      * PumpStreamHandler hangs if System.in is redirect to process input stream .
666      *
667      * @throws Exception the test failed
668      */
669     public void testExec33() throws Exception
670     {
671         CommandLine cl = new CommandLine(testScript);
672         PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( System.out, System.err, System.in );
673         DefaultExecutor executor = new DefaultExecutor();
674         executor.setStreamHandler( pumpStreamHandler );
675         int exitValue = executor.execute(cl);
676         assertFalse(exec.isFailure(exitValue));
677     }
678 
679     /**
680      * EXEC-34 https://issues.apache.org/jira/browse/EXEC-34
681      *
682      * Race condition prevent watchdog working using ExecuteStreamHandler.
683      * The test fails because when watchdog.destroyProcess() is invoked the
684      * external process is not bound to the watchdog yet.
685      *
686      * @throws Exception the test failed
687      */
688     public void testExec34_1() throws Exception {
689 
690         CommandLine cmdLine = new CommandLine(pingScript);
691         cmdLine.addArgument("10"); // sleep 10 secs
692 
693         ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
694         DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
695         exec.setWatchdog(watchdog);
696         exec.execute(cmdLine, handler);
697         assertTrue(watchdog.isWatching());
698         watchdog.destroyProcess();
699         assertTrue("Watchdog should have killed the process",watchdog.killedProcess());
700         assertFalse("Watchdog is no longer watching the process",watchdog.isWatching());
701     }
702 
703     /**
704      * EXEC-34 https://issues.apache.org/jira/browse/EXEC-34
705      *
706      * Some user waited for an asynchronous process using watchdog.isWatching() which
707      * is now properly implemented  using <code>DefaultExecuteResultHandler</code>.
708      *
709      * @throws Exception the test failed
710      */
711     public void testExec34_2() throws Exception {
712 
713         CommandLine cmdLine = new CommandLine(pingScript);
714         cmdLine.addArgument("10"); // sleep 10 secs
715 
716         ExecuteWatchdog watchdog = new ExecuteWatchdog(5000);
717         DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
718         exec.setWatchdog(watchdog);
719         exec.execute(cmdLine, handler);
720         handler.waitFor();
721         assertTrue("Process has exited", handler.hasResult());
722         assertNotNull("Process was aborted", handler.getException());
723         assertTrue("Watchdog should have killed the process", watchdog.killedProcess());
724         assertFalse("Watchdog is no longer watching the process", watchdog.isWatching());
725     }
726 
727     /**
728      * Test EXEC-36 see https://issues.apache.org/jira/browse/EXEC-36
729      *
730      * Original example from Kai Hu which only can be tested on Unix
731      *
732      * @throws Exception the test failed
733      */
734     public void testExec36_1() throws Exception {
735 
736         if(OS.isFamilyUnix()) {
737 
738             CommandLine cmdl;
739 
740             /**
741              * ./script/jrake cruise:publish_installers INSTALLER_VERSION=unstable_2_1 \
742              *     INSTALLER_PATH="/var/lib/ cruise-agent/installers" INSTALLER_DOWNLOAD_SERVER='something' WITHOUT_HELP_DOC=true
743              */
744 
745             String expected = "./script/jrake\n" +
746                     "cruise:publish_installers\n" +
747                     "INSTALLER_VERSION=unstable_2_1\n" +
748                     "INSTALLER_PATH=\"/var/lib/ cruise-agent/installers\"\n" +
749                     "INSTALLER_DOWNLOAD_SERVER='something'\n" +
750                     "WITHOUT_HELP_DOC=true";
751 
752             cmdl = new CommandLine(printArgsScript);
753             cmdl.addArgument("./script/jrake", false);
754             cmdl.addArgument("cruise:publish_installers", false);
755             cmdl.addArgument("INSTALLER_VERSION=unstable_2_1", false);
756             cmdl.addArgument("INSTALLER_PATH=\"/var/lib/ cruise-agent/installers\"", false);
757             cmdl.addArgument("INSTALLER_DOWNLOAD_SERVER='something'", false);
758             cmdl.addArgument("WITHOUT_HELP_DOC=true", false);
759 
760             int exitValue = exec.execute(cmdl);
761             String result = baos.toString().trim();
762             assertFalse(exec.isFailure(exitValue));
763             assertEquals(expected, result);
764         }
765         else {
766             System.err.println("The test 'testExec36_1' does not support the following OS : " + System.getProperty("os.name"));
767             return;
768         }
769     }
770 
771     /**
772      * Test EXEC-36 see https://issues.apache.org/jira/browse/EXEC-36
773      *
774      * Test a complex real example found at
775      * http://blogs.msdn.com/b/astebner/archive/2005/12/13/503471.aspx
776      *
777      * The command line is so weird that it even falls apart under Windows
778      *
779      * @throws Exception the test failed
780      */
781     public void testExec36_2() throws Exception {
782 
783         String expected;
784 
785         // the original command line
786         // dotnetfx.exe /q:a /c:"install.exe /l ""\Documents and Settings\myusername\Local Settings\Temp\netfx.log"" /q"
787 
788         if(OS.isFamilyWindows()) {
789             expected = "dotnetfx.exe\n" +
790                 "/q:a\n" +
791                 "/c:\"install.exe /l \"\"\\Documents and Settings\\myusername\\Local Settings\\Temp\\netfx.log\"\" /q\"";
792         }
793         else if(OS.isFamilyUnix()) {
794             expected = "dotnetfx.exe\n" +
795                 "/q:a\n" +
796                 "/c:\"install.exe /l \"\"/Documents and Settings/myusername/Local Settings/Temp/netfx.log\"\" /q\"";
797         }
798         else {
799             System.err.println("The test 'testExec36_3' does not support the following OS : " + System.getProperty("os.name"));
800             return;
801         }
802 
803         CommandLine cmdl;
804         File file = new File("/Documents and Settings/myusername/Local Settings/Temp/netfx.log");
805         Map map = new HashMap();
806         map.put("FILE", file);
807 
808         cmdl = new CommandLine(printArgsScript);
809         cmdl.setSubstitutionMap(map);
810         cmdl.addArgument("dotnetfx.exe", false);
811         cmdl.addArgument("/q:a", false);
812         cmdl.addArgument("/c:\"install.exe /l \"\"${FILE}\"\" /q\"", false);
813 
814         int exitValue = exec.execute(cmdl);
815         String result = baos.toString().trim();
816         assertFalse(exec.isFailure(exitValue));
817 
818         if(OS.isFamilyUnix()) {
819         	// the parameters fall literally apart under Windows - need to disable the check for Win32
820         	assertEquals(expected, result);
821         }
822     }
823 
824     /**
825      * Test the patch for EXEC-41 (https://issues.apache.org/jira/browse/EXEC-41).
826      *
827      * When a process runs longer than allowed by a configured watchdog's
828      * timeout, the watchdog tries to destroy it and then DefaultExecutor
829      * tries to clean up by joining with all installed pump stream threads.
830      * Problem is, that sometimes the native process doesn't die and thus
831      * streams aren't closed and the stream threads do not complete.
832      *
833      * @throws Exception the test failed
834      */
835     public void testExec41WithStreams() throws Exception {
836 
837     	CommandLine cmdLine;
838 
839     	if(OS.isFamilyWindows()) {
840     		cmdLine = CommandLine.parse("ping.exe -n 10 -w 1000 127.0.0.1");
841     	}
842     	else if("HP-UX".equals(System.getProperty("os.name"))) {
843     		cmdLine = CommandLine.parse("ping 127.0.0.1 -n 10"); // see EXEC-52 - option must appear after the hostname!
844     	}
845     	else if(OS.isFamilyUnix()) {
846     		cmdLine = CommandLine.parse("ping -c 10 127.0.0.1");
847     	}
848     	else {
849     		System.err.println("The test 'testExec41WithStreams' does not support the following OS : " + System.getProperty("os.name"));
850     		return;
851     	}
852 
853 		DefaultExecutor executor = new DefaultExecutor();
854 		ExecuteWatchdog watchdog = new ExecuteWatchdog(2*1000); // allow process no more than 2 secs
855         PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( System.out, System.err);
856         // this method was part of the patch I reverted
857         // pumpStreamHandler.setAlwaysWaitForStreamThreads(false);
858 
859 		executor.setWatchdog(watchdog);
860         executor.setStreamHandler(pumpStreamHandler);
861 
862 		long startTime = System.currentTimeMillis();
863 
864 		try {
865 			executor.execute(cmdLine);
866 		} catch (ExecuteException e) {
867 			// nothing to do
868 		}
869 
870         long duration = System.currentTimeMillis() - startTime;
871 
872 		System.out.println("Process completed in " + duration +" millis; below is its output");
873 
874 		if (watchdog.killedProcess()) {
875 			System.out.println("Process timed out and was killed by watchdog.");
876 		}
877 
878         assertTrue("The process was killed by the watchdog", watchdog.killedProcess());
879         assertTrue("Skipping the Thread.join() did not work", duration < 9000);
880     }
881 
882     /**
883      * Test EXEC-41 with a disabled PumpStreamHandler to check if we could return
884      * immediately after killing the process (no streams implies no blocking
885      * stream pumper threads). But you have to be 100% sure that the subprocess
886      * is not writing to 'stdout' and 'stderr'.
887      *
888      * For this test we are using the batch file - under Windows the 'ping'
889      * process can't be killed (not supported by Win32) and will happily
890      * run the given time (e.g. 10 seconds) even hwen the batch file is already
891      * killed.
892      *
893      * @throws Exception the test failed
894      */
895     public void testExec41WithoutStreams() throws Exception {
896 
897 		CommandLine cmdLine = new CommandLine(pingScript);
898 		cmdLine.addArgument("10"); // sleep 10 secs
899 		DefaultExecutor executor = new DefaultExecutor();
900 		ExecuteWatchdog watchdog = new ExecuteWatchdog(2*1000); // allow process no more than 2 secs
901 
902         // create a custom "PumpStreamHandler" doing no pumping at all
903         PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(null, null, null);
904 
905 		executor.setWatchdog(watchdog);
906         executor.setStreamHandler(pumpStreamHandler);
907 
908 		long startTime = System.currentTimeMillis();
909 
910 		try {
911 			executor.execute(cmdLine);
912 		} catch (ExecuteException e) {
913 			System.out.println(e);
914 		}
915 
916         long duration = System.currentTimeMillis() - startTime;
917 
918 		System.out.println("Process completed in " + duration +" millis; below is its output");
919 
920 		if (watchdog.killedProcess()) {
921 			System.out.println("Process timed out and was killed.");
922 		}
923 
924         assertTrue("The process was killed by the watchdog", watchdog.killedProcess());
925         assertTrue("Skipping the Thread.join() did not work, duration="+duration, duration < 9000);
926     }
927 
928     /**
929      * Test EXEC-44 (https://issues.apache.org/jira/browse/EXEC-44).
930      *
931      * Because the ExecuteWatchdog is the only way to destroy asynchronous
932      * processes, it should be possible to set it to an infinite timeout,
933      * for processes which should not timeout, but manually destroyed
934      * under some circumstances.
935      *
936      * @throws Exception the test failed
937      */
938     public void testExec44() throws Exception {
939 
940         CommandLine cl = new CommandLine(foreverTestScript);
941         DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
942         ExecuteWatchdog watchdog = new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT);
943 
944         exec.setWatchdog(watchdog);
945         exec.execute(cl, resultHandler);
946 
947         // wait for script to run
948         Thread.sleep(5000);
949         assertTrue("The watchdog is watching the process", watchdog.isWatching());
950 
951         // terminate it
952         watchdog.destroyProcess();
953         assertTrue("The watchdog has killed the process", watchdog.killedProcess());
954         assertFalse("The watchdog is no longer watching any process", watchdog.isWatching());
955     }
956 
957     /**
958      * Test EXEC-49 (https://issues.apache.org/jira/browse/EXEC-49).
959      *
960      * The issue was detected when trying to capture stdout/stderr with a PipedOutputStream and
961      * then pass that to a PipedInputStream. The following code will produce the error.
962      * The reason for the error is the PipedOutputStream is not being closed correctly,
963      * causing the PipedInputStream to break.
964      *
965      * @throws Exception the test failed
966      */
967     public void testExec49_1() throws Exception {
968 
969         if(OS.isFamilyUnix()) {
970 
971             CommandLine cl = CommandLine.parse("/bin/ls");
972             cl.addArgument("/opt");
973 
974             Executor exec = new DefaultExecutor();
975 
976             // redirect stdout/stderr to pipedOutputStream
977             PipedOutputStream pipedOutputStream = new PipedOutputStream();
978             PumpStreamHandler psh = new PumpStreamHandler(pipedOutputStream);
979             exec.setStreamHandler(psh);
980 
981             // start an asynchronous process to enable the main thread
982             System.out.println("Preparing to execute process - commandLine=" + cl.toString());
983             DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
984             exec.execute(cl, handler);
985             System.out.println("Process spun off successfully - process=" + cl.getExecutable());
986 
987             int x;
988             PipedInputStream pis = new PipedInputStream(pipedOutputStream);
989             while ((x = pis.read()) >= 0) {
990                 // System.out.println("pis.available() " + pis.available());
991                 // System.out.println("x " + x);
992             }
993             pis.close();
994 
995             handler.waitFor();
996         }
997     }
998 
999     /**
1000      * Test EXEC-49 (https://issues.apache.org/jira/browse/EXEC-49).
1001      *
1002      * The issue was detected when trying to capture stdout with a PipedOutputStream and
1003      * then pass that to a PipedInputStream. The following code will produce the error.
1004      * The reason for the error is the PipedOutputStream is not being closed correctly,
1005      * causing the PipedInputStream to break.
1006      *
1007      * @throws Exception the test failed
1008      */
1009     public void testExec49_2() throws Exception {
1010 
1011         if(OS.isFamilyUnix()) {
1012 
1013             CommandLine cl = CommandLine.parse("/bin/ls");
1014             cl.addArgument("/opt");
1015 
1016             Executor exec = new DefaultExecutor();
1017 
1018             // redirect only stdout to pipedOutputStream
1019             PipedOutputStream pipedOutputStream = new PipedOutputStream();
1020             PumpStreamHandler psh = new PumpStreamHandler(pipedOutputStream, new ByteArrayOutputStream());
1021             exec.setStreamHandler(psh);
1022 
1023             // start an asynchronous process to enable the main thread
1024             System.out.println("Preparing to execute process - commandLine=" + cl.toString());
1025             DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
1026             exec.execute(cl, handler);
1027             System.out.println("Process spun off successfully - process=" + cl.getExecutable());
1028 
1029             int x;
1030             PipedInputStream pis = new PipedInputStream(pipedOutputStream);
1031             while ((x = pis.read()) >= 0) {
1032                 // System.out.println("pis.available() " + pis.available());
1033                 // System.out.println("x " + x);
1034             }
1035             pis.close();
1036 
1037             handler.waitFor();
1038         }
1039     }
1040 
1041     /**
1042      * Test EXEC-57 (https://issues.apache.org/jira/browse/EXEC-57).
1043      *
1044      * DefaultExecutor.execute() does not return even if child process terminated - in this
1045      * case the child process hangs because the grand children is connected to stdout & stderr
1046      * and is still running. As work-around a stop timeout is used for the PumpStreamHandler
1047      * to ensure that the caller does not block forever but if the stop timeout is exceeded
1048      * an ExecuteException is thrown to notify the caller.
1049      *
1050      * @throws Exception the test failed
1051      */
1052     public void testExec_57() throws IOException {
1053 
1054         if(!OS.isFamilyUnix()) {
1055             System.err.println("The test 'testSyncInvocationOfBackgroundProcess' does not support the following OS : " + System.getProperty("os.name"));
1056             return;
1057         }
1058 
1059         CommandLine cmdLine = new CommandLine("sh").addArgument("-c").addArgument(testDir + "/invoker.sh", false);
1060 
1061         DefaultExecutor executor = new DefaultExecutor();
1062         PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(System.out, System.err);
1063 
1064         // Without this timeout current thread will be blocked
1065         // even if command we'll invoke will terminate immediately.
1066         pumpStreamHandler.setStopTimeout(2000);
1067         executor.setStreamHandler(pumpStreamHandler);
1068         long startTime = System.currentTimeMillis();
1069 
1070         System.out.println("Executing " + cmdLine);
1071 
1072         try {
1073             executor.execute(cmdLine);
1074         }
1075         catch(ExecuteException e) {
1076             long duration = System.currentTimeMillis() - startTime;
1077             System.out.println("Process completed in " + duration +" millis; above is its output");
1078             return;
1079         }
1080 
1081         fail("Expecting an ExecuteException");
1082     }
1083 
1084     /**
1085      * Test EXEC-60 (https://issues.apache.org/jira/browse/EXEC-60).
1086      *
1087      * Possible deadlock when a process is terminating at the same time its timing out. Please
1088      * note that a successful test is no proof that the issues was indeed fixed.
1089      *
1090      */
1091     public void testExec_60() throws Exception {
1092 
1093         int start = 0;
1094         final int seconds = 1;
1095         final int offsetMultiplier = 1;
1096         final int maxRetries = 180;
1097         int processTerminatedCounter = 0;
1098         int watchdogKilledProcessCounter = 0;
1099         CommandLine cmdLine = new CommandLine(pingScript);
1100         cmdLine.addArgument(Integer.toString(seconds + 1)); // need to add "1" to wait the requested number of seconds
1101 
1102         final long startTime = System.currentTimeMillis();
1103         for (int offset = start; offset <= maxRetries; offset++) {
1104             // wait progressively longer for process to complete
1105             // tricky to get this test right. We want to try and catch the process while it is terminating,
1106             // so we increase the timeout gradually until the test terminates normally.
1107             // However if the increase is too gradual, we never wait long enough for any test to exit normally
1108             ExecuteWatchdog watchdog = new ExecuteWatchdog(seconds * 1000 + offset * offsetMultiplier);
1109             exec.setWatchdog(watchdog);
1110             try {
1111                 exec.execute(cmdLine);
1112                 processTerminatedCounter++;
1113 //                System.out.println(offset + ": process has terminated: " + watchdog.killedProcess());
1114                 if(processTerminatedCounter > 5) {
1115                     break;
1116                 }
1117             } catch (ExecuteException ex) {
1118 //                System.out.println(offset + ": process was killed: " + watchdog.killedProcess());
1119                 assertTrue("Watchdog killed the process", watchdog.killedProcess());
1120                 watchdogKilledProcessCounter++;
1121             }
1122         }
1123 
1124         final long avg = (System.currentTimeMillis() - startTime) / 
1125                 (watchdogKilledProcessCounter+processTerminatedCounter);
1126         System.out.println("Processes terminated: "+processTerminatedCounter+" killed: "+watchdogKilledProcessCounter
1127                 +" Multiplier: "+offsetMultiplier+" MaxRetries: "+maxRetries+" Elapsed (avg ms): "+avg);
1128         assertTrue("Not a single process terminated on its own", processTerminatedCounter > 0);
1129         assertTrue("Not a single process was killed by the watch dog", watchdogKilledProcessCounter > 0);
1130     }
1131 
1132     // ======================================================================
1133     // === Long running tests
1134     // ======================================================================
1135 
1136     /**
1137      * Start any processes in a loop to make sure that we do
1138      * not leave any handles/resources open.
1139      *
1140      * @throws Exception the test failed
1141      */
1142     public void _testExecuteStability() throws Exception {
1143 
1144         // make a plain-vanilla test
1145         for(int i=0; i<100; i++) {
1146             Map env = new HashMap();
1147             env.put("TEST_ENV_VAR", new Integer(i));
1148             CommandLine cl = new CommandLine(testScript);
1149             int exitValue = exec.execute(cl,env);
1150             assertFalse(exec.isFailure(exitValue));
1151             assertEquals("FOO." + i + ".", baos.toString().trim());
1152             baos.reset();
1153         }
1154 
1155         // now be nasty and use the watchdog to kill out sub-processes
1156         for(int i=0; i<100; i++) {
1157             Map env = new HashMap();
1158             env.put("TEST_ENV_VAR", new Integer(i));
1159             DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
1160             CommandLine cl = new CommandLine(foreverTestScript);
1161             ExecuteWatchdog watchdog = new ExecuteWatchdog(500);
1162             exec.setWatchdog(watchdog);
1163             exec.execute(cl, env, resultHandler);
1164             resultHandler.waitFor(WAITFOR_TIMEOUT);
1165             assertTrue("ResultHandler received a result", resultHandler.hasResult());
1166             assertNotNull(resultHandler.getException());
1167             baos.reset();
1168         }
1169     }
1170 
1171 
1172     // ======================================================================
1173     // === Helper methods
1174     // ======================================================================
1175 
1176     private String readFile(File file) throws Exception {
1177 
1178         String text;
1179         StringBuffer contents = new StringBuffer();
1180         BufferedReader reader = new BufferedReader(new FileReader(file));
1181 
1182         while ((text = reader.readLine()) != null)
1183         {
1184             contents.append(text)
1185                 .append(System.getProperty(
1186                     "line.separator"));
1187         }
1188         reader.close();
1189         return contents.toString();
1190     }
1191 
1192     private int getOccurrences(String data, char c) {
1193 
1194         int result = 0;
1195 
1196         for(int i=0; i<data.length(); i++) {
1197             if(data.charAt(i) == c) {
1198                 result++;
1199             }
1200         }
1201 
1202         return result;
1203     }
1204 }