From 0b67c7d825f3f833af8be1722fab6dd6d56c2647 Mon Sep 17 00:00:00 2001 From: Mazen Kamal <71020170+Mazen050@users.noreply.github.com> Date: Sat, 8 Aug 2026 10:54:28 +0000 Subject: [PATCH 1/2] Fix duplicate model problem reporting --- .../project/ProjectBuildingException.java | 84 +-------- .../project/ProjectBuildingExceptionTest.java | 163 ------------------ 2 files changed, 1 insertion(+), 246 deletions(-) delete mode 100644 impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java index 89ca05d43a13..39c20e108269 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java @@ -21,8 +21,6 @@ import java.io.File; import java.util.List; -import org.apache.maven.model.building.ModelProblem; - /** * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead */ @@ -63,7 +61,7 @@ protected ProjectBuildingException(String projectId, String message, File pomFil } public ProjectBuildingException(List results) { - super(createMessage(results)); + super("Some problems were encountered while processing the POMs"); this.projectId = ""; this.results = results; } @@ -101,84 +99,4 @@ private static String createMessage(String message, String projectId, File pomFi } return buffer.toString(); } - - private static String createMessage(List results) { - if (results == null || results.isEmpty()) { - return "Some problems were encountered while processing the POMs"; - } - - long totalProblems = 0; - long errorProblems = 0; - - for (ProjectBuildingResult result : results) { - List problems = result.getProblems(); - totalProblems += problems.size(); - - for (ModelProblem problem : problems) { - if (problem.getSeverity() != ModelProblem.Severity.WARNING) { - errorProblems++; - } - } - } - - StringBuilder buffer = new StringBuilder(1024); - buffer.append(totalProblems); - buffer.append(totalProblems == 1 ? " problem was " : " problems were "); - buffer.append("encountered while processing the POMs"); - - if (errorProblems > 0) { - buffer.append(" (") - .append(errorProblems) - .append(" ") - .append(errorProblems > 1 ? "errors" : "error") - .append(")"); - } - - buffer.append(":\n"); - - for (ProjectBuildingResult result : results) { - if (!result.getProblems().isEmpty()) { - String projectInfo = result.getProjectId(); - if (projectInfo.trim().isEmpty()) { - projectInfo = - result.getPomFile() != null ? result.getPomFile().getName() : "unknown project"; - } - - buffer.append("\n[").append(projectInfo).append("]\n"); - - for (ModelProblem problem : result.getProblems()) { - if (errorProblems > 0 && problem.getSeverity() == ModelProblem.Severity.WARNING) { - continue; - } - - buffer.append(" [").append(problem.getSeverity()).append("] "); - buffer.append(problem.getMessage()); - - String location = ""; - if (!problem.getSource().trim().isEmpty()) { - location = problem.getSource(); - } - if (problem.getLineNumber() > 0) { - if (!location.isEmpty()) { - location += ", "; - } - location += "line " + problem.getLineNumber(); - } - if (problem.getColumnNumber() > 0) { - if (!location.isEmpty()) { - location += ", "; - } - location += "column " + problem.getColumnNumber(); - } - - if (!location.isEmpty()) { - buffer.append(" @ ").append(location); - } - buffer.append("\n"); - } - } - } - - return buffer.toString(); - } } diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java deleted file mode 100644 index fae4355e0e37..000000000000 --- a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.project; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.apache.maven.model.building.DefaultModelProblem; -import org.apache.maven.model.building.ModelProblem; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Test for {@link ProjectBuildingException} message generation. - */ -@SuppressWarnings("deprecation") -class ProjectBuildingExceptionTest { - - @Test - void testDetailedExceptionMessageWithMultipleProblems() { - List results = new ArrayList<>(); - - List problems1 = new ArrayList<>(); - Collections.addAll( - problems1, - new DefaultModelProblem( - "Missing required dependency", - ModelProblem.Severity.ERROR, - null, - "pom.xml", - 25, - 10, - null, - null), - new DefaultModelProblem( - "Invalid version format", ModelProblem.Severity.ERROR, null, "pom.xml", 30, 5, null, null)); - DefaultProjectBuildingResult result1 = - new DefaultProjectBuildingResult("com.example:project1:1.0", new File("project1/pom.xml"), problems1); - results.add(result1); - - List problems2 = new ArrayList<>(); - Collections.addAll( - problems2, - new DefaultModelProblem( - "Deprecated plugin usage", ModelProblem.Severity.WARNING, null, "pom.xml", 15, 3, null, null)); - DefaultProjectBuildingResult result2 = - new DefaultProjectBuildingResult("com.example:project2:1.0", new File("project2/pom.xml"), problems2); - results.add(result2); - - ProjectBuildingException exception = new ProjectBuildingException(results); - String message = exception.getMessage(); - - assertTrue( - message.contains("3 problems were encountered while processing the POMs (2 errors)"), - "Message should contain problem count and error count"); - - assertTrue(message.contains("[com.example:project1:1.0]"), "Message should contain project1 identifier"); - - assertTrue(message.contains("[com.example:project2:1.0]"), "Message should contain project2 identifier"); - - assertTrue( - message.contains("[ERROR] Missing required dependency @ pom.xml, line 25, column 10"), - "Message should contain error details with location"); - - assertTrue( - message.contains("[ERROR] Invalid version format @ pom.xml, line 30, column 5"), - "Message should contain second error details"); - - assertTrue( - !message.contains("[WARNING]") || message.contains("[WARNING] Deprecated plugin usage"), - "Warnings should be filtered when errors are present or shown if explicitly included"); - } - - @Test - void testExceptionMessageWithOnlyWarnings() { - List results = new ArrayList<>(); - - List problems = new ArrayList<>(); - Collections.addAll( - problems, - new DefaultModelProblem( - "Deprecated feature used", ModelProblem.Severity.WARNING, null, "pom.xml", 10, 1, null, null)); - DefaultProjectBuildingResult result = - new DefaultProjectBuildingResult("com.example:project:1.0", new File("project/pom.xml"), problems); - results.add(result); - - ProjectBuildingException exception = new ProjectBuildingException(results); - String message = exception.getMessage(); - - assertTrue( - message.contains("1 problem was encountered while processing the POMs"), - "Message should use singular form for single problem"); - - assertTrue( - message.contains("[WARNING] Deprecated feature used"), - "Message should contain warning when no errors are present"); - - assertTrue( - !message.contains("(") || !message.contains("error"), - "Message should not contain error count when there are no errors"); - } - - @Test - void testExceptionMessageWithEmptyResults() { - List results = Collections.emptyList(); - - ProjectBuildingException exception = new ProjectBuildingException(results); - String message = exception.getMessage(); - - assertEquals( - "Some problems were encountered while processing the POMs", - message, - "Empty results should fall back to generic message"); - } - - @Test - void testExceptionMessageWithNullResults() { - ProjectBuildingException exception = new ProjectBuildingException((List) null); - String message = exception.getMessage(); - - assertEquals( - "Some problems were encountered while processing the POMs", - message, - "Null results should fall back to generic message"); - } - - @Test - void testExceptionMessageWithUnknownProject() { - List results = new ArrayList<>(); - - List problems = new ArrayList<>(); - Collections.addAll( - problems, - new DefaultModelProblem("Some error", ModelProblem.Severity.ERROR, null, "unknown", 1, 1, null, null)); - DefaultProjectBuildingResult result = new DefaultProjectBuildingResult(null, null, problems); - results.add(result); - - ProjectBuildingException exception = new ProjectBuildingException(results); - String message = exception.getMessage(); - - assertTrue(message.contains("[unknown project]"), "Message should handle unknown project gracefully"); - } -} From bf53934991ccff7f2914a27aeefffb4d828c2df9 Mon Sep 17 00:00:00 2001 From: Mazen Kamal <71020170+Mazen050@users.noreply.github.com> Date: Sat, 8 Aug 2026 16:23:07 +0000 Subject: [PATCH 2/2] Add regression test for duplicate error rendering --- .../DefaultExceptionHandlerTest.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java b/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java index 88359e2e8f6b..2f4a3a8b3b36 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java @@ -18,17 +18,23 @@ */ package org.apache.maven.exception; +import java.io.File; import java.io.IOException; import java.net.ConnectException; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.maven.model.Plugin; +import org.apache.maven.model.building.DefaultModelProblem; +import org.apache.maven.model.building.ModelProblem; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.PluginContainerException; import org.apache.maven.plugin.PluginExecutionException; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.project.ProjectBuildingResult; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactResolutionException; @@ -38,6 +44,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** */ @@ -179,4 +187,72 @@ void testHandleExceptionSelfReferencing() { assertEquals(0, summary.getChildren().size()); assertEquals(boom1, summary.getException()); } + + @Test + void testProjectBuildingExceptionNotRenderedTwice() { + ModelProblem problem = new DefaultModelProblem( + "Malformed POM test.xml: unexpected element", + ModelProblem.Severity.FATAL, + null, + "test.xml", + 8, + 3, + null, + null); + + ProjectBuildingResult result = mock(ProjectBuildingResult.class); + when(result.getProjectId()).thenReturn("test:fail-build:0.1-SNAPSHOT"); + when(result.getPomFile()).thenReturn(new File("test.xml")); + when(result.getProblems()).thenReturn(List.of(problem)); + + ProjectBuildingException exception = new ProjectBuildingException(List.of(result)); + + assertEquals( + "Some problems were encountered while processing the POMs", + exception.getMessage(), + "exception message must stay the short summary, not per-problem detail"); + + ExceptionSummary summary = new DefaultExceptionHandler().handleException(exception); + + ExceptionSummary projectSummary = summary.getChildren().get(0); + ExceptionSummary problemSummary = projectSummary.getChildren().get(0); + + assertTrue( + problemSummary.getMessage().contains("Malformed POM test.xml"), + "the handler's own tree should still render the problem detail exactly once"); + + assertEquals(1, countOccurrences(flatten(summary), "Malformed POM test.xml: unexpected element")); + } + + private static String flatten(ExceptionSummary summary) { + if (summary == null) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + if (summary.getMessage() != null) { + sb.append(summary.getMessage()); + } + + for (ExceptionSummary child : summary.getChildren()) { + if (!sb.isEmpty()) { + sb.append('\n'); + } + sb.append(flatten(child)); + } + + return sb.toString(); + } + + private static int countOccurrences(String text, String substring) { + int count = 0; + int index = 0; + + while ((index = text.indexOf(substring, index)) != -1) { + count++; + index += substring.length(); + } + + return count; + } }