View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl.collect;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.LinkedList;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.eclipse.aether.DefaultRepositorySystemSession;
33  import org.eclipse.aether.RepositorySystemSession;
34  import org.eclipse.aether.artifact.Artifact;
35  import org.eclipse.aether.artifact.ArtifactProperties;
36  import org.eclipse.aether.artifact.DefaultArtifact;
37  import org.eclipse.aether.collection.CollectRequest;
38  import org.eclipse.aether.collection.CollectResult;
39  import org.eclipse.aether.collection.DependencyCollectionContext;
40  import org.eclipse.aether.collection.DependencyCollectionException;
41  import org.eclipse.aether.collection.DependencyManagement;
42  import org.eclipse.aether.collection.DependencyManager;
43  import org.eclipse.aether.graph.DefaultDependencyNode;
44  import org.eclipse.aether.graph.Dependency;
45  import org.eclipse.aether.graph.DependencyCycle;
46  import org.eclipse.aether.graph.DependencyNode;
47  import org.eclipse.aether.graph.Exclusion;
48  import org.eclipse.aether.impl.ArtifactDescriptorReader;
49  import org.eclipse.aether.internal.impl.IniArtifactDescriptorReader;
50  import org.eclipse.aether.internal.test.util.DependencyGraphParser;
51  import org.eclipse.aether.internal.test.util.TestUtils;
52  import org.eclipse.aether.repository.RemoteRepository;
53  import org.eclipse.aether.resolution.ArtifactDescriptorException;
54  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
55  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
56  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
57  import org.eclipse.aether.util.graph.manager.ClassicDependencyManager;
58  import org.eclipse.aether.util.graph.manager.DefaultDependencyManager;
59  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
60  import org.eclipse.aether.util.graph.manager.TransitiveDependencyManager;
61  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
62  import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
63  import org.eclipse.aether.util.graph.transformer.JavaScopeSelector;
64  import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
65  import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
66  import org.eclipse.aether.util.graph.version.HighestVersionFilter;
67  import org.junit.Before;
68  import org.junit.Test;
69  
70  import static java.util.Collections.singletonList;
71  import static java.util.Objects.requireNonNull;
72  import static org.junit.Assert.assertEquals;
73  import static org.junit.Assert.assertNotEquals;
74  import static org.junit.Assert.assertNotNull;
75  import static org.junit.Assert.assertNull;
76  import static org.junit.Assert.assertSame;
77  import static org.junit.Assert.assertTrue;
78  import static org.junit.Assert.fail;
79  
80  /**
81   * Common tests for various {@link DependencyCollectorDelegate} implementations.
82   */
83  public abstract class DependencyCollectorDelegateTestSupport {
84  
85      protected DefaultRepositorySystemSession session;
86  
87      protected DependencyGraphParser parser;
88  
89      protected RemoteRepository repository;
90  
91      protected DependencyCollectorDelegate collector;
92  
93      protected IniArtifactDescriptorReader newReader(String prefix) {
94          return new IniArtifactDescriptorReader("artifact-descriptions/" + prefix);
95      }
96  
97      protected Dependency newDep(String coords) {
98          return newDep(coords, "");
99      }
100 
101     protected Dependency newDep(String coords, String scope) {
102         return new Dependency(new DefaultArtifact(coords), scope);
103     }
104 
105     @Before
106     public void setup() {
107         session = TestUtils.newSession();
108         parser = new DependencyGraphParser("artifact-descriptions/");
109         repository = new RemoteRepository.Builder("id", "default", "file:///").build();
110         setupCollector();
111     }
112 
113     protected abstract void setupCollector();
114 
115     private static void assertEqualSubtree(DependencyNode expected, DependencyNode actual) {
116         assertEqualSubtree(expected, actual, new LinkedList<>());
117     }
118 
119     private static void assertEqualSubtree(
120             DependencyNode expected, DependencyNode actual, LinkedList<DependencyNode> parents) {
121         assertEquals("path: " + parents, expected.getDependency(), actual.getDependency());
122 
123         if (actual.getDependency() != null) {
124             Artifact artifact = actual.getDependency().getArtifact();
125             for (DependencyNode parent : parents) {
126                 if (parent.getDependency() != null
127                         && artifact.equals(parent.getDependency().getArtifact())) {
128                     return;
129                 }
130             }
131         }
132 
133         parents.addLast(expected);
134 
135         assertEquals(
136                 "path: " + parents + ", expected: " + expected.getChildren() + ", actual: " + actual.getChildren(),
137                 expected.getChildren().size(),
138                 actual.getChildren().size());
139 
140         Iterator<DependencyNode> iterator1 = expected.getChildren().iterator();
141         Iterator<DependencyNode> iterator2 = actual.getChildren().iterator();
142 
143         while (iterator1.hasNext()) {
144             assertEqualSubtree(iterator1.next(), iterator2.next(), parents);
145         }
146 
147         parents.removeLast();
148     }
149 
150     protected Dependency dep(DependencyNode root, int... coords) {
151         return path(root, coords).getDependency();
152     }
153 
154     protected DependencyNode path(DependencyNode root, int... coords) {
155         try {
156             DependencyNode node = root;
157             for (int coord : coords) {
158                 node = node.getChildren().get(coord);
159             }
160 
161             return node;
162         } catch (IndexOutOfBoundsException | NullPointerException e) {
163             throw new IllegalArgumentException("illegal coordinates for child", e);
164         }
165     }
166 
167     @Test
168     public void testSimpleCollection() throws DependencyCollectionException {
169         Dependency dependency = newDep("gid:aid:ext:ver", "compile");
170         CollectRequest request = new CollectRequest(dependency, singletonList(repository));
171         CollectResult result = collector.collectDependencies(session, request);
172 
173         assertEquals(0, result.getExceptions().size());
174 
175         DependencyNode root = result.getRoot();
176         Dependency newDependency = root.getDependency();
177 
178         assertEquals(dependency, newDependency);
179         assertEquals(dependency.getArtifact(), newDependency.getArtifact());
180 
181         assertEquals(1, root.getChildren().size());
182 
183         Dependency expect = newDep("gid:aid2:ext:ver", "compile");
184         assertEquals(expect, root.getChildren().get(0).getDependency());
185     }
186 
187     @Test
188     public void testMissingDependencyDescription() {
189         CollectRequest request = new CollectRequest(newDep("missing:description:ext:ver"), singletonList(repository));
190         try {
191             collector.collectDependencies(session, request);
192             fail("expected exception");
193         } catch (DependencyCollectionException e) {
194             CollectResult result = e.getResult();
195             assertSame(request, result.getRequest());
196             assertNotNull(result.getExceptions());
197             assertEquals(1, result.getExceptions().size());
198 
199             assertTrue(result.getExceptions().get(0) instanceof ArtifactDescriptorException);
200 
201             assertEquals(request.getRoot(), result.getRoot().getDependency());
202         }
203     }
204 
205     @Test
206     public void testDuplicates() throws DependencyCollectionException {
207         Dependency dependency = newDep("duplicate:transitive:ext:dependency");
208         CollectRequest request = new CollectRequest(dependency, singletonList(repository));
209 
210         CollectResult result = collector.collectDependencies(session, request);
211 
212         assertEquals(0, result.getExceptions().size());
213 
214         DependencyNode root = result.getRoot();
215         Dependency newDependency = root.getDependency();
216 
217         assertEquals(dependency, newDependency);
218         assertEquals(dependency.getArtifact(), newDependency.getArtifact());
219 
220         assertEquals(2, root.getChildren().size());
221 
222         Dependency dep = newDep("gid:aid:ext:ver", "compile");
223         assertEquals(dep, dep(root, 0));
224 
225         dep = newDep("gid:aid2:ext:ver", "compile");
226         assertEquals(dep, dep(root, 1));
227         assertEquals(dep, dep(root, 0, 0));
228         assertEquals(dep(root, 1), dep(root, 0, 0));
229     }
230 
231     @Test
232     public void testEqualSubtree() throws IOException, DependencyCollectionException {
233         DependencyNode root = parser.parseResource("expectedSubtreeComparisonResult.txt");
234         Dependency dependency = root.getDependency();
235         CollectRequest request = new CollectRequest(dependency, singletonList(repository));
236 
237         CollectResult result = collector.collectDependencies(session, request);
238         assertEqualSubtree(root, result.getRoot());
239     }
240 
241     @Test
242     public void testCyclicDependencies() throws Exception {
243         DependencyNode root = parser.parseResource("cycle.txt");
244         CollectRequest request = new CollectRequest(root.getDependency(), singletonList(repository));
245         CollectResult result = collector.collectDependencies(session, request);
246         assertEqualSubtree(root, result.getRoot());
247     }
248 
249     @Test
250     public void testCyclicDependenciesBig() throws Exception {
251         CollectRequest request = new CollectRequest(newDep("1:2:pom:5.50-SNAPSHOT"), singletonList(repository));
252         collector.setArtifactDescriptorReader(newReader("cycle-big/"));
253         CollectResult result = collector.collectDependencies(session, request);
254         assertNotNull(result.getRoot());
255         // we only care about the performance here, this test must not hang or run out of mem
256     }
257 
258     @Test
259     public void testCyclicProjects() throws Exception {
260         CollectRequest request = new CollectRequest(newDep("test:a:2"), singletonList(repository));
261         collector.setArtifactDescriptorReader(newReader("versionless-cycle/"));
262         CollectResult result = collector.collectDependencies(session, request);
263         DependencyNode root = result.getRoot();
264         DependencyNode a1 = path(root, 0, 0);
265         assertEquals("a", a1.getArtifact().getArtifactId());
266         assertEquals("1", a1.getArtifact().getVersion());
267         for (DependencyNode child : a1.getChildren()) {
268             assertNotEquals("1", child.getArtifact().getVersion());
269         }
270 
271         assertEquals(1, result.getCycles().size());
272         DependencyCycle cycle = result.getCycles().get(0);
273         assertEquals(Collections.emptyList(), cycle.getPrecedingDependencies());
274         assertEquals(
275                 Arrays.asList(root.getDependency(), path(root, 0).getDependency(), a1.getDependency()),
276                 cycle.getCyclicDependencies());
277     }
278 
279     @Test
280     public void testCyclicProjects_ConsiderLabelOfRootlessGraph() throws Exception {
281         Dependency dep = newDep("gid:aid:ver", "compile");
282         CollectRequest request = new CollectRequest()
283                 .addDependency(dep)
284                 .addRepository(repository)
285                 .setRootArtifact(dep.getArtifact());
286         CollectResult result = collector.collectDependencies(session, request);
287         DependencyNode root = result.getRoot();
288         DependencyNode a1 = root.getChildren().get(0);
289         assertEquals("aid", a1.getArtifact().getArtifactId());
290         assertEquals("ver", a1.getArtifact().getVersion());
291         DependencyNode a2 = a1.getChildren().get(0);
292         assertEquals("aid2", a2.getArtifact().getArtifactId());
293         assertEquals("ver", a2.getArtifact().getVersion());
294 
295         assertEquals(1, result.getCycles().size());
296         DependencyCycle cycle = result.getCycles().get(0);
297         assertEquals(Collections.emptyList(), cycle.getPrecedingDependencies());
298         assertEquals(
299                 Arrays.asList(new Dependency(dep.getArtifact(), null), a1.getDependency()),
300                 cycle.getCyclicDependencies());
301     }
302 
303     @Test
304     public void testPartialResultOnError() throws IOException {
305         DependencyNode root = parser.parseResource("expectedPartialSubtreeOnError.txt");
306 
307         Dependency dependency = root.getDependency();
308         CollectRequest request = new CollectRequest(dependency, singletonList(repository));
309 
310         CollectResult result;
311         try {
312             result = collector.collectDependencies(session, request);
313             fail("expected exception ");
314         } catch (DependencyCollectionException e) {
315             result = e.getResult();
316 
317             assertSame(request, result.getRequest());
318             assertNotNull(result.getExceptions());
319             assertEquals(1, result.getExceptions().size());
320 
321             assertTrue(result.getExceptions().get(0) instanceof ArtifactDescriptorException);
322 
323             assertEqualSubtree(root, result.getRoot());
324         }
325     }
326 
327     @Test
328     public void testCollectMultipleDependencies() throws DependencyCollectionException {
329         Dependency root1 = newDep("gid:aid:ext:ver", "compile");
330         Dependency root2 = newDep("gid:aid2:ext:ver", "compile");
331         List<Dependency> dependencies = Arrays.asList(root1, root2);
332         CollectRequest request = new CollectRequest(dependencies, null, singletonList(repository));
333         CollectResult result = collector.collectDependencies(session, request);
334 
335         assertEquals(0, result.getExceptions().size());
336         assertEquals(2, result.getRoot().getChildren().size());
337         assertEquals(root1, dep(result.getRoot(), 0));
338 
339         assertEquals(1, path(result.getRoot(), 0).getChildren().size());
340         assertEquals(root2, dep(result.getRoot(), 0, 0));
341 
342         assertEquals(0, path(result.getRoot(), 1).getChildren().size());
343         assertEquals(root2, dep(result.getRoot(), 1));
344     }
345 
346     @Test
347     public void testArtifactDescriptorResolutionNotRestrictedToRepoHostingSelectedVersion() throws Exception {
348         RemoteRepository repo2 = new RemoteRepository.Builder("test", "default", "file:///").build();
349 
350         final List<RemoteRepository> repos = new ArrayList<>();
351 
352         collector.setArtifactDescriptorReader(new ArtifactDescriptorReader() {
353             public ArtifactDescriptorResult readArtifactDescriptor(
354                     RepositorySystemSession session, ArtifactDescriptorRequest request) {
355                 repos.addAll(request.getRepositories());
356                 return new ArtifactDescriptorResult(request);
357             }
358         });
359 
360         List<Dependency> dependencies = singletonList(newDep("verrange:parent:jar:1[1,)", "compile"));
361         CollectRequest request = new CollectRequest(dependencies, null, Arrays.asList(repository, repo2));
362         CollectResult result = collector.collectDependencies(session, request);
363 
364         assertEquals(0, result.getExceptions().size());
365         assertEquals(2, repos.size());
366         assertEquals("id", repos.get(0).getId());
367         assertEquals("test", repos.get(1).getId());
368     }
369 
370     @Test
371     public void testManagedVersionScope() throws DependencyCollectionException {
372         Dependency dependency = newDep("managed:aid:ext:ver");
373         CollectRequest request = new CollectRequest(dependency, singletonList(repository));
374 
375         session.setDependencyManager(new ClassicDependencyManager());
376 
377         CollectResult result = collector.collectDependencies(session, request);
378 
379         assertEquals(0, result.getExceptions().size());
380 
381         DependencyNode root = result.getRoot();
382 
383         assertEquals(dependency, dep(root));
384         assertEquals(dependency.getArtifact(), dep(root).getArtifact());
385 
386         assertEquals(1, root.getChildren().size());
387         Dependency expect = newDep("gid:aid:ext:ver", "compile");
388         assertEquals(expect, dep(root, 0));
389 
390         assertEquals(1, path(root, 0).getChildren().size());
391         expect = newDep("gid:aid2:ext:managedVersion", "managedScope");
392         assertEquals(expect, dep(root, 0, 0));
393     }
394 
395     @Test
396     public void testDependencyManagement() throws IOException, DependencyCollectionException {
397         collector.setArtifactDescriptorReader(newReader("managed/"));
398 
399         DependencyNode root = parser.parseResource("expectedSubtreeComparisonResult.txt");
400         TestDependencyManager depMgmt = new TestDependencyManager();
401         depMgmt.add(dep(root, 0), "managed", null, null);
402         depMgmt.add(dep(root, 0, 1), "managed", "managed", null);
403         depMgmt.add(dep(root, 1), null, null, "managed");
404         session.setDependencyManager(depMgmt);
405 
406         // collect result will differ from expectedSubtreeComparisonResult.txt
407         // set localPath -> no dependency traversal
408         CollectRequest request = new CollectRequest(dep(root), singletonList(repository));
409         CollectResult result = collector.collectDependencies(session, request);
410 
411         DependencyNode node = result.getRoot();
412         assertEquals("managed", dep(node, 0, 1).getArtifact().getVersion());
413         assertEquals("managed", dep(node, 0, 1).getScope());
414 
415         assertEquals("managed", dep(node, 1).getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null));
416         assertEquals("managed", dep(node, 0, 0).getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null));
417     }
418 
419     @Test
420     public void testDependencyManagement_VerboseMode() throws Exception {
421         String depId = "gid:aid2:ext";
422         TestDependencyManager depMgmt = new TestDependencyManager();
423         depMgmt.version(depId, "managedVersion");
424         depMgmt.scope(depId, "managedScope");
425         depMgmt.optional(depId, Boolean.TRUE);
426         depMgmt.path(depId, "managedPath");
427         depMgmt.exclusions(depId, new Exclusion("gid", "aid", "*", "*"));
428         session.setDependencyManager(depMgmt);
429         session.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, Boolean.TRUE);
430 
431         CollectRequest request = new CollectRequest().setRoot(newDep("gid:aid:ver"));
432         CollectResult result = collector.collectDependencies(session, request);
433         DependencyNode node = result.getRoot().getChildren().get(0);
434         assertEquals(
435                 DependencyNode.MANAGED_VERSION
436                         | DependencyNode.MANAGED_SCOPE
437                         | DependencyNode.MANAGED_OPTIONAL
438                         | DependencyNode.MANAGED_PROPERTIES
439                         | DependencyNode.MANAGED_EXCLUSIONS,
440                 node.getManagedBits());
441         assertEquals("ver", DependencyManagerUtils.getPremanagedVersion(node));
442         assertEquals("compile", DependencyManagerUtils.getPremanagedScope(node));
443         assertEquals(Boolean.FALSE, DependencyManagerUtils.getPremanagedOptional(node));
444     }
445 
446     @Test
447     public void testDependencyManagement_TransitiveDependencyManager()
448             throws DependencyCollectionException, IOException {
449         collector.setArtifactDescriptorReader(newReader("managed/"));
450         parser = new DependencyGraphParser("artifact-descriptions/managed/");
451         session.setDependencyManager(new TransitiveDependencyManager());
452         final Dependency root = newDep("gid:root:ext:ver", "compile");
453         CollectRequest request = new CollectRequest(root, singletonList(repository));
454         request.addManagedDependency(newDep("gid:root:ext:must-retain-core-management"));
455         CollectResult result = collector.collectDependencies(session, request);
456 
457         final DependencyNode expectedTree = parser.parseResource("management-tree.txt");
458         assertEqualSubtree(expectedTree, result.getRoot());
459 
460         // Same test for root artifact (POM) request.
461         final CollectRequest rootArtifactRequest = new CollectRequest();
462         rootArtifactRequest.setRepositories(singletonList(repository));
463         rootArtifactRequest.setRootArtifact(new DefaultArtifact("gid:root:ext:ver"));
464         rootArtifactRequest.addDependency(newDep("gid:direct:ext:ver", "compile"));
465         rootArtifactRequest.addManagedDependency(newDep("gid:root:ext:must-retain-core-management"));
466         rootArtifactRequest.addManagedDependency(newDep("gid:direct:ext:must-retain-core-management"));
467         rootArtifactRequest.addManagedDependency(newDep("gid:transitive-1:ext:managed-by-root"));
468         session.setDependencyManager(new TransitiveDependencyManager());
469         result = collector.collectDependencies(session, rootArtifactRequest);
470         assertEqualSubtree(expectedTree, toDependencyResult(result.getRoot(), "compile", null));
471     }
472 
473     @Test
474     public void testDependencyManagement_DefaultDependencyManager() throws DependencyCollectionException, IOException {
475         collector.setArtifactDescriptorReader(newReader("managed/"));
476         parser = new DependencyGraphParser("artifact-descriptions/managed/");
477         session.setDependencyManager(new DefaultDependencyManager());
478         final Dependency root = newDep("gid:root:ext:ver", "compile");
479         CollectRequest request = new CollectRequest(root, singletonList(repository));
480         request.addManagedDependency(newDep("gid:root:ext:must-not-manage-root"));
481         request.addManagedDependency(newDep("gid:direct:ext:managed-by-dominant-request"));
482         CollectResult result = collector.collectDependencies(session, request);
483 
484         final DependencyNode expectedTree = parser.parseResource("default-management-tree.txt");
485         assertEqualSubtree(expectedTree, result.getRoot());
486 
487         // Same test for root artifact (POM) request.
488         final CollectRequest rootArtifactRequest = new CollectRequest();
489         rootArtifactRequest.setRepositories(singletonList(repository));
490         rootArtifactRequest.setRootArtifact(new DefaultArtifact("gid:root:ext:ver"));
491         rootArtifactRequest.addDependency(newDep("gid:direct:ext:ver", "compile"));
492         rootArtifactRequest.addManagedDependency(newDep("gid:root:ext:must-not-manage-root"));
493         rootArtifactRequest.addManagedDependency(newDep("gid:direct:ext:managed-by-dominant-request"));
494         rootArtifactRequest.addManagedDependency(newDep("gid:transitive-1:ext:managed-by-root"));
495         session.setDependencyManager(new DefaultDependencyManager());
496         result = collector.collectDependencies(session, rootArtifactRequest);
497         assertEqualSubtree(expectedTree, toDependencyResult(result.getRoot(), "compile", null));
498     }
499 
500     @Test
501     public void testTransitiveDepsUseRangesDirtyTree() throws DependencyCollectionException, IOException {
502         // Note: DF depends on version order (ultimately the order of versions as returned by VersionRangeResolver
503         // that in case of Maven, means order as in maven-metadata.xml
504         // BF on the other hand explicitly sorts versions from range in descending order
505         //
506         // Hence, the "dirty tree" of two will not match.
507         DependencyNode root = parser.parseResource(getTransitiveDepsUseRangesDirtyTreeResource());
508         Dependency dependency = root.getDependency();
509         CollectRequest request = new CollectRequest(dependency, singletonList(repository));
510 
511         CollectResult result = collector.collectDependencies(session, request);
512         assertEqualSubtree(root, result.getRoot());
513     }
514 
515     protected abstract String getTransitiveDepsUseRangesDirtyTreeResource();
516 
517     private DependencyNode toDependencyResult(
518             final DependencyNode root, final String rootScope, final Boolean optional) {
519         // Make the root artifact resultion result a dependency resolution result for the subtree check.
520         assertNull("Expected root artifact resolution result.", root.getDependency());
521         final DefaultDependencyNode defaultNode =
522                 new DefaultDependencyNode(new Dependency(root.getArtifact(), rootScope));
523 
524         defaultNode.setChildren(root.getChildren());
525 
526         if (optional != null) {
527             defaultNode.setOptional(optional);
528         }
529 
530         return defaultNode;
531     }
532 
533     @Test
534     public void testVersionFilter() throws Exception {
535         session.setVersionFilter(new HighestVersionFilter());
536         CollectRequest request = new CollectRequest().setRoot(newDep("gid:aid:1"));
537         CollectResult result = collector.collectDependencies(session, request);
538         assertEquals(1, result.getRoot().getChildren().size());
539     }
540 
541     @Test
542     public void testDescriptorDependenciesEmpty() throws Exception {
543         collector.setArtifactDescriptorReader(newReader("dependencies-empty/"));
544 
545         session.setDependencyGraphTransformer(new ConflictResolver(
546                 new NearestVersionSelector(),
547                 new JavaScopeSelector(),
548                 new SimpleOptionalitySelector(),
549                 new JavaScopeDeriver()));
550 
551         DependencyNode root = parser.parseResource("expectedSubtreeOnDescriptorDependenciesEmptyLeft.txt");
552         Dependency dependency = root.getDependency();
553         CollectRequest request = new CollectRequest(dependency, singletonList(repository));
554         CollectResult result = collector.collectDependencies(session, request);
555         assertEqualSubtree(root, result.getRoot());
556 
557         root = parser.parseResource("expectedSubtreeOnDescriptorDependenciesEmptyRight.txt");
558         dependency = root.getDependency();
559         request = new CollectRequest(dependency, singletonList(repository));
560         result = collector.collectDependencies(session, request);
561         assertEqualSubtree(root, result.getRoot());
562     }
563 
564     static class TestDependencyManager implements DependencyManager {
565 
566         private final Map<String, String> versions = new HashMap<>();
567 
568         private final Map<String, String> scopes = new HashMap<>();
569 
570         private final Map<String, Boolean> optionals = new HashMap<>();
571 
572         private final Map<String, String> paths = new HashMap<>();
573 
574         private final Map<String, Collection<Exclusion>> exclusions = new HashMap<>();
575 
576         public void add(Dependency d, String version, String scope, String localPath) {
577             String id = toKey(d);
578             version(id, version);
579             scope(id, scope);
580             path(id, localPath);
581         }
582 
583         public void version(String id, String version) {
584             versions.put(id, version);
585         }
586 
587         public void scope(String id, String scope) {
588             scopes.put(id, scope);
589         }
590 
591         public void optional(String id, Boolean optional) {
592             optionals.put(id, optional);
593         }
594 
595         public void path(String id, String path) {
596             paths.put(id, path);
597         }
598 
599         public void exclusions(String id, Exclusion... exclusions) {
600             this.exclusions.put(id, exclusions != null ? Arrays.asList(exclusions) : null);
601         }
602 
603         @Override
604         public DependencyManagement manageDependency(Dependency dependency) {
605             requireNonNull(dependency, "dependency cannot be null");
606             String id = toKey(dependency);
607             DependencyManagement mgmt = new DependencyManagement();
608             mgmt.setVersion(versions.get(id));
609             mgmt.setScope(scopes.get(id));
610             mgmt.setOptional(optionals.get(id));
611             String path = paths.get(id);
612             if (path != null) {
613                 mgmt.setProperties(Collections.singletonMap(ArtifactProperties.LOCAL_PATH, path));
614             }
615             mgmt.setExclusions(exclusions.get(id));
616             return mgmt;
617         }
618 
619         private String toKey(Dependency dependency) {
620             return ArtifactIdUtils.toVersionlessId(dependency.getArtifact());
621         }
622 
623         @Override
624         public DependencyManager deriveChildManager(DependencyCollectionContext context) {
625             requireNonNull(context, "context cannot be null");
626             return this;
627         }
628     }
629 }