001package org.eclipse.aether.internal.impl; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import static org.junit.Assert.*; 023 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.HashMap; 030import java.util.Iterator; 031import java.util.LinkedList; 032import java.util.List; 033import java.util.Map; 034 035import org.eclipse.aether.DefaultRepositorySystemSession; 036import org.eclipse.aether.RepositorySystemSession; 037import org.eclipse.aether.artifact.Artifact; 038import org.eclipse.aether.artifact.ArtifactProperties; 039import org.eclipse.aether.artifact.DefaultArtifact; 040import org.eclipse.aether.collection.CollectRequest; 041import org.eclipse.aether.collection.CollectResult; 042import org.eclipse.aether.collection.DependencyCollectionContext; 043import org.eclipse.aether.collection.DependencyCollectionException; 044import org.eclipse.aether.collection.DependencyManagement; 045import org.eclipse.aether.collection.DependencyManager; 046import org.eclipse.aether.graph.Dependency; 047import org.eclipse.aether.graph.DependencyCycle; 048import org.eclipse.aether.graph.DependencyNode; 049import org.eclipse.aether.graph.Exclusion; 050import org.eclipse.aether.impl.ArtifactDescriptorReader; 051import org.eclipse.aether.internal.test.util.DependencyGraphParser; 052import org.eclipse.aether.internal.test.util.TestLoggerFactory; 053import org.eclipse.aether.internal.test.util.TestUtils; 054import org.eclipse.aether.repository.RemoteRepository; 055import org.eclipse.aether.resolution.ArtifactDescriptorException; 056import org.eclipse.aether.resolution.ArtifactDescriptorRequest; 057import org.eclipse.aether.resolution.ArtifactDescriptorResult; 058import org.eclipse.aether.util.artifact.ArtifactIdUtils; 059import org.eclipse.aether.util.graph.manager.ClassicDependencyManager; 060import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; 061import org.eclipse.aether.util.graph.version.HighestVersionFilter; 062import org.junit.Before; 063import org.junit.Test; 064 065/** 066 */ 067public class DefaultDependencyCollectorTest 068{ 069 070 private DefaultDependencyCollector collector; 071 072 private DefaultRepositorySystemSession session; 073 074 private DependencyGraphParser parser; 075 076 private RemoteRepository repository; 077 078 private IniArtifactDescriptorReader newReader( String prefix ) 079 { 080 return new IniArtifactDescriptorReader( "artifact-descriptions/" + prefix ); 081 } 082 083 private Dependency newDep( String coords ) 084 { 085 return newDep( coords, "" ); 086 } 087 088 private Dependency newDep( String coords, String scope ) 089 { 090 return new Dependency( new DefaultArtifact( coords ), scope ); 091 } 092 093 @Before 094 public void setup() 095 throws IOException 096 { 097 session = TestUtils.newSession(); 098 099 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}