View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugin.eclipse.it;
20  
21  import java.io.BufferedReader;
22  import java.io.File;
23  import java.io.FileReader;
24  import java.io.IOException;
25  import java.io.StringReader;
26  import java.net.MalformedURLException;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Map.Entry;
34  import java.util.Properties;
35  
36  import junit.framework.AssertionFailedError;
37  
38  import org.apache.maven.artifact.Artifact;
39  import org.apache.maven.artifact.factory.ArtifactFactory;
40  import org.apache.maven.artifact.factory.DefaultArtifactFactory;
41  import org.apache.maven.artifact.handler.DefaultArtifactHandler;
42  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
43  import org.apache.maven.artifact.handler.manager.DefaultArtifactHandlerManager;
44  import org.apache.maven.artifact.repository.ArtifactRepository;
45  import org.apache.maven.artifact.repository.DefaultArtifactRepository;
46  import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
47  import org.apache.maven.plugin.MojoExecutionException;
48  import org.apache.maven.plugin.eclipse.ExecutionFailedException;
49  import org.apache.maven.plugin.eclipse.Messages;
50  import org.apache.maven.plugin.ide.IdeUtils;
51  import org.apache.maven.plugin.testing.AbstractMojoTestCase;
52  import org.apache.maven.project.MavenProject;
53  import org.apache.maven.shared.invoker.InvocationRequest;
54  import org.apache.maven.shared.invoker.InvocationResult;
55  import org.apache.maven.shared.test.plugin.BuildTool;
56  import org.apache.maven.shared.test.plugin.PluginTestTool;
57  import org.apache.maven.shared.test.plugin.ProjectTool;
58  import org.apache.maven.shared.test.plugin.TestToolsException;
59  import org.codehaus.classworlds.ClassRealm;
60  import org.codehaus.plexus.PlexusContainer;
61  import org.codehaus.plexus.util.FileUtils;
62  import org.codehaus.plexus.util.IOUtil;
63  import org.codehaus.plexus.util.StringUtils;
64  import org.custommonkey.xmlunit.Diff;
65  import org.custommonkey.xmlunit.XMLAssert;
66  import org.custommonkey.xmlunit.XMLUnit;
67  import org.xml.sax.EntityResolver;
68  import org.xml.sax.InputSource;
69  import org.xml.sax.SAXException;
70  
71  /**
72   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
73   * @author <a href="mailto:fgiust@apache.org">Fabrizio Giustina</a>
74   * @version $Id: AbstractEclipsePluginIT.java 1213573 2011-12-13 06:22:43Z baerrach $
75   */
76  public abstract class AbstractEclipsePluginIT
77      extends AbstractMojoTestCase
78  {
79  
80      private BuildTool buildTool;
81  
82      private ProjectTool projectTool;
83  
84      /**
85       * Test repository directory.
86       */
87      protected static File localRepositoryDirectory = getTestFile( "target/test-classes/m2repo" );
88  
89      /**
90       * Pom File
91       */
92      protected static File PomFile = new File( getBasedir(), "pom.xml" );
93  
94      /**
95       * Group-Id for running test builds.
96       */
97      protected static final String GROUP_ID = "org.apache.maven.plugins";
98  
99      /**
100      * Artifact-Id for running test builds.
101      */
102     protected static final String ARTIFACT_ID = "maven-eclipse-plugin";
103 
104     /**
105      * Version under which the plugin was installed to the test-time local repository for running test builds.
106      */
107     protected static final String VERSION = "test";
108 
109     private static final String BUILD_OUTPUT_DIRECTORY = "target/failsafe-reports/build-output";
110 
111     private static boolean installed = false;
112 
113     /**
114      * The name of the directory used for comparison of expected output.
115      */
116     private static final String EXPECTED_DIRECTORY_NAME = "expected";
117 
118     /**
119      * The XML Header used to check if the file contains XML content.
120      */
121     private static final String XML_HEADER = "<?xml";
122 
123     /**
124      * .classpath file name
125      */
126     private static final String CLASSPATH_FILENAME = ".classpath";
127 
128     /**
129      * @see org.codehaus.plexus.PlexusTestCase#setUp()
130      */
131     protected void setUp()
132         throws Exception
133     {
134         if ( !installed )
135         {
136             System.out.println( "*** Running test builds; output will be directed to: " + BUILD_OUTPUT_DIRECTORY + "\n" );
137         }
138 
139         super.setUp();
140 
141         buildTool = (BuildTool) lookup( BuildTool.ROLE, "default" );
142 
143         projectTool = (ProjectTool) lookup( ProjectTool.ROLE, "default" );
144 
145         String mavenHome = System.getProperty( "maven.home" );
146 
147         // maven.home is set by surefire when the test is run with maven, but better make the test
148         // run in IDEs without the need of additional properties
149         if ( mavenHome == null )
150         {
151             String path = System.getProperty( "java.library.path" );
152             String[] paths = StringUtils.split( path, System.getProperty( "path.separator" ) );
153             for ( int j = 0; j < paths.length; j++ )
154             {
155                 String pt = paths[j];
156                 if ( new File( pt, "mvn" ).exists() )
157                 {
158                     System.setProperty( "maven.home", new File( pt ).getAbsoluteFile().getParent() );
159                     break;
160                 }
161 
162             }
163         }
164 
165         System.setProperty( "MAVEN_TERMINATE_CMD", "on" );
166 
167         synchronized ( AbstractEclipsePluginIT.class )
168         {
169             if ( !installed )
170             {
171                 PluginTestTool pluginTestTool = (PluginTestTool) lookup( PluginTestTool.ROLE, "default" );
172 
173                 localRepositoryDirectory =
174                     pluginTestTool.preparePluginForUnitTestingWithMavenBuilds( PomFile, "test",
175                                                                                localRepositoryDirectory );
176 
177                 System.out.println( "*** Installed test-version of the Eclipse plugin to: " + localRepositoryDirectory
178                     + "\n" );
179 
180                 // Hack: to work around proxys and DTDs retrievals.
181                 EntityResolver ignoreDtds = new EntityResolver()
182                 {
183 
184                     public InputSource resolveEntity( String publicId, String systemId )
185                         throws SAXException, IOException
186                     {
187                         return new InputSource( new StringReader( "<!ELEMENT ignored (#PCDATA)>" ) );
188                     }
189 
190                 };
191                 XMLUnit.setTestEntityResolver( ignoreDtds );
192                 XMLUnit.setControlEntityResolver( ignoreDtds );
193 
194                 installed = true;
195             }
196         }
197 
198     }
199 
200     /**
201      * @see org.codehaus.plexus.PlexusTestCase#tearDown()
202      */
203     protected void tearDown()
204         throws Exception
205     {
206         super.tearDown();
207 
208         List containers = new ArrayList();
209 
210         containers.add( getContainer() );
211 
212         for ( Iterator iter = containers.iterator(); iter.hasNext(); )
213         {
214             PlexusContainer container = (PlexusContainer) iter.next();
215 
216             if ( container != null )
217             {
218                 container.dispose();
219 
220                 ClassRealm realm = container.getContainerRealm();
221 
222                 if ( realm != null )
223                 {
224                     realm.getWorld().disposeRealm( realm.getId() );
225                 }
226             }
227         }
228     }
229 
230     /**
231      * Execute the eclipse:eclipse goal on a test project and verify generated files.
232      *
233      * @param projectName project directory
234      * @throws Exception any exception generated during test
235      */
236     protected void testProject( String projectName )
237         throws Exception
238     {
239         testProject( projectName, new Properties(), "clean", "eclipse" );
240     }
241 
242     /**
243      * Execute the eclipse:eclipse goal on a test project and verify generated files.
244      *
245      * @param basedir basedir of mvn execution
246      * @throws Exception any exception generated during test
247      */
248     protected void testProject( File basedir )
249         throws Exception
250     {
251         testProject( basedir, new Properties(), "clean", "eclipse" );
252     }
253 
254     /**
255      * Execute the eclipse:eclipse goal on a test project and verify generated files.
256      *
257      * @param projectName project directory
258      * @param properties additional properties
259      * @param cleanGoal TODO
260      * @param genGoal TODO
261      * @throws Exception any exception generated during test
262      */
263     protected void testProject( String projectName, Properties properties, String cleanGoal, String genGoal )
264         throws Exception
265     {
266         testProject( projectName, properties, cleanGoal, genGoal, false );
267     }
268 
269     /**
270      * Execute the eclipse:eclipse goal on a test project and verify generated files.
271      *
272      * @param projectName project directory
273      * @param properties additional properties
274      * @param cleanGoal TODO
275      * @param genGoal TODO
276      * @param withInstall true to include the install goal, false to exclude it.
277      * @throws Exception any exception generated during test
278      */
279     protected void testProject( String projectName, Properties properties, String cleanGoal, String genGoal,
280                                 boolean withInstall )
281         throws Exception
282     {
283         File basedir = getTestFile( "target/test-classes/projects/" + projectName );
284         testProject( basedir, properties, cleanGoal, genGoal, withInstall );
285     }
286 
287     /**
288      * @param basedir Execute the eclipse:eclipse goal on a test project and verify generated files.
289      * @param properties additional properties
290      * @param cleanGoal TODO
291      * @param genGoal TODO
292      * @throws Exception any exception generated during test
293      */
294     protected void testProject( File basedir, Properties properties, String cleanGoal, String genGoal )
295         throws Exception
296     {
297         testProject( basedir, properties, cleanGoal, genGoal, false );
298     }
299 
300     /**
301      * Execute the eclipse:eclipse goal on a test project and verify generated files.
302      *
303      * @param basedir basedir of mvn execution
304      * @param properties additional properties
305      * @param cleanGoal TODO
306      * @param genGoal TODO
307      * @param withInstall true to include the install goal, false to exclude it.
308      * @throws Exception any exception generated during test
309      */
310     protected void testProject( File basedir, Properties properties, String cleanGoal, String genGoal,
311                                 boolean withInstall )
312         throws Exception
313     {
314         File pom = new File( basedir, "pom.xml" );
315 
316         String pluginSpec = getPluginCLISpecification();
317 
318         List goals = new ArrayList();
319 
320         goals.add( pluginSpec + cleanGoal );
321         goals.add( pluginSpec + genGoal );
322         if ( withInstall )
323         {
324             goals.add( "install" );
325         }
326 
327         executeMaven( pom, properties, goals );
328 
329         MavenProject project = readProject( pom );
330 
331         String outputDirPath =
332             IdeUtils.getPluginSetting( project, "org.apache.maven.plugins:maven-eclipse-plugin", "outputDir", null );
333         File projectOutputDir = basedir;
334 
335         if ( outputDirPath != null )
336         {
337             File outputDir = new File( basedir, outputDirPath );
338             outputDir.mkdirs();
339             projectOutputDir = new File( outputDir, project.getArtifactId() );
340         }
341 
342         compareDirectoryContent( basedir, projectOutputDir );
343     }
344 
345     /**
346      * Execute the eclipse:configure-workspace goal on a test project and verify generated files.
347      *
348      * @param projectName project directory
349      * @throws Exception any exception generated during test
350      */
351     protected void testWorkspace( String projectName )
352         throws Exception
353     {
354         testWorkspace( projectName, new Properties(), "configure-workspace" );
355     }
356 
357     /**
358      * Execute the eclipse:configure-workspace goal on a test project and verify generated files.
359      *
360      * @param projectName project directory
361      * @throws Exception any exception generated during test
362      */
363     protected void testWorkspace( String projectName, String goal )
364         throws Exception
365     {
366         testWorkspace( projectName, new Properties(), goal );
367     }
368 
369     /**
370      * Execute the eclipse:configure-workspace goal on a test project and verify generated files.
371      *
372      * @param projectName project directory
373      * @param properties additional properties
374      * @param cleanGoal TODO
375      * @param genGoal TODO
376      * @throws Exception any exception generated during test
377      */
378     protected void testWorkspace( String projectName, Properties properties, String genGoal )
379         throws Exception
380     {
381         File basedir = getOutputDirectory( projectName );
382 
383         File pom = new File( basedir, "pom.xml" );
384 
385         String pluginSpec = getPluginCLISpecification();
386 
387         List goals = new ArrayList();
388 
389         goals.add( pluginSpec + genGoal );
390 
391         executeMaven( pom, properties, goals );
392 
393         MavenProject project = readProject( pom );
394 
395         String outputDirPath =
396             IdeUtils.getPluginSetting( project, "org.apache.maven.plugins:maven-eclipse-plugin", "outputDir", null );
397         File outputDir;
398         File projectOutputDir = basedir;
399 
400         if ( outputDirPath == null )
401         {
402             outputDir = basedir;
403         }
404         else
405         {
406             outputDir = new File( basedir, outputDirPath );
407             outputDir.mkdirs();
408             projectOutputDir = new File( outputDir, project.getArtifactId() );
409         }
410 
411         compareDirectoryContent( basedir, projectOutputDir );
412 
413     }
414 
415     protected File getOutputDirectory( String projectName )
416     {
417         return getTestFile( "target/test-classes/projects/" + projectName );
418     }
419 
420     protected File getTestWorkspaceWorkDirectory( String projectName )
421     {
422         return new File( this.getOutputDirectory( projectName ), ".metadata" );
423     }
424 
425     protected void executeMaven( File pom, Properties properties, List goals )
426         throws TestToolsException, ExecutionFailedException
427     {
428         executeMaven( pom, properties, goals, true );
429     }
430 
431     protected void executeMaven( File pom, Properties properties, List goals, boolean switchLocalRepo )
432         throws TestToolsException, ExecutionFailedException
433     {
434         System.out.println( "  Building " + pom.getParentFile().getName() );
435 
436         new File( BUILD_OUTPUT_DIRECTORY ).mkdirs();
437 
438         NullPointerException npe = new NullPointerException();
439         StackTraceElement[] trace = npe.getStackTrace();
440 
441         File buildLog = null;
442 
443         for ( int i = 0; i < trace.length; i++ )
444         {
445             StackTraceElement element = trace[i];
446 
447             String methodName = element.getMethodName();
448 
449             if ( methodName.startsWith( "test" ) && !methodName.equals( "testProject" ) )
450             {
451                 String classname = element.getClassName();
452 
453                 buildLog = new File( BUILD_OUTPUT_DIRECTORY, classname + "_" + element.getMethodName() + ".build.log" );
454 
455                 break;
456             }
457         }
458 
459         if ( buildLog == null )
460         {
461             buildLog = new File( BUILD_OUTPUT_DIRECTORY, "unknown.build.log" );
462         }
463 
464         if (properties == null) properties = new Properties();
465         InvocationRequest request = buildTool.createBasicInvocationRequest( pom, properties, goals, buildLog );
466         request.setUpdateSnapshots( false );
467         request.setShowErrors( true );
468         request.getProperties().setProperty( "downloadSources", "false" );
469         request.getProperties().setProperty( "downloadJavadocs", "false" );
470 
471         request.setDebug( true );
472 
473         if ( switchLocalRepo )
474         {
475             request.setLocalRepositoryDirectory( localRepositoryDirectory );
476         }
477 
478         InvocationResult result = buildTool.executeMaven( request );
479 
480         if ( result.getExitCode() != 0 )
481         {
482             String buildLogUrl = buildLog.getAbsolutePath();
483 
484             try
485             {
486                 buildLogUrl = buildLog.toURL().toExternalForm();
487             }
488             catch ( MalformedURLException e )
489             {
490             }
491 
492             throw new ExecutionFailedException( "Failed to execute build.\nPOM: " + pom + "\nGoals: "
493                 + StringUtils.join( goals.iterator(), ", " ) + "\nExit Code: " + result.getExitCode() + "\nError: "
494                 + result.getExecutionException() + "\nBuild Log: " + buildLogUrl + "\n", result );
495         }
496     }
497 
498     protected MavenProject readProject( File pom )
499         throws TestToolsException
500     {
501         return projectTool.readProject( pom, localRepositoryDirectory );
502     }
503 
504     protected String getPluginCLISpecification()
505     {
506         String pluginSpec = GROUP_ID + ":" + ARTIFACT_ID + ":";
507 
508         // String pluginVersion = System.getProperty( "pluginVersion" );
509         //
510         // if ( pluginVersion != null )
511         // {
512         // pluginSpec += pluginVersion + ":";
513         // }
514         //
515         // System.out.println( "\n\nUsing Eclipse plugin version: " + pluginVersion + "\n\n" );
516 
517         // try using the test-version installed during setUp()
518         pluginSpec += VERSION + ":";
519 
520         return pluginSpec;
521     }
522 
523     /**
524      * @param basedir the base directory of the project
525      * @param projectOutputDir the directory where the eclipse plugin will write the output files.
526      * @throws MojoExecutionException
527      */
528     protected void compareDirectoryContent( File basedir, File projectOutputDir )
529         throws MojoExecutionException
530     {
531         File[] expectedDirectories = getExpectedDirectories( basedir );
532 
533         for ( int i = 0; i < expectedDirectories.length; i++ )
534         {
535             File expectedDirectory = expectedDirectories[i];
536             File[] expectedFilesToCompare = getExpectedFilesToCompare( expectedDirectory );
537 
538             for ( int j = 0; j < expectedFilesToCompare.length; j++ )
539             {
540                 File expectedFile = expectedFilesToCompare[j];
541                 File actualFile = getActualFile( projectOutputDir, basedir, expectedFile );
542 
543                 assertFileEquals( expectedFile, actualFile );
544             }
545         }
546     }
547 
548     protected void assertFileEquals( File expectedFile, File actualFile )
549         throws MojoExecutionException
550     {
551         if ( !actualFile.exists() )
552         {
553             throw new AssertionFailedError( "Generated file not found: " + actualFile.getAbsolutePath() );
554         }
555 
556         HashMap variableReplacement = new HashMap();
557         variableReplacement.put( "${basedir}",
558                                  IdeUtils.fixSeparator( IdeUtils.getCanonicalPath( new File( getBasedir() ) ) ) );
559         variableReplacement.put( "${M2_REPO}",
560                                  IdeUtils.fixSeparator( IdeUtils.getCanonicalPath( localRepositoryDirectory ) ) );
561 
562         String expectedFileContents = preprocess( expectedFile, variableReplacement );
563         String actualFileContents = preprocess( actualFile, null );
564 
565         if ( isXml( expectedFile ) )
566         {
567             assertXmlFileEquals( expectedFile, expectedFileContents, actualFile, actualFileContents );
568         }
569         else
570         {
571             assertTextFileEquals( expectedFile, expectedFileContents, actualFile, actualFileContents );
572         }
573     }
574 
575     /**
576      * Assert that two XML files are equal.
577      *
578      * @param expectedFile the expected file - only used for path information
579      * @param expectedFileContents the contents of the expected file
580      * @param actualFile the actual file - only used for path information
581      * @param actualFileContents the contents of the actual file
582      * @throws MojoExecutionException failures.
583      */
584     private void assertXmlFileEquals( File expectedFile, String expectedFileContents, File actualFile,
585                                       String actualFileContents )
586         throws MojoExecutionException
587     {
588         try
589         {
590             String message =
591                 "Comparing '" + IdeUtils.getCanonicalPath(actualFile) + "' against '"
592                     + IdeUtils.getCanonicalPath(expectedFile) + "'";
593             if (CLASSPATH_FILENAME.equals(actualFile.getName())) {                
594                 Diff diff = new Diff(expectedFileContents, actualFileContents);
595                 XMLAssert.assertXMLIdentical( message, diff, true );
596             }
597             else {
598                 XMLAssert.assertXMLEqual( message, expectedFileContents, actualFileContents );
599             }
600         }
601         catch ( IOException e )
602         {
603             throw new MojoExecutionException( IdeUtils.getCanonicalPath( expectedFile )
604                 + "assertXmlFileEquals failure: IO " + e.getMessage(), e );
605         }
606         catch ( SAXException e )
607         {
608             throw new MojoExecutionException( "assertXmlFileEquals failure: SAX " + e.getMessage(), e );
609         }
610     }
611 
612     /**
613      * Assert that two text files are equals. Lines that start with # are comments and ignored.
614      *
615      * @param expectedFile the expected file - only used for path information
616      * @param expectedFileContents the contents of the expected file
617      * @param actualFile the actual file - only used for path information
618      * @param actualFileContents the contents of the actual fiel
619      * @throws MojoExecutionException failures.
620      */
621     private void assertTextFileEquals( File expectedFile, String expectedFileContents, File actualFile,
622                                        String actualFileContents )
623         throws MojoExecutionException
624     {
625         List expectedLines = getLines( expectedFileContents );
626         List actualLines = getLines( actualFileContents );
627         for ( int i = 0; i < expectedLines.size(); i++ )
628         {
629             String expected = expectedLines.get( i ).toString();
630             if ( actualLines.size() <= i )
631             {
632                 fail( "Too few lines in the actual file. Was " + actualLines.size() + ", expected: "
633                     + expectedLines.size() );
634             }
635             String actual = actualLines.get( i ).toString();
636             if ( expected.startsWith( "#" ) && actual.startsWith( "#" ) )
637             {
638                 // ignore comments, for settings file
639                 continue;
640             }
641             assertEquals( "Comparing '" + IdeUtils.getCanonicalPath( actualFile ) + "' against '"
642                 + IdeUtils.getCanonicalPath( expectedFile ) + "' at line #" + ( i + 1 ), expected, actual );
643         }
644         assertTrue( "Unequal number of lines.", expectedLines.size() == actualLines.size() );
645     }
646 
647     /**
648      * Preprocess the file so that equals comparison can be done. Preprocessing may vary based on filename.
649      *
650      * @param file the file being processed
651      * @param variables if not null, then replace all keys with the corresponding values in the expected string.
652      * @return processed input
653      */
654     private String preprocess( File file, Map variables )
655         throws MojoExecutionException
656     {
657         String result = null;
658         try
659         {
660             result = FileUtils.fileRead( file, "UTF-8" );
661         }
662         catch ( IOException ex )
663         {
664             throw new MojoExecutionException( "Unable to read file", ex );
665         }
666         result = replaceVariables( result, variables );
667         result = IdeUtils.fixWindowsDriveURI( result );
668 
669         /*
670          * NOTE: This is another hack to compensate for some metadata files that contain a complete XML file as the
671          * value for a key like "org.eclipse.jdt.ui.formatterprofiles" from "org.eclipse.jdt.ui.prefs". Line terminators
672          * in this value are platform-dependent.
673          */
674         if ( file.getName().endsWith( ".prefs" ) )
675         {
676             result = normalizeNewlineTerminators( result );
677         }
678 
679         /*
680          * NOTE: This is a hack to compensate for files that contain generated values like dependent-object in
681          * org.eclipse.wst.common.component. Regex would be a better solution.
682          */
683         if ( file.getName().equals( "org.eclipse.wst.common.component" ) || file.getName().equals( ".modulemaps" )
684             || file.getName().equals( "application.xml" ) )
685         {
686             result = result.replaceAll( "_\\d+", "" );
687         }
688         return result;
689     }
690 
691     /**
692      * Normalize line terminators into \n. \r\n, \r, \n all get changed into \n.
693      *
694      * @param input the string to normalize
695      * @return string with line terminators normalized
696      */
697     private String normalizeNewlineTerminators( String input )
698     {
699         return input.replaceAll( "(\\\\r\\\\n)|(\\\\n)|(\\\\r)", "\\n" );
700     }
701 
702     /**
703      * @param str input string
704      * @param variables map of variables (keys) and replacement value (values)
705      * @return the string with all variable values replaced.
706      */
707     private String replaceVariables( String str, Map variables )
708     {
709         String result = str;
710         if ( variables != null && !variables.isEmpty() )
711         {
712             Iterator iter = variables.entrySet().iterator();
713             while ( iter.hasNext() )
714             {
715                 Map.Entry entry = (Entry) iter.next();
716                 String variable = (String) entry.getKey();
717                 String replacement = (String) entry.getValue();
718                 result = StringUtils.replace( result, variable, replacement );
719             }
720         }
721 
722         return result;
723     }
724 
725     protected void assertContains( String message, String full, String substring )
726     {
727         if ( full == null || full.indexOf( substring ) == -1 )
728         {
729             StringBuffer buf = new StringBuffer();
730             if ( message != null )
731             {
732                 buf.append( message );
733             }
734             buf.append( ". " );
735             buf.append( "Expected \"" );
736             buf.append( substring );
737             buf.append( "\" not found" );
738             fail( buf.toString() );
739         }
740     }
741 
742     protected void assertDoesNotContain( String message, String full, String substring )
743     {
744         if ( full == null || full.indexOf( substring ) != -1 )
745         {
746             StringBuffer buf = new StringBuffer();
747             if ( message != null )
748             {
749                 buf.append( message );
750             }
751             buf.append( ". " );
752             buf.append( "Unexpected \"" );
753             buf.append( substring );
754             buf.append( "\" found" );
755             fail( buf.toString() );
756         }
757     }
758 
759     private List getLines( String input )
760         throws MojoExecutionException
761     {
762         try
763 
764         {
765             List lines = new ArrayList();
766 
767             BufferedReader reader = new BufferedReader( new StringReader( input ) );
768 
769             String line;
770 
771             while ( ( line = reader.readLine() ) != null )
772             {
773                 lines.add( line );
774             }
775 
776             IOUtil.close( reader );
777 
778             return lines;
779         }
780         catch ( IOException e )
781         {
782             throw new MojoExecutionException( "failed to getLines", e );
783         }
784     }
785 
786     /**
787      * @param basedir base directory to search for directories named "expected"
788      * @return an array of directories that match "expected"
789      */
790     private File[] getExpectedDirectories( File basedir )
791     {
792         List expectedDirectories = new ArrayList();
793         List subdirectories = new ArrayList();
794 
795         File[] allFiles = basedir.listFiles();
796         if ( allFiles != null )
797         {
798             for ( int i = 0; i < allFiles.length; i++ )
799             {
800                 File currentFile = allFiles[i];
801                 if ( currentFile.isDirectory() )
802                 {
803                     if ( currentFile.getName().equals( EXPECTED_DIRECTORY_NAME ) )
804                     {
805                         expectedDirectories.add( currentFile );
806                     }
807                     else
808                     {
809                         subdirectories.add( currentFile );
810                     }
811                 }
812             }
813         }
814         if ( !subdirectories.isEmpty() )
815         {
816             for ( Iterator iter = subdirectories.iterator(); iter.hasNext(); )
817             {
818                 File subdirectory = (File) iter.next();
819                 File[] subdirectoryFiles = getExpectedDirectories( subdirectory );
820                 expectedDirectories.addAll( Arrays.asList( subdirectoryFiles ) );
821             }
822         }
823         return (File[]) expectedDirectories.toArray( new File[expectedDirectories.size()] );
824     }
825 
826     /**
827      * @param expectedDirectory the expected directory to locate expected Files
828      * @return an array of Files found under the expectedDirectory - will recurse through the directory structure.
829      */
830     private File[] getExpectedFilesToCompare( File expectedDirectory )
831     {
832         List expectedFiles = new ArrayList();
833         List subdirectories = new ArrayList();
834 
835         File[] allFiles = expectedDirectory.listFiles();
836         if ( allFiles != null )
837         {
838             for ( int i = 0; i < allFiles.length; i++ )
839             {
840                 File currentFile = allFiles[i];
841                 if ( currentFile.isDirectory() )
842                 {
843                     subdirectories.add( currentFile );
844                 }
845                 else
846                 {
847                     expectedFiles.add( currentFile );
848                 }
849             }
850         }
851         if ( !subdirectories.isEmpty() )
852         {
853             for ( Iterator iter = subdirectories.iterator(); iter.hasNext(); )
854             {
855                 File subdirectory = (File) iter.next();
856                 File[] subdirectoryFiles = getExpectedFilesToCompare( subdirectory );
857                 expectedFiles.addAll( Arrays.asList( subdirectoryFiles ) );
858             }
859         }
860 
861         return (File[]) expectedFiles.toArray( new File[expectedFiles.size()] );
862     }
863 
864     /**
865      * Locate the actual file needed for comparison. The expectedFile has the baseDir prefix removed and the resulting
866      * relative path used to locate the file within the projectOutputDir.
867      *
868      * @param projectOutputDir the directory where the eclipse plugin writes files to
869      * @param basedir the base dir of the project being tested
870      * @param expectedFile the expected file used to compare to the actual file
871      * @return the actual file needed for comparison against the expectedFile
872      * @throws MojoExecutionException failures for obtaining actual file.
873      */
874     private File getActualFile( File projectOutputDir, File basedir, File expectedFile )
875         throws MojoExecutionException
876     {
877         String relativePath = IdeUtils.toRelativeAndFixSeparator( basedir, expectedFile, false );
878         relativePath = relativePath.replaceFirst( EXPECTED_DIRECTORY_NAME, "" );
879         File actualFile = new File( projectOutputDir, relativePath );
880         try
881         {
882             return actualFile.getCanonicalFile();
883         }
884         catch ( IOException e )
885         {
886             throw new MojoExecutionException(
887                                               Messages.getString(
888                                                                   "EclipsePlugin.cantcanonicalize", actualFile.getAbsolutePath() ), e ); //$NON-NLS-1$
889         }
890     }
891 
892     /**
893      * Test if the file contains xml content.
894      *
895      * @param f the file to test
896      * @return true if the file contains xml content, false otherwise.
897      */
898     private boolean isXml( File f )
899     {
900         FileReader reader = null;
901         try
902         {
903             reader = new FileReader( f );
904             char[] header = new char[XML_HEADER.length()];
905             reader.read( header );
906             return XML_HEADER.equals( new String( header ) );
907         }
908         catch ( Exception e )
909         {
910             return false;
911         }
912         finally
913         {
914             IOUtil.close( reader );
915         }
916     }
917 
918     /**
919      * Return the not available marker file for the specified artifact details.
920      *
921      * @param groupId group id of artifact
922      * @param artifactId artifact id of artifact
923      * @param version version of artifact
924      * @param classifier the classifier of the artifact
925      * @param inClassifier the sources/javadocs to be attached
926      * @return the not available marker file
927      * @throws Exception failures.
928      * @see IdeUtils#createArtifactWithClassifier(String, String, String, String, String, ArtifactFactory)
929      */
930     protected File getNotAvailableMarkerFile( String groupId, String artifactId, String version, String classifier,
931                                               String inClassifier )
932         throws Exception
933     {
934         // HACK: START
935         // TODO: Work out how to use Plexus to obtain these values
936         String url = "file://" + localRepositoryDirectory;
937         ArtifactRepository localRepository =
938             new DefaultArtifactRepository( "local", url, new DefaultRepositoryLayout() );
939 
940         ArtifactFactory artifactFactory = new DefaultArtifactFactory();
941 
942         DefaultArtifactHandler javaSourceArtifactHandler = new DefaultArtifactHandler( "java-source" );
943         setVariableValueToObject( javaSourceArtifactHandler, "extension", "jar" );
944 
945         DefaultArtifactHandler javadocArtifactHandler = new DefaultArtifactHandler( "javadoc" );
946         setVariableValueToObject( javadocArtifactHandler, "extension", "jar" );
947 
948         Map artifactHandlers = new HashMap();
949         artifactHandlers.put( "java-source", javaSourceArtifactHandler );
950         artifactHandlers.put( "javadoc", javadocArtifactHandler );
951 
952         ArtifactHandlerManager artifactHandlerManager = new DefaultArtifactHandlerManager();
953         setVariableValueToObject( artifactHandlerManager, "artifactHandlers", artifactHandlers );
954         setVariableValueToObject( artifactFactory, "artifactHandlerManager", artifactHandlerManager );
955         // HACK: END
956 
957         Artifact artifact =
958             IdeUtils.createArtifactWithClassifier( groupId, artifactId, version, classifier, inClassifier,
959                                                    artifactFactory );
960         return IdeUtils.getNotAvailableMarkerFile( localRepository, artifact );
961     }
962 
963     /**
964      * Assert that the not available marker file exists for the specified artifact details.
965      *
966      * @param groupId group id of artifact
967      * @param artifactId artifact id of artifact
968      * @param version version of artifact
969      * @param classifier the classifier of the artifact
970      * @param inClassifier the sources/javadocs to be attached
971      * @throws Exception failures
972      */
973     protected void assertNotAvailableMarkerFileExists( String groupId, String artifactId, String version,
974                                                        String classifier, String inClassifier )
975         throws Exception
976     {
977         File markerFile = getNotAvailableMarkerFile( groupId, artifactId, version, classifier, inClassifier );
978         assertTrue( "The \"Not Available\" marker file does not exist: " + markerFile, markerFile.exists() );
979     }
980 
981     /**
982      * Assert that the not available marker file does not exist for the specified artifact details.
983      *
984      * @param groupId group id of artifact
985      * @param artifactId artifact id of artifact
986      * @param version version of artifact
987      * @param classifier the classifier of the artifact
988      * @param inClassifier the sources/javadocs to be attached
989      * @throws Exception failures
990      */
991     protected void assertNotAvailableMarkerFileDoesNotExist( String groupId, String artifactId, String version,
992                                                              String classifier, String inClassifier )
993         throws Exception
994     {
995         File markerFile = getNotAvailableMarkerFile( groupId, artifactId, version, classifier, inClassifier );
996         assertTrue( "The \"Not Available\" marker file incorrectly exists: " + markerFile, !markerFile.exists() );
997     }
998 
999 }