View Javadoc

1   package org.apache.maven.shared.dependency.tree;
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.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.LinkedHashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import junit.framework.AssertionFailedError;
33  
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.artifact.DefaultArtifact;
36  import org.apache.maven.artifact.factory.ArtifactFactory;
37  import org.apache.maven.artifact.handler.DefaultArtifactHandler;
38  import org.apache.maven.artifact.repository.ArtifactRepository;
39  import org.apache.maven.artifact.repository.DefaultArtifactRepository;
40  import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
41  import org.apache.maven.artifact.resolver.ArtifactCollector;
42  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
43  import org.apache.maven.artifact.resolver.ResolutionNode;
44  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
45  import org.apache.maven.artifact.versioning.ArtifactVersion;
46  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
47  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
48  import org.apache.maven.artifact.versioning.VersionRange;
49  import org.apache.maven.project.MavenProject;
50  import org.codehaus.plexus.PlexusTestCase;
51  
52  /**
53   * Tests <code>DefaultDependencyTreeBuilder</code>.
54   * 
55   * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
56   * @version $Id: DefaultDependencyTreeBuilderTest.java 1100703 2011-05-08 08:27:33Z hboutemy $
57   * @see DefaultDependencyTreeBuilder
58   */
59  public class DefaultDependencyTreeBuilderTest
60      extends PlexusTestCase
61  {
62      // fields -----------------------------------------------------------------
63  
64      private DefaultDependencyTreeBuilder builder;
65  
66      private ArtifactRepository artifactRepository;
67  
68      private ArtifactFactory artifactFactory;
69  
70      private ArtifactMetadataSourceStub artifactMetadataSource;
71  
72      private ArtifactCollector artifactCollector;
73  
74      // TestCase methods -------------------------------------------------------
75  
76      /**
77       * {@inheritDoc}
78       */
79      protected void setUp()
80          throws Exception
81      {
82          super.setUp();
83  
84          builder = (DefaultDependencyTreeBuilder) lookup( DependencyTreeBuilder.ROLE );
85  
86          String repositoryURL = getTestFile( "target/local-repo" ).toURI().toString();
87          artifactRepository = new DefaultArtifactRepository( "local", repositoryURL, new DefaultRepositoryLayout() );
88  
89          artifactFactory = (ArtifactFactory) lookup( ArtifactFactory.ROLE );
90          artifactMetadataSource = new ArtifactMetadataSourceStub();
91          artifactCollector = (ArtifactCollector) lookup( ArtifactCollector.class.getName() );
92      }
93  
94      /**
95       * {@inheritDoc}
96       */
97      protected void tearDown()
98          throws Exception
99      {
100         super.tearDown();
101 
102         builder = null;
103     }
104 
105     // tests ------------------------------------------------------------------
106 
107     /**
108      * Tests building a tree for a project with one dependency:
109      * 
110      * <pre>
111      * g:p:t:1
112      * \- g:a:t:1
113      * </pre>
114      * 
115      * @throws DependencyTreeBuilderException 
116      */
117     public void testProjectWithDependency()
118         throws DependencyTreeBuilderException
119     {
120         Artifact projectArtifact = createArtifact( "g:p:t:1" );
121         Artifact childArtifact = createArtifact( "g:a:t:1" );
122 
123         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
124 
125         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
126         expectedRootNode.addChild( createNode( "g:a:t:1" ) );
127 
128         assertDependencyTree( expectedRootNode, project );
129     }
130 
131     /**
132      * Tests building a tree for a project with one transitive dependency:
133      * 
134      * <pre>
135      * g:p:t:1
136      * \- g:a:t:1
137      *    \- g:b:t:1
138      * </pre>
139      *
140      * @throws DependencyTreeBuilderException
141      */
142     public void testProjectWithTransitiveDependency()
143         throws DependencyTreeBuilderException
144     {
145         Artifact projectArtifact = createArtifact( "g:p:t:1" );
146         Artifact childArtifact = createArtifact( "g:a:t:1" );
147         Artifact transitiveArtifact = createArtifact( "g:b:t:1" );
148         addArtifactMetadata( childArtifact, transitiveArtifact );
149 
150         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
151 
152         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
153         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
154         expectedRootNode.addChild( childArtifactNode );
155         childArtifactNode.addChild( createNode( "g:b:t:1" ) );
156 
157         assertDependencyTree( expectedRootNode, project );
158     }
159 
160     /**
161      * Tests building a tree for a project with a duplicate transitive dependency:
162      * 
163      * <pre>
164      * g:p:t:1
165      * +- g:a:t:1
166      * |  \- g:c:t:1
167      * \- g:b:t:1
168      *    \- (g:c:t:1 - omitted for duplicate)
169      * </pre>
170      *
171      * @throws DependencyTreeBuilderException
172      */
173     public void testProjectWithDuplicateDependency()
174         throws DependencyTreeBuilderException
175     {
176         Artifact projectArtifact = createArtifact( "g:p:t:1" );
177         Artifact child1Artifact = createArtifact( "g:a:t:1" );
178         Artifact transitiveArtifact = createArtifact( "g:c:t:1" );
179         Artifact child2Artifact = createArtifact( "g:b:t:1" );
180         Artifact duplicateTransitiveArtifact = createArtifact( "g:c:t:1" );
181         addArtifactMetadata( child1Artifact, transitiveArtifact );
182         addArtifactMetadata( child2Artifact, duplicateTransitiveArtifact );
183 
184         MavenProject project = createProject( projectArtifact, new Artifact[] { child1Artifact, child2Artifact } );
185 
186         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
187         DependencyNode child1ArtifactNode = createNode( "g:a:t:1" );
188         expectedRootNode.addChild( child1ArtifactNode );
189         DependencyNode transitiveArtifactNode = createNode( "g:c:t:1" );
190         child1ArtifactNode.addChild( transitiveArtifactNode );
191         DependencyNode child2ArtifactNode = createNode( "g:b:t:1" );
192         expectedRootNode.addChild( child2ArtifactNode );
193         child2ArtifactNode.addChild( createNode( "g:c:t:1", DependencyNode.OMITTED_FOR_DUPLICATE, transitiveArtifactNode.getArtifact() ) );
194 
195         assertDependencyTree( expectedRootNode, project );
196     }
197 
198     /**
199      * Tests building a tree for a project with a dependency that has conflicting versions, where the nearest is
200      * encountered first:
201      * 
202      * <pre>
203      * g:p:t:1
204      * +- g:a:t:1
205      * \- g:b:t:1
206      *    \- (g:a:t:2 - omitted for conflict with 1)
207      * </pre>
208      *
209      * @throws DependencyTreeBuilderException
210      */
211     public void testProjectWithConflictDependencyVersionFirstWins()
212         throws DependencyTreeBuilderException
213     {
214         Artifact projectArtifact = createArtifact( "g:p:t:1" );
215         Artifact nearestArtifact = createArtifact( "g:a:t:1" );
216         Artifact childArtifact = createArtifact( "g:b:t:1" );
217         Artifact farthestArtifact = createArtifact( "g:a:t:2" );
218         addArtifactMetadata( childArtifact, farthestArtifact );
219 
220         MavenProject project = createProject( projectArtifact, new Artifact[] { nearestArtifact, childArtifact } );
221 
222         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
223         DependencyNode nearestArtifactNode = createNode( "g:a:t:1" );
224         expectedRootNode.addChild( nearestArtifactNode );
225         DependencyNode childArtifactNode = createNode( "g:b:t:1" );
226         expectedRootNode.addChild( childArtifactNode );
227         childArtifactNode.addChild( createNode( "g:a:t:2", DependencyNode.OMITTED_FOR_CONFLICT, nearestArtifactNode.getArtifact() ) );
228 
229         assertDependencyTree( expectedRootNode, project );
230     }
231 
232     /**
233      * Tests building a tree for a project with a dependency that has conflicting versions, where the nearest is
234      * encountered last:
235      * 
236      * <pre>
237      * g:p:t:1
238      * +- g:a:t:1
239      * |  \- (g:b:t:2 - omitted for conflict with 1)
240      * \- g:b:t:1
241      * </pre>
242      *
243      * @throws DependencyTreeBuilderException
244      */
245     public void testProjectWithConflictDependencyVersionLastWins()
246         throws DependencyTreeBuilderException
247     {
248         Artifact projectArtifact = createArtifact( "g:p:t:1" );
249         Artifact childArtifact = createArtifact( "g:a:t:1" );
250         Artifact farthestArtifact = createArtifact( "g:b:t:2" );
251         Artifact nearestArtifact = createArtifact( "g:b:t:1" );
252         addArtifactMetadata( childArtifact, farthestArtifact );
253 
254         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact, nearestArtifact } );
255 
256         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
257         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
258         expectedRootNode.addChild( childArtifactNode );
259         DependencyNode farthestArtifactNode = createNode( "g:b:t:1" );
260         expectedRootNode.addChild( farthestArtifactNode );
261         childArtifactNode.addChild( createNode( "g:b:t:2", DependencyNode.OMITTED_FOR_CONFLICT, farthestArtifactNode.getArtifact() ) );
262 
263         assertDependencyTree( expectedRootNode, project );
264     }
265 
266     /**
267      * Tests building a tree for a project with a dependency that has conflicting scopes, where the nearest is not
268      * broadened since it is defined in the top-level POM:
269      * 
270      * <pre>
271      * g:p:t:1
272      * +- g:b:t:1:test (scope not updated to compile)
273      * \- g:a:t:1
274      *    \- (g:b:t:1:compile - omitted for duplicate)
275      * </pre>
276      *
277      * @throws DependencyTreeBuilderException
278      */
279     public void testProjectWithConflictDependencyScopeCurrentPom()
280         throws DependencyTreeBuilderException
281     {
282         Artifact projectArtifact = createArtifact( "g:p:t:1" );
283         Artifact nearestArtifact = createArtifact( "g:b:t:1:test" );
284         Artifact childArtifact = createArtifact( "g:a:t:1" );
285         Artifact farthestArtifact = createArtifact( "g:b:t:1:compile" );
286         addArtifactMetadata( childArtifact, farthestArtifact );
287 
288         MavenProject project = createProject( projectArtifact, new Artifact[] { nearestArtifact, childArtifact } );
289 
290         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
291         DependencyNode nearestArtifactNode = createNode( "g:b:t:1:test" );
292         nearestArtifactNode.setFailedUpdateScope( "compile" );
293         expectedRootNode.addChild( nearestArtifactNode );
294         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
295         expectedRootNode.addChild( childArtifactNode );
296         childArtifactNode.addChild( createNode( "g:b:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE, nearestArtifactNode.getArtifact() ) );
297         
298         assertDependencyTree( expectedRootNode, project );
299     }
300 
301     /**
302      * Tests building a tree for a project with a dependency that has conflicting scopes, where the winner is
303      * encountered first:
304      * 
305      * <pre>
306      * g:p:t:1
307      * \- g:a:t:1
308      *    +- g:b:t:1
309      *    |  \- g:c:t:1:compile
310      *    \- (g:c:t:1:compile - scope updated from test; omitted for duplicate)
311      * </pre>
312      *
313      * @throws DependencyTreeBuilderException
314      */
315     public void testProjectWithConflictDependencyScopeFirstWins()
316         throws DependencyTreeBuilderException
317     {
318         Artifact projectArtifact = createArtifact( "g:p:t:1" );
319         Artifact childArtifact = createArtifact( "g:a:t:1" );
320         Artifact grandchildArtifact = createArtifact( "g:b:t:1" );
321         Artifact farthestArtifact = createArtifact( "g:c:t:1:compile" );
322         Artifact nearestArtifact = createArtifact( "g:c:t:1:test" );
323         addArtifactMetadata( childArtifact, new Artifact[] { grandchildArtifact, nearestArtifact } );
324         addArtifactMetadata( grandchildArtifact, farthestArtifact );
325 
326         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
327 
328         /*
329          * TODO: Not entirely convinced that the expected tree is correct - I would have expected:
330          * 
331          * <pre>
332          * g:p:t:1
333          * \- g:a:t:1
334          *    +- g:b:t:1
335          *    |  \- (g:c:t:1:compile - omitted for duplicate)
336          *    \- g:c:t:1:compile (scope updated from test)
337          * </pre>
338          * 
339          * @see http://www.mail-archive.com/dev@maven.apache.org/msg68011.html
340          */
341         /*
342         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
343         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
344         expectedRootNode.addChild( childArtifactNode );
345         DependencyNode grandchildArtifactNode = createNode( "g:b:t:1" );
346         childArtifactNode.addChild( grandchildArtifactNode );
347         DependencyNode nearestArtifactNode = createNode( "g:c:t:1:compile" );
348         DependencyNode farthestArtifactNode = createNode( "g:c:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE, nearestArtifactNode.getArtifact() );
349         grandchildArtifactNode.addChild( farthestArtifactNode );
350         nearestArtifactNode.setOriginalScope( "test" );
351         childArtifactNode.addChild( nearestArtifactNode );
352         */
353 
354         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
355         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
356         expectedRootNode.addChild( childArtifactNode );
357         DependencyNode grandchildArtifactNode = createNode( "g:b:t:1" );
358         childArtifactNode.addChild( grandchildArtifactNode );
359         DependencyNode farthestArtifactNode = createNode( "g:c:t:1:compile" );
360         grandchildArtifactNode.addChild( farthestArtifactNode );
361         DependencyNode nearestArtifactNode = createNode( "g:c:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE, farthestArtifactNode.getArtifact() );
362         nearestArtifactNode.setOriginalScope( "test" );
363         childArtifactNode.addChild( nearestArtifactNode );
364         
365         assertDependencyTree( expectedRootNode, project );
366     }
367 
368     /**
369      * Tests building a tree for a project with a dependency that has conflicting scopes, where the winner is
370      * encountered last:
371      * 
372      * <pre>
373      * g:p:t:1
374      * \- g:a:t:1
375      *    +- (g:c:t:1:compile - scope updated from test; omitted for duplicate)
376      *    \- g:b:t:1
377      *       \- g:c:t:1:compile
378      * </pre>
379      *
380      * @throws DependencyTreeBuilderException
381      */
382     public void testProjectWithConflictDependencyScopeLastWins()
383         throws DependencyTreeBuilderException
384     {
385         Artifact projectArtifact = createArtifact( "g:p:t:1" );
386         Artifact childArtifact = createArtifact( "g:a:t:1" );
387         Artifact nearestArtifact = createArtifact( "g:c:t:1:test" );
388         Artifact grandchildArtifact = createArtifact( "g:b:t:1" );
389         Artifact farthestArtifact = createArtifact( "g:c:t:1:compile" );
390         addArtifactMetadata( childArtifact, new Artifact[] { nearestArtifact, grandchildArtifact } );
391         addArtifactMetadata( grandchildArtifact, farthestArtifact );
392 
393         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
394 
395         /*
396          * TODO: Not entirely convinced that the expected tree is correct - I would have expected:
397          * 
398          * <pre>
399          * g:p:t:1
400          * \- g:a:t:1
401          *    +- g:c:t:1:compile (scope updated from test)
402          *    \- g:b:t:1
403          *       \- (g:c:t:1:compile - omitted for duplicate)
404          * </pre>
405          * 
406          * @see http://www.mail-archive.com/dev@maven.apache.org/msg68011.html
407          */
408         /*
409         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
410         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
411         expectedRootNode.addChild( childArtifactNode );
412         DependencyNode nearestArtifactNode = createNode( "g:c:t:1:compile" );
413         nearestArtifactNode.setOriginalScope( "test" );
414         childArtifactNode.addChild( nearestArtifactNode );
415         DependencyNode grandchildArtifactNode = createNode( "g:b:t:1" );
416         childArtifactNode.addChild( grandchildArtifactNode );
417         grandchildArtifactNode.addChild( createNode( "g:c:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE, nearestArtifactNode.getArtifact() ) );
418         */
419         
420         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
421         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
422         expectedRootNode.addChild( childArtifactNode );
423         DependencyNode farthestArtifactNode = createNode( "g:c:t:1:compile" );
424         DependencyNode nearestArtifactNode = createNode( "g:c:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE, farthestArtifactNode.getArtifact() );
425         nearestArtifactNode.setOriginalScope( "test" );
426         childArtifactNode.addChild( nearestArtifactNode );
427         DependencyNode grandchildArtifactNode = createNode( "g:b:t:1" );
428         childArtifactNode.addChild( grandchildArtifactNode );
429         grandchildArtifactNode.addChild( farthestArtifactNode );
430         
431         assertDependencyTree( expectedRootNode, project );
432     }
433 
434     /**
435      * Tests building a tree for a project with one transitive dependency whose version is fixed in dependency
436      * management:
437      * 
438      * <pre>
439      * g:p:t:1
440      * \- g:a:t:1
441      *    \- g:b:t:2 (version managed from 1)
442      * </pre>
443      * 
444      * @throws DependencyTreeBuilderException
445      */
446     public void testProjectWithManagedTransitiveDependencyVersion()
447         throws DependencyTreeBuilderException
448     {
449         Artifact projectArtifact = createArtifact( "g:p:t:1" );
450         Artifact childArtifact = createArtifact( "g:a:t:1" );
451         Artifact transitiveArtifact = createArtifact( "g:b:t:1" );
452         Artifact managedTransitiveArtifact = createArtifact( "g:b:t:2" );
453         addArtifactMetadata( childArtifact, transitiveArtifact );
454 
455         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
456         setManagedVersionMap( project, Collections.singleton( managedTransitiveArtifact ) );
457 
458         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
459         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
460         expectedRootNode.addChild( childArtifactNode );
461         DependencyNode managedTransitiveArtifactNode = createNode( "g:b:t:2" );
462         managedTransitiveArtifactNode.setPremanagedVersion( "1" );
463         childArtifactNode.addChild( managedTransitiveArtifactNode );
464 
465         assertDependencyTree( expectedRootNode, project );
466     }
467 
468     // TODO: see MNG-3548
469     /**
470      * Tests building a tree for a project with a dependency that is duplicated and the version is also fixed in 
471      * dependency management:
472      * 
473      * <pre>
474      * g:p:t:1
475      * \- g:a:t:1
476      *    +- g:b:t:1
477      *    |  \- (g:c:t:2 - version managed from 1; omitted for duplicate)
478      *    \- g:c:t:2 (version managed from 1)
479      * </pre>
480      * 
481      * @throws DependencyTreeBuilderException
482      */
483     /*
484     public void testProjectWithManagedTransitiveDependencyVersionAndDuplicate() throws DependencyTreeBuilderException
485     {
486         Artifact projectArtifact = createArtifact( "g:p:t:1" );
487         Artifact childArtifact = createArtifact( "g:a:t:1" );
488         Artifact grandchildArtifact = createArtifact( "g:b:t:1" );
489         Artifact farthestTransitiveArtifact = createArtifact( "g:c:t:1" );
490         Artifact nearestTransitiveArtifact = createArtifact( "g:c:t:1" );
491         Artifact managedTransitiveArtifact = createArtifact( "g:c:t:2" );
492         addArtifactMetadata( childArtifact, new Artifact[] { grandchildArtifact, nearestTransitiveArtifact } );
493         addArtifactMetadata( grandchildArtifact, farthestTransitiveArtifact );
494 
495         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
496         setManagedVersionMap( project, Collections.singleton( managedTransitiveArtifact ) );
497 
498         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
499         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
500         expectedRootNode.addChild( childArtifactNode );
501         DependencyNode grandchildArtifactNode = createNode( "g:b:t:2" );
502         childArtifactNode.addChild( grandchildArtifactNode );
503         DependencyNode managedTransitiveArtifactNode = createNode( "g:c:t:2" );
504         managedTransitiveArtifactNode.setPremanagedVersion( "1" );
505         childArtifactNode.addChild( managedTransitiveArtifactNode );
506         DependencyNode omittedManagedTransitiveArtifactNode = createNode( "g:c:t:2" );
507         omittedManagedTransitiveArtifactNode.setPremanagedVersion( "1" );
508         omittedManagedTransitiveArtifactNode.omitForConflict( managedTransitiveArtifact );
509         grandchildArtifactNode.addChild( omittedManagedTransitiveArtifactNode );
510 
511         assertDependencyTree( expectedRootNode, project );
512     }
513     */
514 
515     /**
516      * Tests building a tree for a project with one transitive dependency whose scope is fixed in dependency management:
517      * 
518      * <pre>
519      * g:p:t:1
520      * \- g:a:t:1
521      *    \- g:b:t:1:test (scope managed from compile)
522      * </pre>
523      *
524      * @throws DependencyTreeBuilderException
525      */
526     public void testProjectWithManagedTransitiveDependencyScope()
527         throws DependencyTreeBuilderException
528     {
529         Artifact projectArtifact = createArtifact( "g:p:t:1" );
530         Artifact childArtifact = createArtifact( "g:a:t:1" );
531         Artifact transitiveArtifact = createArtifact( "g:b:t:1:compile" );
532         Artifact managedTransitiveArtifact = createArtifact( "g:b:t:1:test" );
533         addArtifactMetadata( childArtifact, transitiveArtifact );
534 
535         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
536         setManagedVersionMap( project, Collections.singleton( managedTransitiveArtifact ) );
537 
538         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
539         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
540         expectedRootNode.addChild( childArtifactNode );
541         DependencyNode managedTransitiveArtifactNode = createNode( "g:b:t:1:test" );
542         managedTransitiveArtifactNode.setPremanagedScope( "compile" );
543         childArtifactNode.addChild( managedTransitiveArtifactNode );
544 
545         assertDependencyTree( expectedRootNode, project );
546     }
547 
548     /**
549      * Tests building a tree for a project with one transitive dependency whose version and scope are fixed in
550      * dependency management:
551      * 
552      * <pre>
553      * g:p:t:1
554      * \- g:a:t:1
555      *    \- g:b:t:2:test (version managed from 1; scope managed from compile)
556      * </pre>
557      * 
558      * @throws DependencyTreeBuilderException
559      */
560     public void testProjectWithManagedTransitiveDependencyVersionAndScope()
561         throws DependencyTreeBuilderException
562     {
563         Artifact projectArtifact = createArtifact( "g:p:t:1" );
564         Artifact childArtifact = createArtifact( "g:a:t:1" );
565         Artifact transitiveArtifact = createArtifact( "g:b:t:1:compile" );
566         Artifact managedTransitiveArtifact = createArtifact( "g:b:t:2:test" );
567         addArtifactMetadata( childArtifact, transitiveArtifact );
568 
569         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
570         setManagedVersionMap( project, Collections.singleton( managedTransitiveArtifact ) );
571 
572         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
573         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
574         expectedRootNode.addChild( childArtifactNode );
575         DependencyNode managedTransitiveArtifactNode = createNode( "g:b:t:2:test" );
576         managedTransitiveArtifactNode.setPremanagedVersion( "1" );
577         managedTransitiveArtifactNode.setPremanagedScope( "compile" );
578         childArtifactNode.addChild( managedTransitiveArtifactNode );
579 
580         assertDependencyTree( expectedRootNode, project );
581     }
582     
583     /**
584      * Tests building a tree for a project with a dependency that has conflicting versions and the version is also fixed
585      * in dependency management:
586      * 
587      * <pre>
588      * g:p:t:1
589      * +- g:a:t:1
590      * \- g:b:t:1
591      *    \- (g:a:t:3 - version managed from 2; omitted for conflict with 1)
592      * </pre>
593      * 
594      * @throws DependencyTreeBuilderException
595      */
596     public void testProjectWithManagedTransitiveDependencyVersionAndConflictDependencyVersion()
597         throws DependencyTreeBuilderException
598     {
599         Artifact projectArtifact = createArtifact( "g:p:t:1" );
600         Artifact nearestArtifact = createArtifact( "g:a:t:1" );
601         Artifact childArtifact = createArtifact( "g:b:t:1" );
602         Artifact farthestArtifact = createArtifact( "g:a:t:2" );
603         Artifact managedTransitiveArtifact = createArtifact( "g:a:t:3" );
604         addArtifactMetadata( childArtifact, farthestArtifact );
605 
606         MavenProject project = createProject( projectArtifact, new Artifact[] { nearestArtifact, childArtifact } );
607         setManagedVersionMap( project, Collections.singleton( managedTransitiveArtifact ) );
608 
609         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
610         DependencyNode nearestArtifactNode = createNode( "g:a:t:1" );
611         expectedRootNode.addChild( nearestArtifactNode );
612         DependencyNode childArtifactNode = createNode( "g:b:t:1" );
613         expectedRootNode.addChild( childArtifactNode );
614         DependencyNode managedTransitiveArtifactNode = createNode( "g:a:t:3", DependencyNode.OMITTED_FOR_CONFLICT, nearestArtifactNode.getArtifact() );
615         managedTransitiveArtifactNode.setPremanagedVersion( "2" );
616         childArtifactNode.addChild( managedTransitiveArtifactNode );
617 
618         assertDependencyTree( expectedRootNode, project );
619     }
620 
621     
622     /**
623      * Tests building a tree for a project with a dependency with version range
624      * 
625      * <pre>
626      * g:p:t:1
627      * \- g:a:t:1
628      * </pre>
629      * 
630      * @throws InvalidVersionSpecificationException 
631      * @throws DependencyTreeBuilderException 
632      */
633     public void testProjectWithVersionRange()
634         throws InvalidVersionSpecificationException, DependencyTreeBuilderException
635     {
636         String range = "[1,2)";
637         Artifact projectArtifact = createArtifact( "g:p:t:1" );
638         Artifact childArtifact = createArtifact( "g:a:t:" + range);
639 
640         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
641 
642         ArtifactVersion version = new DefaultArtifactVersion( "1.0" );
643         List<ArtifactVersion> availableVersions = new ArrayList<ArtifactVersion>();
644         availableVersions.add( version );
645 
646         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
647         DependencyNode childNode = createNode( "g:a:t:1.0" );
648         childNode.setAvailableVersions( availableVersions );
649         childNode.setVersionSelectedFromRange( VersionRange.createFromVersionSpec( range ) );
650         expectedRootNode.addChild( childNode );
651 
652         artifactMetadataSource.addAvailableVersions( childArtifact, availableVersions );
653         assertDependencyTree( expectedRootNode, project );
654     }
655 
656     // TODO: reinstate when MNG-3236 fixed
657     /*
658     public void testProjectWithFilter() throws DependencyTreeBuilderException, ArtifactResolutionException
659     {
660         Artifact projectArtifact = createArtifact( "g:p:t:1" );
661         Artifact child1Artifact = createArtifact( "g:a:t:1" );
662         Artifact child2Artifact = createArtifact( "g:b:t:1:test" );
663 
664         MavenProject project = createProject( projectArtifact, new Artifact[] { child1Artifact, child2Artifact } );
665 
666         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
667         expectedRootNode.addChild( createNode( "g:a:t:1" ) );
668 
669         ArtifactFilter artifactFilter = new ScopeArtifactFilter( Artifact.SCOPE_COMPILE );
670         
671         assertDependencyTree( expectedRootNode, project, artifactFilter );
672     }
673     */
674 
675     // private methods --------------------------------------------------------
676     
677     private DependencyNode createNode( String id )
678     {
679         return createNode( id, DependencyNode.INCLUDED, null );
680     }
681     
682     private DependencyNode createNode( String id, int state, Artifact relatedArtifact )
683     {
684         return new DependencyNode( createArtifact( id ), state, relatedArtifact );
685     }
686     
687     private Artifact createArtifact( String id )
688     {
689         String[] tokens = id.split( ":" );
690 
691         String groupId = get( tokens, 0 );
692         String artifactId = get( tokens, 1 );
693         String type = get( tokens, 2, "jar" );
694         String version = get( tokens, 3 );
695         String scope = get( tokens, 4 );
696         
697         VersionRange versionRange;
698         try
699         {
700             versionRange = VersionRange.createFromVersionSpec( version );
701         }
702         catch ( InvalidVersionSpecificationException e )
703         {
704             throw new RuntimeException(e);
705         }
706 
707         return new DefaultArtifact( groupId, artifactId, versionRange, scope, type, null, new DefaultArtifactHandler() );
708     }
709     
710     private MavenProject createProject( Artifact projectArtifact, Artifact[] dependencyArtifacts )
711     {
712         MavenProject project = new MavenProject();
713         project.setArtifact( projectArtifact );
714         // LinkedHashSet since order is significant when omitting conflicts
715         project.setDependencyArtifacts( new LinkedHashSet<Artifact>( Arrays.asList( dependencyArtifacts ) ) );
716         project.setManagedVersionMap( new HashMap<String, Artifact>() );
717         project.setRemoteArtifactRepositories( Collections.EMPTY_LIST );
718         return project;
719     }
720 
721     private void addArtifactMetadata( Artifact artifact, Artifact dependencyArtifact )
722     {
723         addArtifactMetadata( artifact, new Artifact[] { dependencyArtifact } );
724     }
725     
726     private void addArtifactMetadata( Artifact artifact, Artifact[] dependencyArtifacts )
727     {
728         addArtifactMetadata( artifact, new LinkedHashSet<Artifact>( Arrays.asList( dependencyArtifacts ) ) );
729     }
730     
731     private void addArtifactMetadata( Artifact artifact, Set<Artifact> dependencyArtifacts )
732     {
733         artifactMetadataSource.addArtifactMetadata( artifact, dependencyArtifacts );
734     }
735     
736     private void setManagedVersionMap( MavenProject project, Set<Artifact> managedArtifacts )
737     {
738         Map<String, Artifact> managedVersionMap = new HashMap<String, Artifact>();
739         
740         for ( Artifact artifact : managedArtifacts )
741         {
742             String managementKey = getManagementKey( artifact );
743             
744             managedVersionMap.put( managementKey, artifact );
745         }
746 
747         project.setManagedVersionMap( managedVersionMap );
748     }
749     
750     private String getManagementKey( Artifact artifact )
751     {
752         return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType()
753             + ( artifact.getClassifier() != null ? ":" + artifact.getClassifier() : "" );
754     }
755     
756     private void assertDependencyTree( DependencyNode expectedRootNode, MavenProject project )
757         throws DependencyTreeBuilderException
758     {
759         assertDependencyTree( expectedRootNode, project, null );
760     }
761     
762     private void assertDependencyTree( DependencyNode expectedRootNode, MavenProject project,
763                                        ArtifactFilter artifactFilter )
764         throws DependencyTreeBuilderException
765     {
766         // assert built dependency tree is as expected
767 
768         DependencyNode actualRootNode =
769             builder.buildDependencyTree( project, artifactRepository, artifactFactory, artifactMetadataSource,
770                                          artifactFilter, artifactCollector );
771 
772         assertEquals( "Dependency tree", expectedRootNode, actualRootNode );
773 
774         // assert resolution tree is as expected
775 
776         ArtifactResolutionResult result = builder.getArtifactResolutionResult();
777 
778         assertTreeEquals( expectedRootNode, project, result );
779     }
780     
781     private void assertTreeEquals( DependencyNode dependencyNode, MavenProject project,
782                                    ArtifactResolutionResult resolutionResult )
783     {
784         List<ResolutionNode> rootChildrenResolutionNodes =
785             ResolutionNodeUtils.getRootChildrenResolutionNodes( project, resolutionResult );
786 
787         try
788         {
789             assertEquals( "Root node artifact", dependencyNode.getArtifact(), project.getArtifact() );
790 
791             assertNodesEquals( dependencyNode.getChildren(), rootChildrenResolutionNodes );
792         }
793         catch ( AssertionFailedError error )
794         {
795             StringBuffer buffer = new StringBuffer();
796 
797             buffer.append( error.getMessage() ).append( "; " );
798             buffer.append( "expected dependency tree <" ).append( dependencyNode ).append( "> " );
799             buffer.append( "actual resolution tree <" );
800             ResolutionNodeUtils.append( buffer, project, resolutionResult );
801             buffer.append( ">" );
802 
803             throw new AssertionFailedError( buffer.toString() );
804         }
805     }
806     
807     private void assertNodesEquals( List<DependencyNode> dependencyNodes, List<ResolutionNode> resolutionNodes )
808     {
809         assertNodesEquals( dependencyNodes.iterator(), resolutionNodes.iterator() );
810     }
811 
812     private void assertNodesEquals( Iterator<DependencyNode> dependencyNodesIterator,
813                                     Iterator<ResolutionNode> resolutionNodesIterator )
814     {
815         while ( dependencyNodesIterator.hasNext() && resolutionNodesIterator.hasNext() )
816         {
817             DependencyNode dependencyNode = dependencyNodesIterator.next();
818             ResolutionNode resolutionNode = resolutionNodesIterator.next();
819             
820             assertNodeEquals( dependencyNode, resolutionNode );
821         }
822         
823         if ( dependencyNodesIterator.hasNext() || resolutionNodesIterator.hasNext() )
824         {
825             fail( "Node list size differs" );
826         }
827     }
828 
829     private void assertNodeEquals( DependencyNode dependencyNode, ResolutionNode resolutionNode )
830     {
831         assertEquals( "Node state", dependencyNode.getState() == DependencyNode.INCLUDED, resolutionNode.isActive() );
832         
833         assertEquals( "Node artifact", dependencyNode.getArtifact(), resolutionNode.getArtifact() );
834         
835         assertNodesEquals( dependencyNode.getChildren().iterator(), resolutionNode.getChildrenIterator() );
836     }
837     
838     private String get( String[] array, int index )
839     {
840         return get( array, index, null );
841     }
842     
843     private String get( String[] array, int index, String defaultValue )
844     {
845         return ( index < array.length ) ? array[index] : defaultValue;
846     }
847 }