From f51d34c91e7c690a30a5909c5544458fa2f6448e Mon Sep 17 00:00:00 2001 From: Mykola Dzyuba <7852483+mdzyuba@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:30:03 -0800 Subject: [PATCH] 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 --- pom.xml | 6 + .../androidxmlformatter/IntegrationTest.java | 298 ++++++++++++++++++ .../integration/attribute_sort_expected.xml | 8 + .../integration/attribute_sort_input.xml | 7 + .../integration/combined_options_expected.xml | 9 + .../integration/combined_options_input.xml | 8 + .../custom_attribute_indention_expected.xml | 7 + .../custom_attribute_indention_input.xml | 6 + .../custom_attribute_order_expected.xml | 8 + .../custom_attribute_order_input.xml | 7 + .../integration/custom_indention_expected.xml | 14 + .../integration/custom_indention_input.xml | 13 + .../custom_namespace_order_expected.xml | 8 + .../custom_namespace_order_input.xml | 7 + .../integration/default_options_expected.xml | 22 ++ .../integration/default_options_input.xml | 21 ++ .../integration/namespace_sort_expected.xml | 8 + .../integration/namespace_sort_input.xml | 7 + 18 files changed, 464 insertions(+) create mode 100644 src/test/java/com/bytehamster/androidxmlformatter/IntegrationTest.java create mode 100644 src/test/resources/integration/attribute_sort_expected.xml create mode 100644 src/test/resources/integration/attribute_sort_input.xml create mode 100644 src/test/resources/integration/combined_options_expected.xml create mode 100644 src/test/resources/integration/combined_options_input.xml create mode 100644 src/test/resources/integration/custom_attribute_indention_expected.xml create mode 100644 src/test/resources/integration/custom_attribute_indention_input.xml create mode 100644 src/test/resources/integration/custom_attribute_order_expected.xml create mode 100644 src/test/resources/integration/custom_attribute_order_input.xml create mode 100644 src/test/resources/integration/custom_indention_expected.xml create mode 100644 src/test/resources/integration/custom_indention_input.xml create mode 100644 src/test/resources/integration/custom_namespace_order_expected.xml create mode 100644 src/test/resources/integration/custom_namespace_order_input.xml create mode 100644 src/test/resources/integration/default_options_expected.xml create mode 100644 src/test/resources/integration/default_options_input.xml create mode 100644 src/test/resources/integration/namespace_sort_expected.xml create mode 100644 src/test/resources/integration/namespace_sort_input.xml diff --git a/pom.xml b/pom.xml index 2b712b9..21b0b71 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,12 @@ jdom 1.1.3 + + org.junit.jupiter + junit-jupiter + 5.10.2 + test + diff --git a/src/test/java/com/bytehamster/androidxmlformatter/IntegrationTest.java b/src/test/java/com/bytehamster/androidxmlformatter/IntegrationTest.java new file mode 100644 index 0000000..7359f0a --- /dev/null +++ b/src/test/java/com/bytehamster/androidxmlformatter/IntegrationTest.java @@ -0,0 +1,298 @@ +package com.bytehamster.androidxmlformatter; + +import static org.junit.jupiter.api.Assertions.*; + +import org.jdom.Document; +import org.jdom.input.SAXBuilder; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +/** + * Integration tests that verify XML formatting with various options by comparing input XML files + * against expected output XML files. + */ +class IntegrationTest { + + private static final String INTEGRATION_DIR = "/integration/"; + private static final String INPUT_SUFFIX = "_input.xml"; + private static final String EXPECTED_SUFFIX = "_expected.xml"; + + private String loadResource(String path) throws Exception { + try (InputStream is = getClass().getResourceAsStream(path)) { + if (is == null) { + throw new IllegalArgumentException("Resource not found: " + path); + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int bytesRead; + byte[] data = new byte[1024]; + while ((bytesRead = is.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, bytesRead); + } + return new String(buffer.toByteArray(), StandardCharsets.UTF_8); + } + } + + private Document parseResource(String path) throws Exception { + try (InputStream is = getClass().getResourceAsStream(path)) { + if (is == null) { + throw new IllegalArgumentException("Resource not found: " + path); + } + return new SAXBuilder().build(is); + } + } + + private String formatDocument(AndroidXmlOutputter outputter, Document doc) throws Exception { + StringWriter writer = new StringWriter(); + outputter.output(doc, writer); + return writer.toString(); + } + + private void assertFormattedOutputMatches( + String testName, + int indention, + int attributeIndention, + String[] namespaceOrder, + String[] attributeOrder, + boolean attributeSort, + boolean namespaceSort) + throws Exception { + + Document inputDoc = parseResource(INTEGRATION_DIR + testName + INPUT_SUFFIX); + String expected = loadResource(INTEGRATION_DIR + testName + EXPECTED_SUFFIX); + + AndroidXmlOutputter outputter = new AndroidXmlOutputter( + indention, + attributeIndention, + namespaceOrder, + attributeOrder, + attributeSort, + namespaceSort); + + String actual = formatDocument(outputter, inputDoc); + + // Normalize line endings for comparison + expected = expected.replace("\r\n", "\n").trim(); + actual = actual.replace("\r\n", "\n").trim(); + + assertEquals(expected, actual, "Formatted output should match expected for: " + testName); + } + + // === Default Options Test === + + @Test + @DisplayName("Default options: id, layout_width, layout_height first with 4-space indentation") + void testDefaultOptions() throws Exception { + assertFormattedOutputMatches( + "default_options", + 4, // indention + 4, // attribute indention + new String[] { "android" }, // namespace order + new String[] { "id", "layout_width", "layout_height" }, // attribute order + false, // attribute sort + false // namespace sort + ); + } + + // === Custom Indentation Test === + + @Test + @DisplayName("Custom indentation: 2 spaces instead of 4") + void testCustomIndention() throws Exception { + assertFormattedOutputMatches( + "custom_indention", + 2, // indention + 2, // attribute indention + new String[] { "android" }, // namespace order + new String[] { "id", "layout_width", "layout_height" }, // attribute order + false, // attribute sort + false // namespace sort + ); + } + + // === Custom Attribute Indentation Test === + + @Test + @DisplayName("Custom attribute indentation: 8 spaces for attributes") + void testCustomAttributeIndention() throws Exception { + assertFormattedOutputMatches( + "custom_attribute_indention", + 4, // indention + 8, // attribute indention + new String[] { "android" }, // namespace order + new String[] { "id", "layout_width", "layout_height" }, // attribute order + false, // attribute sort + false // namespace sort + ); + } + + // === Custom Attribute Order Test === + + @Test + @DisplayName("Custom attribute order: text, background first") + void testCustomAttributeOrder() throws Exception { + assertFormattedOutputMatches( + "custom_attribute_order", + 4, // indention + 4, // attribute indention + new String[] { "android" }, // namespace order + new String[] { "text", "background" }, // attribute order + false, // attribute sort + false // namespace sort + ); + } + + // === Alphabetical Attribute Sort Test === + + @Test + @DisplayName("Attribute sort: alphabetical ordering of attributes") + void testAttributeSort() throws Exception { + assertFormattedOutputMatches( + "attribute_sort", + 4, // indention + 4, // attribute indention + new String[] { "android" }, // namespace order + new String[] {}, // attribute order (empty for pure alphabetical) + true, // attribute sort + false // namespace sort + ); + } + + // === Custom Namespace Order Test === + + @Test + @DisplayName("Custom namespace order: tools, app, android") + void testCustomNamespaceOrder() throws Exception { + assertFormattedOutputMatches( + "custom_namespace_order", + 4, // indention + 4, // attribute indention + new String[] { "tools", "app", "android" }, // namespace order + new String[] { "id", "layout_width", "layout_height" }, // attribute order + false, // attribute sort + false // namespace sort + ); + } + + // === Alphabetical Namespace Sort Test === + + @Test + @DisplayName("Namespace sort: alphabetical ordering of namespaces") + void testNamespaceSort() throws Exception { + assertFormattedOutputMatches( + "namespace_sort", + 4, // indention + 4, // attribute indention + new String[] {}, // namespace order (empty for pure alphabetical) + new String[] { "id", "layout_width", "layout_height" }, // attribute order + false, // attribute sort + true // namespace sort + ); + } + + // === Combined Options Test === + + @Test + @DisplayName("Combined options: 2-space indent, 6-space attr indent, both sorts enabled") + void testCombinedOptions() throws Exception { + assertFormattedOutputMatches( + "combined_options", + 2, // indention + 6, // attribute indention + new String[] {}, // namespace order (empty for alphabetical) + new String[] {}, // attribute order (empty for alphabetical) + true, // attribute sort + true // namespace sort + ); + } + + // === Additional Integration Tests === + + @Test + @DisplayName("Zero indentation: no indentation for elements or attributes") + void testZeroIndentation() throws Exception { + Document inputDoc = parseResource(INTEGRATION_DIR + "default_options" + INPUT_SUFFIX); + + AndroidXmlOutputter outputter = new AndroidXmlOutputter( + 0, + 0, + new String[] { "android" }, + new String[] { "id", "layout_width", "layout_height" }, + false, + false); + + String result = formatDocument(outputter, inputDoc); + + // With zero indentation, child elements should not be indented + assertTrue(result.contains(""), "Should have closing LinearLayout tag"); + assertTrue(result.contains(""); + int buttonPos = result.indexOf(" linearLayoutStart && buttonPos < linearLayoutEnd, + "Button should be inside LinearLayout"); + assertTrue( + textViewPos > linearLayoutStart && textViewPos < linearLayoutEnd, + "TextView should be inside LinearLayout"); + } +} diff --git a/src/test/resources/integration/attribute_sort_expected.xml b/src/test/resources/integration/attribute_sort_expected.xml new file mode 100644 index 0000000..79f6d08 --- /dev/null +++ b/src/test/resources/integration/attribute_sort_expected.xml @@ -0,0 +1,8 @@ + + diff --git a/src/test/resources/integration/attribute_sort_input.xml b/src/test/resources/integration/attribute_sort_input.xml new file mode 100644 index 0000000..b05d0f5 --- /dev/null +++ b/src/test/resources/integration/attribute_sort_input.xml @@ -0,0 +1,7 @@ + + diff --git a/src/test/resources/integration/combined_options_expected.xml b/src/test/resources/integration/combined_options_expected.xml new file mode 100644 index 0000000..5d28cb7 --- /dev/null +++ b/src/test/resources/integration/combined_options_expected.xml @@ -0,0 +1,9 @@ + + diff --git a/src/test/resources/integration/combined_options_input.xml b/src/test/resources/integration/combined_options_input.xml new file mode 100644 index 0000000..57f509a --- /dev/null +++ b/src/test/resources/integration/combined_options_input.xml @@ -0,0 +1,8 @@ + + diff --git a/src/test/resources/integration/custom_attribute_indention_expected.xml b/src/test/resources/integration/custom_attribute_indention_expected.xml new file mode 100644 index 0000000..0e1da65 --- /dev/null +++ b/src/test/resources/integration/custom_attribute_indention_expected.xml @@ -0,0 +1,7 @@ + +