XPath Functional Test

XPath Functional Test (XPath-FT) is a functional test for XPath 1.0. It consists of several groups of queries each covering a different functional aspect of the language like navigational axes, filters, node tests, operators and functions. The queries are interpreted over a small educational document and each query is accompanied with the correct answer.

The main goals of XPath-FT are testing completeness (which features of the language are supported?) and correctness (which features of the language are correctly implemented?) of an XML query processing system with respect to XPath 1.0. It is worth noticing that XPath-FT is not meant to test performance of a query processor. To this end, you may take advantage of XPath Performance Test. Incidentally, XPath-FT can also be used as an educational tool to learn XPath (if your goal is to learn XPath you may also find useful the section about XPath in Caffè XML).

Queries of the test are interpreted over an educational XML document, alphabet.xml, obeying to the following DTD, whose skeleton rapresents the English alphabet as shown below (notice that a preorder traversal of the tree corresponds to the alphabet sorted from A to Z):

A tree representing the English alphabet

There are 5 groups of queries: axes, filters, node tests, operators (including Boolean, relational and arithmetic operators) and functions. Below we report the queries along with their answers in XML format. The XML answer begins with an XML declaration. The query result has no indentation and is wrapped into a document element called answer (which is not part of the query result). For some queries we also add the answer in graphical format. Here is an archive containing all the queries (in XPath, XQuery, and XSLT formats) and their answers (in XML format).


A1 //L/* {answer: text, picture}

A2 //L/parent::* {answer: text, picture}

A3 //L/descendant::* {answer: text, picture}

A4 //L/descendant-or-self::* {answer: text, picture}

A5 //L/ancestor::* {answer: text, picture}

A6 //L/ancestor-or-self::* {answer: text, picture}

A7 //L/following-sibling::* {answer: text, picture}

A8 //L/preceding-sibling::* {answer: text, picture}

A9 //L/following::* {answer: text, picture}

A10 //L/preceding::* {answer: text, picture}

A11 //L/self::* {answer: text, picture}

A12 //L/@id/parent::* {answer: text}


P1 //*[L] {answer: text, picture}

P2 //*[parent::L] {answer: text, picture}

P3 //*[descendant::L] {answer: text, picture}

P4 //*[descendant-or-self::L] {answer: text, picture}

P5 //*[ancestor::L] {answer: text, picture}

P6 //*[ancestor-or-self::L] {answer: text, picture}

P7 //*[following-sibling::L] {answer: text, picture}

P8 //*[preceding-sibling::L] {answer: text, picture}

P9 //*[following::L] {answer: text, picture}

P10 //*[preceding::L] {answer: text, picture}

P11 //*[self::L] {answer: text, picture}

P12 //*[@id] {answer: text, picture}

Node tests

T1 //L/text() {answer: text}

T2 //L/comment() {answer: text}

T3 //L/processing-instruction() {answer: text}

T4 //L/processing-instruction("myPI") {answer: text}

T5 //L/node() {answer: text}

T6 //L/N {answer: text}

T7 //L/* {answer: text}


O1 //*[child::* and preceding::Q] {answer: text, picture}

O2 //*[not(child::*) and preceding::Q] {answer: text, picture}

O3 //*[preceding::L or following::L] {answer: text, picture}

O4 //L/ancestor::* | //L/descendant::* {answer: text, picture}

O5 //*[.="happy-go-lucky man"] {answer: text}

O6 //*[@pre > 12 and @post < 15] {answer: text}

O7 //*[@pre != @post] {answer: text}

O8 //*[((@post * @post + @pre * @pre) div (@post + @pre)) > ((@post - @pre) * (@post - @pre))] {answer: text}

O9 //*[@pre mod 2 = 0] {answer: text}


F1 //*[contains(.,"plentiful")] {answer: text}

F2 //*[starts-with(.,"plentiful")] {answer: text}

F3 //*[substring(.,1,9) = "plentiful"] {answer: text}

F4 //*[substring-after(.,"oven") = "ware"] {answer: text}

F5 //*[substring-before(.,"ful") = "plenti"] {answer: text}

F6 //*[string-length(translate(normalize-space(.)," ","")) > 100] {answer: text}

F7 //*[concat(.,..) = ..] {answer: text}

F8 //*[ceiling(@pre div @post) = 1] {answer: text}

F9 //*[floor(@pre div @post) = 0] {answer: text}

F10 //*[round(@pre div @post) = 0] {answer: text}

F11 //*[name(.) = "X"] {answer: text}

F12 //*[lang("it")] {answer: text}

F13 //L/child::*[last()] {answer: text}

F14 //L/descendant::*[4] {answer: text}

F15 //L/ancestor::*[2] {answer: text}

F16 //L/following-sibling::*[1] {answer: text}

F17 //L/preceding-sibling::*[1] {answer: text}

F18 //L/following::*[7] {answer: text}

F19 //L/preceding::*[7] {answer: text}

F20 //*[count(ancestor::*) > 3] {answer: text}

F21 //*[sum(ancestor::*/@pre) < sum(descendant::*/@pre)] {answer: text}

F22 id("n1 n26") {answer: text}

F23 id(id(//*[.="happy-go-lucky man"]/@idrefs)/@idrefs) {answer: text}

F24 //*[number(@pre) < number(@post)] {answer: text}

F25 //*[string(@pre - 1) = "0"] {answer: text}

F26 //*[boolean(@id) = true() and boolean(@idrefs) = false()] {answer: text}