View Javadoc
1   package org.apache.maven.shared.release.phase;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.Reader;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Stack;
32  
33  import org.apache.commons.lang.SystemUtils;
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.artifact.ArtifactUtils;
36  import org.apache.maven.artifact.factory.ArtifactFactory;
37  import org.apache.maven.artifact.manager.WagonManager;
38  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
39  import org.apache.maven.artifact.repository.ArtifactRepository;
40  import org.apache.maven.artifact.repository.DefaultArtifactRepository;
41  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
42  import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
43  import org.apache.maven.artifact.resolver.ArtifactCollector;
44  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
45  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
46  import org.apache.maven.artifact.versioning.VersionRange;
47  import org.apache.maven.model.Dependency;
48  import org.apache.maven.model.DependencyManagement;
49  import org.apache.maven.model.Profile;
50  import org.apache.maven.model.Repository;
51  import org.apache.maven.profiles.DefaultProfileManager;
52  import org.apache.maven.profiles.ProfileManager;
53  import org.apache.maven.project.MavenProject;
54  import org.apache.maven.project.MavenProjectBuilder;
55  import org.apache.maven.project.ProjectBuildingException;
56  import org.apache.maven.project.ProjectSorter;
57  import org.apache.maven.shared.release.util.ReleaseUtil;
58  import org.codehaus.plexus.PlexusTestCase;
59  import org.codehaus.plexus.context.ContextException;
60  import org.codehaus.plexus.context.DefaultContext;
61  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
62  import org.codehaus.plexus.util.FileUtils;
63  import org.codehaus.plexus.util.IOUtil;
64  import org.codehaus.plexus.util.ReaderFactory;
65  import org.codehaus.plexus.util.StringUtils;
66  import org.custommonkey.xmlunit.Diff;
67  import org.custommonkey.xmlunit.Difference;
68  import org.custommonkey.xmlunit.DifferenceListener;
69  import org.custommonkey.xmlunit.XMLAssert;
70  import org.custommonkey.xmlunit.XMLUnit;
71  import org.w3c.dom.Node;
72  import org.xml.sax.SAXException;
73  
74  /**
75   * Base class for some release tests.
76   *
77   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
78   */
79  public abstract class AbstractReleaseTestCase
80      extends PlexusTestCase
81  {
82      protected MavenProjectBuilder projectBuilder;
83  
84      protected ArtifactRepository localRepository;
85  
86      protected ReleasePhase phase;
87  
88      private static final DefaultContext EMPTY_CONTEXT = new DefaultContext()
89      {
90          public Object get( Object key )
91              throws ContextException
92          {
93              return null;
94          }
95      };
96  
97      protected void setUp()
98          throws Exception
99      {
100         super.setUp();
101 
102         projectBuilder = (MavenProjectBuilder) lookup( MavenProjectBuilder.ROLE );
103 
104         ArtifactRepositoryLayout layout = (ArtifactRepositoryLayout) lookup( ArtifactRepositoryLayout.ROLE, "default" );
105         String localRepoPath = getTestFile( "target/local-repository" ).getAbsolutePath().replace( '\\', '/' );
106         localRepository = new DefaultArtifactRepository( "local", "file://" + localRepoPath, layout );
107     }
108 
109     protected void tearDown()
110         throws Exception
111     {
112         // unhook circular references to the container that would avoid memory being cleaned up
113         ( (Contextualizable) projectBuilder ).contextualize( EMPTY_CONTEXT );
114         ( (Contextualizable) lookup( WagonManager.ROLE ) ).contextualize( EMPTY_CONTEXT );
115 
116         super.tearDown();
117     }
118 
119     private Map<String,Artifact> createManagedVersionMap( String projectId, DependencyManagement dependencyManagement,
120                                          ArtifactFactory artifactFactory )
121         throws ProjectBuildingException
122     {
123         Map<String,Artifact> map;
124         if ( dependencyManagement != null && dependencyManagement.getDependencies() != null )
125         {
126             map = new HashMap<String,Artifact>();
127             for ( Iterator i = dependencyManagement.getDependencies().iterator(); i.hasNext(); )
128             {
129                 Dependency d = (Dependency) i.next();
130 
131                 try
132                 {
133                     VersionRange versionRange = VersionRange.createFromVersionSpec( d.getVersion() );
134                     Artifact artifact = artifactFactory.createDependencyArtifact( d.getGroupId(), d.getArtifactId(),
135                                                                                   versionRange, d.getType(),
136                                                                                   d.getClassifier(), d.getScope() );
137                     map.put( d.getManagementKey(), artifact );
138                 }
139                 catch ( InvalidVersionSpecificationException e )
140                 {
141                     throw new ProjectBuildingException( projectId, "Unable to parse version '" + d.getVersion() +
142                         "' for dependency '" + d.getManagementKey() + "': " + e.getMessage(), e );
143                 }
144             }
145         }
146         else
147         {
148             map = Collections.emptyMap();
149         }
150         return map;
151     }
152 
153     protected List<MavenProject> createReactorProjects( String path, String subpath )
154         throws Exception
155     {
156         return createReactorProjects( path, path, subpath );
157     }
158 
159     protected List<MavenProject> createReactorProjects( String path, String targetPath, String subpath )
160         throws Exception
161     {
162         File testFile = getTestFile( "target/test-classes/projects/" + path + subpath + "/pom.xml" );
163         Stack<File> projectFiles = new Stack<File>();
164         projectFiles.push( testFile );
165 
166         List<DefaultArtifactRepository> repos =
167             Collections.singletonList( new DefaultArtifactRepository( "central", getRemoteRepositoryURL(), new DefaultRepositoryLayout() ) );
168 
169         Repository repository = new Repository();
170         repository.setId( "central" );
171         repository.setUrl( getRemoteRepositoryURL() );
172 
173         ProfileManager profileManager = new DefaultProfileManager( getContainer() );
174         Profile profile = new Profile();
175         profile.setId( "profile" );
176         profile.addRepository( repository );
177         profileManager.addProfile( profile );
178         profileManager.activateAsDefault( profile.getId() );
179 
180         List<MavenProject> reactorProjects = new ArrayList<MavenProject>();
181         while ( !projectFiles.isEmpty() )
182         {
183             File file = (File) projectFiles.pop();
184 
185             // Recopy the test resources since they are modified in some tests
186             String filePath = file.getPath();
187             int index = filePath.indexOf( "test-classes" ) + "test-classes".length() + 1;
188             filePath = filePath.substring( index ).replace( '\\', '/' );
189 
190             File newFile = getTestFile( "target/test-classes/" + StringUtils.replace( filePath, path, targetPath ) );
191             FileUtils.copyFile( getTestFile( "src/test/resources/" + filePath ), newFile );
192 
193             MavenProject project = projectBuilder.build( newFile, localRepository, profileManager );
194 
195             for ( Iterator i = project.getModules().iterator(); i.hasNext(); )
196             {
197                 String module = (String) i.next();
198 
199                 projectFiles.push( new File( file.getParentFile(), module + "/pom.xml" ) );
200             }
201 
202             reactorProjects.add( project );
203         }
204 
205         ProjectSorter sorter = new ProjectSorter( reactorProjects );
206 
207         reactorProjects = sorter.getSortedProjects();
208 
209         ArtifactFactory artifactFactory = (ArtifactFactory) lookup( ArtifactFactory.ROLE );
210         ArtifactCollector artifactCollector = (ArtifactCollector) lookup( ArtifactCollector.class.getName() );
211         ArtifactMetadataSource artifactMetadataSource = (ArtifactMetadataSource) lookup( ArtifactMetadataSource.ROLE );
212 
213         // pass back over and resolve dependencies - can't be done earlier as the order may not be correct
214         for ( Iterator i = reactorProjects.iterator(); i.hasNext(); )
215         {
216             MavenProject project = (MavenProject) i.next();
217 
218             project.setRemoteArtifactRepositories( repos );
219             project.setPluginArtifactRepositories( repos );
220 
221             Artifact projectArtifact = project.getArtifact();
222 
223             Map managedVersions = createManagedVersionMap(
224                 ArtifactUtils.versionlessKey( projectArtifact.getGroupId(), projectArtifact.getArtifactId() ),
225                 project.getDependencyManagement(), artifactFactory );
226 
227             project.setDependencyArtifacts( project.createArtifacts( artifactFactory, null, null ) );
228 
229             ArtifactResolutionResult result = artifactCollector.collect( project.getDependencyArtifacts(),
230                                                                          projectArtifact, managedVersions,
231                                                                          localRepository, repos, artifactMetadataSource,
232                                                                          null, Collections.EMPTY_LIST );
233 
234             project.setArtifacts( result.getArtifacts() );
235         }
236 
237         return reactorProjects;
238     }
239 
240     protected static Map<String,MavenProject> getProjectsAsMap( List<MavenProject> reactorProjects )
241     {
242         Map<String,MavenProject> map = new HashMap<String,MavenProject>();
243         for ( Iterator<MavenProject> i = reactorProjects.iterator(); i.hasNext(); )
244         {
245             MavenProject project = (MavenProject) i.next();
246 
247             map.put( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ), project );
248         }
249         return map;
250     }
251 
252     protected boolean comparePomFiles( List<MavenProject> reactorProjects )
253         throws IOException
254     {
255         return comparePomFiles( reactorProjects, true );
256     }
257 
258     protected boolean comparePomFiles( List<MavenProject> reactorProjects, boolean normalizeLineEndings )
259         throws IOException
260     {
261         comparePomFiles( reactorProjects, "", normalizeLineEndings );
262 
263         // TODO: return void since this is redundant
264         return true;
265     }
266 
267     protected void comparePomFiles( List<MavenProject> reactorProjects, String expectedFileSuffix )
268         throws IOException
269     {
270         comparePomFiles( reactorProjects, expectedFileSuffix, true );
271     }
272 
273     protected void comparePomFiles( List<MavenProject> reactorProjects, String expectedFileSuffix, boolean normalizeLineEndings )
274         throws IOException
275     {
276         for ( Iterator<MavenProject> i = reactorProjects.iterator(); i.hasNext(); )
277         {
278             MavenProject project = i.next();
279 
280             comparePomFiles( project, expectedFileSuffix, normalizeLineEndings );
281         }
282     }
283 
284     protected void comparePomFiles( MavenProject project, String expectedFileSuffix )
285         throws IOException
286     {
287         comparePomFiles( project, expectedFileSuffix, true );
288     }
289 
290     protected void comparePomFiles( MavenProject project, String expectedFileSuffix, boolean normalizeLineEndings )
291         throws IOException
292     {
293         File actualFile = project.getFile();
294         File expectedFile = new File( actualFile.getParentFile(), "expected-pom" + expectedFileSuffix + ".xml" );
295 
296         comparePomFiles( expectedFile, actualFile, normalizeLineEndings );
297     }
298 
299     protected void comparePomFiles( File expectedFile, File actualFile )
300         throws IOException
301     {
302         comparePomFiles( expectedFile, actualFile, true );
303     }
304 
305     protected void comparePomFiles( File expectedFile, File actualFile, boolean normalizeLineEndings )
306         throws IOException
307     {
308         Reader expected = null;
309         Reader actual = null;
310         try
311         {
312             expected = ReaderFactory.newXmlReader( expectedFile );
313             actual = ReaderFactory.newXmlReader( actualFile );
314             
315             StringBuffer sb = new StringBuffer( "Check the transformed POM " + actualFile );
316             sb.append( SystemUtils.LINE_SEPARATOR );
317             
318             final String remoteRepositoryURL = getRemoteRepositoryURL();
319 
320             XMLUnit.setNormalizeWhitespace( true );
321             
322             Diff diff = XMLUnit.compareXML( expected, actual );
323             
324             diff.overrideDifferenceListener( new DifferenceListener()
325             {
326                 
327                 public void skippedComparison( Node arg0, Node arg1 )
328                 {
329                     //do nothing
330                 }
331                 
332                 public int differenceFound( Difference difference )
333                 {
334                     if ( "${remoterepo}".equals( difference.getControlNodeDetail().getValue() ) &&
335                                     remoteRepositoryURL.equals( difference.getTestNodeDetail().getValue() ) )
336                     {
337                         return DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
338                     }
339                     else 
340                     {
341                         return DifferenceListener.RETURN_ACCEPT_DIFFERENCE;
342                     }
343                 }
344             });
345             diff.appendMessage( sb );
346             
347             XMLAssert.assertXMLIdentical( diff, true );
348         }
349         catch ( SAXException e )
350         {
351             fail( e.getMessage() );
352         }
353         finally 
354         {
355             IOUtil.close( expected );
356             IOUtil.close( actual );
357         }
358     }
359 
360     private String getRemoteRepositoryURL()
361       throws IOException
362     {
363         File testFile = getTestFile( "src/test/remote-repository" );
364         if (testFile.getAbsolutePath().equals( testFile.getCanonicalPath() ) )
365         {
366             return "file://" + getTestFile( "src/test/remote-repository" ).getAbsolutePath().replace( '\\', '/' );
367         }
368         return "file://" + getTestFile( "src/test/remote-repository" ).getCanonicalPath().replace( '\\', '/' );
369     }
370     
371     public static String getPath( File file )
372         throws IOException
373     {
374         return ReleaseUtil.isSymlink( file ) ? file.getCanonicalPath() : file.getAbsolutePath();
375     }    
376 }