001    package org.codehaus.mojo.exec;
002    
003    /*
004     * Copyright 2005 The Codehaus.
005     *
006     * Licensed under the Apache License, Version 2.0 (the "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *      http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    import org.apache.maven.artifact.repository.ArtifactRepository;
020    import org.apache.maven.artifact.repository.DefaultArtifactRepository;
021    import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
022    import org.apache.maven.plugin.MojoExecutionException;
023    import org.apache.maven.plugin.logging.SystemStreamLog;
024    import org.apache.maven.project.MavenProject;
025    import org.apache.maven.project.MavenProjectBuilder;
026    
027    import java.io.BufferedReader;
028    import java.io.File;
029    import java.io.FileReader;
030    import java.io.IOException;
031    import java.io.OutputStream;
032    import java.io.PrintStream;
033    import org.codehaus.plexus.util.StringOutputStream;
034    import java.util.ArrayList;
035    import java.util.Arrays;
036    import java.util.Collections;
037    import java.util.HashMap;
038    import java.util.List;
039    import java.util.Map;
040    import org.apache.commons.exec.CommandLine;
041    import org.apache.commons.exec.ExecuteException;
042    import org.apache.commons.exec.Executor;
043    import org.apache.commons.exec.OS;
044    import org.codehaus.plexus.logging.Logger;
045    import org.codehaus.plexus.logging.console.ConsoleLogger;
046    import org.apache.maven.monitor.logging.DefaultLog;
047    import org.apache.maven.plugin.testing.AbstractMojoTestCase;
048    
049    /**
050     * @author Jerome Lacoste <jerome@coffeebreaks.org>
051     * @version $Id: ExecMojoTest.java 12372 2010-07-09 20:51:00Z rfscholte $
052     */
053    public class ExecMojoTest
054        extends AbstractMojoTestCase
055    {
056        private MockExecMojo mojo;
057    
058        static class MockExecMojo
059            extends ExecMojo
060        {
061            public int executeResult;
062    
063            public List commandLines = new ArrayList();
064    
065            public String failureMsg;
066    
067            public Map systemProperties = new HashMap();
068    
069            protected int executeCommandLine( Executor exec, CommandLine commandLine, Map enviro, OutputStream out, OutputStream err )
070                throws IOException, ExecuteException
071            {
072                commandLines.add( commandLine );
073                if ( failureMsg != null )
074                {
075                    throw new ExecuteException( failureMsg, executeResult );
076                }
077                return executeResult;
078            }
079    
080            protected String getSystemProperty( String key )
081            {
082                return (String) systemProperties.get( key );
083            }
084    
085            int getAmountExecutedCommandLines() {
086                return commandLines.size();
087            }
088    
089            CommandLine getExecutedCommandline( int index ) {
090                return ((CommandLine) commandLines.get( index ));
091            }
092        }
093    
094        public void setUp()
095            throws Exception
096        {
097            super.setUp();
098            mojo = new MockExecMojo();
099            // note: most of the tests below assume that the specified 
100            // executable path is not fully specicied. See ExecMojo#getExecutablePath
101            mojo.setExecutable( "mvn" );
102            mojo.setArguments( Arrays.asList( new String[]{"--version"} ) );
103            mojo.executeResult = 0;
104            mojo.setBasedir( File.createTempFile( "mvn-temp", "txt" ).getParentFile() );
105        }
106    
107        /**
108         */
109        public void testRunOK()
110            throws MojoExecutionException
111        {
112            mojo.execute();
113    
114            checkMojo( "mvn --version" );
115        }
116    
117        /*
118        This one won't work yet
119        public void xxtestSimpleRunPropertiesAndArguments()
120            throws MojoExecutionException, Exception
121        {
122            File pom = new File( getBasedir(), "src/test/projects/project1/pom.xml" );
123    
124            String output = execute( pom, "exec" );
125    
126            System.out.println(" OUTPUT" + output + "\n\n\n");
127    
128            String expectedOutput = "arg.arg1\narg.arg2\nproject.env1=value1"; // FIXME should work on Windows as well
129    
130            assertEquals( expectedOutput, output );
131        }
132        */
133    
134        /**
135         * integration test...
136         * - compile the Test class using mvn clean compile
137         * - run the test file using java, use it to generate a file whose contains are compared to expected output
138         */
139    /*
140        public void testRunOKWithAutoComputedClasspath()
141            throws MojoExecutionException, Exception
142        {
143            String projectName = "project1";
144    
145            ExecMojo mojo = new ExecMojo();
146    
147            setUpProject( projectName, mojo );
148    
149            // compile project
150            mojo.setExecutable( "mvn" );
151            mojo.setWorkingDirectory( new File( "src/test/projects/" + projectName + "/" ) );
152            mojo.setArguments( Arrays.asList( new String[]{"clean", "compile"} ) );
153    
154            mojo.execute();
155    
156            mojo.getLog().info( "executed mvn clean compile" );
157    
158            // the command executes the test class
159            mojo.setExecutable( "java" );
160            mojo.setWorkingDirectory( (File) null );
161            Classpath classpath = new Classpath();
162            mojo.setArguments( Arrays.asList( new Object[]{"-Dproject.env1=value1", "-classpath", classpath,
163                "org.codehaus.mojo.exec.test.Test",
164                new File( "src/test/projects/" + projectName + "/target/exec/output.txt" ).getAbsolutePath(), "arg1",
165                "arg2"} ) );
166    
167            mojo.execute();
168    
169            // checking the command line would involve resolving the repository
170            // checkMojo( "java -cp" );
171    
172            assertFileEquals( null, getTestFile( "src/test/projects/" + projectName + "/output.txt" ),
173                              getTestFile( "src/test/projects/" + projectName + "/target/exec/output.txt" ) );
174    
175            // the command executes the test class, this time specifying the dependencies
176            mojo.setExecutable( "java" );
177            mojo.setWorkingDirectory( (File) null );
178            classpath = new Classpath();
179            List dependencies = new ArrayList();
180            dependencies.add( "commons-io:commons-io" );
181            classpath.setDependencies( dependencies );
182            mojo.setArguments( Arrays.asList( new Object[]{"-Dproject.env1=value1", "-classpath", classpath,
183                "org.codehaus.mojo.exec.test.Test",
184                new File( "src/test/projects/" + projectName + "/target/exec/output.txt" ).getAbsolutePath(), "arg1",
185                "arg2"} ) );
186    
187            mojo.execute();
188    
189            // checking the command line would involve resolving the repository
190            // checkMojo( "java -cp" );
191    
192            assertFileEquals( null, getTestFile( "src/test/projects/" + projectName + "/output.txt" ),
193                              getTestFile( "src/test/projects/" + projectName + "/target/exec/output.txt" ) );
194        }
195    */
196    
197        /**
198         * @return output from System.out during mojo execution
199         */
200        private String execute( File pom, String goal ) throws Exception {
201    
202            ExecMojo mojo;
203            mojo = (ExecMojo) lookupMojo( goal, pom );
204    
205            setUpProject( pom, mojo );
206    
207            MavenProject project = (MavenProject) getVariableValueFromObject( mojo, "project" );
208    
209            // why isn't this set up by the harness based on the default-value?  TODO get to bottom of this!
210            // setVariableValueToObject( mojo, "includeProjectDependencies", Boolean.TRUE );
211            // setVariableValueToObject( mojo, "killAfter", new Long( -1 ) );
212    
213            assertNotNull( mojo );
214            assertNotNull( project );
215    
216            // trap System.out
217            PrintStream out = System.out;
218            StringOutputStream stringOutputStream = new StringOutputStream();
219            System.setOut( new PrintStream( stringOutputStream ) );
220            // ensure we don't log unnecessary stuff which would interfere with assessing success of tests
221            mojo.setLog( new DefaultLog( new ConsoleLogger( Logger.LEVEL_ERROR, "exec:exec" ) ) );
222    
223            try
224            {
225                mojo.execute();
226            }
227            catch ( Throwable e )
228            {
229                e.printStackTrace( System.err );
230                fail( e.getMessage() );
231            }
232            finally
233            {
234                System.setOut( out );
235            }
236    
237            return stringOutputStream.toString();
238        }
239    
240    
241        private void setUpProject( File pomFile, ExecMojo mojo )
242            throws Exception
243        {
244            MavenProjectBuilder builder = (MavenProjectBuilder) lookup( MavenProjectBuilder.ROLE );
245    
246            ArtifactRepositoryLayout localRepositoryLayout =
247                (ArtifactRepositoryLayout) lookup( ArtifactRepositoryLayout.ROLE, "default" );
248    
249            String path = "src/test/repository";
250    
251            ArtifactRepository localRepository = new DefaultArtifactRepository( "local", "file://" +
252                new File( path ).getAbsolutePath(), localRepositoryLayout );
253    
254            mojo.setBasedir( File.createTempFile( "mvn-temp", "txt" ).getParentFile() );
255    
256            MavenProject project = builder.buildWithDependencies( pomFile, localRepository, null );
257    
258            // this gets the classes for these tests of this mojo (exec plugin) onto the project classpath for the test
259            project.getBuild().setOutputDirectory( new File( "target/test-classes" ).getAbsolutePath() );
260    
261            mojo.setProject( project );
262    
263            mojo.setLog( new SystemStreamLog()
264            {
265                public boolean isDebugEnabled()
266                {
267                    return true;
268                }
269            } );
270        }
271    
272        // MEXEC-12, MEXEC-72
273        public void testGetExecutablePath() throws IOException
274        {
275    
276            ExecMojo realMojo = new ExecMojo();
277    
278            File workdir = new File( System.getProperty( "user.dir" ) );
279            Map enviro = new HashMap();
280    
281            String myJavaPath = "target" + File.separator + "javax";
282            File f = new File( myJavaPath );
283            assertTrue( "file created...", f.createNewFile() ); 
284            assertTrue( "file exists...", f.exists() ); 
285    
286            realMojo.setExecutable( myJavaPath );
287    
288            CommandLine cmd = realMojo.getExecutablePath(enviro, workdir);
289            assertTrue( "File exists so path is absolute",
290                        cmd.getExecutable().startsWith( System.getProperty( "user.dir" ) ) );
291    
292            f.delete();
293            assertFalse( "file deleted...", f.exists() ); 
294            cmd = realMojo.getExecutablePath(enviro, workdir);
295            assertEquals( "File doesn't exist. Let the system find it (in that PATH?)",
296                        myJavaPath, cmd.getExecutable() );
297    
298    
299            if ( OS.isFamilyWindows() ) //how to make this part of the test run on other platforms as well??
300            {
301    
302                myJavaPath = "target" + File.separator + "javax.bat";
303                f = new File( myJavaPath );
304                assertTrue( "file created...", f.createNewFile() );
305                assertTrue( "file exists...", f.exists() );
306    
307    
308                realMojo.setExecutable( "javax.bat" );
309                cmd = realMojo.getExecutablePath( enviro, workdir );
310                assertTrue( "is bat file on windows, execute using cmd.",
311                        cmd.getExecutable().equals( "cmd" ) );
312    
313                enviro.put( "PATH", workdir.getAbsolutePath() + File.separator + "target" );
314                cmd = realMojo.getExecutablePath( enviro, workdir );
315                assertTrue( "is bat file on windows' PATH, execute using cmd.",
316                        cmd.getExecutable().equals( "cmd" ) );
317                f.delete();
318                assertFalse( "file deleted...", f.exists() );
319            }
320        }
321    
322        public void testRunFailure()
323        {
324            mojo.executeResult = 1;
325    
326            try
327            {
328                mojo.execute();
329                fail( "expected failure" );
330            }
331            catch ( MojoExecutionException e )
332            {
333                assertEquals( "Result of " + mojo.getExecutedCommandline( 0 ) + " execution is: '1'.",
334                              e.getMessage() );
335            }
336    
337            checkMojo( "mvn --version" );
338        }
339    
340        public void testRunError()
341        {
342            mojo.failureMsg = "simulated failure";
343    
344            try
345            {
346                mojo.execute();
347                fail( "expected failure" );
348            }
349            catch ( MojoExecutionException e )
350            {
351                assertEquals( "Command execution failed.", e.getMessage() );
352            }
353    
354            checkMojo( "mvn --version" );
355        }
356    
357        public void testOverrides()
358            throws MojoExecutionException
359        {
360            mojo.systemProperties.put( "exec.args", "-f pom.xml" );
361            mojo.execute();
362    
363            checkMojo( "mvn -f pom.xml" );
364        }
365    
366        public void testOverrides3()
367            throws MojoExecutionException
368        {
369            mojo.systemProperties.put( "exec.args", null );
370            mojo.execute();
371    
372            checkMojo( "mvn --version" );
373    
374            mojo.commandLines.clear();
375            mojo.systemProperties.put( "exec.args", "" );
376            mojo.execute();
377    
378            checkMojo( "mvn --version" );
379        }
380    
381        public void testIsResultCodeAFailure() 
382        {
383            ExecMojo execMojo = new ExecMojo();
384            assertTrue(execMojo.isResultCodeAFailure(1));
385            assertFalse(execMojo.isResultCodeAFailure(0));
386    
387            execMojo.setSuccessCodes(new ArrayList());
388            assertTrue(execMojo.isResultCodeAFailure(1));
389            assertFalse(execMojo.isResultCodeAFailure(0));
390    
391            execMojo.setSuccessCodes(Arrays.asList(new String[] { "2", "5" }));
392            assertTrue(execMojo.isResultCodeAFailure(0));
393            assertTrue(execMojo.isResultCodeAFailure(10));
394            assertFalse(execMojo.isResultCodeAFailure(2));
395            assertFalse(execMojo.isResultCodeAFailure(5));
396        }
397        
398        // MEXEC-81
399        public void testParseCommandlineOSWin() throws Exception 
400        {
401            ExecMojo execMojo = new ExecMojo();
402            final String javaHome = "C:\\Java\\jdk1.5.0_15";
403            // can only be set by expression or plugin-configuration
404            setVariableValueToObject( execMojo, "commandlineArgs", javaHome );
405            String[] args = execMojo.parseCommandlineArgs();
406            assertEquals( javaHome, args[0] );
407        }
408        
409        private void checkMojo( String expectedCommandLine )
410        {
411            assertEquals( 1, mojo.getAmountExecutedCommandLines() );
412            CommandLine commandline = mojo.getExecutedCommandline( 0 );
413            // do NOT depend on Commandline toString()
414            assertEquals(expectedCommandLine, getCommandLineAsString( commandline ));
415        }
416    
417        private String getCommandLineAsString( CommandLine commandline ) {
418            //for the sake of the test comparisons, cut out the eventual
419            //cmd /c *.bat conversion
420            String result = commandline.getExecutable();
421            boolean isCmd = false;
422            if (OS.isFamilyWindows() && result.equals("cmd")) {
423                result = "";
424                isCmd = true;
425            }
426            String[] arguments = commandline.getArguments();
427            for (int i = 0; i < arguments.length; i++) 
428            {
429                String arg = arguments[i];
430                if (isCmd && i == 0 && "/c".equals(arg)) {
431                    continue;
432            }
433                if (isCmd && i == 1 && arg.endsWith( ".bat")) {
434                    arg = arg.substring( 0, arg.length() - ".bat".length());
435                }
436                result += (result.length() == 0 ? "" : " ") + arg;
437            }
438            return result;
439        }
440    
441        // TAKEN FROM NetbeansFreeformPluginTest - refactor ?
442    
443        /**
444         * This method asserts that the two given files are equals in their
445         * content.
446         *
447         * @param mavenRepo    Not used.
448         * @param expectedFile The file that is expected.
449         * @param actualFile   The file that is.
450         * @throws java.io.IOException if something goes wrong.
451         */
452        private void assertFileEquals( String mavenRepo, File expectedFile, File actualFile )
453            throws IOException
454        {
455            List expectedLines = getLines( mavenRepo, expectedFile );
456    
457            List actualLines = getLines( mavenRepo, actualFile );
458    
459            for ( int i = 0; i < expectedLines.size(); i++ )
460            {
461                String expected = expectedLines.get( i ).toString();
462    
463                if ( actualLines.size() < i )
464                {
465                    fail( "Too few lines in the actual file. Was " + actualLines.size() + ", expected: " +
466                        expectedLines.size() );
467                }
468    
469                String actual = actualLines.get( i ).toString();
470    
471                assertEquals( "Checking line #" + ( i + 1 ), expected, actual );
472            }
473    
474            assertTrue( "Unequal number of lines.", expectedLines.size() == actualLines.size() );
475        }
476    
477        /**
478         * This method gives the list of String in a file.
479         *
480         * @param mavenRepo Not used.
481         * @param file      The file to be read.
482         * @return The list of the lines of the file.
483         * @throws java.io.IOException if something goes wrong.
484         */
485        private List getLines( String mavenRepo, File file )
486            throws IOException
487        {
488            List lines = new ArrayList();
489    
490            BufferedReader reader = new BufferedReader( new FileReader( file ) );
491    
492            String line;
493    
494            while ( ( line = reader.readLine() ) != null )
495            {
496                lines.add(
497                    line ); //StringUtils.replace( line, "#ArtifactRepositoryPath#", mavenRepo.replace( '\\', '/' ) ) );
498            }
499    
500            return lines;
501        }
502    }