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