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 1595642 2014-05-18 17:32:08Z jvanzyl $
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,
194                                                  transitiveArtifactNode.getArtifact() ) );
195 
196         assertDependencyTree( expectedRootNode, project );
197     }
198 
199     /**
200      * Tests building a tree for a project with a dependency that has conflicting versions, where the nearest is
201      * encountered first:
202      * 
203      * <pre>
204      * g:p:t:1
205      * +- g:a:t:1
206      * \- g:b:t:1
207      *    \- (g:a:t:2 - omitted for conflict with 1)
208      * </pre>
209      *
210      * @throws DependencyTreeBuilderException
211      */
212     public void testProjectWithConflictDependencyVersionFirstWins()
213         throws DependencyTreeBuilderException
214     {
215         Artifact projectArtifact = createArtifact( "g:p:t:1" );
216         Artifact nearestArtifact = createArtifact( "g:a:t:1" );
217         Artifact childArtifact = createArtifact( "g:b:t:1" );
218         Artifact farthestArtifact = createArtifact( "g:a:t:2" );
219         addArtifactMetadata( childArtifact, farthestArtifact );
220 
221         MavenProject project = createProject( projectArtifact, new Artifact[] { nearestArtifact, childArtifact } );
222 
223         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
224         DependencyNode nearestArtifactNode = createNode( "g:a:t:1" );
225         expectedRootNode.addChild( nearestArtifactNode );
226         DependencyNode childArtifactNode = createNode( "g:b:t:1" );
227         expectedRootNode.addChild( childArtifactNode );
228         childArtifactNode.addChild( createNode( "g:a:t:2", DependencyNode.OMITTED_FOR_CONFLICT,
229                                                 nearestArtifactNode.getArtifact() ) );
230 
231         assertDependencyTree( expectedRootNode, project );
232     }
233 
234     /**
235      * Tests building a tree for a project with a dependency that has conflicting versions, where the nearest is
236      * encountered last:
237      * 
238      * <pre>
239      * g:p:t:1
240      * +- g:a:t:1
241      * |  \- (g:b:t:2 - omitted for conflict with 1)
242      * \- g:b:t:1
243      * </pre>
244      *
245      * @throws DependencyTreeBuilderException
246      */
247     public void testProjectWithConflictDependencyVersionLastWins()
248         throws DependencyTreeBuilderException
249     {
250         Artifact projectArtifact = createArtifact( "g:p:t:1" );
251         Artifact childArtifact = createArtifact( "g:a:t:1" );
252         Artifact farthestArtifact = createArtifact( "g:b:t:2" );
253         Artifact nearestArtifact = createArtifact( "g:b:t:1" );
254         addArtifactMetadata( childArtifact, farthestArtifact );
255 
256         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact, nearestArtifact } );
257 
258         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
259         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
260         expectedRootNode.addChild( childArtifactNode );
261         DependencyNode farthestArtifactNode = createNode( "g:b:t:1" );
262         expectedRootNode.addChild( farthestArtifactNode );
263         childArtifactNode.addChild( createNode( "g:b:t:2", DependencyNode.OMITTED_FOR_CONFLICT,
264                                                 farthestArtifactNode.getArtifact() ) );
265 
266         assertDependencyTree( expectedRootNode, project );
267     }
268 
269     /**
270      * Tests building a tree for a project with a dependency that has conflicting scopes, where the nearest is not
271      * broadened since it is defined in the top-level POM:
272      * 
273      * <pre>
274      * g:p:t:1
275      * +- g:b:t:1:test (scope not updated to compile)
276      * \- g:a:t:1
277      *    \- (g:b:t:1:compile - omitted for duplicate)
278      * </pre>
279      *
280      * @throws DependencyTreeBuilderException
281      */
282     public void testProjectWithConflictDependencyScopeCurrentPom()
283         throws DependencyTreeBuilderException
284     {
285         Artifact projectArtifact = createArtifact( "g:p:t:1" );
286         Artifact nearestArtifact = createArtifact( "g:b:t:1:test" );
287         Artifact childArtifact = createArtifact( "g:a:t:1" );
288         Artifact farthestArtifact = createArtifact( "g:b:t:1:compile" );
289         addArtifactMetadata( childArtifact, farthestArtifact );
290 
291         MavenProject project = createProject( projectArtifact, new Artifact[] { nearestArtifact, childArtifact } );
292 
293         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
294         DependencyNode nearestArtifactNode = createNode( "g:b:t:1:test" );
295         nearestArtifactNode.setFailedUpdateScope( "compile" );
296         expectedRootNode.addChild( nearestArtifactNode );
297         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
298         expectedRootNode.addChild( childArtifactNode );
299         childArtifactNode.addChild( createNode( "g:b:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE,
300                                                 nearestArtifactNode.getArtifact() ) );
301 
302         assertDependencyTree( expectedRootNode, project );
303     }
304 
305     /**
306      * Tests building a tree for a project with a dependency that has conflicting scopes, where the winner is
307      * encountered first:
308      * 
309      * <pre>
310      * g:p:t:1
311      * \- g:a:t:1
312      *    +- g:b:t:1
313      *    |  \- g:c:t:1:compile
314      *    \- (g:c:t:1:compile - scope updated from test; omitted for duplicate)
315      * </pre>
316      *
317      * @throws DependencyTreeBuilderException
318      */
319     public void testProjectWithConflictDependencyScopeFirstWins()
320         throws DependencyTreeBuilderException
321     {
322         Artifact projectArtifact = createArtifact( "g:p:t:1" );
323         Artifact childArtifact = createArtifact( "g:a:t:1" );
324         Artifact grandchildArtifact = createArtifact( "g:b:t:1" );
325         Artifact farthestArtifact = createArtifact( "g:c:t:1:compile" );
326         Artifact nearestArtifact = createArtifact( "g:c:t:1:test" );
327         addArtifactMetadata( childArtifact, new Artifact[] { grandchildArtifact, nearestArtifact } );
328         addArtifactMetadata( grandchildArtifact, farthestArtifact );
329 
330         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
331 
332         /*
333          * TODO: Not entirely convinced that the expected tree is correct - I would have expected: <pre> g:p:t:1 \-
334          * g:a:t:1 +- g:b:t:1 | \- (g:c:t:1:compile - omitted for duplicate) \- g:c:t:1:compile (scope updated from
335          * test) </pre>
336          * @see http://www.mail-archive.com/dev@maven.apache.org/msg68011.html
337          */
338         /*
339          * DependencyNode expectedRootNode = createNode( "g:p:t:1" ); DependencyNode childArtifactNode = createNode(
340          * "g:a:t:1" ); expectedRootNode.addChild( childArtifactNode ); DependencyNode grandchildArtifactNode =
341          * createNode( "g:b:t:1" ); childArtifactNode.addChild( grandchildArtifactNode ); DependencyNode
342          * nearestArtifactNode = createNode( "g:c:t:1:compile" ); DependencyNode farthestArtifactNode = createNode(
343          * "g:c:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE, nearestArtifactNode.getArtifact() );
344          * grandchildArtifactNode.addChild( farthestArtifactNode ); nearestArtifactNode.setOriginalScope( "test" );
345          * childArtifactNode.addChild( nearestArtifactNode );
346          */
347 
348         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
349         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
350         expectedRootNode.addChild( childArtifactNode );
351         DependencyNode grandchildArtifactNode = createNode( "g:b:t:1" );
352         childArtifactNode.addChild( grandchildArtifactNode );
353         DependencyNode farthestArtifactNode = createNode( "g:c:t:1:compile" );
354         grandchildArtifactNode.addChild( farthestArtifactNode );
355         DependencyNode nearestArtifactNode =
356             createNode( "g:c:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE, farthestArtifactNode.getArtifact() );
357         nearestArtifactNode.setOriginalScope( "test" );
358         childArtifactNode.addChild( nearestArtifactNode );
359 
360         assertDependencyTree( expectedRootNode, project );
361     }
362 
363     /**
364      * Tests building a tree for a project with a dependency that has conflicting scopes, where the winner is
365      * encountered last:
366      * 
367      * <pre>
368      * g:p:t:1
369      * \- g:a:t:1
370      *    +- (g:c:t:1:compile - scope updated from test; omitted for duplicate)
371      *    \- g:b:t:1
372      *       \- g:c:t:1:compile
373      * </pre>
374      *
375      * @throws DependencyTreeBuilderException
376      */
377     public void testProjectWithConflictDependencyScopeLastWins()
378         throws DependencyTreeBuilderException
379     {
380         Artifact projectArtifact = createArtifact( "g:p:t:1" );
381         Artifact childArtifact = createArtifact( "g:a:t:1" );
382         Artifact nearestArtifact = createArtifact( "g:c:t:1:test" );
383         Artifact grandchildArtifact = createArtifact( "g:b:t:1" );
384         Artifact farthestArtifact = createArtifact( "g:c:t:1:compile" );
385         addArtifactMetadata( childArtifact, new Artifact[] { nearestArtifact, grandchildArtifact } );
386         addArtifactMetadata( grandchildArtifact, farthestArtifact );
387 
388         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
389 
390         /*
391          * TODO: Not entirely convinced that the expected tree is correct - I would have expected: <pre> g:p:t:1 \-
392          * g:a:t:1 +- g:c:t:1:compile (scope updated from test) \- g:b:t:1 \- (g:c:t:1:compile - omitted for duplicate)
393          * </pre>
394          * @see http://www.mail-archive.com/dev@maven.apache.org/msg68011.html
395          */
396         /*
397          * DependencyNode expectedRootNode = createNode( "g:p:t:1" ); DependencyNode childArtifactNode = createNode(
398          * "g:a:t:1" ); expectedRootNode.addChild( childArtifactNode ); DependencyNode nearestArtifactNode = createNode(
399          * "g:c:t:1:compile" ); nearestArtifactNode.setOriginalScope( "test" ); childArtifactNode.addChild(
400          * nearestArtifactNode ); DependencyNode grandchildArtifactNode = createNode( "g:b:t:1" );
401          * childArtifactNode.addChild( grandchildArtifactNode ); grandchildArtifactNode.addChild( createNode(
402          * "g:c:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE, nearestArtifactNode.getArtifact() ) );
403          */
404 
405         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
406         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
407         expectedRootNode.addChild( childArtifactNode );
408         DependencyNode farthestArtifactNode = createNode( "g:c:t:1:compile" );
409         DependencyNode nearestArtifactNode =
410             createNode( "g:c:t:1:compile", DependencyNode.OMITTED_FOR_DUPLICATE, farthestArtifactNode.getArtifact() );
411         nearestArtifactNode.setOriginalScope( "test" );
412         childArtifactNode.addChild( nearestArtifactNode );
413         DependencyNode grandchildArtifactNode = createNode( "g:b:t:1" );
414         childArtifactNode.addChild( grandchildArtifactNode );
415         grandchildArtifactNode.addChild( farthestArtifactNode );
416 
417         assertDependencyTree( expectedRootNode, project );
418     }
419 
420     /**
421      * Tests building a tree for a project with one transitive dependency whose version is fixed in dependency
422      * management:
423      * 
424      * <pre>
425      * g:p:t:1
426      * \- g:a:t:1
427      *    \- g:b:t:2 (version managed from 1)
428      * </pre>
429      * 
430      * @throws DependencyTreeBuilderException
431      */
432     public void testProjectWithManagedTransitiveDependencyVersion()
433         throws DependencyTreeBuilderException
434     {
435         Artifact projectArtifact = createArtifact( "g:p:t:1" );
436         Artifact childArtifact = createArtifact( "g:a:t:1" );
437         Artifact transitiveArtifact = createArtifact( "g:b:t:1" );
438         Artifact managedTransitiveArtifact = createArtifact( "g:b:t:2" );
439         addArtifactMetadata( childArtifact, transitiveArtifact );
440 
441         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
442         setManagedVersionMap( project, Collections.singleton( managedTransitiveArtifact ) );
443 
444         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
445         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
446         expectedRootNode.addChild( childArtifactNode );
447         DependencyNode managedTransitiveArtifactNode = createNode( "g:b:t:2" );
448         managedTransitiveArtifactNode.setPremanagedVersion( "1" );
449         childArtifactNode.addChild( managedTransitiveArtifactNode );
450 
451         assertDependencyTree( expectedRootNode, project );
452     }
453 
454     // TODO: see MNG-3548
455     /**
456      * Tests building a tree for a project with a dependency that is duplicated and the version is also fixed in
457      * dependency management:
458      * 
459      * <pre>
460      * g:p:t:1
461      * \- g:a:t:1
462      *    +- g:b:t:1
463      *    |  \- (g:c:t:2 - version managed from 1; omitted for duplicate)
464      *    \- g:c:t:2 (version managed from 1)
465      * </pre>
466      * 
467      * @throws DependencyTreeBuilderException
468      */
469     /*
470      * public void testProjectWithManagedTransitiveDependencyVersionAndDuplicate() throws DependencyTreeBuilderException
471      * { Artifact projectArtifact = createArtifact( "g:p:t:1" ); Artifact childArtifact = createArtifact( "g:a:t:1" );
472      * Artifact grandchildArtifact = createArtifact( "g:b:t:1" ); Artifact farthestTransitiveArtifact = createArtifact(
473      * "g:c:t:1" ); Artifact nearestTransitiveArtifact = createArtifact( "g:c:t:1" ); Artifact managedTransitiveArtifact
474      * = createArtifact( "g:c:t:2" ); addArtifactMetadata( childArtifact, new Artifact[] { grandchildArtifact,
475      * nearestTransitiveArtifact } ); addArtifactMetadata( grandchildArtifact, farthestTransitiveArtifact );
476      * MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } ); setManagedVersionMap(
477      * project, Collections.singleton( managedTransitiveArtifact ) ); DependencyNode expectedRootNode = createNode(
478      * "g:p:t:1" ); DependencyNode childArtifactNode = createNode( "g:a:t:1" ); expectedRootNode.addChild(
479      * childArtifactNode ); DependencyNode grandchildArtifactNode = createNode( "g:b:t:2" ); childArtifactNode.addChild(
480      * grandchildArtifactNode ); DependencyNode managedTransitiveArtifactNode = createNode( "g:c:t:2" );
481      * managedTransitiveArtifactNode.setPremanagedVersion( "1" ); childArtifactNode.addChild(
482      * managedTransitiveArtifactNode ); DependencyNode omittedManagedTransitiveArtifactNode = createNode( "g:c:t:2" );
483      * omittedManagedTransitiveArtifactNode.setPremanagedVersion( "1" );
484      * omittedManagedTransitiveArtifactNode.omitForConflict( managedTransitiveArtifact );
485      * grandchildArtifactNode.addChild( omittedManagedTransitiveArtifactNode ); assertDependencyTree( expectedRootNode,
486      * project ); }
487      */
488 
489     /**
490      * Tests building a tree for a project with one transitive dependency whose scope is fixed in dependency management:
491      * 
492      * <pre>
493      * g:p:t:1
494      * \- g:a:t:1
495      *    \- g:b:t:1:test (scope managed from compile)
496      * </pre>
497      *
498      * @throws DependencyTreeBuilderException
499      */
500     public void testProjectWithManagedTransitiveDependencyScope()
501         throws DependencyTreeBuilderException
502     {
503         Artifact projectArtifact = createArtifact( "g:p:t:1" );
504         Artifact childArtifact = createArtifact( "g:a:t:1" );
505         Artifact transitiveArtifact = createArtifact( "g:b:t:1:compile" );
506         Artifact managedTransitiveArtifact = createArtifact( "g:b:t:1:test" );
507         addArtifactMetadata( childArtifact, transitiveArtifact );
508 
509         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
510         setManagedVersionMap( project, Collections.singleton( managedTransitiveArtifact ) );
511 
512         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
513         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
514         expectedRootNode.addChild( childArtifactNode );
515         DependencyNode managedTransitiveArtifactNode = createNode( "g:b:t:1:test" );
516         managedTransitiveArtifactNode.setPremanagedScope( "compile" );
517         childArtifactNode.addChild( managedTransitiveArtifactNode );
518 
519         assertDependencyTree( expectedRootNode, project );
520     }
521 
522     /**
523      * Tests building a tree for a project with one transitive dependency whose version and scope are fixed in
524      * dependency management:
525      * 
526      * <pre>
527      * g:p:t:1
528      * \- g:a:t:1
529      *    \- g:b:t:2:test (version managed from 1; scope managed from compile)
530      * </pre>
531      * 
532      * @throws DependencyTreeBuilderException
533      */
534     public void testProjectWithManagedTransitiveDependencyVersionAndScope()
535         throws DependencyTreeBuilderException
536     {
537         Artifact projectArtifact = createArtifact( "g:p:t:1" );
538         Artifact childArtifact = createArtifact( "g:a:t:1" );
539         Artifact transitiveArtifact = createArtifact( "g:b:t:1:compile" );
540         Artifact managedTransitiveArtifact = createArtifact( "g:b:t:2:test" );
541         addArtifactMetadata( childArtifact, transitiveArtifact );
542 
543         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
544         setManagedVersionMap( project, Collections.singleton( managedTransitiveArtifact ) );
545 
546         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
547         DependencyNode childArtifactNode = createNode( "g:a:t:1" );
548         expectedRootNode.addChild( childArtifactNode );
549         DependencyNode managedTransitiveArtifactNode = createNode( "g:b:t:2:test" );
550         managedTransitiveArtifactNode.setPremanagedVersion( "1" );
551         managedTransitiveArtifactNode.setPremanagedScope( "compile" );
552         childArtifactNode.addChild( managedTransitiveArtifactNode );
553 
554         assertDependencyTree( expectedRootNode, project );
555     }
556 
557     /**
558      * Tests building a tree for a project with a dependency that has conflicting versions and the version is also fixed
559      * in dependency management:
560      * 
561      * <pre>
562      * g:p:t:1
563      * +- g:a:t:1
564      * \- g:b:t:1
565      *    \- (g:a:t:3 - version managed from 2; omitted for conflict with 1)
566      * </pre>
567      * 
568      * @throws DependencyTreeBuilderException
569      */
570     public void testProjectWithManagedTransitiveDependencyVersionAndConflictDependencyVersion()
571         throws DependencyTreeBuilderException
572     {
573         Artifact projectArtifact = createArtifact( "g:p:t:1" );
574         Artifact nearestArtifact = createArtifact( "g:a:t:1" );
575         Artifact childArtifact = createArtifact( "g:b:t:1" );
576         Artifact farthestArtifact = createArtifact( "g:a:t:2" );
577         Artifact managedTransitiveArtifact = createArtifact( "g:a:t:3" );
578         addArtifactMetadata( childArtifact, farthestArtifact );
579 
580         MavenProject project = createProject( projectArtifact, new Artifact[] { nearestArtifact, childArtifact } );
581         setManagedVersionMap( project, Collections.singleton( managedTransitiveArtifact ) );
582 
583         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
584         DependencyNode nearestArtifactNode = createNode( "g:a:t:1" );
585         expectedRootNode.addChild( nearestArtifactNode );
586         DependencyNode childArtifactNode = createNode( "g:b:t:1" );
587         expectedRootNode.addChild( childArtifactNode );
588         DependencyNode managedTransitiveArtifactNode =
589             createNode( "g:a:t:3", DependencyNode.OMITTED_FOR_CONFLICT, nearestArtifactNode.getArtifact() );
590         managedTransitiveArtifactNode.setPremanagedVersion( "2" );
591         childArtifactNode.addChild( managedTransitiveArtifactNode );
592 
593         assertDependencyTree( expectedRootNode, project );
594     }
595 
596     /**
597      * Tests building a tree for a project with a dependency with version range
598      * 
599      * <pre>
600      * g:p:t:1
601      * \- g:a:t:1
602      * </pre>
603      * 
604      * @throws InvalidVersionSpecificationException
605      * @throws DependencyTreeBuilderException
606      */
607     public void testProjectWithVersionRange()
608         throws InvalidVersionSpecificationException, DependencyTreeBuilderException
609     {
610         String range = "[1,2)";
611         Artifact projectArtifact = createArtifact( "g:p:t:1" );
612         Artifact childArtifact = createArtifact( "g:a:t:" + range );
613 
614         MavenProject project = createProject( projectArtifact, new Artifact[] { childArtifact } );
615 
616         ArtifactVersion version = new DefaultArtifactVersion( "1.0" );
617         List<ArtifactVersion> availableVersions = new ArrayList<ArtifactVersion>();
618         availableVersions.add( version );
619 
620         DependencyNode expectedRootNode = createNode( "g:p:t:1" );
621         DependencyNode childNode = createNode( "g:a:t:1.0" );
622         childNode.setAvailableVersions( availableVersions );
623         childNode.setVersionSelectedFromRange( VersionRange.createFromVersionSpec( range ) );
624         expectedRootNode.addChild( childNode );
625 
626         artifactMetadataSource.addAvailableVersions( childArtifact, availableVersions );
627         assertDependencyTree( expectedRootNode, project );
628     }
629 
630     // TODO: reinstate when MNG-3236 fixed
631     /*
632      * public void testProjectWithFilter() throws DependencyTreeBuilderException, ArtifactResolutionException { Artifact
633      * projectArtifact = createArtifact( "g:p:t:1" ); Artifact child1Artifact = createArtifact( "g:a:t:1" ); Artifact
634      * child2Artifact = createArtifact( "g:b:t:1:test" ); MavenProject project = createProject( projectArtifact, new
635      * Artifact[] { child1Artifact, child2Artifact } ); DependencyNode expectedRootNode = createNode( "g:p:t:1" );
636      * expectedRootNode.addChild( createNode( "g:a:t:1" ) ); ArtifactFilter artifactFilter = new ScopeArtifactFilter(
637      * Artifact.SCOPE_COMPILE ); assertDependencyTree( expectedRootNode, project, artifactFilter ); }
638      */
639 
640     // private methods --------------------------------------------------------
641 
642     private DependencyNode createNode( String id )
643     {
644         return createNode( id, DependencyNode.INCLUDED, null );
645     }
646 
647     private DependencyNode createNode( String id, int state, Artifact relatedArtifact )
648     {
649         return new DependencyNode( createArtifact( id ), state, relatedArtifact );
650     }
651 
652     private Artifact createArtifact( String id )
653     {
654         String[] tokens = id.split( ":" );
655 
656         String groupId = get( tokens, 0 );
657         String artifactId = get( tokens, 1 );
658         String type = get( tokens, 2, "jar" );
659         String version = get( tokens, 3 );
660         String scope = get( tokens, 4 );
661 
662         VersionRange versionRange;
663         try
664         {
665             versionRange = VersionRange.createFromVersionSpec( version );
666         }
667         catch ( InvalidVersionSpecificationException e )
668         {
669             throw new RuntimeException( e );
670         }
671 
672         return new DefaultArtifact( groupId, artifactId, versionRange, scope, type, null, new DefaultArtifactHandler() );
673     }
674 
675     private MavenProject createProject( Artifact projectArtifact, Artifact[] dependencyArtifacts )
676     {
677         MavenProject project = new MavenProject();
678         project.setArtifact( projectArtifact );
679         // LinkedHashSet since order is significant when omitting conflicts
680         project.setDependencyArtifacts( new LinkedHashSet<Artifact>( Arrays.asList( dependencyArtifacts ) ) );
681         project.setManagedVersionMap( new HashMap<String, Artifact>() );
682         project.setRemoteArtifactRepositories( Collections.EMPTY_LIST );
683         return project;
684     }
685 
686     private void addArtifactMetadata( Artifact artifact, Artifact dependencyArtifact )
687     {
688         addArtifactMetadata( artifact, new Artifact[] { dependencyArtifact } );
689     }
690 
691     private void addArtifactMetadata( Artifact artifact, Artifact[] dependencyArtifacts )
692     {
693         addArtifactMetadata( artifact, new LinkedHashSet<Artifact>( Arrays.asList( dependencyArtifacts ) ) );
694     }
695 
696     private void addArtifactMetadata( Artifact artifact, Set<Artifact> dependencyArtifacts )
697     {
698         artifactMetadataSource.addArtifactMetadata( artifact, dependencyArtifacts );
699     }
700 
701     private void setManagedVersionMap( MavenProject project, Set<Artifact> managedArtifacts )
702     {
703         Map<String, Artifact> managedVersionMap = new HashMap<String, Artifact>();
704 
705         for ( Artifact artifact : managedArtifacts )
706         {
707             String managementKey = getManagementKey( artifact );
708 
709             managedVersionMap.put( managementKey, artifact );
710         }
711 
712         project.setManagedVersionMap( managedVersionMap );
713     }
714 
715     private String getManagementKey( Artifact artifact )
716     {
717         return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType()
718             + ( artifact.getClassifier() != null ? ":" + artifact.getClassifier() : "" );
719     }
720 
721     private void assertDependencyTree( DependencyNode expectedRootNode, MavenProject project )
722         throws DependencyTreeBuilderException
723     {
724         assertDependencyTree( expectedRootNode, project, null );
725     }
726 
727     private void assertDependencyTree( DependencyNode expectedRootNode, MavenProject project,
728                                        ArtifactFilter artifactFilter )
729         throws DependencyTreeBuilderException
730     {
731         // assert built dependency tree is as expected
732 
733         DependencyNode actualRootNode =
734             builder.buildDependencyTree( project, artifactRepository, artifactFactory, artifactMetadataSource,
735                                          artifactFilter, artifactCollector );
736 
737         assertEquals( "Dependency tree", expectedRootNode, actualRootNode );
738 
739         // assert resolution tree is as expected
740 
741         ArtifactResolutionResult result = builder.getArtifactResolutionResult();
742 
743         assertTreeEquals( expectedRootNode, project, result );
744     }
745 
746     private void assertTreeEquals( DependencyNode dependencyNode, MavenProject project,
747                                    ArtifactResolutionResult resolutionResult )
748     {
749         List<ResolutionNode> rootChildrenResolutionNodes =
750             ResolutionNodeUtils.getRootChildrenResolutionNodes( project, resolutionResult );
751 
752         try
753         {
754             assertEquals( "Root node artifact", dependencyNode.getArtifact(), project.getArtifact() );
755 
756             assertNodesEquals( dependencyNode.getChildren(), rootChildrenResolutionNodes );
757         }
758         catch ( AssertionFailedError error )
759         {
760             StringBuffer buffer = new StringBuffer();
761 
762             buffer.append( error.getMessage() ).append( "; " );
763             buffer.append( "expected dependency tree <" ).append( dependencyNode ).append( "> " );
764             buffer.append( "actual resolution tree <" );
765             ResolutionNodeUtils.append( buffer, project, resolutionResult );
766             buffer.append( ">" );
767 
768             throw new AssertionFailedError( buffer.toString() );
769         }
770     }
771 
772     private void assertNodesEquals( List<DependencyNode> dependencyNodes, List<ResolutionNode> resolutionNodes )
773     {
774         assertNodesEquals( dependencyNodes.iterator(), resolutionNodes.iterator() );
775     }
776 
777     private void assertNodesEquals( Iterator<DependencyNode> dependencyNodesIterator,
778                                     Iterator<ResolutionNode> resolutionNodesIterator )
779     {
780         while ( dependencyNodesIterator.hasNext() && resolutionNodesIterator.hasNext() )
781         {
782             DependencyNode dependencyNode = dependencyNodesIterator.next();
783             ResolutionNode resolutionNode = resolutionNodesIterator.next();
784 
785             assertNodeEquals( dependencyNode, resolutionNode );
786         }
787 
788         if ( dependencyNodesIterator.hasNext() || resolutionNodesIterator.hasNext() )
789         {
790             fail( "Node list size differs" );
791         }
792     }
793 
794     private void assertNodeEquals( DependencyNode dependencyNode, ResolutionNode resolutionNode )
795     {
796         assertEquals( "Node state", dependencyNode.getState() == DependencyNode.INCLUDED, resolutionNode.isActive() );
797 
798         assertEquals( "Node artifact", dependencyNode.getArtifact(), resolutionNode.getArtifact() );
799 
800         assertNodesEquals( dependencyNode.getChildren().iterator(), resolutionNode.getChildrenIterator() );
801     }
802 
803     private String get( String[] array, int index )
804     {
805         return get( array, index, null );
806     }
807 
808     private String get( String[] array, int index, String defaultValue )
809     {
810         return ( index < array.length ) ? array[index] : defaultValue;
811     }
812 }