Skip to content

Commit 9a9bdb1

Browse files
committed
Adding DefaultDependencyResolverResultTest
1 parent 9b95526 commit 9a9bdb1

File tree

1 file changed

+365
-0
lines changed

1 file changed

+365
-0
lines changed
Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
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.apache.maven.impl;
20+
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.util.ArrayList;
24+
import java.util.List;
25+
import java.util.Optional;
26+
import java.util.function.Predicate;
27+
import java.util.jar.Attributes;
28+
import java.util.jar.JarOutputStream;
29+
import java.util.jar.Manifest;
30+
31+
import org.apache.maven.api.Dependency;
32+
import org.apache.maven.api.JavaPathType;
33+
import org.apache.maven.api.Node;
34+
import org.apache.maven.api.PathType;
35+
import org.apache.maven.api.services.DependencyResolverRequest;
36+
import org.apache.maven.impl.resolver.type.DefaultType;
37+
import org.junit.jupiter.api.Test;
38+
39+
import static org.junit.jupiter.api.Assertions.assertEquals;
40+
import static org.junit.jupiter.api.Assertions.assertThrows;
41+
import static org.junit.jupiter.api.Assertions.assertTrue;
42+
import static org.mockito.Mockito.mock;
43+
import static org.mockito.Mockito.when;
44+
45+
/** Unit tests for {@link DefaultDependencyResolverResult}. */
46+
public class DefaultDependencyResolverResultTest {
47+
48+
private static DefaultType createJarType(PathType pathType) {
49+
return new DefaultType("jar", org.apache.maven.api.Language.JAVA_FAMILY, "jar", null, false, pathType);
50+
}
51+
52+
@Test
53+
public void testAddDependencyWithNullDependencyAddsNodeOnly() throws Exception {
54+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
55+
List<Exception> exceptions = new ArrayList<>();
56+
Node root = mock(Node.class);
57+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
58+
59+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 4);
60+
61+
Node node = mock(Node.class);
62+
// addDependency with null dependency should only add the node
63+
result.addDependency(node, null, (Predicate<PathType>) (t) -> true, null);
64+
65+
assertEquals(1, result.getNodes().size());
66+
assertEquals(0, result.getDependencies().size());
67+
assertEquals(0, result.getPaths().size());
68+
assertTrue(result.getDispatchedPaths().isEmpty());
69+
}
70+
71+
@Test
72+
public void testAddDependencyDuplicateThrows() throws Exception {
73+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
74+
List<Exception> exceptions = new ArrayList<>();
75+
Node root = mock(Node.class);
76+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
77+
78+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 4);
79+
80+
Dependency dep = mock(Dependency.class);
81+
when(dep.getGroupId()).thenReturn("g");
82+
when(dep.getArtifactId()).thenReturn("a");
83+
when(dep.getType()).thenReturn(createJarType(JavaPathType.MODULES));
84+
85+
Node node = mock(Node.class);
86+
Path p = Files.createTempFile("dup", ".jar");
87+
88+
result.addDependency(node, dep, (Predicate<PathType>) (t) -> true, p);
89+
90+
// adding the same dependency again should throw
91+
IllegalStateException ex = assertThrows(
92+
IllegalStateException.class,
93+
() -> result.addDependency(node, dep, (Predicate<PathType>) (t) -> true, p));
94+
assertTrue(ex.getMessage().contains("Duplicated key"));
95+
}
96+
97+
@Test
98+
public void testAddDependencyWithAutomaticModuleNameAndGetModuleName() throws Exception {
99+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
100+
List<Exception> exceptions = new ArrayList<>();
101+
Node root = mock(Node.class);
102+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
103+
104+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 4);
105+
106+
// create a jar with Automatic-Module-Name manifest attribute
107+
Path jar = Files.createTempFile("auto-module", ".jar");
108+
Manifest mf = new Manifest();
109+
mf.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
110+
mf.getMainAttributes().put(new Attributes.Name("Automatic-Module-Name"), "auto.mod");
111+
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(jar), mf)) {
112+
// empty jar with manifest
113+
}
114+
115+
Dependency dep = mock(Dependency.class);
116+
when(dep.getGroupId()).thenReturn("g");
117+
when(dep.getArtifactId()).thenReturn("a");
118+
when(dep.getType()).thenReturn(createJarType(JavaPathType.MODULES));
119+
120+
Node node = mock(Node.class);
121+
122+
result.addDependency(node, dep, (Predicate<PathType>) (t) -> true, jar);
123+
124+
assertEquals(1, result.getDependencies().size());
125+
assertEquals(1, result.getPaths().size());
126+
127+
Optional<String> moduleName = result.getModuleName(jar);
128+
assertTrue(moduleName.isPresent());
129+
assertEquals("auto.mod", moduleName.get());
130+
131+
Optional<java.lang.module.ModuleDescriptor> descriptor = result.getModuleDescriptor(jar);
132+
assertTrue(descriptor.isEmpty());
133+
}
134+
135+
@Test
136+
public void testSelectPathTypeUnknownsBecomeUnresolved() throws Exception {
137+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
138+
List<Exception> exceptions = new ArrayList<>();
139+
Node root = mock(Node.class);
140+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
141+
142+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 4);
143+
144+
Dependency dep = mock(Dependency.class);
145+
when(dep.getGroupId()).thenReturn("g.u");
146+
when(dep.getArtifactId()).thenReturn("a.u");
147+
Path p = Files.createTempFile("unres", ".jar");
148+
149+
// Type returns a known CLASSES and an unknown custom PathType => selectPathType should return empty
150+
when(dep.getType()).thenReturn(createJarType(JavaPathType.UNRESOLVED));
151+
152+
Node node = mock(Node.class);
153+
result.addDependency(node, dep, (Predicate<PathType>) (t) -> true, p);
154+
155+
assertTrue(result.getDispatchedPaths().containsKey(org.apache.maven.api.PathType.UNRESOLVED));
156+
assertTrue(result.getDispatchedPaths()
157+
.get(org.apache.maven.api.PathType.UNRESOLVED)
158+
.contains(p));
159+
}
160+
161+
@Test
162+
public void testAddOutputDirectoryWithNullMainPlacesTestOnClasspath() throws Exception {
163+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
164+
List<Exception> exceptions = new ArrayList<>();
165+
Node root = mock(Node.class);
166+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
167+
168+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 4);
169+
170+
Path testDir = Files.createTempDirectory("test-out");
171+
result.addOutputDirectory(null, testDir);
172+
173+
assertTrue(result.getDispatchedPaths().containsKey(JavaPathType.CLASSES));
174+
assertEquals(1, result.getDispatchedPaths().get(JavaPathType.CLASSES).size());
175+
assertEquals(
176+
testDir, result.getDispatchedPaths().get(JavaPathType.CLASSES).get(0));
177+
}
178+
179+
@Test
180+
public void testAddOutputDirectoryCalledTwiceThrows() throws Exception {
181+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
182+
List<Exception> exceptions = new ArrayList<>();
183+
Node root = mock(Node.class);
184+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
185+
186+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 4);
187+
188+
Path testDir = Files.createTempDirectory("test-out2");
189+
result.addOutputDirectory(null, testDir);
190+
191+
assertThrows(IllegalStateException.class, () -> result.addOutputDirectory(null, testDir));
192+
}
193+
194+
@Test
195+
public void testReturnedCollectionsAreUnmodifiable() throws Exception {
196+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
197+
List<Exception> exceptions = new ArrayList<>();
198+
Node root = mock(Node.class);
199+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
200+
201+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 4);
202+
203+
Node n1 = mock(Node.class);
204+
Node n2 = mock(Node.class);
205+
result.addNode(n1);
206+
result.addNode(n2);
207+
208+
// nodes list should be unmodifiable
209+
assertThrows(
210+
UnsupportedOperationException.class, () -> result.getNodes().add(mock(Node.class)));
211+
212+
// add a dependency to populate paths
213+
Dependency dep = mock(Dependency.class);
214+
when(dep.getGroupId()).thenReturn("g3");
215+
when(dep.getArtifactId()).thenReturn("a3");
216+
when(dep.getType()).thenReturn(createJarType(JavaPathType.CLASSES));
217+
218+
Path p = Files.createTempFile("path", ".jar");
219+
result.addDependency(n1, dep, (Predicate<PathType>) (t) -> true, p);
220+
221+
assertThrows(
222+
UnsupportedOperationException.class, () -> result.getPaths().add(Path.of("x")));
223+
}
224+
225+
@Test
226+
public void testAddOutputDirectoryWithMainPlacesMainOnModulePath() throws Exception {
227+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
228+
List<Exception> exceptions = new ArrayList<>();
229+
Node root = mock(Node.class);
230+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
231+
232+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 4);
233+
234+
// create a jar with Automatic-Module-Name manifest attribute to simulate a modular main output
235+
Path mainJar = Files.createTempFile("main-module", ".jar");
236+
Manifest mf = new Manifest();
237+
mf.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
238+
mf.getMainAttributes().put(new Attributes.Name("Automatic-Module-Name"), "main.mod");
239+
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(mainJar), mf)) {
240+
// empty jar with manifest
241+
}
242+
243+
result.addOutputDirectory(mainJar, null);
244+
245+
// main output should have been placed on the module path
246+
assertTrue(result.getDispatchedPaths().containsKey(JavaPathType.MODULES));
247+
assertEquals(1, result.getDispatchedPaths().get(JavaPathType.MODULES).size());
248+
assertEquals(
249+
mainJar, result.getDispatchedPaths().get(JavaPathType.MODULES).get(0));
250+
}
251+
252+
@Test
253+
public void testAddDependencyPatchingExistingModuleUsesPatchModule() throws Exception {
254+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
255+
List<Exception> exceptions = new ArrayList<>();
256+
Node root = mock(Node.class);
257+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
258+
259+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 8);
260+
261+
// first dependency: modular artifact that will be placed on module path
262+
Path moduleJar1 = Files.createTempFile("mod1", ".jar");
263+
Manifest mf1 = new Manifest();
264+
mf1.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
265+
mf1.getMainAttributes().put(new Attributes.Name("Automatic-Module-Name"), "modA");
266+
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(moduleJar1), mf1)) {
267+
// empty jar with manifest
268+
}
269+
270+
Dependency dep1 = mock(Dependency.class);
271+
when(dep1.getGroupId()).thenReturn("g1");
272+
when(dep1.getArtifactId()).thenReturn("a1");
273+
when(dep1.getType()).thenReturn(createJarType(JavaPathType.MODULES));
274+
275+
Node node = mock(Node.class);
276+
// add first dependency -> should be on MODULES
277+
result.addDependency(node, dep1, (Predicate<PathType>) (t) -> true, moduleJar1);
278+
279+
// second dependency: a patch-module for the same module name "modA"
280+
Path moduleJar2 = Files.createTempFile("mod2", ".jar");
281+
Manifest mf2 = new Manifest();
282+
mf2.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
283+
mf2.getMainAttributes().put(new Attributes.Name("Automatic-Module-Name"), "modA");
284+
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(moduleJar2), mf2)) {
285+
// empty jar with manifest
286+
}
287+
288+
Dependency dep2 = mock(Dependency.class);
289+
when(dep2.getGroupId()).thenReturn("g2");
290+
when(dep2.getArtifactId()).thenReturn("a2");
291+
when(dep2.getType())
292+
.thenReturn(new DefaultType(
293+
"jar",
294+
org.apache.maven.api.Language.JAVA_FAMILY,
295+
"jar",
296+
null,
297+
false,
298+
JavaPathType.PATCH_MODULE));
299+
300+
// add second dependency -> should detect existing module and dispatch as patch-module(modA)
301+
result.addDependency(node, dep2, (Predicate<PathType>) (t) -> true, moduleJar2);
302+
303+
JavaPathType.Modular patchForModA = JavaPathType.patchModule("modA");
304+
assertTrue(result.getDispatchedPaths().containsKey(patchForModA));
305+
assertTrue(result.getDispatchedPaths().get(patchForModA).contains(moduleJar2));
306+
}
307+
308+
@Test
309+
public void testAddDependencyPatchingByArtifactWhenNoModuleInfoButMatchingArtifactExists() throws Exception {
310+
DependencyResolverRequest req = mock(DependencyResolverRequest.class);
311+
List<Exception> exceptions = new ArrayList<>();
312+
Node root = mock(Node.class);
313+
PathModularizationCache cache = new PathModularizationCache(Runtime.version());
314+
315+
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(req, cache, exceptions, root, 8);
316+
317+
// main artifact (provides module info)
318+
Path mainJar = Files.createTempFile("main-artifact", ".jar");
319+
Manifest mfMain = new Manifest();
320+
mfMain.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
321+
mfMain.getMainAttributes().put(new Attributes.Name("Automatic-Module-Name"), "modB");
322+
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(mainJar), mfMain)) {
323+
// empty jar with manifest
324+
}
325+
326+
Dependency mainDep = mock(Dependency.class);
327+
when(mainDep.getGroupId()).thenReturn("gX");
328+
when(mainDep.getArtifactId()).thenReturn("aX");
329+
when(mainDep.getType())
330+
.thenReturn(new DefaultType(
331+
"jar", org.apache.maven.api.Language.JAVA_FAMILY, "jar", null, false, JavaPathType.MODULES));
332+
333+
Node node = mock(Node.class);
334+
// add main artifact
335+
result.addDependency(node, mainDep, (Predicate<PathType>) (t) -> true, mainJar);
336+
337+
// patch artifact which has no module info itself
338+
Path patchJar = Files.createTempFile("patch-no-modinfo", ".jar");
339+
// create an empty jar without Automatic-Module-Name
340+
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(patchJar), new Manifest())) {
341+
// empty jar
342+
}
343+
344+
Dependency patchDep = mock(Dependency.class);
345+
when(patchDep.getGroupId()).thenReturn("gX");
346+
when(patchDep.getArtifactId()).thenReturn("aX"); // same identifiers -> findArtifactPath should find mainDep
347+
when(patchDep.getType())
348+
.thenReturn(new DefaultType(
349+
"jar",
350+
org.apache.maven.api.Language.JAVA_FAMILY,
351+
"jar",
352+
null,
353+
false,
354+
JavaPathType.PATCH_MODULE));
355+
356+
// add the patch dependency; since it has no module info, findArtifactPath should pick up mainJar and add a
357+
// patch for modB
358+
result.addDependency(node, patchDep, (Predicate<PathType>) (t) -> true, patchJar);
359+
360+
JavaPathType.Modular patchForModB = JavaPathType.patchModule("modB");
361+
assertTrue(result.getDispatchedPaths().containsKey(patchForModB));
362+
// The code will add the main artifact's descriptor path for patching (info.getKey()), assert mainJar present
363+
assertTrue(result.getDispatchedPaths().get(patchForModB).contains(mainJar));
364+
}
365+
}

0 commit comments

Comments
 (0)