Skip to content

Commit f51d34c

Browse files
committed
Add integration tests for all formatter options
Integration tests that verify XML formatting by comparing input files against expected output files for each tool option: Test cases: - Default options (id, layout_width, layout_height first) - Custom indentation (--indention) - Custom attribute indentation (--attribute-indention) - Custom attribute order (--attribute-order) - Alphabetical attribute sort (--attribute-sort) - Custom namespace order (--namespace-order) - Alphabetical namespace sort (--namespace-sort) - Combined options (multiple flags together) - Zero indentation - Valid XML output verification - Nested element structure verification
1 parent 9c6bc0e commit f51d34c

18 files changed

+464
-0
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
<artifactId>jdom</artifactId>
3030
<version>1.1.3</version>
3131
</dependency>
32+
<dependency>
33+
<groupId>org.junit.jupiter</groupId>
34+
<artifactId>junit-jupiter</artifactId>
35+
<version>5.10.2</version>
36+
<scope>test</scope>
37+
</dependency>
3238
</dependencies>
3339

3440
<build>
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
package com.bytehamster.androidxmlformatter;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import org.jdom.Document;
6+
import org.jdom.input.SAXBuilder;
7+
import org.junit.jupiter.api.DisplayName;
8+
import org.junit.jupiter.api.Test;
9+
10+
import java.io.ByteArrayOutputStream;
11+
import java.io.InputStream;
12+
import java.io.StringWriter;
13+
import java.nio.charset.StandardCharsets;
14+
15+
/**
16+
* Integration tests that verify XML formatting with various options by comparing input XML files
17+
* against expected output XML files.
18+
*/
19+
class IntegrationTest {
20+
21+
private static final String INTEGRATION_DIR = "/integration/";
22+
private static final String INPUT_SUFFIX = "_input.xml";
23+
private static final String EXPECTED_SUFFIX = "_expected.xml";
24+
25+
private String loadResource(String path) throws Exception {
26+
try (InputStream is = getClass().getResourceAsStream(path)) {
27+
if (is == null) {
28+
throw new IllegalArgumentException("Resource not found: " + path);
29+
}
30+
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
31+
int bytesRead;
32+
byte[] data = new byte[1024];
33+
while ((bytesRead = is.read(data, 0, data.length)) != -1) {
34+
buffer.write(data, 0, bytesRead);
35+
}
36+
return new String(buffer.toByteArray(), StandardCharsets.UTF_8);
37+
}
38+
}
39+
40+
private Document parseResource(String path) throws Exception {
41+
try (InputStream is = getClass().getResourceAsStream(path)) {
42+
if (is == null) {
43+
throw new IllegalArgumentException("Resource not found: " + path);
44+
}
45+
return new SAXBuilder().build(is);
46+
}
47+
}
48+
49+
private String formatDocument(AndroidXmlOutputter outputter, Document doc) throws Exception {
50+
StringWriter writer = new StringWriter();
51+
outputter.output(doc, writer);
52+
return writer.toString();
53+
}
54+
55+
private void assertFormattedOutputMatches(
56+
String testName,
57+
int indention,
58+
int attributeIndention,
59+
String[] namespaceOrder,
60+
String[] attributeOrder,
61+
boolean attributeSort,
62+
boolean namespaceSort)
63+
throws Exception {
64+
65+
Document inputDoc = parseResource(INTEGRATION_DIR + testName + INPUT_SUFFIX);
66+
String expected = loadResource(INTEGRATION_DIR + testName + EXPECTED_SUFFIX);
67+
68+
AndroidXmlOutputter outputter = new AndroidXmlOutputter(
69+
indention,
70+
attributeIndention,
71+
namespaceOrder,
72+
attributeOrder,
73+
attributeSort,
74+
namespaceSort);
75+
76+
String actual = formatDocument(outputter, inputDoc);
77+
78+
// Normalize line endings for comparison
79+
expected = expected.replace("\r\n", "\n").trim();
80+
actual = actual.replace("\r\n", "\n").trim();
81+
82+
assertEquals(expected, actual, "Formatted output should match expected for: " + testName);
83+
}
84+
85+
// === Default Options Test ===
86+
87+
@Test
88+
@DisplayName("Default options: id, layout_width, layout_height first with 4-space indentation")
89+
void testDefaultOptions() throws Exception {
90+
assertFormattedOutputMatches(
91+
"default_options",
92+
4, // indention
93+
4, // attribute indention
94+
new String[] { "android" }, // namespace order
95+
new String[] { "id", "layout_width", "layout_height" }, // attribute order
96+
false, // attribute sort
97+
false // namespace sort
98+
);
99+
}
100+
101+
// === Custom Indentation Test ===
102+
103+
@Test
104+
@DisplayName("Custom indentation: 2 spaces instead of 4")
105+
void testCustomIndention() throws Exception {
106+
assertFormattedOutputMatches(
107+
"custom_indention",
108+
2, // indention
109+
2, // attribute indention
110+
new String[] { "android" }, // namespace order
111+
new String[] { "id", "layout_width", "layout_height" }, // attribute order
112+
false, // attribute sort
113+
false // namespace sort
114+
);
115+
}
116+
117+
// === Custom Attribute Indentation Test ===
118+
119+
@Test
120+
@DisplayName("Custom attribute indentation: 8 spaces for attributes")
121+
void testCustomAttributeIndention() throws Exception {
122+
assertFormattedOutputMatches(
123+
"custom_attribute_indention",
124+
4, // indention
125+
8, // attribute indention
126+
new String[] { "android" }, // namespace order
127+
new String[] { "id", "layout_width", "layout_height" }, // attribute order
128+
false, // attribute sort
129+
false // namespace sort
130+
);
131+
}
132+
133+
// === Custom Attribute Order Test ===
134+
135+
@Test
136+
@DisplayName("Custom attribute order: text, background first")
137+
void testCustomAttributeOrder() throws Exception {
138+
assertFormattedOutputMatches(
139+
"custom_attribute_order",
140+
4, // indention
141+
4, // attribute indention
142+
new String[] { "android" }, // namespace order
143+
new String[] { "text", "background" }, // attribute order
144+
false, // attribute sort
145+
false // namespace sort
146+
);
147+
}
148+
149+
// === Alphabetical Attribute Sort Test ===
150+
151+
@Test
152+
@DisplayName("Attribute sort: alphabetical ordering of attributes")
153+
void testAttributeSort() throws Exception {
154+
assertFormattedOutputMatches(
155+
"attribute_sort",
156+
4, // indention
157+
4, // attribute indention
158+
new String[] { "android" }, // namespace order
159+
new String[] {}, // attribute order (empty for pure alphabetical)
160+
true, // attribute sort
161+
false // namespace sort
162+
);
163+
}
164+
165+
// === Custom Namespace Order Test ===
166+
167+
@Test
168+
@DisplayName("Custom namespace order: tools, app, android")
169+
void testCustomNamespaceOrder() throws Exception {
170+
assertFormattedOutputMatches(
171+
"custom_namespace_order",
172+
4, // indention
173+
4, // attribute indention
174+
new String[] { "tools", "app", "android" }, // namespace order
175+
new String[] { "id", "layout_width", "layout_height" }, // attribute order
176+
false, // attribute sort
177+
false // namespace sort
178+
);
179+
}
180+
181+
// === Alphabetical Namespace Sort Test ===
182+
183+
@Test
184+
@DisplayName("Namespace sort: alphabetical ordering of namespaces")
185+
void testNamespaceSort() throws Exception {
186+
assertFormattedOutputMatches(
187+
"namespace_sort",
188+
4, // indention
189+
4, // attribute indention
190+
new String[] {}, // namespace order (empty for pure alphabetical)
191+
new String[] { "id", "layout_width", "layout_height" }, // attribute order
192+
false, // attribute sort
193+
true // namespace sort
194+
);
195+
}
196+
197+
// === Combined Options Test ===
198+
199+
@Test
200+
@DisplayName("Combined options: 2-space indent, 6-space attr indent, both sorts enabled")
201+
void testCombinedOptions() throws Exception {
202+
assertFormattedOutputMatches(
203+
"combined_options",
204+
2, // indention
205+
6, // attribute indention
206+
new String[] {}, // namespace order (empty for alphabetical)
207+
new String[] {}, // attribute order (empty for alphabetical)
208+
true, // attribute sort
209+
true // namespace sort
210+
);
211+
}
212+
213+
// === Additional Integration Tests ===
214+
215+
@Test
216+
@DisplayName("Zero indentation: no indentation for elements or attributes")
217+
void testZeroIndentation() throws Exception {
218+
Document inputDoc = parseResource(INTEGRATION_DIR + "default_options" + INPUT_SUFFIX);
219+
220+
AndroidXmlOutputter outputter = new AndroidXmlOutputter(
221+
0,
222+
0,
223+
new String[] { "android" },
224+
new String[] { "id", "layout_width", "layout_height" },
225+
false,
226+
false);
227+
228+
String result = formatDocument(outputter, inputDoc);
229+
230+
// With zero indentation, child elements should not be indented
231+
assertTrue(result.contains("<Button"), "Should contain Button element");
232+
assertTrue(result.contains("<TextView"), "Should contain TextView element");
233+
assertFalse(
234+
result.contains("\n <Button"),
235+
"Button should not be indented with 4 spaces when indentation is 0");
236+
}
237+
238+
@Test
239+
@DisplayName("Verify output is valid XML that can be parsed")
240+
void testOutputIsValidXml() throws Exception {
241+
Document inputDoc = parseResource(INTEGRATION_DIR + "default_options" + INPUT_SUFFIX);
242+
243+
AndroidXmlOutputter outputter = new AndroidXmlOutputter(
244+
4,
245+
4,
246+
new String[] { "android" },
247+
new String[] { "id", "layout_width", "layout_height" },
248+
false,
249+
false);
250+
251+
String result = formatDocument(outputter, inputDoc);
252+
253+
// Parse the output to verify it's valid XML
254+
SAXBuilder builder = new SAXBuilder();
255+
Document parsedDoc = builder.build(new java.io.StringReader(result));
256+
257+
assertNotNull(parsedDoc, "Output should be parseable as XML");
258+
assertEquals(
259+
"LinearLayout",
260+
parsedDoc.getRootElement().getName(),
261+
"Root element should be LinearLayout");
262+
}
263+
264+
@Test
265+
@DisplayName("Nested elements maintain correct structure")
266+
void testNestedElementsStructure() throws Exception {
267+
Document inputDoc = parseResource(INTEGRATION_DIR + "default_options" + INPUT_SUFFIX);
268+
269+
AndroidXmlOutputter outputter = new AndroidXmlOutputter(
270+
4,
271+
4,
272+
new String[] { "android" },
273+
new String[] { "id", "layout_width", "layout_height" },
274+
false,
275+
false);
276+
277+
String result = formatDocument(outputter, inputDoc);
278+
279+
// Verify nested structure
280+
assertTrue(result.contains("<LinearLayout"), "Should contain LinearLayout");
281+
assertTrue(result.contains("</LinearLayout>"), "Should have closing LinearLayout tag");
282+
assertTrue(result.contains("<Button"), "Should contain Button");
283+
assertTrue(result.contains("<TextView"), "Should contain TextView");
284+
285+
// Verify proper nesting (Button and TextView are inside LinearLayout)
286+
int linearLayoutStart = result.indexOf("<LinearLayout");
287+
int linearLayoutEnd = result.indexOf("</LinearLayout>");
288+
int buttonPos = result.indexOf("<Button");
289+
int textViewPos = result.indexOf("<TextView");
290+
291+
assertTrue(
292+
buttonPos > linearLayoutStart && buttonPos < linearLayoutEnd,
293+
"Button should be inside LinearLayout");
294+
assertTrue(
295+
textViewPos > linearLayoutStart && textViewPos < linearLayoutEnd,
296+
"TextView should be inside LinearLayout");
297+
}
298+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<View
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:alpha="0.5"
5+
android:background="#FFF"
6+
android:elevation="4dp"
7+
android:id="@+id/view"
8+
android:zIndex="1" />
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<View xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:zIndex="1"
4+
android:alpha="0.5"
5+
android:background="#FFF"
6+
android:elevation="4dp"
7+
android:id="@+id/view" />
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<View
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:app="http://schemas.android.com/apk/res-auto"
5+
android:alpha="0.5"
6+
android:id="@+id/view"
7+
android:zIndex="1"
8+
app:customA="a"
9+
app:customZ="z" />
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<View xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
android:zIndex="1"
5+
android:alpha="0.5"
6+
android:id="@+id/view"
7+
app:customZ="z"
8+
app:customA="a" />
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Button
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:id="@+id/btn"
5+
android:layout_width="wrap_content"
6+
android:layout_height="wrap_content"
7+
android:text="Click" />
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Button xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:text="Click"
4+
android:id="@+id/btn"
5+
android:layout_width="wrap_content"
6+
android:layout_height="wrap_content" />
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Button
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:text="Click"
5+
android:background="#FFFFFF"
6+
android:id="@+id/btn"
7+
android:layout_width="wrap_content"
8+
android:layout_height="wrap_content" />
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Button xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:id="@+id/btn"
4+
android:layout_width="wrap_content"
5+
android:layout_height="wrap_content"
6+
android:text="Click"
7+
android:background="#FFFFFF" />

0 commit comments

Comments
 (0)