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&lt;java.lang.String&gt; (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("&", "&amp;")
+                                    .replace("<", "&lt;")
+                                    .replace(">", "&gt;")
+                                    .replace("\"", "&quot;")
+                                    .replace("'", "&apos;");
+                        }
+                        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>

Reply via email to