Skip to content

Commit

Permalink
8289508: Improve test coverage for XPath Axes: ancestor, ancestor-or-…
Browse files Browse the repository at this point in the history
…self, preceding, and preceding-sibling

Reviewed-by: phh
Backport-of: caae53f4dadd146426a1a2cd121ac00bb98ebc97
  • Loading branch information
GoeLin committed Jun 25, 2023
1 parent 7f1047e commit b2e04df
Show file tree
Hide file tree
Showing 2 changed files with 371 additions and 0 deletions.
186 changes: 186 additions & 0 deletions test/jaxp/javax/xml/jaxp/unittest/xpath/XPathAncestorsTest.java
Original file line number Diff line number Diff line change
@@ -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 =
"<store>\n" +
" <book id=\"1\" lang=\"en\">\n" +
" <title/>\n" +
" <author id=\"1\"/>\n" +
" <isbn>1234</isbn>\n" +
" </book>\n" +
" <book id=\"2\" lang=\"en\">\n" +
" <title/>\n" +
" <author id=\"2\"/>\n" +
" <isbn>5678</isbn>\n" +
" </book>\n" +
"</store>\n"
;
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);
}
}
185 changes: 185 additions & 0 deletions test/jaxp/javax/xml/jaxp/unittest/xpath/XPathPrecedingTest.java
Original file line number Diff line number Diff line change
@@ -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>\n" +
" <book id=\"1\" lang=\"en\">\n" +
" <title>Book1</title>\n" +
" <author id=\"1\"/>\n" +
" <isbn>1234</isbn>\n" +
" </book>\n" +
" <book id=\"2\" lang=\"en\" xmlns=\"www.foo.com\">\n" +
" <title>Book2</title>\n" +
" <author id=\"2\"/>\n" +
" <isbn>5678</isbn>\n" +
" </book>\n" +
"</store>\n"
;
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);
}
}

1 comment on commit b2e04df

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.