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.bf;
20  
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.stream.Collectors;
26  
27  import org.eclipse.aether.artifact.DefaultArtifact;
28  import org.eclipse.aether.graph.DefaultDependencyNode;
29  import org.eclipse.aether.graph.Dependency;
30  import org.eclipse.aether.graph.DependencyNode;
31  import org.eclipse.aether.internal.test.util.TestVersion;
32  import org.eclipse.aether.internal.test.util.TestVersionConstraint;
33  import org.junit.Test;
34  
35  import static org.junit.Assert.assertEquals;
36  import static org.junit.Assert.assertFalse;
37  import static org.junit.Assert.assertTrue;
38  
39  public class DependencyResolutionSkipperTest {
40      private static DependencyNode makeDependencyNode(String groupId, String artifactId, String version) {
41          return makeDependencyNode(groupId, artifactId, version, "compile");
42      }
43  
44      private static List<DependencyNode> mutableList(DependencyNode... nodes) {
45          return new ArrayList<>(Arrays.asList(nodes));
46      }
47  
48      private static DependencyNode makeDependencyNode(String groupId, String artifactId, String version, String scope) {
49          DefaultDependencyNode node = new DefaultDependencyNode(
50                  new Dependency(new DefaultArtifact(groupId + ':' + artifactId + ':' + version), scope));
51          node.setVersion(new TestVersion(version));
52          node.setVersionConstraint(new TestVersionConstraint(node.getVersion()));
53          return node;
54      }
55  
56      @Test
57      public void testSkipVersionConflict() {
58          // A -> B -> C 3.0 -> D   => C3.0 SHOULD BE SKIPPED
59          // | -> E -> F -> G
60          // | -> C 2.0 -> H  => C2.0 is the winner
61          DependencyNode aNode = makeDependencyNode("some-group", "A", "1.0");
62          DependencyNode bNode = makeDependencyNode("some-group", "B", "1.0");
63          DependencyNode c3Node = makeDependencyNode("some-group", "C", "3.0");
64          DependencyNode dNode = makeDependencyNode("some-group", "D", "1.0");
65          DependencyNode eNode = makeDependencyNode("some-group", "E", "1.0");
66          DependencyNode fNode = makeDependencyNode("some-group", "F", "1.0");
67          DependencyNode c2Node = makeDependencyNode("some-group", "C", "2.0");
68          DependencyNode gNode = makeDependencyNode("some-group", "G", "1.0");
69          DependencyNode hNode = makeDependencyNode("some-group", "H", "1.0");
70  
71          aNode.setChildren(mutableList(bNode, eNode, c2Node));
72          bNode.setChildren(mutableList(c3Node));
73          c3Node.setChildren(mutableList(dNode));
74          eNode.setChildren(mutableList(fNode));
75          fNode.setChildren(mutableList(gNode));
76          c2Node.setChildren(mutableList(hNode));
77  
78          // follow the BFS resolve sequence
79          DependencyResolutionSkipper.DefaultDependencyResolutionSkipper skipper =
80                  DependencyResolutionSkipper.defaultSkipper();
81          assertFalse(skipper.skipResolution(aNode, new ArrayList<>()));
82          skipper.cache(aNode, new ArrayList<>());
83          assertFalse(skipper.skipResolution(bNode, mutableList(aNode)));
84          skipper.cache(bNode, mutableList(aNode));
85          assertFalse(skipper.skipResolution(eNode, mutableList(aNode)));
86          skipper.cache(eNode, mutableList(aNode));
87          assertFalse(skipper.skipResolution(c2Node, mutableList(aNode)));
88          skipper.cache(c2Node, mutableList(aNode));
89          assertTrue(skipper.skipResolution(c3Node, mutableList(aNode, bNode))); // version conflict
90          assertFalse(skipper.skipResolution(fNode, mutableList(aNode, eNode)));
91          skipper.cache(fNode, mutableList(aNode, eNode));
92          assertFalse(skipper.skipResolution(gNode, mutableList(aNode, eNode, fNode)));
93          skipper.cache(gNode, mutableList(aNode, eNode, fNode));
94  
95          Map<DependencyNode, DependencyResolutionSkipper.DependencyResolutionResult> results = skipper.getResults();
96          assertEquals(results.size(), 7);
97  
98          List<DependencyResolutionSkipper.DependencyResolutionResult> skipped = results.values().stream()
99                  .filter(dependencyResolutionResult -> dependencyResolutionResult.skippedAsVersionConflict)
100                 .collect(Collectors.toList());
101         assertEquals(skipped.size(), 1);
102         assertTrue(skipped.get(0).current == c3Node);
103     }
104 
105     @Test
106     public void testSkipDeeperDuplicateNode() {
107         // A -> B
108         // |--> C -> B  => B here will be skipped
109         // |--> D -> C  => C here will be skipped
110         DependencyNode aNode = makeDependencyNode("some-group", "A", "1.0");
111         DependencyNode bNode = makeDependencyNode("some-group", "B", "1.0");
112         DependencyNode cNode = makeDependencyNode("some-group", "C", "1.0");
113         DependencyNode dNode = makeDependencyNode("some-group", "D", "1.0");
114         DependencyNode b1Node = new DefaultDependencyNode(bNode);
115         DependencyNode c1Node = new DefaultDependencyNode(cNode);
116 
117         aNode.setChildren(mutableList(bNode, cNode, dNode));
118         bNode.setChildren(new ArrayList<>());
119         cNode.setChildren(mutableList(b1Node));
120         dNode.setChildren(mutableList(c1Node));
121 
122         // follow the BFS resolve sequence
123         DependencyResolutionSkipper.DefaultDependencyResolutionSkipper skipper =
124                 DependencyResolutionSkipper.defaultSkipper();
125         assertFalse(skipper.skipResolution(aNode, new ArrayList<>()));
126         skipper.cache(aNode, new ArrayList<>());
127         assertFalse(skipper.skipResolution(bNode, mutableList(aNode)));
128         skipper.cache(bNode, mutableList(aNode));
129         assertFalse(skipper.skipResolution(cNode, mutableList(aNode)));
130         skipper.cache(cNode, mutableList(aNode));
131         assertFalse(skipper.skipResolution(dNode, mutableList(aNode)));
132         skipper.cache(dNode, mutableList(aNode));
133 
134         assertTrue(skipper.skipResolution(b1Node, mutableList(aNode, cNode)));
135         skipper.cache(b1Node, mutableList(aNode, cNode));
136 
137         assertTrue(skipper.skipResolution(c1Node, mutableList(aNode, dNode)));
138         skipper.cache(c1Node, mutableList(aNode, dNode));
139 
140         Map<DependencyNode, DependencyResolutionSkipper.DependencyResolutionResult> results = skipper.getResults();
141         assertEquals(results.size(), 6);
142 
143         List<DependencyResolutionSkipper.DefaultDependencyResolutionSkipper.DependencyResolutionResult> skipped =
144                 results.values().stream()
145                         .filter(dependencyResolutionResult -> dependencyResolutionResult.skippedAsDuplicate)
146                         .collect(Collectors.toList());
147         assertEquals(skipped.size(), 2);
148         assertTrue(skipped.get(0).current == b1Node);
149         assertTrue(skipped.get(1).current == c1Node);
150     }
151 
152     @Test
153     public void testForceResolution() {
154         // A -> B -> C -> D => 3rd D here will be force-resolved
155         // |--> C -> D => 2nd D will be force-resolved
156         // |--> D => 1st D to resolve
157         DependencyNode aNode = makeDependencyNode("some-group", "A", "1.0");
158         DependencyNode bNode = makeDependencyNode("some-group", "B", "1.0");
159         DependencyNode cNode = makeDependencyNode("some-group", "C", "1.0");
160         DependencyNode dNode = makeDependencyNode("some-group", "D", "1.0");
161         DependencyNode c1Node = new DefaultDependencyNode(cNode);
162         DependencyNode d1Node = new DefaultDependencyNode(dNode);
163         DependencyNode d2Node = new DefaultDependencyNode(dNode);
164 
165         aNode.setChildren(mutableList(bNode, cNode, dNode));
166         bNode.setChildren(mutableList(c1Node));
167         c1Node.setChildren(mutableList(d2Node));
168         cNode.setChildren(mutableList(d1Node));
169         dNode.setChildren(new ArrayList<>());
170 
171         // follow the BFS resolve sequence
172         DependencyResolutionSkipper.DefaultDependencyResolutionSkipper skipper =
173                 DependencyResolutionSkipper.defaultSkipper();
174         assertFalse(skipper.skipResolution(aNode, new ArrayList<>()));
175         skipper.cache(aNode, new ArrayList<>());
176         assertFalse(skipper.skipResolution(bNode, mutableList(aNode)));
177         skipper.cache(bNode, mutableList(aNode));
178         assertFalse(skipper.skipResolution(cNode, mutableList(aNode)));
179         skipper.cache(cNode, mutableList(aNode));
180         assertFalse(skipper.skipResolution(dNode, mutableList(aNode)));
181         skipper.cache(dNode, mutableList(aNode));
182 
183         assertFalse(skipper.skipResolution(c1Node, mutableList(aNode, bNode)));
184         skipper.cache(c1Node, mutableList(aNode, bNode));
185 
186         assertFalse(skipper.skipResolution(d1Node, mutableList(aNode, cNode)));
187         skipper.cache(d1Node, mutableList(aNode, cNode));
188 
189         assertFalse(skipper.skipResolution(d2Node, mutableList(aNode, bNode, c1Node)));
190         skipper.cache(d2Node, mutableList(aNode, bNode, c1Node));
191 
192         Map<DependencyNode, DependencyResolutionSkipper.DependencyResolutionResult> results = skipper.getResults();
193         assertEquals(results.size(), 7);
194 
195         List<DependencyResolutionSkipper.DefaultDependencyResolutionSkipper.DependencyResolutionResult> forceResolved =
196                 results.values().stream()
197                         .filter(dependencyResolutionResult -> dependencyResolutionResult.forceResolution)
198                         .collect(Collectors.toList());
199         assertEquals(forceResolved.size(), 3);
200         assertTrue(forceResolved.get(0).current == c1Node);
201         assertTrue(forceResolved.get(1).current == d1Node);
202         assertTrue(forceResolved.get(2).current == d2Node);
203     }
204 }