PluginUpgradeShadeTest.java
/*
* 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.cling.invoker.mvnup.goals;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import eu.maveniverse.domtrip.Document;
import eu.maveniverse.domtrip.Editor;
import eu.maveniverse.domtrip.Element;
import org.apache.maven.cling.invoker.mvnup.UpgradeContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
/**
* Tests for shade-plugin custom ResourceTransformer detection in {@link PluginUpgradeStrategy}.
* Verifies that mvnup skips shade-plugin upgrades when custom transformers are present
* (see <a href="https://github.com/apache/maven/issues/12991">MAVEN-12991</a>).
*/
@DisplayName("PluginUpgradeStrategy ��� Shade Plugin Custom Transformers")
class PluginUpgradeShadeTest {
private PluginUpgradeStrategy strategy;
@BeforeEach
void setUp() {
strategy = new PluginUpgradeStrategy();
}
private UpgradeContext createMockContext() {
return TestUtils.createMockContext();
}
@Test
@DisplayName("should skip shade-plugin upgrade when custom ResourceTransformer is present")
void shouldSkipUpgradeWithCustomTransformer() 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-shade-plugin</artifactId>
<version>1.3.3</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.myfaces.extensions.cdi.maven.BeansXmlTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</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(), "Strategy should succeed without errors");
// Verify the version was NOT upgraded
Editor editor = new Editor(document);
String version = editor.root()
.path("build", "plugins", "plugin", "version")
.map(Element::textContentTrimmed)
.orElse(null);
assertEquals("1.3.3", version, "Shade plugin version should remain unchanged");
// Verify a warning was logged
verify(context.logger, atLeastOnce())
.warn(argThat(msg -> msg.contains("Skipping maven-shade-plugin upgrade")
&& msg.contains("custom ResourceTransformer")
&& msg.contains("BeansXmlTransformer")));
}
@Test
@DisplayName("should skip shade-plugin upgrade when custom transformer is in top-level configuration")
void shouldSkipUpgradeWithCustomTransformerInTopLevelConfig() 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-shade-plugin</artifactId>
<version>1.3.3</version>
<configuration>
<transformers>
<transformer implementation="com.example.CustomTransformer"/>
</transformers>
</configuration>
</plugin>
</plugins>
</build>
</project>
""";
Document document = Document.of(pomXml);
Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);
UpgradeContext context = createMockContext();
strategy.doApply(context, pomMap);
// Verify the version was NOT upgraded
Editor editor = new Editor(document);
String version = editor.root()
.path("build", "plugins", "plugin", "version")
.map(Element::textContentTrimmed)
.orElse(null);
assertEquals("1.3.3", version, "Shade plugin version should remain unchanged");
}
@Test
@DisplayName("should upgrade shade-plugin when only standard transformers are present")
void shouldUpgradeWithStandardTransformers() 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-shade-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.Main</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</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(), "Plugin upgrade should succeed");
assertTrue(result.modifiedCount() > 0, "Should have upgraded shade plugin");
// Verify the version WAS upgraded
Editor editor = new Editor(document);
String version = editor.root()
.path("build", "plugins", "plugin", "version")
.map(Element::textContentTrimmed)
.orElse(null);
assertEquals("3.5.0", version, "Shade plugin version should be upgraded to 3.5.0");
}
@Test
@DisplayName("should upgrade shade-plugin when no transformers are configured")
void shouldUpgradeWithNoTransformers() 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-shade-plugin</artifactId>
<version>3.0.0</version>
</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(), "Plugin upgrade should succeed");
assertTrue(result.modifiedCount() > 0, "Should have upgraded shade plugin");
Editor editor = new Editor(document);
String version = editor.root()
.path("build", "plugins", "plugin", "version")
.map(Element::textContentTrimmed)
.orElse(null);
assertEquals("3.5.0", version, "Shade plugin version should be upgraded to 3.5.0");
}
@Test
@DisplayName("should skip shade-plugin upgrade with mixed standard and custom transformers")
void shouldSkipUpgradeWithMixedTransformers() 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-shade-plugin</artifactId>
<version>1.3.3</version>
<executions>
<execution>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/>
<transformer implementation="org.apache.myfaces.extensions.cdi.maven.BeansXmlTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
""";
Document document = Document.of(pomXml);
Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);
UpgradeContext context = createMockContext();
strategy.doApply(context, pomMap);
// Verify the version was NOT upgraded
Editor editor = new Editor(document);
String version = editor.root()
.path("build", "plugins", "plugin", "version")
.map(Element::textContentTrimmed)
.orElse(null);
assertEquals("1.3.3", version, "Shade plugin version should remain unchanged");
}
@Test
@DisplayName("should skip shade-plugin upgrade with property version and custom transformers")
void shouldSkipUpgradeWithPropertyVersionAndCustomTransformers() 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>
<shade.plugin.version>1.3.3</shade.plugin.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${shade.plugin.version}</version>
<configuration>
<transformers>
<transformer implementation="org.apache.myfaces.extensions.cdi.maven.BeansXmlTransformer"/>
</transformers>
</configuration>
</plugin>
</plugins>
</build>
</project>
""";
Document document = Document.of(pomXml);
Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);
UpgradeContext context = createMockContext();
strategy.doApply(context, pomMap);
// Verify the property was NOT upgraded
Editor editor = new Editor(document);
String version = editor.root()
.path("properties", "shade.plugin.version")
.map(Element::textContentTrimmed)
.orElse(null);
assertEquals("1.3.3", version, "Shade plugin property should remain unchanged");
}
@Test
@DisplayName("should skip shade-plugin upgrade without explicit groupId when custom transformers present")
void shouldSkipUpgradeWithoutGroupIdAndCustomTransformers() 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>
<artifactId>maven-shade-plugin</artifactId>
<version>1.3.3</version>
<configuration>
<transformers>
<transformer implementation="com.example.CustomTransformer"/>
</transformers>
</configuration>
</plugin>
</plugins>
</build>
</project>
""";
Document document = Document.of(pomXml);
Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);
UpgradeContext context = createMockContext();
strategy.doApply(context, pomMap);
// Verify the version was NOT upgraded
Editor editor = new Editor(document);
String version = editor.root()
.path("build", "plugins", "plugin", "version")
.map(Element::textContentTrimmed)
.orElse(null);
assertEquals("1.3.3", version, "Shade plugin version should remain unchanged");
}
@Test
@DisplayName("should detect custom transformers in pluginManagement section")
void shouldSkipUpgradeWithCustomTransformerInPluginManagement() 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-shade-plugin</artifactId>
<version>1.3.3</version>
<configuration>
<transformers>
<transformer implementation="com.example.CustomTransformer"/>
</transformers>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
""";
Document document = Document.of(pomXml);
Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);
UpgradeContext context = createMockContext();
strategy.doApply(context, pomMap);
// Verify the version was NOT upgraded
Editor editor = new Editor(document);
String version = editor.root()
.path("build", "pluginManagement", "plugins", "plugin", "version")
.map(Element::textContentTrimmed)
.orElse(null);
assertEquals("1.3.3", version, "Shade plugin version in pluginManagement should remain unchanged");
}
@Test
@DisplayName("findCustomTransformerClasses should return custom class names")
void findCustomTransformerClassesShouldReturnCustomClassNames() 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-shade-plugin</artifactId>
<version>1.3.3</version>
<executions>
<execution>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/>
<transformer implementation="org.apache.myfaces.extensions.cdi.maven.BeansXmlTransformer"/>
<transformer implementation="com.example.AnotherTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
""";
Document document = Document.of(pomXml);
Element pluginElement =
document.root().path("build", "plugins", "plugin").orElseThrow();
List<String> customTransformers = strategy.findCustomTransformerClasses(pluginElement);
assertEquals(2, customTransformers.size(), "Should find exactly 2 custom transformers");
assertTrue(
customTransformers.contains("org.apache.myfaces.extensions.cdi.maven.BeansXmlTransformer"),
"Should contain BeansXmlTransformer");
assertTrue(customTransformers.contains("com.example.AnotherTransformer"), "Should contain AnotherTransformer");
}
@Test
@DisplayName("findCustomTransformerClasses should return empty for standard transformers only")
void findCustomTransformerClassesShouldReturnEmptyForStandardTransformers() 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-shade-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer"/>
</transformers>
</configuration>
</plugin>
</plugins>
</build>
</project>
""";
Document document = Document.of(pomXml);
Element pluginElement =
document.root().path("build", "plugins", "plugin").orElseThrow();
List<String> customTransformers = strategy.findCustomTransformerClasses(pluginElement);
assertTrue(customTransformers.isEmpty(), "Should not find any custom transformers");
}
@Test
@DisplayName("findCustomTransformerClasses should return empty when no transformers configured")
void findCustomTransformerClassesShouldReturnEmptyWhenNoTransformers() 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-shade-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>
</project>
""";
Document document = Document.of(pomXml);
Element pluginElement =
document.root().path("build", "plugins", "plugin").orElseThrow();
List<String> customTransformers = strategy.findCustomTransformerClasses(pluginElement);
assertTrue(customTransformers.isEmpty(), "Should not find any custom transformers");
}
}