From caae53f4dadd146426a1a2cd121ac00bb98ebc97 Mon Sep 17 00:00:00 2001 From: Ruprabhu25 Date: Tue, 20 Sep 2022 17:35:26 +0000 Subject: [PATCH] 8289508: Improve test coverage for XPath Axes: ancestor, ancestor-or-self, preceding, and preceding-sibling Reviewed-by: joehw --- .../unittest/xpath/XPathAncestorsTest.java | 186 ++++++++++++++++++ .../unittest/xpath/XPathPrecedingTest.java | 185 +++++++++++++++++ 2 files changed, 371 insertions(+) create mode 100644 test/jaxp/javax/xml/jaxp/unittest/xpath/XPathAncestorsTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/xpath/XPathPrecedingTest.java diff --git a/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathAncestorsTest.java b/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathAncestorsTest.java new file mode 100644 index 00000000000..b18a642618e --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathAncestorsTest.java @@ -0,0 +1,186 @@ +/* + * 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 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; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/* + * @test + * @bug 8289508 + * @library /javax/xml/jaxp/unittest + * @run testng/othervm xpath.XPathAncestorsTest + * @summary Tests for XPath ancestor and ancestor-or-self axis specifiers. + */ +public class XPathAncestorsTest { + + private static final String XML = """ + + + + <author id="1"/> + <isbn>1234</isbn> + </book> + <book id="2" lang="en"> + <title/> + <author id="2"/> + <isbn>5678</isbn> + </book> + </store> + """; + private static final Document doc; + + static { + try { + var builder = + DocumentBuilderFactory.newInstance().newDocumentBuilder(); + InputStream s = new ByteArrayInputStream(XML.getBytes()); + doc = builder.parse(s); + } catch (Exception e) { + System.out.println("Exception while initializing XML document"); + throw new RuntimeException(e.getMessage()); + } + } + + /* + * DataProvider:provides XPath expression using ancestor/ancestor-or-self + * and the expected node(s) from the expression + */ + @DataProvider(name = "ancestors_axes") + public Object[][] getXPathAncestors() { + return new Object[][]{ + //test ancestor + + // abbreviated text + {"//author/ancestor::book/ancestor::store", "/store"}, + {"//isbn/ancestor::store", "/store"}, + {"//ancestor::book[1]", "//book[1]"}, + + // any node + {"//book/ancestor::*", "/store"}, + {"//author/ancestor::*[ancestor::store]/ancestor::store", "/store"}, + {"//author/ancestor::node()[ancestor::store]", "//book"}, + + // dot reference + {"//author/ancestor::book/..", "/store"}, + {"//author/ancestor::*[ancestor::store]/..", "/store"}, + {"//ancestor::book/..", "/store"}, + + // attributes + {"//author/ancestor::*[@id]/parent::*", "/store"}, + {"//author/parent::*[@id]/ancestor::*", "/store"}, + {"//author[@id='1']/ancestor::book[1]", "//book[1]"}, + {"//author[@*]/ancestor::book[1]", "//book[1]"}, + + //test ancestor-or-self + + // any node, indexing, id + {"/store/ancestor-or-self::*", "/store"}, + {"//book[*]/ancestor-or-self::book[1]", "//book[1]"}, + {"/store/book[@*]/ancestor-or-self::book[1]", "//book[1]"}, + {"//book[@id='1']/ancestor-or-self::book[1]", "//book[1]"}, + {"//author[@id='2']/ancestor-or-self::book", "//book[2]"}, + {"//book[1]/ancestor-or-self::store", "/store"}, + + }; + } + + /* + * DataProvider: provides XPath expressions that return empty NodeSet + */ + @DataProvider(name = "emptyNodeSet") + public Object[][] getEmptyNodeSet() { + return new Object[][]{ + // test ancestor + + // abbreviated text + {"/store/book/ancestor::book"}, + {"//author/ancestor::store[2]"}, + {"//author[3]/ancestor::store"}, + + // any nodes + {"/store/ancestor::*"}, + {"/store/book[3]/ancestor::*"}, + {"//book[*]/../ancestor::*"}, + {"/store/book[@id='3']/ancestor::*"}, + {"//book/ssn/ancestor::*"}, + {"//author/ancestor::*[ancestor::isbn]"}, + {"/store/../ancestor::*"}, + {"//ancestor::author"}, + + // id + {"/store/book[@id='3']/ancestor::*"}, + {"/store[@*]/ancestor::*"}, + {"/book[@*]/ancestor::*/ancestor::*"}, + {"//book[@category]/ancestor::*"}, + + //test ancestor-or-self + + // any nodes, id + {"/store/../ancestor-or-self::*"}, + {"//book[3]/ancestor-or-self::*"}, + {"//author/ancestor-or-self::title"}, + {"//author[@id='2']/ancestor-or-self::*[@id='1']"}, + }; + } + + /** + * Verifies XPath ancestor and ancestor-or-self axis specifiers + * by comparing expression and expected result. + * @param exp XPath expression + * @param expected expected result + * @throws Exception if test failed + */ + @Test(dataProvider = "ancestors_axes") + void testXPathAncestors(String exp, String parent) throws Exception { + XPath xPath = XPathFactory.newInstance().newXPath(); + Node result = xPath.evaluateExpression(exp, doc, Node.class); + Node expected = xPath.evaluateExpression(parent, doc, Node.class); + Assert.assertEquals(result, expected); + } + + /** + * Verifies no nodes returned from the XPath expression. + * + * @param exp XPath expression + * @throws Exception + */ + @Test(dataProvider = "emptyNodeSet") + void testEmptyNodeSet(String exp) throws Exception { + XPath xPath = XPathFactory.newInstance().newXPath(); + Node result = xPath.evaluateExpression(exp, doc, Node.class); + Assert.assertEquals(result, null); + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathPrecedingTest.java b/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathPrecedingTest.java new file mode 100644 index 00000000000..50256589370 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/xpath/XPathPrecedingTest.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 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; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/* + * @test + * @bug 8289508 + * @library /javax/xml/jaxp/unittest + * @run testng/othervm xpath.XPathPrecedingTest + * @summary Tests for XPath preceding and preceding-sibling axis specifiers. + */ +public class XPathPrecedingTest { + + private static final String XML = """ + <store> + <book id="1" lang="en"> + <title>Book1 + + 1234 + + + Book2 + + 5678 + + + """; + private static final Document doc; + + static { + try { + var builder = + DocumentBuilderFactory.newInstance().newDocumentBuilder(); + InputStream s = new ByteArrayInputStream(XML.getBytes()); + doc = builder.parse(s); + } catch (Exception e) { + System.out.println("Exception while initializing XML document"); + throw new RuntimeException(e.getMessage()); + } + } + + /* + * DataProvider: provides XPath expression using preceding/preceding-sibling + * and the expected node(s) from the expression + */ + @DataProvider(name = "preceding_axes") + public Object[][] getXPathPreceding() { + return new Object[][]{ + // test preceding + + // any nodes + {"/store/book[1]/author/preceding::*", "/store/book[1]/title"}, + + // abbreviated text + {"/store/book[1]/isbn/preceding::*[1]", "/store/book[1]/author"}, + {"(/store/book[1]/isbn/preceding::*)[1]", "/store/book[1]/title"}, + {"//isbn/preceding::book", "//book[1]"}, + {"//book[2]/preceding::book", "//book[1]"}, + {"/store/book[preceding::book]", "//book[2]"}, + {"/store/book[preceding::book]/preceding::book", "//book[1]"}, + + // id + {"//author[@id='2']/../preceding::book", "//book[1]"}, + {"//author[@id='2']/preceding::node()/preceding::book", "//book[1]"}, + {"//author[@id='1']/preceding::title", "//book[1]/title"}, + + //test preceding-sibling + + // any node + {"/store/book[1]/author/preceding-sibling::*", "/store/book[1]/title"}, + {"/store/book[2]/preceding-sibling::*", "//book[1]"}, + {"//author/preceding-sibling::*", "//title"}, + + // abbreviated text + {"/store/book[preceding::book]/preceding-sibling::book", "//book[1]"}, + + // id + {"/store/book[1]/isbn[preceding-sibling::author[@id='1']]", "/store/book[1]/isbn"}, + + }; + } + + /* + * DataProvider: provides XPath expressions that return empty NodeSet + */ + @DataProvider(name = "emptyNodeSet") + public Object[][] getEmptyNodeSet() { + return new Object[][]{ + //test preceding + + // abbreviated text + {"/store/preceding::book"}, + {"/store/book[1]/author/preceding::author"}, + + // any nodes/id + {"/store/book[1]/preceding::*"}, + {"/store/book[1]/title/preceding::*"}, + {"/store/book[@id='1']/preceding::*"}, + + //test preceding-sibling + + // any nodes + {"/store/book[1]/preceding-sibling::*"}, + {"/store/book[2]/title/preceding-sibling::*"}, + + // abbreviated text / id + {"/store/book[1]/author/preceding-sibling::isbn"}, + {"//author[@id='2']/preceding-sibling::book"}, + {"//author[@id='2']/preceding-sibling::node()/preceding-sibling::author"}, + + // attribute / namespace + {"/store/book[2]/@id/preceding-sibling::*"}, + {"/store/book/@lang/preceding-sibling::*"}, + {"/store/book[2]/namespace::*/preceding-sibling::*"}, + + // text node + {"/store/book[2]/isbn/text()/preceding-sibling::*"}, + + }; + } + + /** + * Verifies XPath preceding and preceding-sibling axis specifiers by + * comparing expression and expected result. + * @param exp XPath expression + * @param expected expected result + * @throws Exception if test failed + */ + @Test(dataProvider = "preceding_axes") + void testXPathPreceding(String exp, String parent) throws Exception { + XPath xPath = XPathFactory.newInstance().newXPath(); + Node result = xPath.evaluateExpression(exp, doc, Node.class); + Node expected = xPath.evaluateExpression(parent, doc, Node.class); + Assert.assertEquals(result, expected); + } + + /** + * Verifies no nodes returned from the XPath expression. + * + * @param exp XPath expression + * @throws Exception + */ + @Test(dataProvider = "emptyNodeSet") + void testEmptyNodeSet(String exp) throws Exception { + XPath xPath = XPathFactory.newInstance().newXPath(); + Node result = xPath.evaluateExpression(exp, doc, Node.class); + Assert.assertEquals(result, null); + } +} +