View Javadoc
1   package org.codehaus.plexus.util.cli;
2   
3   /*
4    * Copyright The Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import junit.framework.TestCase;
20  import org.codehaus.plexus.util.IOUtil;
21  import org.codehaus.plexus.util.Os;
22  import org.codehaus.plexus.util.StringUtils;
23  import org.codehaus.plexus.util.cli.shell.BourneShell;
24  import org.codehaus.plexus.util.cli.shell.CmdShell;
25  import org.codehaus.plexus.util.cli.shell.Shell;
26  
27  import java.io.*;
28  
29  public class CommandlineTest
30      extends TestCase
31  {
32      private String baseDir;
33  
34      /**
35       * @param testName
36       */
37      public CommandlineTest( final String testName )
38      {
39          super( testName );
40      }
41  
42      /*
43       * @see TestCase#setUp()
44       */
45      public void setUp()
46          throws Exception
47      {
48          super.setUp();
49          baseDir = System.getProperty( "basedir" );
50  
51          if ( baseDir == null )
52          {
53              baseDir = new File( "." ).getCanonicalPath();
54          }
55      }
56  
57      public void testCommandlineWithoutCommandInConstructor()
58      {
59          try
60          {
61              Commandline cmd = new Commandline( new Shell() );
62              cmd.setWorkingDirectory( baseDir );
63              cmd.createArgument().setValue( "cd" );
64              cmd.createArgument().setValue( "." );
65  
66              // NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
67              assertEquals( "cd .", cmd.toString() );
68          }
69          catch ( Exception e )
70          {
71              fail( e.getMessage() );
72          }
73      }
74  
75      public void testCommandlineWithCommandInConstructor()
76      {
77          try
78          {
79              Commandline cmd = new Commandline( "cd .", new Shell() );
80              cmd.setWorkingDirectory( baseDir );
81  
82              // NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
83              assertEquals( "cd .", cmd.toString() );
84          }
85          catch ( Exception e )
86          {
87              fail( e.getMessage() );
88          }
89      }
90  
91      public void testExecuteBinaryOnPath()
92      {
93          try
94          {
95              // Maven startup script on PATH is required for this test
96              Commandline cmd = new Commandline();
97              cmd.setWorkingDirectory( baseDir );
98              cmd.setExecutable( "mvn" );
99              assertEquals( "mvn", cmd.getShell().getOriginalExecutable() );
100             cmd.createArg().setValue( "-version" );
101             Process process = cmd.execute();
102             String out = IOUtil.toString( process.getInputStream() );
103             assertTrue( out.contains( "Apache Maven" ) );
104             assertTrue( out.contains( "Maven home:" ) );
105             assertTrue( out.contains( "Java version:" ) );
106             assertTrue( out.contains( "Java home:" ) );
107         }
108         catch ( Exception e )
109         {
110             fail( "Maven startup script seems not on the PATH: " + e.getMessage() );
111         }
112     }
113 
114     public void testExecute()
115     {
116         try
117         {
118             // allow it to detect the proper shell here.
119             Commandline cmd = new Commandline();
120             cmd.setWorkingDirectory( baseDir );
121             cmd.setExecutable( "echo" );
122             assertEquals( "echo", cmd.getShell().getOriginalExecutable() );
123             cmd.createArgument().setValue( "Hello" );
124 
125             Process process = cmd.execute();
126             assertEquals( "Hello", IOUtil.toString( process.getInputStream() ).trim() );
127         }
128         catch ( Exception e )
129         {
130             fail( e.getMessage() );
131         }
132     }
133 
134     public void testSetLine()
135     {
136         try
137         {
138             Commandline cmd = new Commandline( new Shell() );
139             cmd.setWorkingDirectory( baseDir );
140             cmd.setExecutable( "echo" );
141             cmd.createArgument().setLine( null );
142             cmd.createArgument().setLine( "Hello" );
143 
144             // NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
145             assertEquals( "echo Hello", cmd.toString() );
146         }
147         catch ( Exception e )
148         {
149             fail( e.getMessage() );
150         }
151     }
152 
153     public void testCreateCommandInReverseOrder()
154     {
155         try
156         {
157             Commandline cmd = new Commandline( new Shell() );
158             cmd.setWorkingDirectory( baseDir );
159             cmd.createArgument().setValue( "." );
160             cmd.createArgument( true ).setValue( "cd" );
161 
162             // NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
163             assertEquals( "cd .", cmd.toString() );
164         }
165         catch ( Exception e )
166         {
167             fail( e.getMessage() );
168         }
169     }
170 
171     public void testSetFile()
172     {
173         try
174         {
175             Commandline cmd = new Commandline( new Shell() );
176             cmd.setWorkingDirectory( baseDir );
177             cmd.createArgument().setValue( "more" );
178             File f = new File( "test.txt" );
179             cmd.createArgument().setFile( f );
180             String fileName = f.getAbsolutePath();
181             if ( fileName.contains( " " ) )
182             {
183                 fileName = "\"" + fileName + "\"";
184             }
185 
186             // NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
187             assertEquals( "more " + fileName, cmd.toString() );
188         }
189         catch ( Exception e )
190         {
191             fail( e.getMessage() );
192         }
193     }
194 
195     public void testGetShellCommandLineWindows()
196         throws Exception
197     {
198         Commandline cmd = new Commandline( new CmdShell() );
199         cmd.setExecutable( "c:\\Program Files\\xxx" );
200         cmd.addArguments( new String[] { "a", "b" } );
201         String[] shellCommandline = cmd.getShellCommandline();
202 
203         assertEquals( "Command line size", 4, shellCommandline.length );
204 
205         assertEquals( "cmd.exe", shellCommandline[0] );
206         assertEquals( "/X", shellCommandline[1] );
207         assertEquals( "/C", shellCommandline[2] );
208         String expectedShellCmd = "\"c:" + File.separator + "Program Files" + File.separator + "xxx\" a b";
209         expectedShellCmd = "\"" + expectedShellCmd + "\"";
210         assertEquals( expectedShellCmd, shellCommandline[3] );
211     }
212 
213     public void testGetShellCommandLineWindowsWithSeveralQuotes()
214         throws Exception
215     {
216         Commandline cmd = new Commandline( new CmdShell() );
217         cmd.setExecutable( "c:\\Program Files\\xxx" );
218         cmd.addArguments( new String[] { "c:\\Documents and Settings\\whatever", "b" } );
219         String[] shellCommandline = cmd.getShellCommandline();
220 
221         assertEquals( "Command line size", 4, shellCommandline.length );
222 
223         assertEquals( "cmd.exe", shellCommandline[0] );
224         assertEquals( "/X", shellCommandline[1] );
225         assertEquals( "/C", shellCommandline[2] );
226         String expectedShellCmd = "\"c:" + File.separator + "Program Files" + File.separator
227             + "xxx\" \"c:\\Documents and Settings\\whatever\" b";
228         expectedShellCmd = "\"" + expectedShellCmd + "\"";
229         assertEquals( expectedShellCmd, shellCommandline[3] );
230     }
231 
232     /**
233      * Test the command line generated for the bash shell
234      * 
235      * @throws Exception
236      */
237     public void testGetShellCommandLineBash()
238         throws Exception
239     {
240         Commandline cmd = new Commandline( new BourneShell() );
241         cmd.setExecutable( "/bin/echo" );
242         cmd.addArguments( new String[] { "hello world" } );
243 
244         String[] shellCommandline = cmd.getShellCommandline();
245 
246         assertEquals( "Command line size", 3, shellCommandline.length );
247 
248         assertEquals( "/bin/sh", shellCommandline[0] );
249         assertEquals( "-c", shellCommandline[1] );
250         String expectedShellCmd = "'/bin/echo' 'hello world'";
251         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
252         {
253             expectedShellCmd = "'\\bin\\echo' \'hello world\'";
254         }
255         assertEquals( expectedShellCmd, shellCommandline[2] );
256     }
257 
258     /**
259      * Test the command line generated for the bash shell
260      * 
261      * @throws Exception
262      */
263     public void testGetShellCommandLineBash_WithWorkingDirectory()
264         throws Exception
265     {
266         Commandline cmd = new Commandline( new BourneShell() );
267         cmd.setExecutable( "/bin/echo" );
268         cmd.addArguments( new String[] { "hello world" } );
269         File root = File.listRoots()[0];
270         File workingDirectory = new File( root, "path with spaces" );
271         cmd.setWorkingDirectory( workingDirectory );
272 
273         String[] shellCommandline = cmd.getShellCommandline();
274 
275         assertEquals( "Command line size", 3, shellCommandline.length );
276 
277         assertEquals( "/bin/sh", shellCommandline[0] );
278         assertEquals( "-c", shellCommandline[1] );
279         String expectedShellCmd = "cd '" + root.getAbsolutePath() + "path with spaces' && '/bin/echo' 'hello world'";
280         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
281         {
282             expectedShellCmd = "cd '" + root.getAbsolutePath() + "path with spaces' && '\\bin\\echo' 'hello world'";
283         }
284         assertEquals( expectedShellCmd, shellCommandline[2] );
285     }
286 
287     /**
288      * Test the command line generated for the bash shell
289      * 
290      * @throws Exception
291      */
292     public void testGetShellCommandLineBash_WithSingleQuotedArg()
293         throws Exception
294     {
295         Commandline cmd = new Commandline( new BourneShell() );
296         cmd.setExecutable( "/bin/echo" );
297         cmd.addArguments( new String[] { "\'hello world\'" } );
298 
299         String[] shellCommandline = cmd.getShellCommandline();
300 
301         assertEquals( "Command line size", 3, shellCommandline.length );
302 
303         assertEquals( "/bin/sh", shellCommandline[0] );
304         assertEquals( "-c", shellCommandline[1] );
305         String expectedShellCmd = "'/bin/echo' ''\"'\"'hello world'\"'\"''";
306         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
307         {
308             expectedShellCmd = expectedShellCmd.replace( "/bin/echo", "\\bin\\echo" );
309         }
310         assertEquals( expectedShellCmd, shellCommandline[2] );
311     }
312 
313     public void testGetShellCommandLineNonWindows()
314         throws Exception
315     {
316         Commandline cmd = new Commandline( new BourneShell() );
317         cmd.setExecutable( "/usr/bin" );
318         cmd.addArguments( new String[] { "a", "b" } );
319         String[] shellCommandline = cmd.getShellCommandline();
320 
321         assertEquals( "Command line size", 3, shellCommandline.length );
322 
323         assertEquals( "/bin/sh", shellCommandline[0] );
324         assertEquals( "-c", shellCommandline[1] );
325 
326         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
327         {
328             assertEquals( "'\\usr\\bin' 'a' 'b'", shellCommandline[2] );
329         }
330         else
331         {
332             assertEquals( "'/usr/bin' 'a' 'b'", shellCommandline[2] );
333         }
334     }
335 
336     public void testEnvironment()
337         throws Exception
338     {
339         Commandline cmd = new Commandline();
340         cmd.addEnvironment( "name", "value" );
341         assertEquals( "name=value", cmd.getEnvironmentVariables()[0] );
342     }
343 
344     public void testEnvironmentWitOverrideSystemEnvironment()
345         throws Exception
346     {
347         Commandline cmd = new Commandline();
348         cmd.addSystemEnvironment();
349         cmd.addEnvironment( "JAVA_HOME", "/usr/jdk1.5" );
350         String[] environmentVariables = cmd.getEnvironmentVariables();
351 
352         for ( String environmentVariable : environmentVariables )
353         {
354             if ( "JAVA_HOME=/usr/jdk1.5".equals( environmentVariable ) )
355             {
356                 return;
357             }
358         }
359 
360         fail( "can't find JAVA_HOME=/usr/jdk1.5" );
361     }
362 
363     /**
364      * Test an executable with a single apostrophe <code>'</code> in its path
365      *
366      * @throws Exception
367      */
368     public void testQuotedPathWithSingleApostrophe()
369         throws Exception
370     {
371         File dir = new File( System.getProperty( "basedir" ), "target/test/quotedpath'test" );
372         createAndCallScript( dir, "echo Quoted" );
373 
374         dir = new File( System.getProperty( "basedir" ), "target/test/quoted path'test" );
375         createAndCallScript( dir, "echo Quoted" );
376     }
377 
378     /**
379      * Test an executable with shell-expandable content in its path.
380      *
381      * @throws Exception
382      */
383     public void testPathWithShellExpansionStrings()
384         throws Exception
385     {
386         File dir = new File( System.getProperty( "basedir" ), "target/test/dollar$test" );
387         createAndCallScript( dir, "echo Quoted" );
388     }
389 
390     /**
391      * Test an executable with a single quotation mark <code>\"</code> in its path only for non Windows box.
392      *
393      * @throws Exception
394      */
395     public void testQuotedPathWithQuotationMark()
396         throws Exception
397     {
398         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
399         {
400             System.out.println( "testQuotedPathWithQuotationMark() skipped on Windows" );
401             return;
402         }
403 
404         File dir = new File( System.getProperty( "basedir" ), "target/test/quotedpath\"test" );
405         createAndCallScript( dir, "echo Quoted" );
406 
407         dir = new File( System.getProperty( "basedir" ), "target/test/quoted path\"test" );
408         createAndCallScript( dir, "echo Quoted" );
409     }
410 
411     /**
412      * Test an executable with a single quotation mark <code>\"</code> and <code>'</code> in its path only for non
413      * Windows box.
414      *
415      * @throws Exception
416      */
417     public void testQuotedPathWithQuotationMarkAndApostrophe()
418         throws Exception
419     {
420         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
421         {
422             System.out.println( "testQuotedPathWithQuotationMarkAndApostrophe() skipped on Windows" );
423             return;
424         }
425 
426         File dir = new File( System.getProperty( "basedir" ), "target/test/quotedpath\"'test" );
427         createAndCallScript( dir, "echo Quoted" );
428 
429         dir = new File( System.getProperty( "basedir" ), "target/test/quoted path\"'test" );
430         createAndCallScript( dir, "echo Quoted" );
431     }
432 
433     /**
434      * Test an executable with a quote in its path and no space
435      *
436      * @throws Exception
437      */
438     public void testOnlyQuotedPath()
439         throws Exception
440     {
441         File dir = new File( System.getProperty( "basedir" ), "target/test/quotedpath\'test" );
442 
443         File javaHome = new File( System.getProperty( "java.home" ) );
444         File java;
445         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
446         {
447             java = new File( javaHome, "/bin/java.exe" );
448         }
449         else
450         {
451             java = new File( javaHome, "/bin/java" );
452         }
453 
454         if ( !java.exists() )
455         {
456             throw new IOException( java.getAbsolutePath() + " doesn't exist" );
457         }
458 
459         String javaBinStr = java.getAbsolutePath();
460         if ( Os.isFamily( Os.FAMILY_WINDOWS ) && javaBinStr.contains( " " ) )
461         {
462             javaBinStr = "\"" + javaBinStr + "\"";
463         }
464 
465         createAndCallScript( dir, javaBinStr + " -version" );
466     }
467 
468     public void testDollarSignInArgumentPath()
469         throws Exception
470     {
471         File dir = new File( System.getProperty( "basedir" ), "target/test" );
472         if ( !dir.exists() )
473         {
474             assertTrue( "Can't create dir:" + dir.getAbsolutePath(), dir.mkdirs() );
475         }
476 
477         FileWriter writer = null;
478         try
479         {
480             writer = new FileWriter( new File( dir, "test$1.txt" ) );
481             IOUtil.copy( "Success", writer );
482         }
483         finally
484         {
485             IOUtil.close( writer );
486         }
487 
488         Commandline cmd = new Commandline();
489         // cmd.getShell().setShellCommand( "/bin/sh" );
490         cmd.getShell().setQuotedArgumentsEnabled( true );
491         cmd.setExecutable( "cat" );
492         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
493         {
494             cmd.setExecutable( "dir" );
495         }
496         cmd.setWorkingDirectory( dir );
497         cmd.createArg().setLine( "test$1.txt" );
498 
499         executeCommandLine( cmd );
500     }
501 
502     public void testTimeOutException()
503         throws Exception
504     {
505         File javaHome = new File( System.getProperty( "java.home" ) );
506         File java;
507         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
508         {
509             java = new File( javaHome, "/bin/java.exe" );
510         }
511         else
512         {
513             java = new File( javaHome, "/bin/java" );
514         }
515 
516         if ( !java.exists() )
517         {
518             throw new IOException( java.getAbsolutePath() + " doesn't exist" );
519         }
520 
521         Commandline cli = new Commandline();
522         cli.setExecutable( java.getAbsolutePath() );
523         cli.createArg().setLine( "-version" );
524         CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
525         try
526         {
527             // if the os is faster than 1s to execute java -version the unit will fail :-)
528             CommandLineUtils.executeCommandLine( cli, new DefaultConsumer(), err, 1 );
529         }
530         catch ( CommandLineTimeOutException e )
531         {
532             // it works
533         }
534 
535     }
536 
537     /**
538      * Make the file executable for Unix box.
539      *
540      * @param path not null
541      * @throws IOException if any
542      */
543     private static void makeExecutable( File path )
544         throws IOException
545     {
546         if ( path == null )
547         {
548             throw new IllegalArgumentException( "The file is null" );
549         }
550 
551         if ( !path.isFile() )
552         {
553             throw new IllegalArgumentException( "The file '" + path.getAbsolutePath() + "' should be a file" );
554         }
555 
556         if ( !Os.isFamily( Os.FAMILY_WINDOWS ) )
557         {
558             Process proc = Runtime.getRuntime().exec( new String[] { "chmod", "a+x", path.getAbsolutePath() } );
559             while ( true )
560             {
561                 try
562                 {
563                     proc.waitFor();
564                     break;
565                 }
566                 catch ( InterruptedException e )
567                 {
568                     // ignore
569                 }
570             }
571         }
572     }
573 
574     /**
575      * Create and execute a script file in the given dir with the given content. The script file will be called
576      * <code>echo.bat</code> for Windows box, otherwise <code>echo</code>.
577      *
578      * @param dir the parent dir where echo.bat or echo will be created
579      * @param content the content of the script file
580      * @throws Exception if any
581      */
582     private static void createAndCallScript( File dir, String content )
583         throws Exception
584     {
585         if ( !dir.exists() )
586         {
587             assertTrue( "Can't create dir:" + dir.getAbsolutePath(), dir.mkdirs() );
588         }
589 
590         // Create a script file
591         File bat;
592         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
593         {
594             bat = new File( dir, "echo.bat" );
595         }
596         else
597         {
598             bat = new File( dir, "echo" );
599         }
600 
601         Writer w = new FileWriter( bat );
602         try
603         {
604             IOUtil.copy( content, w );
605         }
606         finally
607         {
608             IOUtil.close( w );
609         }
610 
611         // Change permission
612         makeExecutable( bat );
613 
614         Commandline cmd = new Commandline();
615         cmd.setExecutable( bat.getAbsolutePath() );
616         cmd.setWorkingDirectory( dir );
617 
618         // Execute the script file
619         executeCommandLine( cmd );
620     }
621 
622     /**
623      * Execute the command line
624      *
625      * @param cmd not null
626      * @throws Exception if any
627      */
628     private static void executeCommandLine( Commandline cmd )
629         throws Exception
630     {
631         CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
632 
633         try
634         {
635             System.out.println( "Command line is: " + StringUtils.join( cmd.getShellCommandline(), " " ) );
636 
637             int exitCode = CommandLineUtils.executeCommandLine( cmd, new DefaultConsumer(), err );
638 
639             if ( exitCode != 0 )
640             {
641                 String msg = "Exit code: " + exitCode + " - " + err.getOutput();
642                 throw new Exception( msg.toString() );
643             }
644         }
645         catch ( CommandLineException e )
646         {
647             throw new Exception( "Unable to execute command: " + e.getMessage(), e );
648         }
649     }
650 }