pugixml/tests/test_xpath_operators.cpp
Arseny Kapoulkine c55ea3bc1e XPath: Make remove_duplicates generate stable order
Given an unsorted sequence, remove_duplicates would sort it using the
pointer value of attributes/nodes and then remove consecutive
duplicates.

This was problematic because it meant that the result of XPath queries
was dependent on the memory allocation pattern. While it's technically
incorrect to rely on the order, this results in easy to miss bugs.

This is particularly common when XPath queries use union operators -
although we also will call remove_duplicates in other cases.

This change reworks the code to use a hash set instead, using the same
hash function we use for compact storage. To make sure it performs well,
we allocate enough buckets for count * 1.5 (assuming all elements are
unique); since each bucket is a single pointer unlike xpath_node which
is two pointers, we need somewhere between size * 0.75 and size * 1.5
temporary storage.

The resulting filtering is stable - we remove elements that we have seen
before but we don't change the order - and is actually significantly
faster than sorting was.

With a large union operation, before this change it took ~56 ms per 100
query invocations to remove duplicates, and after this change it takes
~20ms.

Fixes #254.
2019-02-26 23:57:58 -08:00

567 lines
22 KiB
C++

#ifndef PUGIXML_NO_XPATH
#include "test.hpp"
using namespace pugi;
TEST(xpath_operators_arithmetic)
{
xml_node c;
// incorrect unary operator
CHECK_XPATH_FAIL(STR("-"));
// correct unary operator
CHECK_XPATH_NUMBER(c, STR("-1"), -1);
CHECK_XPATH_NUMBER(c, STR("--1"), 1);
CHECK_XPATH_NUMBER(c, STR("---1"), -1);
// incorrect binary operators
CHECK_XPATH_FAIL(STR("5+"));
CHECK_XPATH_FAIL(STR("5-"));
CHECK_XPATH_FAIL(STR("5*"));
CHECK_XPATH_FAIL(STR("+5"));
CHECK_XPATH_FAIL(STR("*5"));
CHECK_XPATH_FAIL(STR("1div2"));
CHECK_XPATH_FAIL(STR("1mod"));
CHECK_XPATH_FAIL(STR("1div"));
// correct trivial binary operators
CHECK_XPATH_NUMBER(c, STR("1 + 2"), 3);
CHECK_XPATH_NUMBER(c, STR("1+2"), 3);
CHECK_XPATH_NUMBER(c, STR("1 * 2"), 2);
CHECK_XPATH_NUMBER(c, STR("1*2"), 2);
CHECK_XPATH_NUMBER(c, STR("1 div 2"), 0.5);
// operator precedence
CHECK_XPATH_NUMBER(c, STR("2 + 2 * 2 div 1 mod 3"), 3);
CHECK_XPATH_NUMBER(c, STR("2 + 2 * 2 div (1 mod 3)"), 6);
CHECK_XPATH_NUMBER(c, STR("(2 + 2) * 2 div (1 mod 3)"), 8);
CHECK_XPATH_NUMBER(c, STR("(2 + 2) * (2 div 1) mod 3"), 2);
CHECK_XPATH_NUMBER(c, STR("2 - -2"), 4);
CHECK_XPATH_NUMBER(c, STR("2 + -2"), 0);
CHECK_XPATH_NUMBER(c, STR("2--2"), 4);
CHECK_XPATH_NUMBER(c, STR("2+-2"), 0);
CHECK_XPATH_NUMBER(c, STR("1-2-3"), -4);
// mod, from W3C standard
CHECK_XPATH_NUMBER(c, STR("5 mod 2"), 1);
CHECK_XPATH_NUMBER(c, STR("5 mod -2"), 1);
CHECK_XPATH_NUMBER(c, STR("-5 mod 2"), -1);
CHECK_XPATH_NUMBER(c, STR("-5 mod -2"), -1);
}
TEST(xpath_operators_arithmetic_specials)
{
xml_node c;
// infinity/nan
CHECK_XPATH_STRING(c, STR("1 div 0"), STR("Infinity"));
CHECK_XPATH_STRING(c, STR("-1 div 0"), STR("-Infinity"));
CHECK_XPATH_STRING(c, STR("-1 div 0 + 1 div 0"), STR("NaN"));
CHECK_XPATH_STRING(c, STR("0 div 0"), STR("NaN"));
CHECK_XPATH_STRING(c, STR("1 div 0 + 1 div 0"), STR("Infinity"));
CHECK_XPATH_STRING(c, STR("-1 div 0 + -1 div 0"), STR("-Infinity"));
CHECK_XPATH_STRING(c, STR("1 div 0 + 100"), STR("Infinity"));
CHECK_XPATH_STRING(c, STR("-1 div 0 + 100"), STR("-Infinity"));
CHECK_XPATH_STRING(c, STR("0 div 0 + 100"), STR("NaN"));
// unary - and multiplication clarifications from recommendations errata
CHECK_XPATH_STRING(c, STR("1 div -0"), STR("-Infinity"));
CHECK_XPATH_STRING(c, STR("-1 div -0"), STR("Infinity"));
CHECK_XPATH_STRING(c, STR("1 div (-0 * 1)"), STR("-Infinity"));
CHECK_XPATH_STRING(c, STR("-1 div (0 * -1)"), STR("Infinity"));
CHECK_XPATH_STRING(c, STR("1 div (-0 div 1)"), STR("-Infinity"));
CHECK_XPATH_STRING(c, STR("-1 div (0 div -1)"), STR("Infinity"));
}
TEST_XML(xpath_operators_arithmetic_subtraction_parse, "<node><foo-bar>10</foo-bar><foo>2</foo><bar>3</bar></node>")
{
xml_node n = doc.child(STR("node"));
// correct subtraction parsing, from W3C standard
CHECK_XPATH_NUMBER(n, STR("foo-bar"), 10);
CHECK_XPATH_NUMBER(n, STR("foo -bar"), -1);
CHECK_XPATH_NUMBER(n, STR("foo - bar"), -1);
CHECK_XPATH_NUMBER(n, STR("-foo-bar"), -10);
CHECK_XPATH_NUMBER(n, STR("-foo -bar"), -5);
}
TEST(xpath_operators_logical)
{
xml_node c;
// boolean arithmetic
CHECK_XPATH_BOOLEAN(c, STR("true() or true()"), true);
CHECK_XPATH_BOOLEAN(c, STR("true() or false()"), true);
CHECK_XPATH_BOOLEAN(c, STR("false() or false()"), false);
CHECK_XPATH_BOOLEAN(c, STR("false() or true()"), true);
CHECK_XPATH_BOOLEAN(c, STR("true() and true()"), true);
CHECK_XPATH_BOOLEAN(c, STR("true() and false()"), false);
CHECK_XPATH_BOOLEAN(c, STR("false() and false()"), false);
CHECK_XPATH_BOOLEAN(c, STR("false() and true()"), false);
// boolean conversion
CHECK_XPATH_BOOLEAN(c, STR("1 or ''"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 and ''"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 or ''"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 or 'a'"), true);
}
TEST(xpath_operators_equality_primitive_boolean)
{
xml_node c;
// boolean vs boolan
CHECK_XPATH_BOOLEAN(c, STR("true() = true()"), true);
CHECK_XPATH_BOOLEAN(c, STR("false() = false()"), true);
CHECK_XPATH_BOOLEAN(c, STR("true() != false()"), true);
CHECK_XPATH_BOOLEAN(c, STR("false() != false()"), false);
// upcast to boolean
CHECK_XPATH_BOOLEAN(c, STR("true() = 2"), true);
CHECK_XPATH_BOOLEAN(c, STR("true() != 2"), false);
CHECK_XPATH_BOOLEAN(c, STR("false() = 2"), false);
CHECK_XPATH_BOOLEAN(c, STR("false() != 2"), true);
CHECK_XPATH_BOOLEAN(c, STR("false() = 0"), true);
CHECK_XPATH_BOOLEAN(c, STR("false() != 0"), false);
CHECK_XPATH_BOOLEAN(c, STR("2 = true()"), true);
CHECK_XPATH_BOOLEAN(c, STR("2 != true()"), false);
CHECK_XPATH_BOOLEAN(c, STR("2 = false()"), false);
CHECK_XPATH_BOOLEAN(c, STR("2 != false()"), true);
CHECK_XPATH_BOOLEAN(c, STR("0 = false()"), true);
CHECK_XPATH_BOOLEAN(c, STR("0 != false()"), false);
}
TEST(xpath_operators_equality_primitive_number)
{
xml_node c;
// number vs number
CHECK_XPATH_BOOLEAN(c, STR("1 = 1"), true);
CHECK_XPATH_BOOLEAN(c, STR("0.5 = 0.5"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 != 2"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 = -1"), false);
// infinity/nan
CHECK_XPATH_BOOLEAN(c, STR("1 div 0 = 2 div 0"), true);
CHECK_XPATH_BOOLEAN(c, STR("-1 div 0 != 2 div 0"), true);
#ifndef MSVC6_NAN_BUG
CHECK_XPATH_BOOLEAN(c, STR("0 div 0 = 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 div 0 != 1"), true);
CHECK_XPATH_BOOLEAN(c, STR("0 div 0 = 0 div 0"), false);
#endif
// upcast to number
CHECK_XPATH_BOOLEAN(c, STR("2 = '2'"), true);
CHECK_XPATH_BOOLEAN(c, STR("2 != '2'"), false);
CHECK_XPATH_BOOLEAN(c, STR("'1' != 2"), true);
CHECK_XPATH_BOOLEAN(c, STR("'1' = 2"), false);
}
TEST(xpath_operators_equality_primitive_string)
{
xml_node c;
// string vs string
CHECK_XPATH_BOOLEAN(c, STR("'a' = 'a'"), true);
CHECK_XPATH_BOOLEAN(c, STR("'a' = 'b'"), false);
CHECK_XPATH_BOOLEAN(c, STR("'ab' != 'a'"), true);
CHECK_XPATH_BOOLEAN(c, STR("'' != 'a'"), true);
CHECK_XPATH_BOOLEAN(c, STR("'a' != ''"), true);
CHECK_XPATH_BOOLEAN(c, STR("'' != ''"), false);
}
TEST_XML(xpath_operators_equality_node_set_node_set, "<node><c1><v>a</v><v>b</v></c1><c2><v>a</v><v>c</v></c2><c3><v>b</v></c3><c4><v>d</v></c4><c5><v>a</v><v>b</v></c5><c6><v>b</v></c6></node>")
{
xml_node c;
xml_node n = doc.child(STR("node"));
// node set vs node set
CHECK_XPATH_BOOLEAN(c, STR("x = x"), false); // empty node set compares as false with any other object via any comparison operator, as per XPath spec
CHECK_XPATH_BOOLEAN(c, STR("x != x"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = c2/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = c3/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c2/v = c3/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = c4/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = x"), false);
CHECK_XPATH_BOOLEAN(n, STR("x = c1"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v != c2/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v != c3/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c2/v != c3/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v != c4/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v != c5/v"), true); // (a, b) != (a, b), since a != b, as per XPath spec (comparison operators are so not intutive)
CHECK_XPATH_BOOLEAN(n, STR("c3/v != c6/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v != x"), false);
CHECK_XPATH_BOOLEAN(n, STR("x != c1/v"), false);
}
TEST_XML(xpath_operators_equality_node_set_primitive, "<node><c1><v>1</v><v>-1</v><v>100</v></c1><c2><v>1</v><v>nan</v></c2></node>")
{
xml_node c;
xml_node n = doc.child(STR("node"));
// node set vs number
CHECK_XPATH_BOOLEAN(c, STR("x = 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("x != 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("1 = x"), false);
CHECK_XPATH_BOOLEAN(c, STR("1 != x"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = 1"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = -1"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v != 1"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = 5"), false);
CHECK_XPATH_BOOLEAN(n, STR("c2/v = 1"), true);
CHECK_XPATH_BOOLEAN(n, STR("1 = c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("-1 = c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("1 != c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("5 = c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("1 = c2/v"), true);
#ifndef MSVC6_NAN_BUG
CHECK_XPATH_BOOLEAN(n, STR("c2/v != 1"), true);
CHECK_XPATH_BOOLEAN(n, STR("1 != c2/v"), true);
#endif
// node set vs string
CHECK_XPATH_BOOLEAN(c, STR("x = '1'"), false);
CHECK_XPATH_BOOLEAN(c, STR("x != '1'"), false);
CHECK_XPATH_BOOLEAN(c, STR("'1' = x"), false);
CHECK_XPATH_BOOLEAN(c, STR("'1' != x"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = '1'"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = '-1'"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v != '1'"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = '5'"), false);
CHECK_XPATH_BOOLEAN(n, STR("c2/v = '1'"), true);
CHECK_XPATH_BOOLEAN(n, STR("c2/v != '1'"), true);
CHECK_XPATH_BOOLEAN(n, STR("'1' = c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("'-1' = c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("'1' != c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("'5' = c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("'1' = c2/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("'1' != c2/v"), true);
// node set vs almost-numeric string just in case
CHECK_XPATH_BOOLEAN(n, STR("c1/v = '1.0'"), false);
// node set vs boolean - special rules! empty sets are equal to true()
CHECK_XPATH_BOOLEAN(n, STR("x = true()"), false);
CHECK_XPATH_BOOLEAN(n, STR("x != true()"), true);
CHECK_XPATH_BOOLEAN(n, STR("x = false()"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = true()"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v != true()"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v = false()"), false);
CHECK_XPATH_BOOLEAN(n, STR("true() = x"), false);
CHECK_XPATH_BOOLEAN(n, STR("true() != x"), true);
CHECK_XPATH_BOOLEAN(n, STR("false() = x"), true);
CHECK_XPATH_BOOLEAN(n, STR("true() = c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("true() != c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("false() = c1/v"), false);
}
TEST(xpath_operators_inequality_primitive)
{
xml_node c;
// number vs number
CHECK_XPATH_BOOLEAN(c, STR("1 < 2"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 <= 2"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 > 2"), false);
CHECK_XPATH_BOOLEAN(c, STR("1 >= 2"), false);
CHECK_XPATH_BOOLEAN(c, STR("1 < 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("1 <= 1"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 > 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("1 >= 1"), true);
// infinity/nan
CHECK_XPATH_BOOLEAN(c, STR("1 div 0 <= 2 div 0"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 div 0 < 2 div 0"), false);
CHECK_XPATH_BOOLEAN(c, STR("-1 div 0 < 2 div 0"), true);
CHECK_XPATH_BOOLEAN(c, STR("-1 div 0 > 2 div 0"), false);
#ifndef MSVC6_NAN_BUG
CHECK_XPATH_BOOLEAN(c, STR("0 div 0 < 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 div 0 <= 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 div 0 > 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 div 0 >= 1"), false);
#endif
// upcast to number
CHECK_XPATH_BOOLEAN(c, STR("2 < '2'"), false);
CHECK_XPATH_BOOLEAN(c, STR("1 < '2'"), true);
CHECK_XPATH_BOOLEAN(c, STR("2 <= '2'"), true);
CHECK_XPATH_BOOLEAN(c, STR("3 <= '2'"), false);
CHECK_XPATH_BOOLEAN(c, STR("2 > '2'"), false);
CHECK_XPATH_BOOLEAN(c, STR("3 > '2'"), true);
CHECK_XPATH_BOOLEAN(c, STR("2 >= '2'"), true);
CHECK_XPATH_BOOLEAN(c, STR("3 >= '2'"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 >= true()"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 > true()"), false);
}
TEST_XML(xpath_operators_inequality_node_set_node_set, "<node><c1><v>1</v><v>-1</v><v>-100</v></c1><c2><v>1</v><v>nan</v></c2><c3><v>1</v><v>-4</v></c3></node>")
{
xml_node c;
xml_node n = doc.child(STR("node"));
// node set vs node set
CHECK_XPATH_BOOLEAN(c, STR("x < x"), false);
CHECK_XPATH_BOOLEAN(c, STR("x > x"), false);
CHECK_XPATH_BOOLEAN(c, STR("x <= x"), false);
CHECK_XPATH_BOOLEAN(c, STR("x >= x"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v > x"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v < x"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v >= x"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v <= x"), false);
CHECK_XPATH_BOOLEAN(n, STR("x > c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("x < c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("x >= c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("x <= c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v > c3/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v >= c3/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v < c3/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v <= c3/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v[1] > c1/v[1]"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v[1] < c1/v[1]"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v[1] >= c1/v[1]"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v[1] <= c1/v[1]"), true);
#ifndef MSVC6_NAN_BUG
CHECK_XPATH_BOOLEAN(n, STR("c1/v > c2/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v >= c2/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v < c2/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v <= c2/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("c2/v[2] < c2/v[2]"), false);
CHECK_XPATH_BOOLEAN(n, STR("c2/v[2] > c2/v[2]"), false);
CHECK_XPATH_BOOLEAN(n, STR("c2/v[2] <= c2/v[2]"), false);
CHECK_XPATH_BOOLEAN(n, STR("c2/v[2] >= c2/v[2]"), false);
#endif
}
TEST_XML(xpath_operators_inequality_node_set_primitive, "<node><c1><v>1</v><v>-1</v><v>-100</v></c1><c2><v>1</v><v>nan</v></c2></node>")
{
xml_node c;
xml_node n = doc.child(STR("node"));
// node set vs number
CHECK_XPATH_BOOLEAN(c, STR("x < 0"), false);
CHECK_XPATH_BOOLEAN(c, STR("x > 0"), false);
CHECK_XPATH_BOOLEAN(c, STR("x <= 0"), false);
CHECK_XPATH_BOOLEAN(c, STR("x >= 0"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 < x"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 > x"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 <= x"), false);
CHECK_XPATH_BOOLEAN(c, STR("0 >= x"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v > 0"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v > 1"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v >= 0"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v < 0"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v <= 0"), true);
CHECK_XPATH_BOOLEAN(n, STR("0 < c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("1 < c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("0 <= c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("0 > c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("0 >= c1/v"), true);
// node set vs string
CHECK_XPATH_BOOLEAN(n, STR("c1/v > '0'"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v > '1'"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v >= '0'"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v < '0'"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v <= '0'"), true);
CHECK_XPATH_BOOLEAN(n, STR("'0' < c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("'1' < c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("'0' <= c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("'0' > c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("'0' >= c1/v"), true);
// node set vs boolean
CHECK_XPATH_BOOLEAN(n, STR("c1/v > false()"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v > true()"), false);
CHECK_XPATH_BOOLEAN(n, STR("c1/v >= false()"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v < false()"), true);
CHECK_XPATH_BOOLEAN(n, STR("c1/v <= false()"), true);
CHECK_XPATH_BOOLEAN(n, STR("false() < c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("true() < c1/v"), false);
CHECK_XPATH_BOOLEAN(n, STR("false() <= c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("false() > c1/v"), true);
CHECK_XPATH_BOOLEAN(n, STR("false() >= c1/v"), true);
}
TEST(xpath_operators_boolean_precedence)
{
xml_node c;
CHECK_XPATH_BOOLEAN(c, STR("1 = 0 or 2 = 2"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 = (0 or 2) = false()"), false);
CHECK_XPATH_BOOLEAN(c, STR("1 < 0 or 2 > 2"), false);
CHECK_XPATH_BOOLEAN(c, STR("2 < 1 = false()"), true);
CHECK_XPATH_BOOLEAN(c, STR("2 < (1 = false())"), false);
CHECK_XPATH_BOOLEAN(c, STR("3 > 2 > 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("(3 > 2) > 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("3 > (2 > 1)"), true);
}
TEST_XML(xpath_operators_union, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/><tail/></node>")
{
xml_node c;
xml_node n = doc.child(STR("node"));
CHECK_XPATH_NODESET(n, STR("employee | .")) % 2 % 3 % 4 % 6 % 8 % 11;
CHECK_XPATH_NODESET(n, STR("employee[@secretary] | employee[@assistant]")) % 4 % 6 % 8 % 11;
CHECK_XPATH_NODESET(n, STR("employee[@assistant] | employee[@secretary]")) % 4 % 6 % 8 % 11;
CHECK_XPATH_NODESET(n, STR("employee[@secretary] | employee[@nobody]")) % 4 % 8 % 11;
CHECK_XPATH_NODESET(n, STR("employee[@nobody] | employee[@secretary]")) % 4 % 8 % 11;
CHECK_XPATH_NODESET(n, STR("tail/preceding-sibling::employee | .")) % 2 % 3 % 4 % 6 % 8 % 11;
CHECK_XPATH_NODESET(n, STR(". | tail/preceding-sibling::employee | .")) % 2 % 3 % 4 % 6 % 8 % 11;
}
TEST_XML(xpath_operators_union_order, "<node />")
{
xml_node n = doc.child(STR("node"));
n.append_child(STR("c"));
n.prepend_child(STR("b"));
n.append_child(STR("d"));
n.prepend_child(STR("a"));
xpath_node_set ns = n.select_nodes(STR("d | d | b | c | b | a | c | d | b"));
CHECK(ns.size() == 4);
CHECK_STRING(ns[0].node().name(), STR("d"));
CHECK_STRING(ns[1].node().name(), STR("b"));
CHECK_STRING(ns[2].node().name(), STR("c"));
CHECK_STRING(ns[3].node().name(), STR("a"));
}
TEST(xpath_operators_union_error)
{
CHECK_XPATH_FAIL(STR(". | true()"));
CHECK_XPATH_FAIL(STR(". | 1"));
CHECK_XPATH_FAIL(STR(". | '1'"));
CHECK_XPATH_FAIL(STR(". | count(.)"));
CHECK_XPATH_FAIL(STR("true() | ."));
CHECK_XPATH_FAIL(STR("1 | ."));
CHECK_XPATH_FAIL(STR("'1' | ."));
CHECK_XPATH_FAIL(STR("count(.) | ."));
}
TEST_XML(xpath_operators_union_minus, "<node1>3</node1><node2>4</node2>")
{
CHECK_XPATH_FAIL(STR("(-node1) | node2"));
CHECK_XPATH_FAIL(STR("node1 | -node2"));
CHECK_XPATH_NUMBER(doc, STR("-(node1 | node2)"), -3);
CHECK_XPATH_NUMBER(doc, STR("-node1 | node2"), -3);
CHECK_XPATH_NUMBER(doc, STR("--node1 | node2"), 3);
CHECK_XPATH_NUMBER(doc, STR("-(-node1 | node2)"), 3);
CHECK_XPATH_NUMBER(doc, STR("--(-node1 | node2)"), -3);
}
TEST(xpath_operators_associativity_boolean)
{
xml_node c;
CHECK_XPATH_BOOLEAN(c, STR("false() or true() and true() and false()"), false);
CHECK_XPATH_BOOLEAN(c, STR("3 > 2 > 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("4 > 3 > 2 > 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("5 > 4 > 3 > 2 > 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("1 < 2 < 3 < 4 < 5"), true);
CHECK_XPATH_BOOLEAN(c, STR("1 <= 2 <= 3 <= 4 <= 5"), true);
CHECK_XPATH_BOOLEAN(c, STR("5 >= 4 >= 3 >= 2 >= 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("3 >= 2 >= 1"), true);
CHECK_XPATH_BOOLEAN(c, STR("2 >= 1"), true);
CHECK_XPATH_BOOLEAN(c, STR("4 >= 3 >= 2 >= 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("((((5 > 4) > 3) > 2) > 1)"), false);
CHECK_XPATH_BOOLEAN(c, STR("2 != 3 != 1 != 4 != 0"), true);
CHECK_XPATH_BOOLEAN(c, STR("(((2 != 3) != 1) != 4) != 0"), true);
CHECK_XPATH_BOOLEAN(c, STR("2 != 3 != 1 != 4 != 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("(((2 != 3) != 1) != 4) != 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("2 = 3 = 1 = 4 = 0"), true);
CHECK_XPATH_BOOLEAN(c, STR("(((2 = 3) = 1) = 4) = 0"), true);
CHECK_XPATH_BOOLEAN(c, STR("2 = 3 = 1 = 4 = 1"), false);
CHECK_XPATH_BOOLEAN(c, STR("(((2 = 3) = 1) = 4) = 1"), false);
}
TEST(xpath_operators_associativity_arithmetic)
{
xml_node c;
CHECK_XPATH_NUMBER(c, STR("2+1-1+1"), 3);
CHECK_XPATH_NUMBER(c, STR("1+2+1-1+1"), 4);
CHECK_XPATH_NUMBER(c, STR("1+1+2+1-1+1"), 5);
CHECK_XPATH_NUMBER(c, STR("1-1+1"), 1);
}
TEST(xpath_operators_mod)
{
// Check that mod operator conforms to Java spec (since this is the only concrete source of information about XPath mod)
xml_node c;
// Basic tests from spec
CHECK_XPATH_NUMBER(c, STR("5 mod 3"), 2);
CHECK_XPATH_NUMBER(c, STR("5 mod -3"), 2);
CHECK_XPATH_NUMBER(c, STR("-5 mod 3"), -2);
CHECK_XPATH_NUMBER(c, STR("-5 mod -3"), -2);
#if !defined(__BORLANDC__)
// If either operand is NaN, the result is NaN
CHECK_XPATH_NUMBER_NAN(c, STR("(0 div 0) mod 3"));
CHECK_XPATH_NUMBER_NAN(c, STR("3 mod (0 div 0)"));
CHECK_XPATH_NUMBER_NAN(c, STR("(0 div 0) mod (0 div 0)"));
// If the dividend is an infinity, or the divisor is a zero, or both, the result is NaN
CHECK_XPATH_NUMBER_NAN(c, STR("(1 div 0) mod 3"));
CHECK_XPATH_NUMBER_NAN(c, STR("(1 div 0) mod -3"));
CHECK_XPATH_NUMBER_NAN(c, STR("(-1 div 0) mod 3"));
CHECK_XPATH_NUMBER_NAN(c, STR("1 mod 0"));
CHECK_XPATH_NUMBER_NAN(c, STR("-1 mod 0"));
CHECK_XPATH_NUMBER_NAN(c, STR("(1 div 0) mod 0"));
CHECK_XPATH_NUMBER_NAN(c, STR("(-1 div 0) mod 0"));
#endif
// If the dividend is finite and the divisor is an infinity, the result equals the dividend
#if !defined(_MSC_VER) && !defined(__MINGW32__)
CHECK_XPATH_NUMBER(c, STR("1 mod (1 div 0)"), 1);
CHECK_XPATH_NUMBER(c, STR("1 mod (-1 div 0)"), 1);
CHECK_XPATH_NUMBER(c, STR("-1 mod (1 div 0)"), -1);
CHECK_XPATH_NUMBER(c, STR("0 mod (1 div 0)"), 0);
CHECK_XPATH_NUMBER(c, STR("0 mod (-1 div 0)"), 0);
CHECK_XPATH_NUMBER(c, STR("100000 mod (1 div 0)"), 100000);
#endif
// If the dividend is a zero and the divisor is finite, the result equals the dividend.
CHECK_XPATH_NUMBER(c, STR("0 mod 1000000"), 0);
CHECK_XPATH_NUMBER(c, STR("0 mod -1000000"), 0);
// In the remaining cases ... the floating-point remainder r from the division of a dividend n by a divisor d
// is defined by the mathematical relation r = n - (d * q) where q is an integer that is negative only if n/d is
// negative and positive only if n/d is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true
// mathematical quotient of n and d.
CHECK_XPATH_NUMBER(c, STR("9007199254740991 mod 2"), 1);
CHECK_XPATH_NUMBER(c, STR("9007199254740991 mod 3"), 1);
CHECK_XPATH_NUMBER(c, STR("18446744073709551615 mod 2"), 0);
CHECK_XPATH_NUMBER(c, STR("18446744073709551615 mod 3"), 1);
CHECK_XPATH_NUMBER(c, STR("115792089237316195423570985008687907853269984665640564039457584007913129639935 mod 2"), 0);
CHECK_XPATH_NUMBER(c, STR("115792089237316195423570985008687907853269984665640564039457584007913129639935 mod 3"), 1);
}
#endif