1
2
3
4
5
6
7
8 package com.tunyk.mvn.plugins.htmlcompressor;
9
10 import java.io.StringReader;
11 import java.lang.reflect.Field;
12 import java.lang.reflect.InvocationTargetException;
13 import java.lang.reflect.Method;
14 import java.util.List;
15
16 import javax.xml.parsers.DocumentBuilderFactory;
17
18 import org.apache.maven.plugin.Mojo;
19 import org.apache.maven.plugin.MojoExecutionException;
20 import org.junit.jupiter.api.Assertions;
21 import org.junit.jupiter.api.Test;
22 import org.w3c.dom.Document;
23 import org.w3c.dom.Node;
24 import org.xml.sax.InputSource;
25
26
27
28
29 class HelpMojoTest {
30
31
32 private static final String HELP_MOJO_CLASS = "com.github.hazendaz.maven.htmlcompressor_maven_plugin.HelpMojo";
33
34 @Test
35 void testExecuteNormalizesInvalidFormattingParameters() throws Exception {
36 Mojo mojo = (Mojo) newHelpMojo();
37 setField(mojo, "lineLength", -1);
38 setField(mojo, "indentSize", 0);
39 setField(mojo, "detail", true);
40
41 Assertions.assertDoesNotThrow(mojo::execute);
42 Assertions.assertEquals(80, getField(mojo, "lineLength"));
43 Assertions.assertEquals(2, getField(mojo, "indentSize"));
44 }
45
46 @Test
47 void testGetPropertyFromExpressionVariants() throws Exception {
48 Assertions.assertEquals("user.prop",
49 invokeStatic("getPropertyFromExpression", new Class<?>[] { String.class }, "${user.prop}"));
50 Assertions.assertNull(invokeStatic("getPropertyFromExpression", new Class<?>[] { String.class }, "user.prop"));
51 Assertions.assertNull(
52 invokeStatic("getPropertyFromExpression", new Class<?>[] { String.class }, "${outer${inner}}"));
53 Assertions
54 .assertNull(invokeStatic("getPropertyFromExpression", new Class<?>[] { String.class }, (Object) null));
55 }
56
57 @Test
58 void testNodeLookupUtilityBranches() throws Exception {
59 Document doc = parse("<root><item>first</item><item>second</item></root>");
60 Node root = doc.getDocumentElement();
61
62 Assertions
63 .assertNull(invokeStatic("findSingleChild", new Class<?>[] { Node.class, String.class }, root, "none"));
64
65 ReflectiveOperationException duplicateSingleChild = Assertions.assertThrows(ReflectiveOperationException.class,
66 () -> invokeStatic("findSingleChild", new Class<?>[] { Node.class, String.class }, root, "item"));
67 Assertions.assertInstanceOf(MojoExecutionException.class, duplicateSingleChild.getCause());
68
69 ReflectiveOperationException missingGetSingleChild = Assertions.assertThrows(ReflectiveOperationException.class,
70 () -> invokeStatic("getSingleChild", new Class<?>[] { Node.class, String.class }, root, "missing"));
71 Assertions.assertInstanceOf(MojoExecutionException.class, missingGetSingleChild.getCause());
72 }
73
74 @SuppressWarnings("unchecked")
75 @Test
76 void testTextFormattingHelpers() throws Exception {
77 List<String> lines = (List<String>) invokeStatic("toLines",
78 new Class<?>[] { String.class, int.class, int.class, int.class },
79 "\talpha beta gamma\u00A0delta epsilon", 1, 2, 14);
80
81 Assertions.assertTrue(lines.size() > 1, "Long text should wrap to multiple lines");
82 Assertions.assertTrue(lines.stream().noneMatch(line -> line.contains("\u00A0")),
83 "Non-breaking spaces should be normalized");
84 Assertions.assertEquals(2, invokeStatic("getIndentLevel", new Class<?>[] { String.class }, "\t \tvalue"));
85 }
86
87 @Test
88 void testWriteParameterIncludesMetadata() throws Exception {
89 Object mojo = newHelpMojo();
90 setField(mojo, "lineLength", 40);
91 setField(mojo, "indentSize", 2);
92
93 Document doc = parse(
94 "<mojo>" + "<configuration><sample default-value=\"42\">${sample.property}</sample></configuration>"
95 + "<parameter><name>sample</name><description>parameter description</description>"
96 + "<required>true</required><deprecated>use replacement</deprecated></parameter>" + "</mojo>");
97
98 StringBuilder sb = new StringBuilder();
99 Node parameter = doc.getElementsByTagName("parameter").item(0);
100 Node configuration = doc.getElementsByTagName("configuration").item(0);
101 invokeInstance(mojo, "writeParameter", new Class<?>[] { StringBuilder.class, Node.class, Node.class }, sb,
102 parameter, configuration);
103
104 String output = sb.toString();
105 Assertions.assertTrue(output.contains("sample (Default: 42)"));
106 Assertions.assertTrue(output.contains("Deprecated. use replacement"));
107 Assertions.assertTrue(output.contains("Required: Yes"));
108 Assertions.assertTrue(output.contains("User property: sample.property"));
109 Assertions.assertTrue(output.contains("parameter description"));
110 }
111
112 private static Object newHelpMojo() throws ReflectiveOperationException {
113 return helpMojoClass().getDeclaredConstructor().newInstance();
114 }
115
116 private static Class<?> helpMojoClass() throws ClassNotFoundException {
117 return Class.forName(HELP_MOJO_CLASS);
118 }
119
120 private static void setField(Object target, String fieldName, Object value) throws ReflectiveOperationException {
121 Field field = target.getClass().getDeclaredField(fieldName);
122 field.setAccessible(true);
123 field.set(target, value);
124 }
125
126 private static Object getField(Object target, String fieldName) throws ReflectiveOperationException {
127 Field field = target.getClass().getDeclaredField(fieldName);
128 field.setAccessible(true);
129 return field.get(target);
130 }
131
132 private static Object invokeStatic(String methodName, Class<?>[] parameterTypes, Object... args)
133 throws ReflectiveOperationException {
134 Method method = helpMojoClass().getDeclaredMethod(methodName, parameterTypes);
135 method.setAccessible(true);
136 try {
137 return method.invoke(null, args);
138 } catch (InvocationTargetException e) {
139 throw new ReflectiveOperationException(e.getCause());
140 }
141 }
142
143 private static Object invokeInstance(Object target, String methodName, Class<?>[] parameterTypes, Object... args)
144 throws ReflectiveOperationException {
145 Method method = target.getClass().getDeclaredMethod(methodName, parameterTypes);
146 method.setAccessible(true);
147 try {
148 return method.invoke(target, args);
149 } catch (InvocationTargetException e) {
150 throw new ReflectiveOperationException(e.getCause());
151 }
152 }
153
154 private static Document parse(String xml) throws Exception {
155 return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
156 }
157 }