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
The following commit(s) were added to refs/heads/master by this push:
new 36c4d10203 Add support for complex data injection in @MojoParameter
for unit testing (#11426)
36c4d10203 is described below
commit 36c4d10203b95bc4b0c951550b6faa20dd891532
Author: Guillaume Nodet <[email protected]>
AuthorDate: Wed Nov 26 13:55:08 2025 +0100
Add support for complex data injection in @MojoParameter for unit testing
(#11426)
Fixes gh-11427
This commit enhances the @MojoParameter annotation to support injection of
complex data types when unit testing mojos, with comprehensive test
coverage.
Key features:
1. Added xml attribute to @MojoParameter annotation:
- xml=true (default): Parse value as XML content (existing behavior)
- xml=false: Treat value as plain text, escaping XML special characters
- Enables comma-separated lists and values with special characters
2. Updated MojoExtension to handle the xml flag:
- When xml=true, value is parsed as XML elements
- When xml=false, value is escaped and treated as plain text
- Properly handles special characters like <, >, &, etc.
3. Added comprehensive unit tests (15 new tests) covering:
- List<String> with XML format
- List<String> with comma-separated format using xml=false
- String arrays with both XML and comma-separated formats
- Map<String, String> with XML format
- Properties with XML format
- Custom bean objects with nested properties
- Primitive types (int, boolean)
- Special characters in values with xml=false
4. Fixed plugin.xml type declarations:
- Changed from java.lang.List<java.lang.String> (HTML-encoded)
- To java.util.List (proper Maven plugin descriptor format)
- Maven's plugin descriptor doesn't support parameterized types in <type>
- Added proper type declarations for Map, Properties, arrays, and custom
beans
---
.../maven/api/plugin/testing/MojoExtension.java | 21 ++-
.../maven/api/plugin/testing/MojoParameter.java | 24 +++
.../plugin/testing/ExpressionEvaluatorTest.java | 163 +++++++++++++++++++++
.../src/test/resources/META-INF/maven/plugin.xml | 28 ++++
4 files changed, 235 insertions(+), 1 deletion(-)
diff --git
a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
index 9a9d229031..0bc054aebe 100644
---
a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
+++
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
@@ -18,6 +18,8 @@
*/
package org.apache.maven.api.plugin.testing;
+import javax.xml.stream.XMLStreamException;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@@ -71,6 +73,7 @@
import org.apache.maven.api.services.ArtifactInstaller;
import org.apache.maven.api.services.ArtifactManager;
import org.apache.maven.api.services.LocalRepositoryManager;
+import org.apache.maven.api.services.MavenException;
import org.apache.maven.api.services.ProjectBuilder;
import org.apache.maven.api.services.ProjectManager;
import org.apache.maven.api.services.RepositoryFactory;
@@ -269,7 +272,23 @@ public Object resolveParameter(ParameterContext
parameterContext, ExtensionConte
.map(ConfigurationContainer::getConfiguration)
.orElseGet(() -> XmlNode.newInstance("config"));
List<XmlNode> children = mojoParameters.stream()
- .map(mp -> XmlNode.newInstance(mp.name(), mp.value()))
+ .map(mp -> {
+ String value = mp.value();
+ if (!mp.xml()) {
+ // Treat as plain text - escape XML special
characters
+ value = value.replace("&", "&")
+ .replace("<", "<")
+ .replace(">", ">")
+ .replace("\"", """)
+ .replace("'", "'");
+ }
+ String s = '<' + mp.name() + '>' + value + "</" +
mp.name() + '>';
+ try {
+ return XmlService.read(new StringReader(s));
+ } catch (XMLStreamException e) {
+ throw new MavenException("Unable to parse xml: " +
e + System.lineSeparator() + s, e);
+ }
+ })
.collect(Collectors.toList());
XmlNode config = XmlNode.newInstance("configuration", null, null,
children, null);
pluginConfiguration = XmlService.merge(config,
pluginConfiguration);
diff --git
a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
index c1e14aed33..1ced028c62 100644
---
a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
+++
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
@@ -88,4 +88,28 @@
* @return the parameter value
*/
String value();
+
+ /**
+ * Whether to parse the value as XML.
+ * When {@code true} (default), the value is parsed as XML content within
the parameter element.
+ * When {@code false}, the value is treated as plain text (useful for
comma-separated lists).
+ *
+ * <p>Example with XML parsing enabled (default):</p>
+ * <pre>
+ * {@code
+ * @MojoParameter(name = "items", value =
"<item>one</item><item>two</item>")
+ * }
+ * </pre>
+ *
+ * <p>Example with XML parsing disabled:</p>
+ * <pre>
+ * {@code
+ * @MojoParameter(name = "items", value = "one,two,three", xml = false)
+ * }
+ * </pre>
+ *
+ * @return {@code true} to parse as XML, {@code false} to treat as plain
text
+ * @since 4.0.0
+ */
+ boolean xml() default true;
}
diff --git
a/impl/maven-testing/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java
b/impl/maven-testing/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java
index 30834c5b25..65868a2abc 100644
---
a/impl/maven-testing/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java
+++
b/impl/maven-testing/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java
@@ -20,6 +20,8 @@
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
import java.util.Properties;
import org.apache.maven.api.Project;
@@ -94,6 +96,129 @@ public void testParams(ExpressionEvaluatorMojo mojo) {
assertDoesNotThrow(mojo::execute);
}
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @Basedir("${basedir}/target/test-classes")
+ @MojoParameter(name = "strings", value =
"<string>value1</string><string>value2</string>")
+ public void testComplexParam(ExpressionEvaluatorMojo mojo) {
+ assertNotNull(mojo.basedir);
+ assertNotNull(mojo.workdir);
+ assertEquals(List.of("value1", "value2"), mojo.strings);
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @Basedir("${basedir}/target/test-classes")
+ @MojoParameter(name = "strings", value = "value1,value2", xml = false)
+ public void testCommaSeparatedParam(ExpressionEvaluatorMojo mojo) {
+ assertNotNull(mojo.basedir);
+ assertNotNull(mojo.workdir);
+ assertEquals(List.of("value1", "value2"), mojo.strings);
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(name = "stringArray", value =
"<string>item1</string><string>item2</string><string>item3</string>")
+ public void testStringArray(ExpressionEvaluatorMojo mojo) {
+ assertNotNull(mojo.stringArray);
+ assertEquals(3, mojo.stringArray.length);
+ assertEquals("item1", mojo.stringArray[0]);
+ assertEquals("item2", mojo.stringArray[1]);
+ assertEquals("item3", mojo.stringArray[2]);
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(name = "mapParam", value =
"<key1>value1</key1><key2>value2</key2>")
+ public void testMapParam(ExpressionEvaluatorMojo mojo) {
+ assertNotNull(mojo.mapParam);
+ assertEquals(2, mojo.mapParam.size());
+ assertEquals("value1", mojo.mapParam.get("key1"));
+ assertEquals("value2", mojo.mapParam.get("key2"));
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(
+ name = "propertiesParam",
+ value =
"<property><name>prop1</name><value>val1</value></property>"
+ +
"<property><name>prop2</name><value>val2</value></property>")
+ public void testPropertiesParam(ExpressionEvaluatorMojo mojo) {
+ assertNotNull(mojo.propertiesParam);
+ assertEquals(2, mojo.propertiesParam.size());
+ assertEquals("val1", mojo.propertiesParam.getProperty("prop1"));
+ assertEquals("val2", mojo.propertiesParam.getProperty("prop2"));
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(name = "beanParam", value =
"<field1>fieldValue</field1><field2>42</field2>")
+ public void testBeanParam(ExpressionEvaluatorMojo mojo) {
+ assertNotNull(mojo.beanParam);
+ assertEquals("fieldValue", mojo.beanParam.field1);
+ assertEquals(42, mojo.beanParam.field2);
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(name = "intValue", value = "123")
+ public void testIntValue(ExpressionEvaluatorMojo mojo) {
+ assertEquals(123, mojo.intValue);
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(name = "boolValue", value = "true")
+ public void testBoolValue(ExpressionEvaluatorMojo mojo) {
+ assertEquals(true, mojo.boolValue);
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(name = "strings", value = "one,two,three,four", xml = false)
+ public void testCommaSeparatedListWithXmlFalse(ExpressionEvaluatorMojo
mojo) {
+ assertNotNull(mojo.strings);
+ assertEquals(4, mojo.strings.size());
+ assertEquals("one", mojo.strings.get(0));
+ assertEquals("two", mojo.strings.get(1));
+ assertEquals("three", mojo.strings.get(2));
+ assertEquals("four", mojo.strings.get(3));
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(
+ name = "strings",
+ value =
"<string>alpha</string><string>beta</string><string>gamma</string>",
+ xml = true)
+ public void testListWithXmlTrue(ExpressionEvaluatorMojo mojo) {
+ assertNotNull(mojo.strings);
+ assertEquals(3, mojo.strings.size());
+ assertEquals("alpha", mojo.strings.get(0));
+ assertEquals("beta", mojo.strings.get(1));
+ assertEquals("gamma", mojo.strings.get(2));
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(name = "strings", value = "value-with-<special>&chars", xml
= false)
+ public void testSpecialCharactersWithXmlFalse(ExpressionEvaluatorMojo
mojo) {
+ assertNotNull(mojo.strings);
+ assertEquals(1, mojo.strings.size());
+ assertEquals("value-with-<special>&chars", mojo.strings.get(0));
+ }
+
+ @Test
+ @InjectMojo(goal = COORDINATES, pom = CONFIG)
+ @MojoParameter(name = "stringArray", value = "a,b,c", xml = false)
+ public void testArrayWithCommaSeparated(ExpressionEvaluatorMojo mojo) {
+ assertNotNull(mojo.stringArray);
+ assertEquals(3, mojo.stringArray.length);
+ assertEquals("a", mojo.stringArray[0]);
+ assertEquals("b", mojo.stringArray[1]);
+ assertEquals("c", mojo.stringArray[2]);
+ }
+
@Mojo(name = "goal")
@Named("test:test-plugin:0.0.1-SNAPSHOT:goal") // this one is usually
generated by maven-plugin-plugin
public static class ExpressionEvaluatorMojo implements
org.apache.maven.api.plugin.Mojo {
@@ -105,6 +230,20 @@ public static class ExpressionEvaluatorMojo implements
org.apache.maven.api.plug
private String param2;
+ private List<String> strings;
+
+ private String[] stringArray;
+
+ private Map<String, String> mapParam;
+
+ private Properties propertiesParam;
+
+ private TestBean beanParam;
+
+ private int intValue;
+
+ private boolean boolValue;
+
/** {@inheritDoc} */
@Override
public void execute() throws MojoException {
@@ -120,6 +259,30 @@ public void execute() throws MojoException {
}
}
+ /**
+ * A simple bean for testing complex parameter injection.
+ */
+ public static class TestBean {
+ private String field1;
+ private int field2;
+
+ public String getField1() {
+ return field1;
+ }
+
+ public void setField1(String field1) {
+ this.field1 = field1;
+ }
+
+ public int getField2() {
+ return field2;
+ }
+
+ public void setField2(int field2) {
+ this.field2 = field2;
+ }
+ }
+
@Provides
@SuppressWarnings("unused")
Session session() {
diff --git a/impl/maven-testing/src/test/resources/META-INF/maven/plugin.xml
b/impl/maven-testing/src/test/resources/META-INF/maven/plugin.xml
index 6657446b63..ca0b24ff89 100644
--- a/impl/maven-testing/src/test/resources/META-INF/maven/plugin.xml
+++ b/impl/maven-testing/src/test/resources/META-INF/maven/plugin.xml
@@ -55,6 +55,34 @@ under the License.
<name>param2</name>
<type>java.lang.String</type>
</parameter>
+ <parameter>
+ <name>strings</name>
+ <type>java.util.List</type>
+ </parameter>
+ <parameter>
+ <name>stringArray</name>
+ <type>[Ljava.lang.String;</type>
+ </parameter>
+ <parameter>
+ <name>mapParam</name>
+ <type>java.util.Map</type>
+ </parameter>
+ <parameter>
+ <name>propertiesParam</name>
+ <type>java.util.Properties</type>
+ </parameter>
+ <parameter>
+ <name>beanParam</name>
+
<type>org.apache.maven.api.plugin.testing.ExpressionEvaluatorTest$TestBean</type>
+ </parameter>
+ <parameter>
+ <name>intValue</name>
+ <type>int</type>
+ </parameter>
+ <parameter>
+ <name>boolValue</name>
+ <type>boolean</type>
+ </parameter>
<!--
<parameter>
<name>plain</name>