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