This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch maven-4.0.x-test-fixes
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 680cd1d14129a1d1d42f2f137da8513d80d9c7a4
Author: Guillaume Nodet <[email protected]>
AuthorDate: Wed May 27 00:55:07 2026 +0200

    Remove invalid combine.self and combine.children attributes instead of 
converting them
    
    Maven 3 silently ignored invalid combine.self and combine.children
    attribute values, so removing them entirely preserves actual Maven 3
    behavior. Previously mvnup converted specific invalid values (e.g.
    combine.self="append" → "merge"), which could miss other invalid values
    and subtly changed semantics. Now both fixers detect any invalid value
    against the full set of valid values (combine.self: override/merge/remove;
    combine.children: append/merge), remove the attribute, and collect to a
    list before mutating to avoid potential stream processing issues.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../mvnup/goals/CompatibilityFixStrategy.java      |  66 ++--
 .../mvnup/goals/CompatibilityFixStrategyTest.java  | 410 +++++++++++++++++++++
 2 files changed, 452 insertions(+), 24 deletions(-)

diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java
index c8c99d30ac..343caf147b 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java
@@ -77,6 +77,10 @@ public class CompatibilityFixStrategy extends 
AbstractUpgradeStrategy {
 
     private static final Pattern EXPRESSION_PATTERN = 
Pattern.compile("\\$\\{([^}]+)}");
 
+    private static final Set<String> VALID_COMBINE_SELF_VALUES = 
Set.of(COMBINE_OVERRIDE, COMBINE_MERGE, "remove");
+
+    private static final Set<String> VALID_COMBINE_CHILDREN_VALUES = 
Set.of(COMBINE_APPEND, COMBINE_MERGE);
+
     @Override
     public boolean isApplicable(UpgradeContext context) {
         UpgradeOptions options = getOptions(context);
@@ -165,44 +169,44 @@ public UpgradeResult doApply(UpgradeContext context, 
Map<Path, Document> pomMap)
 
     /**
      * Fixes unsupported combine.children attribute values.
-     * Maven 4 only supports 'append' and 'merge', not 'override'.
+     * Maven 4 only supports 'append' and 'merge' (default is merge).
+     * Invalid values are removed entirely since Maven 3 silently ignored them.
      */
     private boolean fixUnsupportedCombineChildrenAttributes(Document 
pomDocument, UpgradeContext context) {
-        boolean fixed = false;
         Element root = pomDocument.root();
 
-        // Find all elements with combine.children="override" and change to 
"merge"
-        long fixedCombineChildrenCount = findElementsWithAttribute(root, 
COMBINE_CHILDREN, COMBINE_OVERRIDE)
-                .peek(element -> {
-                    
element.attributeObject(COMBINE_CHILDREN).value(COMBINE_MERGE);
-                    context.detail("Fixed: " + COMBINE_CHILDREN + "='" + 
COMBINE_OVERRIDE + "' → '" + COMBINE_MERGE
-                            + "' in " + element.name());
-                })
-                .count();
-        fixed |= fixedCombineChildrenCount > 0;
+        List<Element> invalidElements = findElementsWithInvalidAttribute(
+                        root, COMBINE_CHILDREN, VALID_COMBINE_CHILDREN_VALUES)
+                .toList();
 
-        return fixed;
+        for (Element element : invalidElements) {
+            String invalidValue = element.attribute(COMBINE_CHILDREN);
+            element.removeAttribute(COMBINE_CHILDREN);
+            context.detail(
+                    "Fixed: removed invalid " + COMBINE_CHILDREN + "='" + 
invalidValue + "' from " + element.name());
+        }
+
+        return !invalidElements.isEmpty();
     }
 
     /**
      * Fixes unsupported combine.self attribute values.
-     * Maven 4 only supports 'override', 'merge', and 'remove' (default is 
merge), not 'append'.
+     * Maven 4 only supports 'override', 'merge', and 'remove' (default is 
merge).
+     * Invalid values are removed entirely since Maven 3 silently ignored them.
      */
     private boolean fixUnsupportedCombineSelfAttributes(Document pomDocument, 
UpgradeContext context) {
-        boolean fixed = false;
         Element root = pomDocument.root();
 
-        // Find all elements with combine.self="append" and change to "merge"
-        long fixedCombineSelfCount = findElementsWithAttribute(root, 
COMBINE_SELF, COMBINE_APPEND)
-                .peek(element -> {
-                    element.attributeObject(COMBINE_SELF).value(COMBINE_MERGE);
-                    context.detail("Fixed: " + COMBINE_SELF + "='" + 
COMBINE_APPEND + "' → '" + COMBINE_MERGE + "' in "
-                            + element.name());
-                })
-                .count();
-        fixed |= fixedCombineSelfCount > 0;
+        List<Element> invalidElements = findElementsWithInvalidAttribute(root, 
COMBINE_SELF, VALID_COMBINE_SELF_VALUES)
+                .toList();
 
-        return fixed;
+        for (Element element : invalidElements) {
+            String invalidValue = element.attribute(COMBINE_SELF);
+            element.removeAttribute(COMBINE_SELF);
+            context.detail("Fixed: removed invalid " + COMBINE_SELF + "='" + 
invalidValue + "' from " + element.name());
+        }
+
+        return !invalidElements.isEmpty();
     }
 
     /**
@@ -527,6 +531,20 @@ private Stream<Element> findElementsWithAttribute(Element 
element, String attrib
                         .flatMap(child -> findElementsWithAttribute(child, 
attributeName, attributeValue)));
     }
 
+    /**
+     * Recursively finds all elements with an attribute whose value is not in 
the set of valid values.
+     */
+    private Stream<Element> findElementsWithInvalidAttribute(
+            Element element, String attributeName, Set<String> validValues) {
+        return Stream.concat(
+                Stream.of(element).filter(e -> {
+                    String attr = e.attribute(attributeName);
+                    return attr != null && !validValues.contains(attr);
+                }),
+                element.childElements()
+                        .flatMap(child -> 
findElementsWithInvalidAttribute(child, attributeName, validValues)));
+    }
+
     /**
      * Helper methods extracted from BaseUpgradeGoal for compatibility fixes.
      */
diff --git 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategyTest.java
 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategyTest.java
index 7795c25d9d..051a33fd34 100644
--- 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategyTest.java
+++ 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategyTest.java
@@ -140,6 +140,416 @@ void shouldHandleAllOptionsDisabled() {
         }
     }
 
+    @Nested
+    @DisplayName("Unsupported combine.children Attribute Fixes")
+    class UnsupportedCombineChildrenFixesTests {
+
+        @Test
+        @DisplayName("should remove combine.children='override' attribute")
+        void shouldRemoveCombineChildrenOverride() throws Exception {
+            String pomXml = """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <project xmlns="http://maven.apache.org/POM/4.0.0";>
+                    <modelVersion>4.0.0</modelVersion>
+                    <groupId>test</groupId>
+                    <artifactId>test</artifactId>
+                    <version>1.0.0</version>
+                    <build>
+                        <plugins>
+                            <plugin>
+                                <groupId>org.apache.maven.plugins</groupId>
+                                <artifactId>maven-compiler-plugin</artifactId>
+                                <configuration>
+                                    <compilerArgs combine.children="override">
+                                        <arg>-Xlint:all</arg>
+                                    </compilerArgs>
+                                </configuration>
+                            </plugin>
+                        </plugins>
+                    </build>
+                </project>
+                """;
+
+            Document document = Document.of(pomXml);
+            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), 
document);
+
+            UpgradeContext context = createMockContext();
+            UpgradeResult result = strategy.doApply(context, pomMap);
+
+            assertTrue(result.success(), "Compatibility fix should succeed");
+            assertTrue(result.modifiedCount() > 0, "Should have fixed 
combine.children attribute");
+
+            String xml = DomUtils.toXml(document);
+            assertFalse(xml.contains("combine.children"), "combine.children 
attribute should be removed entirely");
+            assertTrue(xml.contains("compilerArgs"), "Element should still 
exist");
+        }
+
+        @Test
+        @DisplayName("should remove any invalid combine.children value")
+        void shouldRemoveAnyCombineChildrenInvalidValue() throws Exception {
+            String pomXml = """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <project xmlns="http://maven.apache.org/POM/4.0.0";>
+                    <modelVersion>4.0.0</modelVersion>
+                    <groupId>test</groupId>
+                    <artifactId>test</artifactId>
+                    <version>1.0.0</version>
+                    <build>
+                        <plugins>
+                            <plugin>
+                                <groupId>org.apache.maven.plugins</groupId>
+                                <artifactId>maven-compiler-plugin</artifactId>
+                                <configuration>
+                                    <compilerArgs 
combine.children="invalid_value">
+                                        <arg>-Xlint:all</arg>
+                                    </compilerArgs>
+                                </configuration>
+                            </plugin>
+                        </plugins>
+                    </build>
+                </project>
+                """;
+
+            Document document = Document.of(pomXml);
+            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), 
document);
+
+            UpgradeContext context = createMockContext();
+            UpgradeResult result = strategy.doApply(context, pomMap);
+
+            assertTrue(result.success(), "Compatibility fix should succeed");
+            assertTrue(result.modifiedCount() > 0, "Should have fixed 
combine.children attribute");
+
+            String xml = DomUtils.toXml(document);
+            assertFalse(xml.contains("combine.children"), "combine.children 
attribute should be removed entirely");
+        }
+
+        @Test
+        @DisplayName("should not remove valid combine.children values")
+        void shouldNotRemoveValidCombineChildrenValues() throws Exception {
+            String pomXml = """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <project xmlns="http://maven.apache.org/POM/4.0.0";>
+                    <modelVersion>4.0.0</modelVersion>
+                    <groupId>test</groupId>
+                    <artifactId>test</artifactId>
+                    <version>1.0.0</version>
+                    <build>
+                        <plugins>
+                            <plugin>
+                                <groupId>org.apache.maven.plugins</groupId>
+                                <artifactId>maven-compiler-plugin</artifactId>
+                                <configuration>
+                                    <compilerArgs combine.children="append">
+                                        <arg>-Xlint:all</arg>
+                                    </compilerArgs>
+                                    <includes combine.children="merge">
+                                        <include>**/*.java</include>
+                                    </includes>
+                                </configuration>
+                            </plugin>
+                        </plugins>
+                    </build>
+                </project>
+                """;
+
+            Document document = Document.of(pomXml);
+            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), 
document);
+
+            UpgradeContext context = createMockContext();
+            UpgradeResult result = strategy.doApply(context, pomMap);
+
+            assertTrue(result.success(), "Compatibility fix should succeed");
+            assertEquals(0, result.modifiedCount(), "Should not have modified 
any POMs");
+
+            String xml = DomUtils.toXml(document);
+            assertTrue(xml.contains("combine.children=\"append\""), "append 
should be preserved");
+            assertTrue(xml.contains("combine.children=\"merge\""), "merge 
should be preserved");
+        }
+
+        @Test
+        @DisplayName("should remove invalid combine.children in profile plugin 
configuration")
+        void shouldRemoveInvalidCombineChildrenInProfile() throws Exception {
+            String pomXml = """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <project xmlns="http://maven.apache.org/POM/4.0.0";>
+                    <modelVersion>4.0.0</modelVersion>
+                    <groupId>test</groupId>
+                    <artifactId>test</artifactId>
+                    <version>1.0.0</version>
+                    <profiles>
+                        <profile>
+                            <id>release</id>
+                            <build>
+                                <plugins>
+                                    <plugin>
+                                        
<groupId>org.apache.maven.plugins</groupId>
+                                        
<artifactId>maven-assembly-plugin</artifactId>
+                                        <configuration>
+                                            <descriptorRefs 
combine.children="override">
+                                                
<descriptorRef>src</descriptorRef>
+                                            </descriptorRefs>
+                                        </configuration>
+                                    </plugin>
+                                </plugins>
+                            </build>
+                        </profile>
+                    </profiles>
+                </project>
+                """;
+
+            Document document = Document.of(pomXml);
+            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), 
document);
+
+            UpgradeContext context = createMockContext();
+            UpgradeResult result = strategy.doApply(context, pomMap);
+
+            assertTrue(result.success(), "Compatibility fix should succeed");
+            assertTrue(result.modifiedCount() > 0, "Should have fixed 
combine.children in profile");
+
+            String xml = DomUtils.toXml(document);
+            assertFalse(xml.contains("combine.children"), "combine.children 
attribute should be removed from profile");
+        }
+    }
+
+    @Nested
+    @DisplayName("Unsupported combine.self Attribute Fixes")
+    class UnsupportedCombineSelfFixesTests {
+
+        @Test
+        @DisplayName("should remove combine.self='append' attribute")
+        void shouldRemoveCombineSelfAppend() throws Exception {
+            String pomXml = """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <project xmlns="http://maven.apache.org/POM/4.0.0";>
+                    <modelVersion>4.0.0</modelVersion>
+                    <groupId>test</groupId>
+                    <artifactId>test</artifactId>
+                    <version>1.0.0</version>
+                    <build>
+                        <plugins>
+                            <plugin>
+                                <groupId>org.apache.maven.plugins</groupId>
+                                <artifactId>maven-assembly-plugin</artifactId>
+                                <configuration>
+                                    <descriptorRefs combine.self="append">
+                                        
<descriptorRef>jar-with-dependencies</descriptorRef>
+                                    </descriptorRefs>
+                                </configuration>
+                            </plugin>
+                        </plugins>
+                    </build>
+                </project>
+                """;
+
+            Document document = Document.of(pomXml);
+            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), 
document);
+
+            UpgradeContext context = createMockContext();
+            UpgradeResult result = strategy.doApply(context, pomMap);
+
+            assertTrue(result.success(), "Compatibility fix should succeed");
+            assertTrue(result.modifiedCount() > 0, "Should have fixed 
combine.self attribute");
+
+            String xml = DomUtils.toXml(document);
+            assertFalse(xml.contains("combine.self"), "combine.self attribute 
should be removed entirely");
+            assertTrue(xml.contains("descriptorRefs"), "Element should still 
exist");
+        }
+
+        @Test
+        @DisplayName("should remove any invalid combine.self value")
+        void shouldRemoveAnyCombineSelfInvalidValue() throws Exception {
+            String pomXml = """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <project xmlns="http://maven.apache.org/POM/4.0.0";>
+                    <modelVersion>4.0.0</modelVersion>
+                    <groupId>test</groupId>
+                    <artifactId>test</artifactId>
+                    <version>1.0.0</version>
+                    <build>
+                        <plugins>
+                            <plugin>
+                                <groupId>org.apache.maven.plugins</groupId>
+                                <artifactId>maven-compiler-plugin</artifactId>
+                                <configuration>
+                                    <compilerArgs combine.self="invalid_value">
+                                        <arg>-Xlint:all</arg>
+                                    </compilerArgs>
+                                </configuration>
+                            </plugin>
+                        </plugins>
+                    </build>
+                </project>
+                """;
+
+            Document document = Document.of(pomXml);
+            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), 
document);
+
+            UpgradeContext context = createMockContext();
+            UpgradeResult result = strategy.doApply(context, pomMap);
+
+            assertTrue(result.success(), "Compatibility fix should succeed");
+            assertTrue(result.modifiedCount() > 0, "Should have fixed 
combine.self attribute");
+
+            String xml = DomUtils.toXml(document);
+            assertFalse(xml.contains("combine.self"), "combine.self attribute 
should be removed entirely");
+        }
+
+        @Test
+        @DisplayName("should not remove valid combine.self values")
+        void shouldNotRemoveValidCombineSelfValues() throws Exception {
+            String pomXml = """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <project xmlns="http://maven.apache.org/POM/4.0.0";>
+                    <modelVersion>4.0.0</modelVersion>
+                    <groupId>test</groupId>
+                    <artifactId>test</artifactId>
+                    <version>1.0.0</version>
+                    <build>
+                        <plugins>
+                            <plugin>
+                                <groupId>org.apache.maven.plugins</groupId>
+                                <artifactId>maven-compiler-plugin</artifactId>
+                                <configuration>
+                                    <source combine.self="override">11</source>
+                                    <target combine.self="merge">11</target>
+                                    <compilerArgs combine.self="remove"/>
+                                </configuration>
+                            </plugin>
+                        </plugins>
+                    </build>
+                </project>
+                """;
+
+            Document document = Document.of(pomXml);
+            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), 
document);
+
+            UpgradeContext context = createMockContext();
+            UpgradeResult result = strategy.doApply(context, pomMap);
+
+            assertTrue(result.success(), "Compatibility fix should succeed");
+            assertEquals(0, result.modifiedCount(), "Should not have modified 
any POMs");
+
+            String xml = DomUtils.toXml(document);
+            assertTrue(xml.contains("combine.self=\"override\""), "override 
should be preserved");
+            assertTrue(xml.contains("combine.self=\"merge\""), "merge should 
be preserved");
+            assertTrue(xml.contains("combine.self=\"remove\""), "remove should 
be preserved");
+        }
+
+        @Test
+        @DisplayName("should remove invalid combine.self in profile plugin 
configuration")
+        void shouldRemoveInvalidCombineSelfInProfile() throws Exception {
+            String pomXml = """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <project xmlns="http://maven.apache.org/POM/4.0.0";>
+                    <modelVersion>4.0.0</modelVersion>
+                    <groupId>test</groupId>
+                    <artifactId>test</artifactId>
+                    <version>1.0.0</version>
+                    <profiles>
+                        <profile>
+                            <id>release</id>
+                            <build>
+                                <plugins>
+                                    <plugin>
+                                        
<groupId>org.apache.maven.plugins</groupId>
+                                        
<artifactId>maven-assembly-plugin</artifactId>
+                                        <configuration>
+                                            <descriptorRefs 
combine.self="append">
+                                                
<descriptorRef>src</descriptorRef>
+                                            </descriptorRefs>
+                                        </configuration>
+                                    </plugin>
+                                </plugins>
+                            </build>
+                        </profile>
+                    </profiles>
+                </project>
+                """;
+
+            Document document = Document.of(pomXml);
+            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), 
document);
+
+            UpgradeContext context = createMockContext();
+            UpgradeResult result = strategy.doApply(context, pomMap);
+
+            assertTrue(result.success(), "Compatibility fix should succeed");
+            assertTrue(result.modifiedCount() > 0, "Should have fixed 
combine.self in profile");
+
+            String xml = DomUtils.toXml(document);
+            assertFalse(xml.contains("combine.self"), "combine.self attribute 
should be removed from profile");
+        }
+
+        @Test
+        @DisplayName("should remove all occurrences of invalid combine.self 
across the POM")
+        void shouldRemoveAllOccurrencesOfInvalidCombineSelf() throws Exception 
{
+            String pomXml = """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <project xmlns="http://maven.apache.org/POM/4.0.0";>
+                    <modelVersion>4.0.0</modelVersion>
+                    <groupId>test</groupId>
+                    <artifactId>test</artifactId>
+                    <version>1.0.0</version>
+                    <build>
+                        <pluginManagement>
+                            <plugins>
+                                <plugin>
+                                    <groupId>org.apache.maven.plugins</groupId>
+                                    
<artifactId>maven-compiler-plugin</artifactId>
+                                    <configuration>
+                                        <compilerArgs combine.self="append">
+                                            <arg>-Xlint:all</arg>
+                                        </compilerArgs>
+                                    </configuration>
+                                </plugin>
+                            </plugins>
+                        </pluginManagement>
+                        <plugins>
+                            <plugin>
+                                <groupId>org.apache.maven.plugins</groupId>
+                                <artifactId>maven-assembly-plugin</artifactId>
+                                <configuration>
+                                    <descriptorRefs combine.self="append">
+                                        
<descriptorRef>jar-with-dependencies</descriptorRef>
+                                    </descriptorRefs>
+                                </configuration>
+                            </plugin>
+                        </plugins>
+                    </build>
+                    <profiles>
+                        <profile>
+                            <id>release</id>
+                            <build>
+                                <plugins>
+                                    <plugin>
+                                        
<groupId>org.apache.maven.plugins</groupId>
+                                        
<artifactId>maven-assembly-plugin</artifactId>
+                                        <configuration>
+                                            <descriptorRefs 
combine.self="append">
+                                                
<descriptorRef>src</descriptorRef>
+                                            </descriptorRefs>
+                                        </configuration>
+                                    </plugin>
+                                </plugins>
+                            </build>
+                        </profile>
+                    </profiles>
+                </project>
+                """;
+
+            Document document = Document.of(pomXml);
+            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), 
document);
+
+            UpgradeContext context = createMockContext();
+            UpgradeResult result = strategy.doApply(context, pomMap);
+
+            assertTrue(result.success(), "Compatibility fix should succeed");
+            assertTrue(result.modifiedCount() > 0, "Should have fixed 
combine.self attributes");
+
+            String xml = DomUtils.toXml(document);
+            assertFalse(xml.contains("combine.self"), "All combine.self 
attributes should be removed");
+        }
+    }
+
     @Nested
     @DisplayName("Duplicate Dependency Fixes")
     class DuplicateDependencyFixesTests {

Reply via email to