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