From 1961e81e02e707cd0c8241aa3af6ddabf7668589 Mon Sep 17 00:00:00 2001 From: Mahendra Chhipa Date: Wed, 12 Oct 2022 18:13:20 +0000 Subject: [PATCH] 8289509: Improve test coverage for XPath Axes: descendant, descendant-or-self, following, following-sibling Reviewed-by: bhuang, joehw --- .../xpath/XPathExpDescendantTest.java | 173 ++++++++++++++++ .../unittest/xpath/XPathExpFollowingTest.java | 185 ++++++++++++++++++ 2 files changed, 358 insertions(+) create mode 100644 test/jaxp/javax/xml/jaxp/unittest/xpath/XPathExpDescendantTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/xpath/XPathExpFollowingTest.java diff --git a/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathExpDescendantTest.java b/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathExpDescendantTest.java new file mode 100644 index 00000000000..170285b405f --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathExpDescendantTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package xpath; + + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/* + * @test + * @bug 8289509 + * @library /javax/xml/jaxp/unittest + * @run testng/othervm xpath.XPathExpDescendantTest + * @summary Tests for XPath descendant/descendant-or-self axis specifier. + */ +public class XPathExpDescendantTest extends XPathTestBase { + + /* + * DataProvider: provides XPath Axis descendant expressions and equivalent xpath expression. + */ + @DataProvider(name = "descendantXpath") + public Object[][] getDescendantXpathExpression() { + return new Object[][] { + {"/Customers/descendant::*", "/Customers//*"}, + {"/Customers/descendant::Customer", "//Customer"}, + {"/Customers/descendant::foo:Customer", "//foo:Customer"}, + {"/Customers/Customer[@id='x1']/descendant::Address", + "/Customers/Customer[@id='x1']/Address"}, + {"/Customers/Customer[@id='x1']/descendant::*", + "/Customers/Customer[@id='x1']//*"}, + {"/Customers/foo:Customer/foo:Address/descendant::*", + "/Customers/foo:Customer/foo:Address//*"}, + {"/Customers/descendant::Name", "/Customers//Name"}, + {"/Customers/descendant::Street", "/Customers//Street"}, + {"/Customers/descendant::Street[2]", "Customers/Customer[@id='x2']/Address/Street"}, + {"/Customers/descendant::Street[2]", "(Customers//Street)[2]"}, + {"/Customers/descendant::Street[position() = 2]", + "Customers/Customer[@id='x2']/Address/Street"}, + {"/Customers/descendant-or-self::*", "//*"}, + {"/Customers/descendant-or-self::Customer", "/Customers/Customer"}, + {"/Customers/descendant-or-self::foo:Customer", "/Customers/foo:Customer"}, + {"/Customers/Customer[@id='x1']/descendant-or-self::Address", + "/Customers/Customer[@id = 'x1']/Address"}, + {"/Customers/Customer[@id='x1']/descendant-or-self::*", + "/Customers/Customer[@id='x1'] | /Customers/Customer[@id = 'x1']//*"}, + {"/Customers/foo:Customer/foo:Address/descendant-or-self::*", + "/Customers/foo:Customer/foo:Address | /Customers/foo:Customer/foo:Address//*"}, + {"/Customers/Customer/*[descendant::Street]", "/Customers/Customer/Address"}, + {"/Customers/Customer/*[not(descendant::Street)]", "/Customers/Customer/*[name() != \"Address\"]"}, + {"/Customers/Customer/*[descendant-or-self::Street]", "/Customers/Customer/Address"}, + {"/Customers/Customer/*[not(descendant-or-self::Street)]", + "/Customers/Customer/*[name() != \"Address\"]"} + }; + } + + /* + * DataProvider: provides XPath descendant expressions and expected number of descendant nodes returned + */ + @DataProvider(name = "descendantXpathNodeCount") + public Object[][] getDescendantXpathExpressionNodeCount() { + return new Object[][] { + {"/Customers/descendant::*", 40}, + {"/Customers/descendant::Customer", 3}, + {"/Customers/descendant::foo:Customer", 1}, + {"/Customers/Customer[@id='x1']/descendant::Address", 1}, + {"/Customers/Customer[@id='x1']/descendant::*", 9}, + {"/Customers/foo:Customer/foo:Address/descendant::*", 3}, + {"/Customers/Customer[@id='x1']/Address/descendant::Address", 0}, + {"/Customers/descendant-or-self::*", 41}, + {"/Customers/descendant-or-self::Customer", 3}, + {"/Customers/descendant-or-self::foo:Customer", 1}, + {"/Customers/Customer[@id='x1']/descendant-or-self::Address", 1}, + {"/Customers/Customer[@id='x1']/Address/descendant-or-self::Address", 1}, + {"/Customers/Customer[@id='x1']/descendant-or-self::*", 10}, + {"/Customers/foo:Customer/foo:Address/descendant-or-self::*", 4}, + {"/Customers/*[descendant::Name]", 3}, + {"/Customers/foo:Customer/*[descendant-or-self::foo:Street]", 1} + }; + } + + /* + * DataProvider: provides XPath descendant expressions which should return null. + */ + @DataProvider(name = "descendantXpathEmpty") + public Object[][] getDescendantXpathExpressionEmpty() { + return new Object[][] { + {"/Customers/Customer/Name/descendant::*"}, + {"/Customers/foo:Customer/descendant::Name"}, + {"/Customers/Customer/descendant::foo:Name"}, + {"/Customers/descendant::id"}, + {"/Customers/Customer/Name/descendant-or-self::id"}, + {"/Customers/foo:Customer/descendant-or-self::Name"}, + {"/Customers/Customer/descendant-or-self::foo:Name"}, + {"/Customers/descendant-or-self::id"} + }; + } + + /** + * Verifies descendant xpath expression returns same nodes as returns when used normal xpath expression + * @param descexp descendant XPath expression. + * @param expath normal xPath expression + * @throws XPathExpressionException + */ + @Test(dataProvider = "descendantXpath") + public void descendantExpTests(String descexp, String expath) throws XPathExpressionException { + Document doc = documentOf(DECLARATION + RAW_XML); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList actualNodeList = (NodeList) xPath.evaluate(descexp, doc, XPathConstants.NODESET); + NodeList expectedNodeList = (NodeList) xPath.evaluate(expath, doc, XPathConstants.NODESET); + Assert.assertEquals(actualNodeList.getLength(), expectedNodeList.getLength()); + + for(int i = 0; i < actualNodeList.getLength(); i++) { + actualNodeList.item(i).equals(expectedNodeList.item(i)); + } + } + + /** + * Verifies descendant xpath expression return descendant nodes list with correct number of nodes. + * @param exp XPath expression. + * @param nodeCount number of descendant nodes in nodelist. + * @throws XPathExpressionException + */ + @Test(dataProvider = "descendantXpathNodeCount") + public void descendantNodesCountTests(String exp, int nodeCount) throws XPathExpressionException { + Document doc = documentOf(DECLARATION + RAW_XML); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList nodeList = (NodeList) xPath.evaluate(exp, doc, XPathConstants.NODESET); + Assert.assertEquals(nodeList.getLength(), nodeCount); + } + + /** + * Verifies descendant xpath expression return no nodes if descendant expression context nodes don't have matching descendants + * @param exp XPath expression. + * @throws XPathExpressionException + */ + @Test(dataProvider = "descendantXpathEmpty") + public void DescendantScopeTests(String exp) throws XPathExpressionException { + Document doc = documentOf(DECLARATION + RAW_XML); + XPath xPath = XPathFactory.newInstance().newXPath(); + Node node = xPath.evaluateExpression(exp, doc, Node.class); + Assert.assertNull(node); + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathExpFollowingTest.java b/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathExpFollowingTest.java new file mode 100644 index 00000000000..49e0bceab3b --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathExpFollowingTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package xpath; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/* + * @test + * @bug 8289509 + * @library /javax/xml/jaxp/unittest + * @run testng/othervm xpath.XPathExpFollowingTest + * @summary Tests for XPath following/following-sibling axis specifier. + */ +public class XPathExpFollowingTest extends XPathTestBase { + /* + * DataProvider: provides XPath Axis following expressions and equivalent xpath expression. + */ + @DataProvider(name = "followingXpath") + public Object[][] getFollowingXpathExpression() { + return new Object[][] { + {"/Customers/following::*", "/None"}, + {"/Customers/Customer/following::Customer", "//Customer[@id != 'x1']"}, + {"/Customers/Customer/following::foo:Customer", "//foo:Customer"}, + {"/Customers/Customer[@id='x1']/following::Address", + "/Customers/Customer[@id != 'x1']/Address"}, + {"/Customers/Customer[@id='x1']/following::Street", + "/Customers/Customer[@id != 'x1']/Address/Street"}, + {"/Customers/Customer[@id='x1']/following::Street[2]", + "/Customers/Customer[@id='x2']/Address/Street"}, + {"/Customers/Customer[@id='x1']/following::*", + "/Customers/Customer[@id != 'x1']/descendant-or-self::*" + + " | /Customers/foo:Customer/descendant-or-self::*"}, + {"/Customers/foo:Customer/foo:Address/following::*", + "/Customers/foo:Customer/foo:Age | /Customers/foo:Customer/foo:ClubMember"}, + {"/Customers/Customer[@id = 'x1']/*[following::Street]", "/Customers/Customer[@id = 'x1']/*"}, + {"/Customers/foo:Customer/*[following::foo:Name]", "/None"}, + {"/Customers/foo:Customer/*[not(following::foo:Name)]", "/Customers/foo:Customer/*"}, + {"/Customers/following-sibling::*", "/None"}, + {"/Customers/Customer/following-sibling::Customer", + "/Customers/Customer[@id != 'x1']"}, + {"/Customers/Customer/following-sibling::foo:Customer", + "/Customers/foo:Customer"}, + {"/Customers/Customer[@id='x1']/Name/following-sibling::Address", + "/Customers/Customer[@id='x1']/Address"}, + {"/Customers/Customer/Name/following-sibling::Address", + "/Customers//Address"}, + {"(/Customers/Customer/Address/Street/following-sibling::State)[3]", + "/Customers/Customer[@id='x3']/Address/State"}, + {"/Customers/Customer[@id='x1']/Address/Street/following-sibling::*[2]", + "/Customers/Customer[@id='x3']/Address/State"}, + {"/Customers/Customer[@id='x1']/following-sibling::*", + "/Customers/Customer[@id != 'x1'] | /Customers/foo:Customer"}, + {"/Customers/foo:Customer/foo:Address/following-sibling::*", + "/Customers/foo:Customer/foo:Age | /Customers/foo:Customer/foo:ClubMember"}, + {"/Customers/Customer[@id = 'x1']/*[following-sibling::Street]", "/None"}, + {"/Customers/foo:Customer/*[following-sibling::foo:Address]", "/Customers/foo:Customer/foo:Name |" + + "/Customers/foo:Customer/foo:Phone | /Customers/foo:Customer/foo:Email"}, + {"/Customers/foo:Customer/*[not(following-sibling::foo:Address)]", "/Customers/foo:Customer/foo:Age | " + + "/Customers/foo:Customer/foo:ClubMember | /Customers/foo:Customer/foo:Address"} + }; + } + + /* + * DataProvider: provides XPath following expressions and expected number of following nodes returned + */ + @DataProvider(name = "followingXpathNodeCount") + public Object[][] getFollowingXpathExpressionNodeCount() { + return new Object[][] { + {"/Customers/following::*", 0}, + {"/Customers/Customer/following::*", 30}, + {"/Customers/Customer/following::Customer", 2}, + {"/Customers/Customer/following::foo:Customer", 1}, + {"/Customers/Customer[@id='x1']/Name/following::*", 38}, + {"/Customers/Customer/Address/following::*", 32}, + {"/Customers/foo:Customer/foo:Address/following::*", 2}, + {"/Customers/foo:Customer/foo:Name/following::*", 8}, + {"/Customers/foo:Customer/*[following::foo:Name]", 0}, + {"/Customers/foo:Customer/*[not(following::foo:Name)]", 6}, + {"/Customers/following-sibling::*", 0}, + {"/Customers/Customer/following-sibling::*", 3}, + {"/Customers/Customer/following-sibling::Customer", 2}, + {"/Customers/Customer/following-sibling::foo:Customer", 1}, + {"/Customers/Customer[@id='x1']/Name/following-sibling::*", 5}, + {"/Customers/Customer/Address/following-sibling::*", 6}, + {"/Customers/Customer[@id='x1']/Address/following-sibling::*", 2}, + {"/Customers/foo:Customer/foo:Address/following-sibling::*", 2}, + {"/Customers/Customer[@id = 'x1']/*[following-sibling::Street]", 0}, + {"/Customers/foo:Customer/*[following-sibling::foo:Address]", 3}, + {"/Customers/foo:Customer/*[not(following-sibling::foo:Address)]", 3} + }; + } + + /* + * DataProvider: provides XPath following expressions which should not return any node. + */ + @DataProvider(name = "followingXpathEmpty") + public Object[][] getFollowingXpathExpressionEmpty() { + return new Object[][] { + {"/Customers/following::*"}, + {"/Customers/foo:Customer/following::*"}, + {"/Customers/Customer[@id = 'x3' ]/following::Customer"}, + {"/Customers/following::id"}, + {"/Customers/Customer[@id = 'x3' ]/following-sibling::Customer"}, + {"/Customers/foo:Customer/following-sibling::*"}, + {"/Customers/Customer/following-sibling::foo:Name"}, + {"/Customers/following-sibling::id"} + }; + } + + /** + * Verifies Axis following xpath expression returns same nodes as returns when used normal xpath expression + * @param descexp Axis following XPath expression. + * @param expath normal xPath expression + * @throws XPathExpressionException + */ + @Test(dataProvider = "followingXpath") + public void followingExpTests(String descexp, String expath) throws XPathExpressionException { + Document doc = documentOf(DECLARATION + RAW_XML); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList actualNodeList = (NodeList) xPath.evaluate(descexp, doc, XPathConstants.NODESET); + NodeList expectedNodeList = (NodeList) xPath.evaluate(expath, doc, XPathConstants.NODESET); + Assert.assertEquals(actualNodeList.getLength(), expectedNodeList.getLength()); + + for(int i = 0; i < actualNodeList.getLength(); i++) { + actualNodeList.item(i).equals(expectedNodeList.item(i)); + } + } + + /** + * Verifies following xpath expression return following nodes list with correct number of nodes. + * @param exp XPath expression. + * @param nodeCount number of following nodes in nodelist. + * @throws XPathExpressionException + */ + @Test(dataProvider = "followingXpathNodeCount") + public void followingNodesCountTests(String exp, int nodeCount) throws XPathExpressionException { + Document doc = documentOf(DECLARATION + RAW_XML); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList nodeList = (NodeList) xPath.evaluate(exp, doc, XPathConstants.NODESET); + Assert.assertEquals(nodeList.getLength(), nodeCount); + } + + /** + * Verifies following xpath expression return no nodes if following expression context nodes don't have matching following elements. + * @param exp XPath expression. + * @throws XPathExpressionException + */ + @Test(dataProvider = "followingXpathEmpty") + public void FollowingScopeTests(String exp) throws XPathExpressionException { + Document doc = documentOf(DECLARATION + RAW_XML); + XPath xPath = XPathFactory.newInstance().newXPath(); + Node node = xPath.evaluateExpression(exp, doc, Node.class); + Assert.assertNull(node); + } +}