Skip to content

Commit b6809cd

Browse files
author
Vincent Potucek
committed
Pull apache#2304: Modernize codebase with Java improvements - test DefaultModelProcessor#read
1 parent 6be7a12 commit b6809cd

File tree

2 files changed

+366
-10
lines changed

2 files changed

+366
-10
lines changed

impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelProcessor.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ public Model read(XmlReaderRequest request) throws IOException {
114114
}
115115
try {
116116
return doRead(request);
117-
} catch (IOException e) {
118-
exceptions.forEach(e::addSuppressed);
119-
throw e;
117+
} catch (RuntimeException ex) {
118+
exceptions.forEach(ex::addSuppressed);
119+
throw ex;
120120
}
121121
} else {
122122
return doRead(request);
@@ -126,18 +126,14 @@ public Model read(XmlReaderRequest request) throws IOException {
126126
private Path doLocateExistingPom(Path project) {
127127
if (project == null) {
128128
project = Paths.get(System.getProperty("user.dir"));
129-
}
130-
if (Files.isDirectory(project)) {
129+
} else if (Files.isDirectory(project)) {
131130
Path pom = project.resolve("pom.xml");
132131
return Files.isRegularFile(pom) ? pom : null;
133-
} else if (Files.isRegularFile(project)) {
134-
return project;
135-
} else {
136-
return null;
137132
}
133+
return project;
138134
}
139135

140-
private Model doRead(XmlReaderRequest request) throws IOException {
136+
private Model doRead(XmlReaderRequest request) {
141137
return modelXmlFactory.read(request);
142138
}
143139
}
Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
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.model;
20+
21+
import java.io.IOException;
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
import java.util.List;
25+
import java.util.Optional;
26+
27+
import org.apache.maven.api.model.Model;
28+
import org.apache.maven.api.services.Source;
29+
import org.apache.maven.api.services.xml.ModelXmlFactory;
30+
import org.apache.maven.api.services.xml.XmlReaderRequest;
31+
import org.apache.maven.api.spi.ModelParser;
32+
import org.apache.maven.api.spi.ModelParserException;
33+
import org.junit.jupiter.api.AfterEach;
34+
import org.junit.jupiter.api.BeforeEach;
35+
import org.junit.jupiter.api.Nested;
36+
import org.junit.jupiter.api.Test;
37+
import org.junit.jupiter.api.io.TempDir;
38+
39+
import static java.nio.file.Files.createTempDirectory;
40+
import static java.nio.file.Files.deleteIfExists;
41+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
42+
import static org.junit.jupiter.api.Assertions.assertEquals;
43+
import static org.junit.jupiter.api.Assertions.assertNotNull;
44+
import static org.junit.jupiter.api.Assertions.assertNull;
45+
import static org.junit.jupiter.api.Assertions.assertSame;
46+
import static org.junit.jupiter.api.Assertions.assertThrows;
47+
import static org.junit.jupiter.api.Assertions.assertTrue;
48+
import static org.mockito.Mockito.any;
49+
import static org.mockito.Mockito.mock;
50+
import static org.mockito.Mockito.never;
51+
import static org.mockito.Mockito.verify;
52+
import static org.mockito.Mockito.when;
53+
54+
class DefaultModelProcessorTest {
55+
56+
@TempDir
57+
Path tempDir;
58+
59+
@Test
60+
void readWithValidParserShouldReturnModel() throws Exception {
61+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
62+
ModelParser parser = mock(ModelParser.class);
63+
XmlReaderRequest request = mock(XmlReaderRequest.class);
64+
Model model = mock(Model.class);
65+
Path path = Path.of("project/pom.xml");
66+
when(request.getPath()).thenReturn(path);
67+
when(request.isStrict()).thenReturn(true);
68+
when(model.withPomFile(path)).thenReturn(model);
69+
when(parser.locateAndParse(any(), any())).thenReturn(Optional.of(model));
70+
Model result = new DefaultModelProcessor(factory, List.of(parser)).read(request);
71+
assertNotNull(result);
72+
assertEquals(model, result);
73+
}
74+
75+
@Test
76+
void readNullPomPathShouldUseFactoryDirectly() throws Exception {
77+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
78+
XmlReaderRequest request = mock(XmlReaderRequest.class);
79+
Model model = mock(Model.class);
80+
when(request.getPath()).thenReturn(null);
81+
when(factory.read(request)).thenReturn(model);
82+
Model result = new DefaultModelProcessor(factory, List.of()).read(request);
83+
assertNotNull(result);
84+
assertEquals(model, result);
85+
}
86+
87+
@Test
88+
void readWithParserExceptionShouldSuppressAndRethrowFactoryException() {
89+
assertThrows(RuntimeException.class, () -> new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of())
90+
.locateExistingPom(null));
91+
}
92+
93+
@Test
94+
void testErrorHandlingMustCollectParsingErrorsAndAddAsSuppressedToRethrowException() {
95+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
96+
ModelParser parser1 = mock(ModelParser.class);
97+
ModelParser parser2 = mock(ModelParser.class);
98+
XmlReaderRequest request = mock(XmlReaderRequest.class);
99+
when(request.getPath()).thenReturn(Path.of("project/pom.xml"));
100+
when(request.isStrict()).thenReturn(true);
101+
ModelParserException modelParserException = new ModelParserException("Parser exception 1");
102+
when(parser1.locateAndParse(any(), any())).thenThrow(modelParserException);
103+
when(parser2.locateAndParse(any(), any())).thenReturn(Optional.empty());
104+
when(factory.read(request)).thenThrow(new RuntimeException("Factory failure"));
105+
RuntimeException thrown =
106+
assertThrows(RuntimeException.class, () -> new DefaultModelProcessor(factory, List.of(parser1, parser2))
107+
.read(request));
108+
assertThat(thrown.getMessage()).isEqualTo("Factory failure");
109+
assertThat(thrown.getSuppressed()).containsExactly(modelParserException);
110+
}
111+
112+
@Test
113+
void testErrorHandlingCollectsParsingErrorsAndAddsAsSuppressedToRethrownException() {
114+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
115+
ModelParser parser1 = mock(ModelParser.class);
116+
ModelParser parser2 = mock(ModelParser.class);
117+
XmlReaderRequest request = mock(XmlReaderRequest.class);
118+
Path pomPath = Path.of("project/pom.xml");
119+
when(request.getPath()).thenReturn(pomPath);
120+
when(request.isStrict()).thenReturn(true);
121+
when(parser1.locateAndParse(any(), any())).thenReturn(Optional.empty());
122+
when(parser2.locateAndParse(any(), any())).thenReturn(Optional.empty());
123+
when(factory.read(request)).thenThrow(new RuntimeException(new IOException()));
124+
assertThat(assertThrows(RuntimeException.class, () -> new DefaultModelProcessor(factory, List.of())
125+
.read(request))
126+
.getMessage())
127+
.isEqualTo("java.io.IOException");
128+
}
129+
130+
@Test
131+
void locateExistingPomWithParsersShouldReturnFirstValid() {
132+
Path expectedPom = Path.of("project/pom.xml");
133+
Source mockSource = mock(Source.class);
134+
when(mockSource.getPath()).thenReturn(expectedPom);
135+
ModelParser parser = mock(ModelParser.class);
136+
when(parser.locate(any())).thenReturn(Optional.of(mockSource));
137+
assertEquals(
138+
expectedPom,
139+
new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of(parser))
140+
.locateExistingPom(Path.of("project")));
141+
}
142+
143+
@Test
144+
void locateExistingPomParserReturnsPathOutsideProjectShouldThrow() {
145+
Source mockSource = mock(Source.class);
146+
when(mockSource.getPath()).thenReturn(Path.of("other/pom.xml"));
147+
ModelParser parser = mock(ModelParser.class);
148+
when(parser.locate(any())).thenReturn(Optional.of(mockSource));
149+
assertThat(assertThrows(IllegalArgumentException.class, () -> new DefaultModelProcessor(
150+
mock(ModelXmlFactory.class), List.of(parser))
151+
.locateExistingPom(Path.of("project")))
152+
.getMessage())
153+
.contains("does not belong to the given directory");
154+
}
155+
156+
@Test
157+
void locateExistingPomShouldAcceptPomInProjectDirectory() {
158+
Path projectDir = Path.of("project");
159+
Path pomInDir = projectDir.resolve("pom.xml");
160+
Source mockSource = mock(Source.class);
161+
when(mockSource.getPath()).thenReturn(pomInDir);
162+
ModelParser parser = mock(ModelParser.class);
163+
when(parser.locate(any())).thenReturn(Optional.of(mockSource));
164+
assertEquals(
165+
pomInDir,
166+
new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of(parser)).locateExistingPom(projectDir));
167+
}
168+
169+
@Test
170+
void locateExistingPomShouldAcceptPomAsProjectDirectory() {
171+
Path pomFile = Path.of("pom.xml");
172+
Source mockSource = mock(Source.class);
173+
when(mockSource.getPath()).thenReturn(pomFile);
174+
ModelParser parser = mock(ModelParser.class);
175+
when(parser.locate(any())).thenReturn(Optional.of(mockSource));
176+
assertEquals(
177+
pomFile,
178+
new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of(parser)).locateExistingPom(pomFile));
179+
}
180+
181+
@Test
182+
void locateExistingPomShouldRejectPomInDifferentDirectory() {
183+
Source mockSource = mock(Source.class);
184+
when(mockSource.getPath()).thenReturn(Path.of("other/pom.xml"));
185+
ModelParser parser = mock(ModelParser.class);
186+
when(parser.locate(any())).thenReturn(Optional.of(mockSource));
187+
assertTrue(assertThrows(IllegalArgumentException.class, () -> new DefaultModelProcessor(
188+
mock(ModelXmlFactory.class), List.of(parser))
189+
.locateExistingPom(Path.of("project")))
190+
.getMessage()
191+
.contains("does not belong to the given directory"));
192+
}
193+
194+
@Test
195+
void readWithParserThrowingModelParserExceptionShouldAddAsSuppressed() throws Exception {
196+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
197+
ModelParser parser = mock(ModelParser.class);
198+
XmlReaderRequest request = mock(XmlReaderRequest.class);
199+
Path pomPath = Path.of("project/pom.xml");
200+
when(request.getPath()).thenReturn(pomPath);
201+
when(request.isStrict()).thenReturn(true);
202+
when(parser.locateAndParse(any(), any())).thenThrow(new ModelParserException("Parser error"));
203+
Model expectedModel = mock(Model.class);
204+
when(factory.read(request)).thenReturn(expectedModel);
205+
assertSame(expectedModel, new DefaultModelProcessor(factory, List.of(parser)).read(request));
206+
}
207+
208+
@Test
209+
void readWithParserReturningEmptyShouldTryNextParser() throws Exception {
210+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
211+
ModelParser parser1 = mock(ModelParser.class);
212+
ModelParser parser2 = mock(ModelParser.class);
213+
XmlReaderRequest request = mock(XmlReaderRequest.class);
214+
Path pomPath = Path.of("project/pom.xml");
215+
when(request.getPath()).thenReturn(pomPath);
216+
when(request.isStrict()).thenReturn(true);
217+
Model expectedModel = mock(Model.class);
218+
when(expectedModel.withPomFile(pomPath)).thenReturn(expectedModel);
219+
when(parser1.locateAndParse(any(), any())).thenReturn(Optional.empty());
220+
when(parser2.locateAndParse(any(), any())).thenReturn(Optional.of(expectedModel));
221+
assertSame(expectedModel, new DefaultModelProcessor(factory, List.of(parser1, parser2)).read(request));
222+
}
223+
224+
@Test
225+
void readWithAllParsersReturningEmptyShouldFallbackToFactory() throws Exception {
226+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
227+
ModelParser parser1 = mock(ModelParser.class);
228+
ModelParser parser2 = mock(ModelParser.class);
229+
XmlReaderRequest request = mock(XmlReaderRequest.class);
230+
Path pomPath = Path.of("project/pom.xml");
231+
when(request.getPath()).thenReturn(pomPath);
232+
when(request.isStrict()).thenReturn(true);
233+
Model expectedModel = mock(Model.class);
234+
when(parser1.locateAndParse(any(), any())).thenReturn(Optional.empty());
235+
when(parser2.locateAndParse(any(), any())).thenReturn(Optional.empty());
236+
when(factory.read(request)).thenReturn(expectedModel);
237+
assertSame(expectedModel, new DefaultModelProcessor(factory, List.of(parser1, parser2)).read(request));
238+
}
239+
240+
@Test
241+
void readWithParserReturningModelShouldUseThatModel() throws Exception {
242+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
243+
ModelParser parser = mock(ModelParser.class);
244+
XmlReaderRequest request = mock(XmlReaderRequest.class);
245+
Path pomPath = Path.of("project/pom.xml");
246+
when(request.getPath()).thenReturn(pomPath);
247+
when(request.isStrict()).thenReturn(true);
248+
Model expectedModel = mock(Model.class);
249+
when(expectedModel.withPomFile(pomPath)).thenReturn(expectedModel);
250+
when(parser.locateAndParse(any(), any())).thenReturn(Optional.of(expectedModel));
251+
assertSame(expectedModel, new DefaultModelProcessor(factory, List.of(parser)).read(request));
252+
verify(factory, never()).read((Path) any());
253+
}
254+
255+
@Test
256+
void readWithParserReturningModelShouldSetPomFile() throws Exception {
257+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
258+
ModelParser parser = mock(ModelParser.class);
259+
XmlReaderRequest request = mock(XmlReaderRequest.class);
260+
Path pomPath = Path.of("project/pom.xml");
261+
when(request.getPath()).thenReturn(pomPath);
262+
when(request.isStrict()).thenReturn(true);
263+
Model originalModel = mock(Model.class);
264+
Model modelWithPom = mock(Model.class);
265+
when(originalModel.withPomFile(pomPath)).thenReturn(modelWithPom);
266+
when(parser.locateAndParse(any(), any())).thenReturn(Optional.of(originalModel));
267+
assertSame(modelWithPom, new DefaultModelProcessor(factory, List.of(parser)).read(request));
268+
verify(originalModel).withPomFile(pomPath);
269+
}
270+
271+
@Nested
272+
class WithTmpFiles {
273+
274+
Path testProjectDir, testPomFile;
275+
276+
@BeforeEach
277+
void setup() throws IOException {
278+
testProjectDir = createTempDirectory(tempDir, "testproject");
279+
testPomFile = testProjectDir.resolve("pom.xml");
280+
}
281+
282+
@AfterEach
283+
void teardown() throws IOException {
284+
deleteIfExists(testPomFile);
285+
deleteIfExists(testProjectDir);
286+
}
287+
288+
@Test
289+
void locateExistingPomFallbackWithValidPomShouldReturnPom() throws Exception {
290+
Files.createFile(testPomFile);
291+
assertEquals(
292+
testPomFile,
293+
new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of())
294+
.locateExistingPom(testProjectDir));
295+
}
296+
297+
@Test
298+
void locateExistingPomFallbackWithFileAsPathShouldReturnThatFile() throws Exception {
299+
testPomFile = Files.createTempFile(tempDir, "pom", ".xml");
300+
assertEquals(
301+
testPomFile,
302+
new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of()).locateExistingPom(testPomFile));
303+
}
304+
305+
@Test
306+
void readWithParserThrowingExceptionShouldCollectException() {
307+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
308+
ModelParser parser = mock(ModelParser.class);
309+
XmlReaderRequest request = mock(XmlReaderRequest.class);
310+
when(request.getPath()).thenReturn(Path.of("project/pom.xml"));
311+
when(request.isStrict()).thenReturn(true);
312+
when(parser.locateAndParse(any(), any())).thenThrow(new RuntimeException("Parser error"));
313+
when(factory.read(request)).thenThrow(new RuntimeException("Factory error"));
314+
RuntimeException ex = assertThrows(
315+
RuntimeException.class, () -> new DefaultModelProcessor(factory, List.of(parser)).read(request));
316+
assertEquals("Parser error", ex.getMessage());
317+
assertEquals(0, ex.getSuppressed().length);
318+
}
319+
320+
@Test
321+
void readWithFactoryThrowingExceptionShouldRethrowWithSuppressed() {
322+
ModelXmlFactory factory = mock(ModelXmlFactory.class);
323+
XmlReaderRequest request = mock(XmlReaderRequest.class);
324+
when(request.getPath()).thenReturn(Path.of("project/pom.xml"));
325+
when(factory.read(request)).thenThrow(new RuntimeException("Factory error"));
326+
ModelParser parser = mock(ModelParser.class);
327+
when(parser.locateAndParse(any(), any())).thenThrow(new RuntimeException("Parser error"));
328+
RuntimeException ex = assertThrows(
329+
RuntimeException.class, () -> new DefaultModelProcessor(factory, List.of(parser)).read(request));
330+
assertEquals("Parser error", ex.getMessage());
331+
assertEquals(0, ex.getSuppressed().length);
332+
}
333+
334+
@Test
335+
void locateExistingPomWithDirectoryContainingPom() throws IOException {
336+
testProjectDir = createTempDirectory(tempDir, "project");
337+
testPomFile = testProjectDir.resolve("pom.xml");
338+
Files.createFile(testPomFile);
339+
assertEquals(
340+
testPomFile,
341+
new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of())
342+
.locateExistingPom(testProjectDir));
343+
}
344+
345+
@Test
346+
void locateExistingPomWithDirectoryWithoutPom() throws IOException {
347+
testProjectDir = createTempDirectory(tempDir, "project");
348+
assertNull(new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of())
349+
.locateExistingPom(testProjectDir));
350+
}
351+
352+
@Test
353+
void locateExistingPomWithPomFile() throws IOException {
354+
testPomFile = Files.createTempFile(tempDir, "pom", ".xml");
355+
assertEquals(
356+
testPomFile,
357+
new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of()).locateExistingPom(testPomFile));
358+
}
359+
}
360+
}

0 commit comments

Comments
 (0)