Skip to content

Commit ce70464

Browse files
committed
Add support for complex data injection in @MojoParameter for unit testing
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 All tests pass successfully (22 tests in maven-testing module).
1 parent f067325 commit ce70464

File tree

4 files changed

+239
-1
lines changed

4 files changed

+239
-1
lines changed

impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919
package org.apache.maven.api.plugin.testing;
2020

21+
import javax.xml.stream.XMLStreamException;
22+
2123
import java.io.BufferedReader;
2224
import java.io.File;
2325
import java.io.IOException;
@@ -71,6 +73,7 @@
7173
import org.apache.maven.api.services.ArtifactInstaller;
7274
import org.apache.maven.api.services.ArtifactManager;
7375
import org.apache.maven.api.services.LocalRepositoryManager;
76+
import org.apache.maven.api.services.MavenException;
7477
import org.apache.maven.api.services.ProjectBuilder;
7578
import org.apache.maven.api.services.ProjectManager;
7679
import org.apache.maven.api.services.RepositoryFactory;
@@ -269,7 +272,27 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
269272
.map(ConfigurationContainer::getConfiguration)
270273
.orElseGet(() -> XmlNode.newInstance("config"));
271274
List<XmlNode> children = mojoParameters.stream()
272-
.map(mp -> XmlNode.newInstance(mp.name(), mp.value()))
275+
.map(mp -> {
276+
String s;
277+
if (mp.xml()) {
278+
// Parse as XML - value contains XML elements
279+
s = "<" + mp.name() + ">" + mp.value() + "</" + mp.name() + ">";
280+
} else {
281+
// Treat as plain text - escape XML special characters
282+
String escapedValue = mp.value()
283+
.replace("&", "&amp;")
284+
.replace("<", "&lt;")
285+
.replace(">", "&gt;")
286+
.replace("\"", "&quot;")
287+
.replace("'", "&apos;");
288+
s = "<" + mp.name() + ">" + escapedValue + "</" + mp.name() + ">";
289+
}
290+
try {
291+
return XmlService.read(new StringReader(s));
292+
} catch (XMLStreamException e) {
293+
throw new MavenException("Unable to parse xml: " + e.toString() + "\n" + s, e);
294+
}
295+
})
273296
.collect(Collectors.toList());
274297
XmlNode config = XmlNode.newInstance("configuration", null, null, children, null);
275298
pluginConfiguration = XmlService.merge(config, pluginConfiguration);

impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,28 @@
8888
* @return the parameter value
8989
*/
9090
String value();
91+
92+
/**
93+
* Whether to parse the value as XML.
94+
* When {@code true} (default), the value is parsed as XML content within the parameter element.
95+
* When {@code false}, the value is treated as plain text (useful for comma-separated lists).
96+
*
97+
* <p>Example with XML parsing enabled (default):</p>
98+
* <pre>
99+
* {@code
100+
* @MojoParameter(name = "items", value = "<item>one</item><item>two</item>")
101+
* }
102+
* </pre>
103+
*
104+
* <p>Example with XML parsing disabled:</p>
105+
* <pre>
106+
* {@code
107+
* @MojoParameter(name = "items", value = "one,two,three", xml = false)
108+
* }
109+
* </pre>
110+
*
111+
* @return {@code true} to parse as XML, {@code false} to treat as plain text
112+
* @since 4.0.0
113+
*/
114+
boolean xml() default true;
91115
}

impl/maven-testing/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import java.nio.file.Path;
2222
import java.nio.file.Paths;
23+
import java.util.List;
24+
import java.util.Map;
2325
import java.util.Properties;
2426

2527
import org.apache.maven.api.Project;
@@ -94,6 +96,129 @@ public void testParams(ExpressionEvaluatorMojo mojo) {
9496
assertDoesNotThrow(mojo::execute);
9597
}
9698

99+
@Test
100+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
101+
@Basedir("${basedir}/target/test-classes")
102+
@MojoParameter(name = "strings", value = "<string>value1</string><string>value2</string>")
103+
public void testComplexParam(ExpressionEvaluatorMojo mojo) {
104+
assertNotNull(mojo.basedir);
105+
assertNotNull(mojo.workdir);
106+
assertEquals(List.of("value1", "value2"), mojo.strings);
107+
}
108+
109+
@Test
110+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
111+
@Basedir("${basedir}/target/test-classes")
112+
@MojoParameter(name = "strings", value = "value1,value2", xml = false)
113+
public void testCommaSeparatedParam(ExpressionEvaluatorMojo mojo) {
114+
assertNotNull(mojo.basedir);
115+
assertNotNull(mojo.workdir);
116+
assertEquals(List.of("value1", "value2"), mojo.strings);
117+
}
118+
119+
@Test
120+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
121+
@MojoParameter(name = "stringArray", value = "<string>item1</string><string>item2</string><string>item3</string>")
122+
public void testStringArray(ExpressionEvaluatorMojo mojo) {
123+
assertNotNull(mojo.stringArray);
124+
assertEquals(3, mojo.stringArray.length);
125+
assertEquals("item1", mojo.stringArray[0]);
126+
assertEquals("item2", mojo.stringArray[1]);
127+
assertEquals("item3", mojo.stringArray[2]);
128+
}
129+
130+
@Test
131+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
132+
@MojoParameter(name = "mapParam", value = "<key1>value1</key1><key2>value2</key2>")
133+
public void testMapParam(ExpressionEvaluatorMojo mojo) {
134+
assertNotNull(mojo.mapParam);
135+
assertEquals(2, mojo.mapParam.size());
136+
assertEquals("value1", mojo.mapParam.get("key1"));
137+
assertEquals("value2", mojo.mapParam.get("key2"));
138+
}
139+
140+
@Test
141+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
142+
@MojoParameter(
143+
name = "propertiesParam",
144+
value = "<property><name>prop1</name><value>val1</value></property>"
145+
+ "<property><name>prop2</name><value>val2</value></property>")
146+
public void testPropertiesParam(ExpressionEvaluatorMojo mojo) {
147+
assertNotNull(mojo.propertiesParam);
148+
assertEquals(2, mojo.propertiesParam.size());
149+
assertEquals("val1", mojo.propertiesParam.getProperty("prop1"));
150+
assertEquals("val2", mojo.propertiesParam.getProperty("prop2"));
151+
}
152+
153+
@Test
154+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
155+
@MojoParameter(name = "beanParam", value = "<field1>fieldValue</field1><field2>42</field2>")
156+
public void testBeanParam(ExpressionEvaluatorMojo mojo) {
157+
assertNotNull(mojo.beanParam);
158+
assertEquals("fieldValue", mojo.beanParam.field1);
159+
assertEquals(42, mojo.beanParam.field2);
160+
}
161+
162+
@Test
163+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
164+
@MojoParameter(name = "intValue", value = "123")
165+
public void testIntValue(ExpressionEvaluatorMojo mojo) {
166+
assertEquals(123, mojo.intValue);
167+
}
168+
169+
@Test
170+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
171+
@MojoParameter(name = "boolValue", value = "true")
172+
public void testBoolValue(ExpressionEvaluatorMojo mojo) {
173+
assertEquals(true, mojo.boolValue);
174+
}
175+
176+
@Test
177+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
178+
@MojoParameter(name = "strings", value = "one,two,three,four", xml = false)
179+
public void testCommaSeparatedListWithXmlFalse(ExpressionEvaluatorMojo mojo) {
180+
assertNotNull(mojo.strings);
181+
assertEquals(4, mojo.strings.size());
182+
assertEquals("one", mojo.strings.get(0));
183+
assertEquals("two", mojo.strings.get(1));
184+
assertEquals("three", mojo.strings.get(2));
185+
assertEquals("four", mojo.strings.get(3));
186+
}
187+
188+
@Test
189+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
190+
@MojoParameter(
191+
name = "strings",
192+
value = "<string>alpha</string><string>beta</string><string>gamma</string>",
193+
xml = true)
194+
public void testListWithXmlTrue(ExpressionEvaluatorMojo mojo) {
195+
assertNotNull(mojo.strings);
196+
assertEquals(3, mojo.strings.size());
197+
assertEquals("alpha", mojo.strings.get(0));
198+
assertEquals("beta", mojo.strings.get(1));
199+
assertEquals("gamma", mojo.strings.get(2));
200+
}
201+
202+
@Test
203+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
204+
@MojoParameter(name = "strings", value = "value-with-<special>&chars", xml = false)
205+
public void testSpecialCharactersWithXmlFalse(ExpressionEvaluatorMojo mojo) {
206+
assertNotNull(mojo.strings);
207+
assertEquals(1, mojo.strings.size());
208+
assertEquals("value-with-<special>&chars", mojo.strings.get(0));
209+
}
210+
211+
@Test
212+
@InjectMojo(goal = COORDINATES, pom = CONFIG)
213+
@MojoParameter(name = "stringArray", value = "a,b,c", xml = false)
214+
public void testArrayWithCommaSeparated(ExpressionEvaluatorMojo mojo) {
215+
assertNotNull(mojo.stringArray);
216+
assertEquals(3, mojo.stringArray.length);
217+
assertEquals("a", mojo.stringArray[0]);
218+
assertEquals("b", mojo.stringArray[1]);
219+
assertEquals("c", mojo.stringArray[2]);
220+
}
221+
97222
@Mojo(name = "goal")
98223
@Named("test:test-plugin:0.0.1-SNAPSHOT:goal") // this one is usually generated by maven-plugin-plugin
99224
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
105230

106231
private String param2;
107232

233+
private List<String> strings;
234+
235+
private String[] stringArray;
236+
237+
private Map<String, String> mapParam;
238+
239+
private Properties propertiesParam;
240+
241+
private TestBean beanParam;
242+
243+
private int intValue;
244+
245+
private boolean boolValue;
246+
108247
/** {@inheritDoc} */
109248
@Override
110249
public void execute() throws MojoException {
@@ -120,6 +259,30 @@ public void execute() throws MojoException {
120259
}
121260
}
122261

262+
/**
263+
* A simple bean for testing complex parameter injection.
264+
*/
265+
public static class TestBean {
266+
private String field1;
267+
private int field2;
268+
269+
public String getField1() {
270+
return field1;
271+
}
272+
273+
public void setField1(String field1) {
274+
this.field1 = field1;
275+
}
276+
277+
public int getField2() {
278+
return field2;
279+
}
280+
281+
public void setField2(int field2) {
282+
this.field2 = field2;
283+
}
284+
}
285+
123286
@Provides
124287
@SuppressWarnings("unused")
125288
Session session() {

impl/maven-testing/src/test/resources/META-INF/maven/plugin.xml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,34 @@ under the License.
5555
<name>param2</name>
5656
<type>java.lang.String</type>
5757
</parameter>
58+
<parameter>
59+
<name>strings</name>
60+
<type>java.util.List</type>
61+
</parameter>
62+
<parameter>
63+
<name>stringArray</name>
64+
<type>[Ljava.lang.String;</type>
65+
</parameter>
66+
<parameter>
67+
<name>mapParam</name>
68+
<type>java.util.Map</type>
69+
</parameter>
70+
<parameter>
71+
<name>propertiesParam</name>
72+
<type>java.util.Properties</type>
73+
</parameter>
74+
<parameter>
75+
<name>beanParam</name>
76+
<type>org.apache.maven.api.plugin.testing.ExpressionEvaluatorTest$TestBean</type>
77+
</parameter>
78+
<parameter>
79+
<name>intValue</name>
80+
<type>int</type>
81+
</parameter>
82+
<parameter>
83+
<name>boolValue</name>
84+
<type>boolean</type>
85+
</parameter>
5886
<!--
5987
<parameter>
6088
<name>plain</name>

0 commit comments

Comments
 (0)