View Javadoc
1   package org.eclipse.aether.internal.impl;
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 static org.junit.Assert.*;
23  
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  import java.util.LinkedList;
32  import java.util.List;
33  import java.util.Map;
34  
35  import org.eclipse.aether.DefaultRepositorySystemSession;
36  import org.eclipse.aether.RepositorySystemSession;
37  import org.eclipse.aether.artifact.Artifact;
38  import org.eclipse.aether.artifact.ArtifactProperties;
39  import org.eclipse.aether.artifact.DefaultArtifact;
40  import org.eclipse.aether.collection.CollectRequest;
41  import org.eclipse.aether.collection.CollectResult;
42  import org.eclipse.aether.collection.DependencyCollectionContext;
43  import org.eclipse.aether.collection.DependencyCollectionException;
44  import org.eclipse.aether.collection.DependencyManagement;
45  import org.eclipse.aether.collection.DependencyManager;
46  import org.eclipse.aether.graph.Dependency;
47  import org.eclipse.aether.graph.DependencyCycle;
48  import org.eclipse.aether.graph.DependencyNode;
49  import org.eclipse.aether.graph.Exclusion;
50  import org.eclipse.aether.impl.ArtifactDescriptorReader;
51  import org.eclipse.aether.internal.test.util.DependencyGraphParser;
52  import org.eclipse.aether.internal.test.util.TestLoggerFactory;
53  import org.eclipse.aether.internal.test.util.TestUtils;
54  import org.eclipse.aether.repository.RemoteRepository;
55  import org.eclipse.aether.resolution.ArtifactDescriptorException;
56  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
57  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
58  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
59  import org.eclipse.aether.util.graph.manager.ClassicDependencyManager;
60  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
61  import org.eclipse.aether.util.graph.version.HighestVersionFilter;
62  import org.junit.Before;
63  import org.junit.Test;
64  
65  /**
66   */
67  public class DefaultDependencyCollectorTest
68  {
69  
70      private DefaultDependencyCollector collector;
71  
72      private DefaultRepositorySystemSession session;
73  
74      private DependencyGraphParser parser;
75  
76      private RemoteRepository repository;
77  
78      private IniArtifactDescriptorReader newReader( String prefix )
79      {
80          return new IniArtifactDescriptorReader( "artifact-descriptions/" + prefix );
81      }
82  
83      private Dependency newDep( String coords )
84      {
85          return newDep( coords, "" );
86      }
87  
88      private Dependency newDep( String coords, String scope )
89      {
90          return new Dependency( new DefaultArtifact( coords ), scope );
91      }
92  
93      @Before
94      public void setup()
95          throws IOException
96      {
97          session = TestUtils.newSession();
98  
99          collector = new DefaultDependencyCollector();
100         collector.setArtifactDescriptorReader( newReader( "" ) );
101         collector.setVersionRangeResolver( new StubVersionRangeResolver() );
102         collector.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
103         collector.setLoggerFactory( new TestLoggerFactory() );
104 
105         parser = new DependencyGraphParser( "artifact-descriptions/" );
106 
107         repository = new RemoteRepository.Builder( "id", "default", "file:///" ).build();
108     }
109 
110     private static void assertEqualSubtree( DependencyNode expected, DependencyNode actual )
111     {
112         assertEqualSubtree( expected, actual, new LinkedList<DependencyNode>() );
113     }
114 
115     private static void assertEqualSubtree( DependencyNode expected, DependencyNode actual,
116                                             LinkedList<DependencyNode> parents )
117     {
118         assertEquals( "path: " + parents, expected.getDependency(), actual.getDependency() );
119 
120         if ( actual.getDependency() != null )
121         {
122             Artifact artifact = actual.getDependency().getArtifact();
123             for ( DependencyNode parent : parents )
124             {
125                 if ( parent.getDependency() != null && artifact.equals( parent.getDependency().getArtifact() ) )
126                 {
127                     return;
128                 }
129             }
130         }
131 
132         parents.addLast( expected );
133 
134         assertEquals( "path: " + parents + ", expected: " + expected.getChildren() + ", actual: "
135                           + actual.getChildren(), expected.getChildren().size(), actual.getChildren().size() );
136 
137         Iterator<DependencyNode> iterator1 = expected.getChildren().iterator();
138         Iterator<DependencyNode> iterator2 = actual.getChildren().iterator();
139 
140         while ( iterator1.hasNext() )
141         {
142             assertEqualSubtree( iterator1.next(), iterator2.next(), parents );
143         }
144 
145         parents.removeLast();
146     }
147 
148     private Dependency dep( DependencyNode root, int... coords )
149     {
150         return path( root, coords ).getDependency();
151     }
152 
153     private DependencyNode path( DependencyNode root, int... coords )
154     {
155         try
156         {
157             DependencyNode node = root;
158             for ( int coord : coords )
159             {
160                 node = node.getChildren().get( coord );
161             }
162 
163             return node;
164         }
165         catch ( IndexOutOfBoundsException e )
166         {
167             throw new IllegalArgumentException( "illegal coordinates for child", e );
168         }
169         catch ( NullPointerException e )
170         {
171             throw new IllegalArgumentException( "illegal coordinates for child", e );
172         }
173     }
174 
175     @Test
176     public void testSimpleCollection()
177         throws IOException, DependencyCollectionException
178     {
179         Dependency dependency = newDep( "gid:aid:ext:ver", "compile" );
180         CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
181         CollectResult result = collector.collectDependencies( session, request );
182 
183         assertEquals( 0, result.getExceptions().size() );
184 
185         DependencyNode root = result.getRoot();
186         Dependency newDependency = root.getDependency();
187 
188         assertEquals( dependency, newDependency );
189         assertEquals( dependency.getArtifact(), newDependency.getArtifact() );
190 
191         assertEquals( 1, root.getChildren().size() );
192 
193         Dependency expect = newDep( "gid:aid2:ext:ver", "compile" );
194         assertEquals( expect, root.getChildren().get( 0 ).getDependency() );
195     }
196 
197     @Test
198     public void testMissingDependencyDescription()
199         throws IOException
200     {
201         CollectRequest request =
202             new CollectRequest( newDep( "missing:description:ext:ver" ), Arrays.asList( repository ) );
203         try
204         {
205             collector.collectDependencies( session, request );
206             fail( "expected exception" );
207         }
208         catch ( DependencyCollectionException e )
209         {
210             CollectResult result = e.getResult();
211             assertSame( request, result.getRequest() );
212             assertNotNull( result.getExceptions() );
213             assertEquals( 1, result.getExceptions().size() );
214 
215             assertTrue( result.getExceptions().get( 0 ) instanceof ArtifactDescriptorException );
216 
217             assertEquals( request.getRoot(), result.getRoot().getDependency() );
218         }
219     }
220 
221     @Test
222     public void testDuplicates()
223         throws IOException, DependencyCollectionException
224     {
225         Dependency dependency = newDep( "duplicate:transitive:ext:dependency" );
226         CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
227 
228         CollectResult result = collector.collectDependencies( session, request );
229 
230         assertEquals( 0, result.getExceptions().size() );
231 
232         DependencyNode root = result.getRoot();
233         Dependency newDependency = root.getDependency();
234 
235         assertEquals( dependency, newDependency );
236         assertEquals( dependency.getArtifact(), newDependency.getArtifact() );
237 
238         assertEquals( 2, root.getChildren().size() );
239 
240         Dependency dep = newDep( "gid:aid:ext:ver", "compile" );
241         assertEquals( dep, dep( root, 0 ) );
242 
243         dep = newDep( "gid:aid2:ext:ver", "compile" );
244         assertEquals( dep, dep( root, 1 ) );
245         assertEquals( dep, dep( root, 0, 0 ) );
246         assertEquals( dep( root, 1 ), dep( root, 0, 0 ) );
247     }
248 
249     @Test
250     public void testEqualSubtree()
251         throws IOException, DependencyCollectionException
252     {
253         DependencyNode root = parser.parseResource( "expectedSubtreeComparisonResult.txt" );
254         Dependency dependency = root.getDependency();
255         CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
256 
257         CollectResult result = collector.collectDependencies( session, request );
258         assertEqualSubtree( root, result.getRoot() );
259     }
260 
261     @Test
262     public void testCyclicDependencies()
263         throws Exception
264     {
265         DependencyNode root = parser.parseResource( "cycle.txt" );
266         CollectRequest request = new CollectRequest( root.getDependency(), Arrays.asList( repository ) );
267         CollectResult result = collector.collectDependencies( session, request );
268         assertEqualSubtree( root, result.getRoot() );
269     }
270 
271     @Test
272     public void testCyclicDependenciesBig()
273         throws Exception
274     {
275         CollectRequest request = new CollectRequest( newDep( "1:2:pom:5.50-SNAPSHOT" ), Arrays.asList( repository ) );
276         collector.setArtifactDescriptorReader( newReader( "cycle-big/" ) );
277         CollectResult result = collector.collectDependencies( session, request );
278         assertNotNull( result.getRoot() );
279         // we only care about the performance here, this test must not hang or run out of mem
280     }
281 
282     @Test
283     public void testCyclicProjects()
284         throws Exception
285     {
286         CollectRequest request = new CollectRequest( newDep( "test:a:2" ), Arrays.asList( repository ) );
287         collector.setArtifactDescriptorReader( newReader( "versionless-cycle/" ) );
288         CollectResult result = collector.collectDependencies( session, request );
289         DependencyNode root = result.getRoot();
290         DependencyNode a1 = path( root, 0, 0 );
291         assertEquals( "a", a1.getArtifact().getArtifactId() );
292         assertEquals( "1", a1.getArtifact().getVersion() );
293         for ( DependencyNode child : a1.getChildren() )
294         {
295             assertFalse( "1".equals( child.getArtifact().getVersion() ) );
296         }
297 
298         assertEquals( 1, result.getCycles().size() );
299         DependencyCycle cycle = result.getCycles().get( 0 );
300         assertEquals( Arrays.asList(), cycle.getPrecedingDependencies() );
301         assertEquals( Arrays.asList( root.getDependency(), path( root, 0 ).getDependency(), a1.getDependency() ),
302                       cycle.getCyclicDependencies() );
303     }
304 
305     @Test
306     public void testCyclicProjects_ConsiderLabelOfRootlessGraph()
307         throws Exception
308     {
309         Dependency dep = newDep( "gid:aid:ver", "compile" );
310         CollectRequest request =
311             new CollectRequest().addDependency( dep ).addRepository( repository ).setRootArtifact( dep.getArtifact() );
312         CollectResult result = collector.collectDependencies( session, request );
313         DependencyNode root = result.getRoot();
314         DependencyNode a1 = root.getChildren().get( 0 );
315         assertEquals( "aid", a1.getArtifact().getArtifactId() );
316         assertEquals( "ver", a1.getArtifact().getVersion() );
317         DependencyNode a2 = a1.getChildren().get( 0 );
318         assertEquals( "aid2", a2.getArtifact().getArtifactId() );
319         assertEquals( "ver", a2.getArtifact().getVersion() );
320 
321         assertEquals( 1, result.getCycles().size() );
322         DependencyCycle cycle = result.getCycles().get( 0 );
323         assertEquals( Arrays.asList(), cycle.getPrecedingDependencies() );
324         assertEquals( Arrays.asList( new Dependency( dep.getArtifact(), null ), a1.getDependency() ),
325                       cycle.getCyclicDependencies() );
326     }
327 
328     @Test
329     public void testPartialResultOnError()
330         throws IOException
331     {
332         DependencyNode root = parser.parseResource( "expectedPartialSubtreeOnError.txt" );
333 
334         Dependency dependency = root.getDependency();
335         CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
336 
337         CollectResult result;
338         try
339         {
340             result = collector.collectDependencies( session, request );
341             fail( "expected exception " );
342         }
343         catch ( DependencyCollectionException e )
344         {
345             result = e.getResult();
346 
347             assertSame( request, result.getRequest() );
348             assertNotNull( result.getExceptions() );
349             assertEquals( 1, result.getExceptions().size() );
350 
351             assertTrue( result.getExceptions().get( 0 ) instanceof ArtifactDescriptorException );
352 
353             assertEqualSubtree( root, result.getRoot() );
354         }
355     }
356 
357     @Test
358     public void testCollectMultipleDependencies()
359         throws IOException, DependencyCollectionException
360     {
361         Dependency root1 = newDep( "gid:aid:ext:ver", "compile" );
362         Dependency root2 = newDep( "gid:aid2:ext:ver", "compile" );
363         List<Dependency> dependencies = Arrays.asList( root1, root2 );
364         CollectRequest request = new CollectRequest( dependencies, null, Arrays.asList( repository ) );
365         CollectResult result = collector.collectDependencies( session, request );
366 
367         assertEquals( 0, result.getExceptions().size() );
368         assertEquals( 2, result.getRoot().getChildren().size() );
369         assertEquals( root1, dep( result.getRoot(), 0 ) );
370 
371         assertEquals( 1, path( result.getRoot(), 0 ).getChildren().size() );
372         assertEquals( root2, dep( result.getRoot(), 0, 0 ) );
373 
374         assertEquals( 0, path( result.getRoot(), 1 ).getChildren().size() );
375         assertEquals( root2, dep( result.getRoot(), 1 ) );
376     }
377 
378     @Test
379     public void testArtifactDescriptorResolutionNotRestrictedToRepoHostingSelectedVersion()
380         throws Exception
381     {
382         RemoteRepository repo2 = new RemoteRepository.Builder( "test", "default", "file:///" ).build();
383 
384         final List<RemoteRepository> repos = new ArrayList<RemoteRepository>();
385 
386         collector.setArtifactDescriptorReader( new ArtifactDescriptorReader()
387         {
388             public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session,
389                                                                     ArtifactDescriptorRequest request )
390                 throws ArtifactDescriptorException
391             {
392                 repos.addAll( request.getRepositories() );
393                 return new ArtifactDescriptorResult( request );
394             }
395         } );
396 
397         List<Dependency> dependencies = Arrays.asList( newDep( "verrange:parent:jar:1[1,)", "compile" ) );
398         CollectRequest request = new CollectRequest( dependencies, null, Arrays.asList( repository, repo2 ) );
399         CollectResult result = collector.collectDependencies( session, request );
400 
401         assertEquals( 0, result.getExceptions().size() );
402         assertEquals( 2, repos.size() );
403         assertEquals( "id", repos.get( 0 ).getId() );
404         assertEquals( "test", repos.get( 1 ).getId() );
405     }
406 
407     @Test
408     public void testManagedVersionScope()
409         throws IOException, DependencyCollectionException
410     {
411         Dependency dependency = newDep( "managed:aid:ext:ver" );
412         CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
413 
414         session.setDependencyManager( new ClassicDependencyManager() );
415 
416         CollectResult result = collector.collectDependencies( session, request );
417 
418         assertEquals( 0, result.getExceptions().size() );
419 
420         DependencyNode root = result.getRoot();
421 
422         assertEquals( dependency, dep( root ) );
423         assertEquals( dependency.getArtifact(), dep( root ).getArtifact() );
424 
425         assertEquals( 1, root.getChildren().size() );
426         Dependency expect = newDep( "gid:aid:ext:ver", "compile" );
427         assertEquals( expect, dep( root, 0 ) );
428 
429         assertEquals( 1, path( root, 0 ).getChildren().size() );
430         expect = newDep( "gid:aid2:ext:managedVersion", "managedScope" );
431         assertEquals( expect, dep( root, 0, 0 ) );
432     }
433 
434     @Test
435     public void testDependencyManagement()
436         throws IOException, DependencyCollectionException
437     {
438         collector.setArtifactDescriptorReader( newReader( "managed/" ) );
439 
440         DependencyNode root = parser.parseResource( "expectedSubtreeComparisonResult.txt" );
441         TestDependencyManager depMgmt = new TestDependencyManager();
442         depMgmt.add( dep( root, 0 ), "managed", null, null );
443         depMgmt.add( dep( root, 0, 1 ), "managed", "managed", null );
444         depMgmt.add( dep( root, 1 ), null, null, "managed" );
445         session.setDependencyManager( depMgmt );
446 
447         // collect result will differ from expectedSubtreeComparisonResult.txt
448         // set localPath -> no dependency traversal
449         CollectRequest request = new CollectRequest( dep( root ), Arrays.asList( repository ) );
450         CollectResult result = collector.collectDependencies( session, request );
451 
452         DependencyNode node = result.getRoot();
453         assertEquals( "managed", dep( node, 0, 1 ).getArtifact().getVersion() );
454         assertEquals( "managed", dep( node, 0, 1 ).getScope() );
455 
456         assertEquals( "managed", dep( node, 1 ).getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) );
457         assertEquals( "managed", dep( node, 0, 0 ).getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) );
458     }
459 
460     @Test
461     public void testDependencyManagement_VerboseMode()
462         throws Exception
463     {
464         String depId = "gid:aid2:ext";
465         TestDependencyManager depMgmt = new TestDependencyManager();
466         depMgmt.version( depId, "managedVersion" );
467         depMgmt.scope( depId, "managedScope" );
468         depMgmt.optional( depId, Boolean.TRUE );
469         depMgmt.path( depId, "managedPath" );
470         depMgmt.exclusions( depId, new Exclusion( "gid", "aid", "*", "*" ) );
471         session.setDependencyManager( depMgmt );
472         session.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, Boolean.TRUE );
473 
474         CollectRequest request = new CollectRequest().setRoot( newDep( "gid:aid:ver" ) );
475         CollectResult result = collector.collectDependencies( session, request );
476         DependencyNode node = result.getRoot().getChildren().get( 0 );
477         assertEquals( DependencyNode.MANAGED_VERSION | DependencyNode.MANAGED_SCOPE | DependencyNode.MANAGED_OPTIONAL
478             | DependencyNode.MANAGED_PROPERTIES | DependencyNode.MANAGED_EXCLUSIONS, node.getManagedBits() );
479         assertEquals( "ver", DependencyManagerUtils.getPremanagedVersion( node ) );
480         assertEquals( "compile", DependencyManagerUtils.getPremanagedScope( node ) );
481         assertEquals( Boolean.FALSE, DependencyManagerUtils.getPremanagedOptional( node ) );
482     }
483 
484     @Test
485     public void testVersionFilter()
486         throws Exception
487     {
488         session.setVersionFilter( new HighestVersionFilter() );
489         CollectRequest request = new CollectRequest().setRoot( newDep( "gid:aid:1" ) );
490         CollectResult result = collector.collectDependencies( session, request );
491         assertEquals( 1, result.getRoot().getChildren().size() );
492     }
493 
494     static class TestDependencyManager
495         implements DependencyManager
496     {
497 
498         private Map<String, String> versions = new HashMap<String, String>();
499 
500         private Map<String, String> scopes = new HashMap<String, String>();
501 
502         private Map<String, Boolean> optionals = new HashMap<String, Boolean>();
503 
504         private Map<String, String> paths = new HashMap<String, String>();
505 
506         private Map<String, Collection<Exclusion>> exclusions = new HashMap<String, Collection<Exclusion>>();
507 
508         public void add( Dependency d, String version, String scope, String localPath )
509         {
510             String id = toKey( d );
511             version( id, version );
512             scope( id, scope );
513             path( id, localPath );
514         }
515 
516         public void version( String id, String version )
517         {
518             versions.put( id, version );
519         }
520 
521         public void scope( String id, String scope )
522         {
523             scopes.put( id, scope );
524         }
525 
526         public void optional( String id, Boolean optional )
527         {
528             optionals.put( id, optional );
529         }
530 
531         public void path( String id, String path )
532         {
533             paths.put( id, path );
534         }
535 
536         public void exclusions( String id, Exclusion... exclusions )
537         {
538             this.exclusions.put( id, exclusions != null ? Arrays.asList( exclusions ) : null );
539         }
540 
541         public DependencyManagement manageDependency( Dependency d )
542         {
543             String id = toKey( d );
544             DependencyManagement mgmt = new DependencyManagement();
545             mgmt.setVersion( versions.get( id ) );
546             mgmt.setScope( scopes.get( id ) );
547             mgmt.setOptional( optionals.get( id ) );
548             String path = paths.get( id );
549             if ( path != null )
550             {
551                 mgmt.setProperties( Collections.singletonMap( ArtifactProperties.LOCAL_PATH, path ) );
552             }
553             mgmt.setExclusions( exclusions.get( id ) );
554             return mgmt;
555         }
556 
557         private String toKey( Dependency dependency )
558         {
559             return ArtifactIdUtils.toVersionlessId( dependency.getArtifact() );
560         }
561 
562         public DependencyManager deriveChildManager( DependencyCollectionContext context )
563         {
564             return this;
565         }
566 
567     }
568 
569 }