This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven.git
commit 5583525a1d3a06583b2c26d3774c129ef76227da Author: Guillaume Nodet <[email protected]> AuthorDate: Tue May 26 16:11:30 2026 +0200 Fix #12080: mvnup - comment out dependencies with undefined property expressions Comment out dependencies whose version uses a property expression that is not defined in any POM in the reactor. Scans <properties> sections including those in profiles. Well-known properties (project.*, env.*, maven.*, etc.) are excluded from the check. Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../mvnup/goals/CompatibilityFixStrategy.java | 156 +++++++- .../mvnup/goals/CompatibilityFixStrategyTest.java | 443 +++++++++++++++++++++ 2 files changed, 598 insertions(+), 1 deletion(-) 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 1aac1be4c2..8739d17667 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 @@ -25,9 +25,13 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Stream; +import eu.maveniverse.domtrip.Comment; import eu.maveniverse.domtrip.Document; +import eu.maveniverse.domtrip.Editor; import eu.maveniverse.domtrip.Element; import eu.maveniverse.domtrip.maven.Coordinates; import eu.maveniverse.domtrip.maven.MavenPomElements; @@ -54,6 +58,7 @@ import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PLUGIN_REPOSITORY; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PROFILE; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PROFILES; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PROPERTIES; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.RELATIVE_PATH; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.REPOSITORIES; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.REPOSITORY; @@ -70,6 +75,8 @@ @Priority(20) public class CompatibilityFixStrategy extends AbstractUpgradeStrategy { + private static final Pattern EXPRESSION_PATTERN = Pattern.compile("\\$\\{([^}]+)}"); + @Override public boolean isApplicable(UpgradeContext context) { UpgradeOptions options = getOptions(context); @@ -117,6 +124,8 @@ public UpgradeResult doApply(UpgradeContext context, Map<Path, Document> pomMap) Set<Path> modifiedPoms = new HashSet<>(); Set<Path> errorPoms = new HashSet<>(); + Set<String> allDefinedProperties = collectAllDefinedProperties(pomMap); + for (Map.Entry<Path, Document> entry : pomMap.entrySet()) { Path pomPath = entry.getKey(); Document pomDocument = entry.getValue(); @@ -128,13 +137,13 @@ public UpgradeResult doApply(UpgradeContext context, Map<Path, Document> pomMap) try { boolean hasIssues = false; - // Apply all compatibility fixes hasIssues |= fixUnsupportedCombineChildrenAttributes(pomDocument, context); hasIssues |= fixUnsupportedCombineSelfAttributes(pomDocument, context); hasIssues |= fixDuplicateDependencies(pomDocument, context); hasIssues |= fixDuplicatePlugins(pomDocument, context); hasIssues |= fixUnsupportedRepositoryExpressions(pomDocument, context); hasIssues |= fixIncorrectParentRelativePaths(pomDocument, pomPath, pomMap, context); + hasIssues |= fixUndefinedPropertyExpressions(pomDocument, allDefinedProperties, context); if (hasIssues) { context.success("Maven 4 compatibility issues fixed"); @@ -345,6 +354,151 @@ private boolean fixIncorrectParentRelativePaths( return false; } + private Set<String> collectAllDefinedProperties(Map<Path, Document> pomMap) { + Set<String> properties = new HashSet<>(); + for (Map.Entry<Path, Document> entry : pomMap.entrySet()) { + collectPropertiesFromDom(entry.getValue(), properties); + } + return properties; + } + + private void collectPropertiesFromDom(Document document, Set<String> properties) { + Element root = document.root(); + + root.childElement(PROPERTIES) + .ifPresent(propsElement -> propsElement.childElements().forEach(child -> properties.add(child.name()))); + + root.childElement(PROFILES) + .ifPresent(profiles -> profiles.childElements(PROFILE) + .forEach(profile -> profile.childElement(PROPERTIES) + .ifPresent(propsElement -> + propsElement.childElements().forEach(child -> properties.add(child.name()))))); + } + + /** + * Fixes dependencies with undefined property expressions by commenting them out. + */ + private boolean fixUndefinedPropertyExpressions( + Document pomDocument, Set<String> allDefinedProperties, UpgradeContext context) { + Element root = pomDocument.root(); + + Stream<DependencyContainer> dependencyContainers = Stream.concat( + Stream.of( + new DependencyContainer( + root.childElement(DEPENDENCIES).orElse(null), DEPENDENCIES), + new DependencyContainer( + root.childElement(DEPENDENCY_MANAGEMENT) + .flatMap(dm -> dm.childElement(DEPENDENCIES)) + .orElse(null), + DEPENDENCY_MANAGEMENT)) + .filter(container -> container.element != null), + root.childElement(PROFILES).stream() + .flatMap(profiles -> profiles.childElements(PROFILE)) + .flatMap(profile -> Stream.of( + new DependencyContainer( + profile.childElement(DEPENDENCIES) + .orElse(null), + "profile dependencies"), + new DependencyContainer( + profile.childElement(DEPENDENCY_MANAGEMENT) + .flatMap(dm -> dm.childElement(DEPENDENCIES)) + .orElse(null), + "profile dependencyManagement")) + .filter(container -> container.element != null))); + + return dependencyContainers + .map(container -> fixUndefinedPropertyExpressionsInSection( + container.element, allDefinedProperties, pomDocument, context, container.sectionName)) + .reduce(false, Boolean::logicalOr); + } + + /** + * Fixes undefined property expressions in a specific dependencies section. + */ + private boolean fixUndefinedPropertyExpressionsInSection( + Element dependenciesElement, + Set<String> allDefinedProperties, + Document pomDocument, + UpgradeContext context, + String sectionName) { + boolean fixed = false; + List<Element> dependencies = + dependenciesElement.childElements(DEPENDENCY).toList(); + Editor editor = new Editor(pomDocument); + + for (Element dependency : dependencies) { + Set<String> undefinedProps = findUndefinedProperties(dependency, allDefinedProperties); + if (!undefinedProps.isEmpty()) { + String propLabel = undefinedProps.size() > 1 ? "properties" : "property"; + String propsStr = "'" + String.join("', '", undefinedProps) + "'"; + + Comment comment = editor.commentOutElement(dependency); + String elementXml = comment.content().trim(); + comment.content( + " mvnup: commented out - undefined " + propLabel + " " + propsStr + "\n" + elementXml + " "); + + context.detail("Fixed: Commented out dependency with undefined " + propLabel + " " + propsStr + " in " + + sectionName); + fixed = true; + } + } + + return fixed; + } + + /** + * Finds undefined property expressions in a dependency's coordinate fields. + */ + private Set<String> findUndefinedProperties(Element dependency, Set<String> allDefinedProperties) { + Set<String> undefinedProperties = new HashSet<>(); + + String groupId = dependency.childText(MavenPomElements.Elements.GROUP_ID); + String artifactId = dependency.childText(MavenPomElements.Elements.ARTIFACT_ID); + String version = dependency.childText(MavenPomElements.Elements.VERSION); + + collectUndefinedExpressions(groupId, allDefinedProperties, undefinedProperties); + collectUndefinedExpressions(artifactId, allDefinedProperties, undefinedProperties); + collectUndefinedExpressions(version, allDefinedProperties, undefinedProperties); + + return undefinedProperties; + } + + private void collectUndefinedExpressions(String value, Set<String> allDefinedProperties, Set<String> result) { + if (value == null) { + return; + } + Matcher matcher = EXPRESSION_PATTERN.matcher(value); + while (matcher.find()) { + String propertyName = matcher.group(1); + if (!isWellKnownProperty(propertyName) && !allDefinedProperties.contains(propertyName)) { + result.add(propertyName); + } + } + } + + private static boolean isWellKnownProperty(String propertyName) { + if (propertyName.startsWith("project.") + || propertyName.startsWith("pom.") + || propertyName.startsWith("env.") + || propertyName.startsWith("settings.") + || propertyName.startsWith("maven.")) { + return true; + } + if (propertyName.startsWith("java.") + || propertyName.startsWith("os.") + || propertyName.startsWith("user.") + || propertyName.startsWith("file.") + || propertyName.startsWith("line.") + || propertyName.startsWith("path.") + || propertyName.startsWith("sun.")) { + return true; + } + return "basedir".equals(propertyName) + || "revision".equals(propertyName) + || "sha1".equals(propertyName) + || "changelist".equals(propertyName); + } + /** * Recursively finds all elements with a specific attribute value. */ 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 6144f4ca32..00dacbb1c1 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 @@ -18,6 +18,7 @@ */ package org.apache.maven.cling.invoker.mvnup.goals; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; @@ -32,6 +33,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -58,6 +60,10 @@ private UpgradeContext createMockContext() { return TestUtils.createMockContext(); } + private UpgradeContext createMockContext(Path workingDirectory) { + return TestUtils.createMockContext(workingDirectory); + } + private UpgradeContext createMockContext(UpgradeOptions options) { return TestUtils.createMockContext(options); } @@ -478,6 +484,443 @@ void shouldNotModifyUrlsWithoutDeprecatedExpressions() throws Exception { } } + @Nested + @DisplayName("Undefined Property Expression Fixes") + class UndefinedPropertyExpressionFixesTests { + + @Test + @DisplayName("should comment out dependency with undefined property expression") + void shouldCommentOutDependencyWithUndefinedProperty() 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> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>${guava-version}</version> + </dependency> + </dependencies> + </dependencyManagement> + </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 commented out dependency"); + + String xml = DomUtils.toXml(document); + assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker"); + assertTrue(xml.contains("guava-version"), "Should mention the undefined property"); + + Element root = document.root(); + Element depMgmt = DomUtils.findChildElement(root, "dependencyManagement"); + Element deps = DomUtils.findChildElement(depMgmt, "dependencies"); + assertEquals(0, deps.childElements("dependency").count(), "Should have no dependency elements"); + } + + @Test + @DisplayName("should not comment out dependency with defined property") + void shouldNotCommentOutDependencyWithDefinedProperty() 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> + <properties> + <guava-version>30.0-jre</guava-version> + </properties> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>${guava-version}</version> + </dependency> + </dependencies> + </dependencyManagement> + </project> + """; + + Document document = Document.of(pomXml); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + strategy.doApply(context, pomMap); + + Element root = document.root(); + Element depMgmt = DomUtils.findChildElement(root, "dependencyManagement"); + Element deps = DomUtils.findChildElement(depMgmt, "dependencies"); + assertEquals(1, deps.childElements("dependency").count(), "Dependency should still be present"); + } + + @Test + @DisplayName("should not comment out dependency with well-known built-in property") + void shouldNotCommentOutDependencyWithBuiltinProperty() 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> + <dependencies> + <dependency> + <groupId>test</groupId> + <artifactId>test-dep</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </project> + """; + + Document document = Document.of(pomXml); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + strategy.doApply(context, pomMap); + + Element root = document.root(); + Element deps = DomUtils.findChildElement(root, "dependencies"); + assertEquals( + 1, + deps.childElements("dependency").count(), + "Dependency with built-in property should still be present"); + } + + @Test + @DisplayName("should recognize property defined in another module POM") + void shouldRecognizePropertyFromOtherPom() throws Exception { + String parentPom = """ + <?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>parent</artifactId> + <version>1.0.0</version> + <properties> + <guava-version>30.0-jre</guava-version> + </properties> + </project> + """; + + String childPom = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>test</groupId> + <artifactId>parent</artifactId> + <version>1.0.0</version> + </parent> + <artifactId>child</artifactId> + <dependencies> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>${guava-version}</version> + </dependency> + </dependencies> + </project> + """; + + Document parentDoc = Document.of(parentPom); + Document childDoc = Document.of(childPom); + Map<Path, Document> pomMap = Map.of( + Paths.get("pom.xml"), parentDoc, + Paths.get("child/pom.xml"), childDoc); + + UpgradeContext context = createMockContext(); + strategy.doApply(context, pomMap); + + Element root = childDoc.root(); + Element deps = DomUtils.findChildElement(root, "dependencies"); + assertEquals( + 1, + deps.childElements("dependency").count(), + "Dependency should not be commented out when property is defined in another POM"); + } + } + + @Nested + @DisplayName("Undefined Property Expression Fixes with Effective Model") + class UndefinedPropertyEffectiveModelTests { + + @TempDir + Path tempDir; + + @Test + @DisplayName("should recognize property inherited from external parent via relativePath") + void shouldRecognizePropertyFromExternalParent() throws Exception { + String parentPom = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>com.example</groupId> + <artifactId>external-parent</artifactId> + <version>1.0.0</version> + <packaging>pom</packaging> + <properties> + <guava.version>32.1.3-jre</guava.version> + </properties> + </project> + """; + + String childPom = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.example</groupId> + <artifactId>external-parent</artifactId> + <version>1.0.0</version> + <relativePath>../pom.xml</relativePath> + </parent> + <artifactId>child</artifactId> + <dependencies> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>${guava.version}</version> + </dependency> + </dependencies> + </project> + """; + + Path parentPomPath = tempDir.resolve("pom.xml"); + Path childDir = Files.createDirectories(tempDir.resolve("child")); + Path childPomPath = childDir.resolve("pom.xml"); + + Files.writeString(parentPomPath, parentPom); + Files.writeString(childPomPath, childPom); + + Document parentDoc = Document.of(parentPom); + Document childDoc = Document.of(childPom); + Map<Path, Document> pomMap = Map.of( + parentPomPath, parentDoc, + childPomPath, childDoc); + + UpgradeContext context = createMockContext(tempDir); + strategy.doApply(context, pomMap); + + Element root = childDoc.root(); + Element deps = DomUtils.findChildElement(root, "dependencies"); + assertEquals( + 1, + deps.childElements("dependency").count(), + "Dependency should not be commented out when property is inherited from external parent"); + } + + @Test + @DisplayName("should comment out dependency when property is not in parent either") + void shouldCommentOutWhenPropertyNotInParent() throws Exception { + String parentPom = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>com.example</groupId> + <artifactId>external-parent</artifactId> + <version>1.0.0</version> + <packaging>pom</packaging> + </project> + """; + + String childPom = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.example</groupId> + <artifactId>external-parent</artifactId> + <version>1.0.0</version> + <relativePath>../pom.xml</relativePath> + </parent> + <artifactId>child</artifactId> + <dependencies> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>${undefined.prop}</version> + </dependency> + </dependencies> + </project> + """; + + Path parentPomPath = tempDir.resolve("pom.xml"); + Path childDir = Files.createDirectories(tempDir.resolve("child")); + Path childPomPath = childDir.resolve("pom.xml"); + + Files.writeString(parentPomPath, parentPom); + Files.writeString(childPomPath, childPom); + + Document childDoc = Document.of(childPom); + Map<Path, Document> pomMap = Map.of(childPomPath, childDoc); + + UpgradeContext context = createMockContext(tempDir); + strategy.doApply(context, pomMap); + + String xml = DomUtils.toXml(childDoc); + assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker"); + } + + @Test + @DisplayName("should handle partial undefined - only comment out dependency with undefined property") + void shouldHandlePartialUndefined() throws Exception { + String pomXml = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>com.example</groupId> + <artifactId>test</artifactId> + <version>1.0.0</version> + <properties> + <commons.version>3.14.0</commons.version> + </properties> + <dependencies> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>${commons.version}</version> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>${undefined.version}</version> + </dependency> + </dependencies> + </project> + """; + + Path pomPath = tempDir.resolve("pom.xml"); + Files.writeString(pomPath, pomXml); + + Document document = Document.of(pomXml); + Map<Path, Document> pomMap = Map.of(pomPath, document); + + UpgradeContext context = createMockContext(tempDir); + strategy.doApply(context, pomMap); + + Element root = document.root(); + Element deps = DomUtils.findChildElement(root, "dependencies"); + assertEquals(1, deps.childElements("dependency").count(), "Only defined-property dependency should remain"); + + String xml = DomUtils.toXml(document); + assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker"); + assertTrue(xml.contains("'undefined.version'"), "Should mention the undefined property in comment"); + assertFalse(xml.contains("'commons.version'"), "Should not flag the defined property as undefined"); + } + + @Test + @DisplayName("should recognize property from grandparent POM") + void shouldRecognizePropertyFromGrandparent() throws Exception { + String grandparentPom = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>com.example</groupId> + <artifactId>grandparent</artifactId> + <version>1.0.0</version> + <packaging>pom</packaging> + <properties> + <guava.version>32.1.3-jre</guava.version> + </properties> + </project> + """; + + String parentPom = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.example</groupId> + <artifactId>grandparent</artifactId> + <version>1.0.0</version> + <relativePath>../pom.xml</relativePath> + </parent> + <artifactId>parent</artifactId> + <packaging>pom</packaging> + </project> + """; + + String childPom = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.example</groupId> + <artifactId>parent</artifactId> + <version>1.0.0</version> + <relativePath>../parent/pom.xml</relativePath> + </parent> + <artifactId>child</artifactId> + <dependencies> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>${guava.version}</version> + </dependency> + </dependencies> + </project> + """; + + Path grandparentPath = tempDir.resolve("pom.xml"); + Path parentDir = Files.createDirectories(tempDir.resolve("parent")); + Path parentPath = parentDir.resolve("pom.xml"); + Path childDir = Files.createDirectories(tempDir.resolve("child")); + Path childPomPath = childDir.resolve("pom.xml"); + + Files.writeString(grandparentPath, grandparentPom); + Files.writeString(parentPath, parentPom); + Files.writeString(childPomPath, childPom); + + Document grandparentDoc = Document.of(grandparentPom); + Document parentDoc = Document.of(parentPom); + Document childDoc = Document.of(childPom); + Map<Path, Document> pomMap = Map.of( + grandparentPath, grandparentDoc, + parentPath, parentDoc, + childPomPath, childDoc); + + UpgradeContext context = createMockContext(tempDir); + strategy.doApply(context, pomMap); + + Element root = childDoc.root(); + Element deps = DomUtils.findChildElement(root, "dependencies"); + assertEquals( + 1, + deps.childElements("dependency").count(), + "Dependency should not be commented out when property is inherited from grandparent"); + } + } + @Nested @DisplayName("Strategy Description") class StrategyDescriptionTests {
