From 4f7032dbf0e604ae5e38465ef0869e6b0e29f227 Mon Sep 17 00:00:00 2001 From: Rikkarth <76500344+rikkarth@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:47:18 +0000 Subject: [PATCH] Test Resources Cleanup, XmlHandler Improve, Unit Tests (#4) * Test Resources Cleanup, XmlHandler Improve, Unit Tests - Added getNodeFromXPath, for single Node retrieval. - Deleted test_resource_2.xml - Adapted test_resource_1.xml - Added more unit tests to XmlHandlerTest.java - .editorconfig updated * CHANGELOG.md Updated, README.md Updated * maven.yml Updated * maven.yml Updated --- .editorconfig | 4 + .github/workflows/maven.yml | 22 +++- CHANGELOG.md | 11 +- README.md | 11 +- .../java/org/toolertools/xml/XmlHandler.java | 27 ++++- .../org/toolertools/xml/XmlHandlerTest.java | 111 ++++++++++++++---- src/test/resources/test_resource_1.xml | 21 ++-- src/test/resources/test_resource_2.xml | 18 --- 8 files changed, 158 insertions(+), 67 deletions(-) delete mode 100644 src/test/resources/test_resource_2.xml diff --git a/.editorconfig b/.editorconfig index 5fd3e42..c8a4f18 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,3 +19,7 @@ indent_size = 4 # Ignore some files and directories [**.md] max_line_length = 120 + +[*.xml] +indent_style = tab +tab_width = 4 diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 4532048..c33b660 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -10,9 +10,9 @@ name: Java CI with Maven on: push: - branches: [ "main" ] + branches: [ "main", "dev" ] pull_request: - branches: [ "main" ] + branches: [ "main", "dev" ] jobs: @@ -45,3 +45,21 @@ jobs: cache: maven - name: Build with Maven run: mvn -B package --file pom.xml + + create_tag: + + runs-on: ubuntu-latest + + needs: build + + if: github.ref == 'refs/heads/main' + + steps: + - name: Get version from pom.xml + run: echo "##[set-output name=version]$(xmllint --xpath 'string(/project/version)' pom.xml)" + id: get_version + - name: Create tag + run: | + echo "Creating tag v${{ steps.get_version.outputs.version }}" + git tag -a v${{ steps.get_version.outputs.version }} -m "Release v${{ steps.get_version.outputs.version }}" + git push origin v${{ steps.get_version.outputs.version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index e187ffb..3f50908 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.0.3] - 01-12-2023 -### Changed - -- **XmlHandler** now handles XPathExpressions internally, getStringFromXPath and getNodeListFromXPath both now only - require the expression to be provided as a String instead of an XPathExpression object. - ### Added - CHANGELOG.md - **Installed**: Maven Wrapper - **Installed**: JUnit Params +- Ability to retrieve single Node from XPath with XmlHandler. + +### Changed + +- **XmlHandler** now handles XPathExpressions internally, getStringFromXPath and getNodeListFromXPath both now only + require the expression to be provided as a String instead of an XPathExpression object. diff --git a/README.md b/README.md index 3003030..ba99080 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,17 @@ ## [0.0.3] - 01-12-2023 -### Changed - -- **XmlHandler** now handles XPathExpressions internally, getStringFromXPath and getNodeListFromXPath both now only - require the expression to be provided as a String instead of an XPathExpression object. - ### Added - CHANGELOG.md - **Installed**: Maven Wrapper - **Installed**: JUnit Params +- Ability to retrieve single Node from XPath with XmlHandler. + +### Changed + +- **XmlHandler** now handles XPathExpressions internally, getStringFromXPath and getNodeListFromXPath both now only + require the expression to be provided as a String instead of an XPathExpression object. # Tooler-Tools diff --git a/src/main/java/org/toolertools/xml/XmlHandler.java b/src/main/java/org/toolertools/xml/XmlHandler.java index d43148f..3a012bd 100644 --- a/src/main/java/org/toolertools/xml/XmlHandler.java +++ b/src/main/java/org/toolertools/xml/XmlHandler.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.util.Objects; import java.util.Optional; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -13,6 +14,7 @@ import javax.xml.xpath.XPathFactory; import org.toolertools.internal.DocConstants; import org.w3c.dom.Document; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; @@ -36,13 +38,36 @@ public static String getStringFromXPath(String expression, Document document) { public static NodeList getNodeListFromXPath(String expression, Document document) { try { XPathExpression xPathExpression = createXPathExpression(expression); - return (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET); + return Objects.requireNonNull((NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET)); } catch (XPathExpressionException | NullPointerException e) { Document doc = createEmptyDocument(); return doc.createDocumentFragment().getChildNodes(); } } + public static Node getNodeFromXPath(String expression, Document document) { + try { + XPathExpression xPathExpression = createXPathExpression(expression); + return Objects.requireNonNull((Node) xPathExpression.evaluate(document, XPathConstants.NODE)); + } catch (XPathExpressionException | NullPointerException e) { + Document doc = createEmptyDocument(); + return doc.createElement("null"); + } + } + + public static Node getNodeFromXPath(String expression, Document document, boolean onErrorReturnNull) { + try { + XPathExpression xPathExpression = createXPathExpression(expression); + return Objects.requireNonNull((Node) xPathExpression.evaluate(document, XPathConstants.NODE)); + } catch (XPathExpressionException | NullPointerException e) { + if (onErrorReturnNull) { + return null; + } + Document doc = createEmptyDocument(); + return doc.createElement("null"); + } + } + public static Optional getOptionalDomFromFile(final File file) { try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); diff --git a/src/test/java/org/toolertools/xml/XmlHandlerTest.java b/src/test/java/org/toolertools/xml/XmlHandlerTest.java index 1367791..c0a2828 100644 --- a/src/test/java/org/toolertools/xml/XmlHandlerTest.java +++ b/src/test/java/org/toolertools/xml/XmlHandlerTest.java @@ -2,32 +2,44 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; -import org.junit.jupiter.api.Test; +import java.util.Arrays; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.w3c.dom.Document; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; class XmlHandlerTest { - private static final String ELEMENT_1_X_PATH = "/root/element1/text()"; - private static final String EMPTY_ELEMENT_X_PATH = "/root/emptyElement/text()"; - private static final String COMPLIANT_X_PATH = ELEMENT_1_X_PATH; - private static final String MALFORMED_X_PATH = "/root/element1/tet()"; + private static final String TEST_ELEMENT_TEXT = "//test-element/text()"; + private static final String NESTED_ELEMENT_TEXT = "//nested-element/text()"; + private static final String EMPTY_ELEMENT_TEXT = "//empty-element/text()"; + private static final String SELF_CLOSING_TEXT = "//self-closing/text()"; + private static final String ELEMENT_GROUP_TEXT = "//element-group/text()"; + private static final String ELEMENT_GROUP_NODE = "//element-group"; + private static final String COMPLIANT_X_PATH = TEST_ELEMENT_TEXT; + private static final String MALFORMED_X_PATH = "/root/malformed/text()"; @ParameterizedTest @ValueSource(strings = "src/test/resources/test_resource_1.xml") void testGetStringFromXPath_success(String input) { Document doc = XmlHandler.getOptionalDomFromFile(new File(input)).orElseThrow(IllegalStateException::new); - String element1 = XmlHandler.getStringFromXPath(ELEMENT_1_X_PATH, doc); - String emptyElement = XmlHandler.getStringFromXPath(EMPTY_ELEMENT_X_PATH, doc); + String testElement = XmlHandler.getStringFromXPath(TEST_ELEMENT_TEXT, doc); + String nestedElement = XmlHandler.getStringFromXPath(NESTED_ELEMENT_TEXT, doc); - assertFalse(element1.isEmpty(), ELEMENT_1_X_PATH + " should not be empty"); - assertTrue(emptyElement.isEmpty(), EMPTY_ELEMENT_X_PATH + " should be empty"); + String emptyElement = XmlHandler.getStringFromXPath(EMPTY_ELEMENT_TEXT, doc); + String selfClosing = XmlHandler.getStringFromXPath(SELF_CLOSING_TEXT, doc); + String elementGroup = XmlHandler.getStringFromXPath(ELEMENT_GROUP_TEXT, doc); + + assertPresentElements(testElement, nestedElement, elementGroup); + + assertEmptyElements(emptyElement, selfClosing); } @ParameterizedTest @@ -35,15 +47,12 @@ void testGetStringFromXPath_success(String input) { void givenBadValues_getStringFromXPath_shouldReturnEmptyString(String input) { Document doc = XmlHandler.getOptionalDomFromFile(new File(input)).orElseThrow(IllegalStateException::new); - String element1 = XmlHandler.getStringFromXPath(ELEMENT_1_X_PATH, null); - String emptyElement = XmlHandler.getStringFromXPath(EMPTY_ELEMENT_X_PATH, null); + String testElement = XmlHandler.getStringFromXPath(TEST_ELEMENT_TEXT, null); + String emptyElement = XmlHandler.getStringFromXPath(EMPTY_ELEMENT_TEXT, null); String malformed = XmlHandler.getStringFromXPath(MALFORMED_X_PATH, doc); - String malformed2 = XmlHandler.getStringFromXPath(null, doc); + String nullExpressionCase = XmlHandler.getStringFromXPath(null, doc); - assertTrue(element1.isEmpty(), ELEMENT_1_X_PATH + " should be empty"); - assertTrue(emptyElement.isEmpty(), EMPTY_ELEMENT_X_PATH + "should be empty"); - assertTrue(malformed.isEmpty(), MALFORMED_X_PATH + "should be empty"); - assertTrue(malformed2.isEmpty(), "provided null expression should be empty"); + assertEmptyElements(testElement, emptyElement, malformed, nullExpressionCase); } @ParameterizedTest @@ -51,9 +60,9 @@ void givenBadValues_getStringFromXPath_shouldReturnEmptyString(String input) { void givenCompliantValues_testNodeList_success(String input) { Document doc = XmlHandler.getOptionalDomFromFile(new File(input)).orElseThrow(IllegalStateException::new); - NodeList nlCompliant = XmlHandler.getNodeListFromXPath(COMPLIANT_X_PATH, doc); + NodeList elementGroup = XmlHandler.getNodeListFromXPath(NESTED_ELEMENT_TEXT, doc); - assertEquals(1, nlCompliant.getLength(), "NodeList should be size 1"); + assertEquals(3, elementGroup.getLength(), "NodeList should be size 3"); } @ParameterizedTest @@ -61,12 +70,66 @@ void givenCompliantValues_testNodeList_success(String input) { void givenBadValues_testNodeList_failure(String input) { Document doc = XmlHandler.getOptionalDomFromFile(new File(input)).orElseThrow(IllegalStateException::new); - NodeList nlBad = XmlHandler.getNodeListFromXPath(COMPLIANT_X_PATH, null); - NodeList nlBad2 = XmlHandler.getNodeListFromXPath(MALFORMED_X_PATH, doc); - NodeList nlBad3 = XmlHandler.getNodeListFromXPath(null, doc); + NodeList nullDocumentCase = XmlHandler.getNodeListFromXPath(COMPLIANT_X_PATH, null); + NodeList malformedXPathCase = XmlHandler.getNodeListFromXPath(MALFORMED_X_PATH, doc); + NodeList nullXPathcase = XmlHandler.getNodeListFromXPath(null, doc); + + assertEmptyNodeLists(nullDocumentCase, malformedXPathCase, nullXPathcase); + } + + @ParameterizedTest + @ValueSource(strings = "src/test/resources/test_resource_1.xml") + void givenCompliantValues_testGetNodeFromXPath_success(String input) { + Document doc = XmlHandler.getOptionalDomFromFile(new File(input)).orElseThrow(IllegalStateException::new); + + Node groupElement = XmlHandler.getNodeFromXPath(ELEMENT_GROUP_NODE, doc); + + assertEquals(Document.ELEMENT_NODE, groupElement.getNodeType(), "Should be element node"); + assertEquals("element-group", groupElement.getNodeName(), "Should be element node"); + } + + @ParameterizedTest + @ValueSource(strings = "src/test/resources/test_resource_1.xml") + void givenBadValues_testGetNodeFromXPath_failure(String input) { + Document doc = XmlHandler.getOptionalDomFromFile(new File(input)).orElseThrow(IllegalStateException::new); + + Node groupElement = XmlHandler.getNodeFromXPath(MALFORMED_X_PATH, doc); + Node groupElementNullExpression = XmlHandler.getNodeFromXPath(null, doc); + Node groupElementNullDoc = XmlHandler.getNodeFromXPath(MALFORMED_X_PATH, null); + + assertNotNullNodes(groupElement, groupElementNullExpression, groupElementNullDoc); + + assertNodeNameNull(groupElement, groupElementNullExpression, groupElementNullDoc); + } + + @ParameterizedTest + @ValueSource(strings = "src/test/resources/test_resource_1.xml") + void givenBadValues_testGetNodeFromXPathOverloaded_failure(String input) { + Document doc = XmlHandler.getOptionalDomFromFile(new File(input)).orElseThrow(IllegalStateException::new); + + Node groupElement = XmlHandler.getNodeFromXPath(MALFORMED_X_PATH, doc, true); + + assertNull(groupElement, "Should be null"); + } + + private static void assertEmptyElements(String... strings) { + Arrays.stream(strings).forEach(string -> assertTrue(string.isEmpty(), "Should be empty")); + } + + private static void assertPresentElements(String... strings) { + Arrays.stream(strings).forEach(string -> assertFalse(string.isEmpty(), "Should not be empty")); + } + + private static void assertEmptyNodeLists(NodeList... strings) { + Arrays.stream(strings) + .forEach(nodeList -> assertEquals(0, nodeList.getLength(), "NodeList should be be size 0")); + } + + private static void assertNotNullNodes(Node... nodes) { + Arrays.stream(nodes).forEach(node -> assertNotNull(node, "Should not be null")); + } - assertEquals(0, nlBad.getLength(), "NodeList should be be size 0"); - assertEquals(0, nlBad2.getLength(), "NodeList should be be size 0"); - assertEquals(0, nlBad3.getLength(), "NodeList should be be size 0"); + private static void assertNodeNameNull(Node... nodes) { + Arrays.stream(nodes).forEach(node -> assertEquals("null", node.getNodeName(), "Node name should be 'null'")); } } diff --git a/src/test/resources/test_resource_1.xml b/src/test/resources/test_resource_1.xml index d9eecdb..23c3a6e 100644 --- a/src/test/resources/test_resource_1.xml +++ b/src/test/resources/test_resource_1.xml @@ -1,14 +1,11 @@ - - Value 1 - Value 2 - - - SubValue - - - Item 1 - Item 2 - Item 3 - + + Test Value + + + + Item 1 + Item 2 + Item 3 + diff --git a/src/test/resources/test_resource_2.xml b/src/test/resources/test_resource_2.xml deleted file mode 100644 index 0cfd842..0000000 --- a/src/test/resources/test_resource_2.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - The Great Gatsby - F. Scott Fitzgerald - Fiction - - - To Kill a Mockingbird - Harper Lee - Fiction - - - 1984 - George Orwell - Dystopian - -