Skip to content

Commit 02b5deb

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

File tree

2 files changed

+355
-10
lines changed

2 files changed

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

0 commit comments

Comments
 (0)