#ifndef PUGIXML_NO_XPATH #include "test.hpp" #include #include #include #include #include #include // std::random_shuffle is deprecated in c++14, is removed in c++17. #if defined(__cplusplus) && (__cplusplus >= 201402L) # include # define PUGIXML_SHUFFLE(rng) std::shuffle(rng.begin(), rng.end(), std::default_random_engine{std::random_device{}()}) #else # define PUGIXML_SHUFFLE(rng) std::random_shuffle(rng.begin(), rng.end()) #endif using namespace pugi; static void load_document_copy(xml_document& doc, const char_t* text) { xml_document source; CHECK(source.load_string(text)); doc.append_copy(source.first_child()); } TEST(xpath_allocator_many_pages) { std::basic_string query = STR("0"); for (int i = 0; i < 128; ++i) query += STR("+string-length('abcdefgh')"); CHECK_XPATH_NUMBER(xml_node(), query.c_str(), 1024); } TEST(xpath_allocator_large_page) { std::basic_string query; for (int i = 0; i < 1024; ++i) query += STR("abcdefgh"); CHECK_XPATH_NUMBER(xml_node(), (STR("string-length('") + query + STR("')")).c_str(), 8192); } TEST_XML(xpath_sort_complex, "test") { // just some random union order, it should not matter probably? xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child1 | child2 | child1/@* | . | child2/@* | child2/text()")); ns.sort(false); xpath_node_set sorted = ns; ns.sort(true); xpath_node_set reverse_sorted = ns; xpath_node_set_tester(sorted, "sorted order failed") % 2 % 3 % 4 % 5 % 6 % 7 % 8; xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 8 % 7 % 6 % 5 % 4 % 3 % 2; } TEST(xpath_sort_complex_copy) // copy the document so that document order optimization does not work { xml_document doc; load_document_copy(doc, STR("test")); // just some random union order, it should not matter probably? xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child1 | child2 | child1/@* | . | child2/@* | child2/text()")); ns.sort(false); xpath_node_set sorted = ns; ns.sort(true); xpath_node_set reverse_sorted = ns; xpath_node_set_tester(sorted, "sorted order failed") % 2 % 3 % 4 % 5 % 6 % 7 % 8; xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 8 % 7 % 6 % 5 % 4 % 3 % 2; } TEST_XML(xpath_sort_children, "") { xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child/subchild[@id=1] | child/subchild[@id=2]")); ns.sort(false); xpath_node_set sorted = ns; ns.sort(true); xpath_node_set reverse_sorted = ns; xpath_node_set_tester(sorted, "sorted order failed") % 4 % 7; xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 7 % 4; } TEST(xpath_sort_children_copy) // copy the document so that document order optimization does not work { xml_document doc; load_document_copy(doc, STR("")); xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child/subchild[@id=1] | child/subchild[@id=2]")); ns.sort(false); xpath_node_set sorted = ns; ns.sort(true); xpath_node_set reverse_sorted = ns; xpath_node_set_tester(sorted, "sorted order failed") % 4 % 7; xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 7 % 4; } TEST_XML(xpath_sort_attributes, "") { xml_node n = doc.child(STR("node")); // we need to insert attributes manually since unsorted node sets are (always?) sorted via pointers because of remove_duplicates, // so we need to have different document and pointer order to cover all comparator cases n.append_attribute(STR("attr2")); n.append_attribute(STR("attr3")); n.insert_attribute_before(STR("attr1"), n.attribute(STR("attr2"))); xpath_node_set ns = n.select_nodes(STR("@* | @*")); ns.sort(true); xpath_node_set reverse_sorted = ns; ns.sort(false); xpath_node_set sorted = ns; xpath_node_set_tester(sorted, "sorted order failed") % 3 % 4 % 5; xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 5 % 4 % 3; } TEST_XML(xpath_sort_attributes_docorder, "") { xml_node n = doc.child(STR("node")); n.first_attribute().set_name(STR("attribute1")); n.insert_attribute_after(STR("attr3"), n.attribute(STR("attr2"))); xpath_node_set ns = n.select_nodes(STR("@* | @*")); ns.sort(true); xpath_node_set reverse_sorted = ns; ns.sort(false); xpath_node_set sorted = ns; xpath_node_set_tester(sorted, "sorted order failed") % 3 % 4 % 5 % 6; xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 6 % 5 % 4 % 3; } TEST(xpath_sort_random_medium) { xml_document doc; load_document_copy(doc, STR("") STR("testtest") STR("testtest") STR("testtest") STR("")); xpath_node_set ns = doc.select_nodes(STR("//node() | //@*")); std::vector nsv(ns.begin(), ns.end()); PUGIXML_SHUFFLE(nsv); xpath_node_set copy(&nsv[0], &nsv[0] + nsv.size()); copy.sort(); xpath_node_set_tester tester(copy, "sorted order failed"); for (unsigned int i = 2; i < 39; ++i) tester % i; } TEST(xpath_sort_random_large) { xml_document doc; load_document_copy(doc, STR("") STR("testtest") STR("testtest") STR("testtest") STR("testtest") STR("testtest") STR("testtest") STR("testtest") STR("testtest") STR("testtest") STR("testtest") STR("test")); xpath_node_set ns = doc.select_nodes(STR("//node() | //@*")); std::vector nsv(ns.begin(), ns.end()); PUGIXML_SHUFFLE(nsv); xpath_node_set copy(&nsv[0], &nsv[0] + nsv.size()); copy.sort(); xpath_node_set_tester tester(copy, "sorted order failed"); for (unsigned int i = 2; i < 129; ++i) tester % i; } TEST(xpath_long_numbers_parse) { const char_t* str_flt_max = STR("340282346638528860000000000000000000000"); const char_t* str_flt_max_dec = STR("340282346638528860000000000000000000000.000000"); const char_t* str_dbl_max = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); const char_t* str_dbl_max_dec = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000"); xml_node c; // check parsing CHECK_XPATH_NUMBER(c, str_flt_max, double(std::numeric_limits::max())); CHECK_XPATH_NUMBER(c, str_flt_max_dec, double(std::numeric_limits::max())); CHECK_XPATH_NUMBER(c, str_dbl_max, std::numeric_limits::max()); CHECK_XPATH_NUMBER(c, str_dbl_max_dec, std::numeric_limits::max()); } static bool test_xpath_string_prefix(const xml_node& node, const char_t* query, const char_t* expected, size_t match_length) { xpath_query q(query); char_t result[32]; size_t size = q.evaluate_string(result, sizeof(result) / sizeof(result[0]), node); size_t expected_length = std::char_traits::length(expected); return size == expected_length + 1 && std::char_traits::compare(result, expected, match_length) == 0; } TEST(xpath_long_numbers_stringize) { const char_t* str_flt_max = STR("340282346638528860000000000000000000000"); const char_t* str_flt_max_dec = STR("340282346638528860000000000000000000000.000000"); const char_t* str_dbl_max = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); const char_t* str_dbl_max_dec = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000"); xml_node c; CHECK(test_xpath_string_prefix(c, str_flt_max, str_flt_max, 15)); CHECK(test_xpath_string_prefix(c, str_flt_max_dec, str_flt_max, 15)); CHECK(test_xpath_string_prefix(c, str_dbl_max, str_dbl_max, 15)); CHECK(test_xpath_string_prefix(c, str_dbl_max_dec, str_dbl_max, 15)); } TEST(xpath_denorm_numbers) { std::basic_string query; // 10^-318 - double denormal for (int i = 0; i < 106; ++i) { if (i != 0) query += STR(" * "); query += STR("0.001"); } // check if current fpu setup supports denormals double denorm = xpath_query(query.c_str()).evaluate_number(xml_node()); if (denorm != 0.0) { CHECK_XPATH_STRING(xml_node(), query.c_str(), STR("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009999987484955998")); } } TEST_XML(xpath_rexml_1, "") { CHECK_XPATH_NODESET(doc, STR("//*[local-name()='c' and @id='b']")) % 6; CHECK_XPATH_NODESET(doc, STR("//*[ local-name()='c' and @id='b' ]")) % 6; CHECK_XPATH_NODESET(doc, STR("/a/c[@id]")) % 6; CHECK_XPATH_NODESET(doc, STR("/a/c[(@id)]")) % 6; CHECK_XPATH_NODESET(doc, STR("/a/c[ @id ]")) % 6; CHECK_XPATH_NODESET(doc, STR("/a/c[ (@id) ]")) % 6; CHECK_XPATH_NODESET(doc, STR("/a/c[( @id )]")) % 6; CHECK_XPATH_NODESET(doc, STR("/a/c[ ( @id ) ]")) % 6; CHECK_XPATH_NODESET(doc, STR("/a/c [ ( @id ) ] ")) % 6; CHECK_XPATH_NODESET(doc, STR(" / a / c [ ( @id ) ] ")) % 6; } TEST_XML(xpath_rexml_2, "zzz") { CHECK_XPATH_NODESET(doc, STR("a:x/a:y[@p='p' and @q='q']/a:z/text()")) % 8; } TEST_XML(xpath_rexml_3, "
free flowing text.
free flowing text.
free flowing text.
") { CHECK_XPATH_NODESET(doc, STR("//section[../self::section[@role=\"division\"]]")) % 10 % 15; CHECK_XPATH_NODESET(doc, STR("//section[@role=\"subdivision\" and not(../self::section[@role=\"division\"])]")) % 3; CHECK_XPATH_NODESET(doc, STR("//section[@role=\"subdivision\"][not(../self::section[@role=\"division\"])]")) % 3; } TEST_XML_FLAGS(xpath_rexml_4, "TEXT1TEXT2", parse_default | parse_comments) { CHECK_XPATH_NODESET(doc, STR("/descendant-or-self::node()[count(child::node()|following-sibling::node()|preceding-sibling::node())=0]")) % 6 % 17 % 20; } TEST_XML(xpath_rexml_5, "") { CHECK_XPATH_FAIL(STR(".//[@id]")); CHECK_XPATH_NODESET(doc, STR(".//self::*[@id]")) % 4 % 6; CHECK_XPATH_NODESET(doc, STR(".//node()[@id]")) % 4 % 6; } TEST_XML(xpath_rexml_6, "
ab
") { CHECK_XPATH_NODESET(doc, STR("//em|//strong")) % 4 % 6; CHECK_XPATH_NODESET(doc, STR("//*[self::em | self::strong]")) % 4 % 6; CHECK_XPATH_NODESET(doc, STR("//*[name()=\"em\" or name()=\"strong\"]")) % 4 % 6; CHECK_XPATH_NODESET(doc, STR("//*[self::em or self::strong]")) % 4 % 6; } TEST_XML(xpath_xsl_list_1, "whatevertextselectsomething") { // if I'm not last, and the next input/type isn't select CHECK_XPATH_NODESET(doc, STR("input[type[parent::input/following-sibling::input[1]/type != 'select']]")) % 2 % 8; CHECK_XPATH_NODESET(doc, STR("input[type[../following-sibling::input[1]/type != 'select']]")) % 2 % 8; CHECK_XPATH_NODESET(doc, STR("input[position()+1]")); } TEST_XML(xpath_xsl_list_2, "text1text2text3text4") { CHECK_XPATH_FAIL(STR(".[not(.=ancestor::TR/TD[15]/node())]")); CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("1")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")) % 5; CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("2")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")) % 8; CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("3")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")); CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("4")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")) % 14; CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("1")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")) % 5; CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("2")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")) % 8; CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("3")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")); CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("4")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")) % 14; } TEST_XML(xpath_star_token, "0.5
") { CHECK_XPATH_NODESET(doc, STR("//*[/* * 4]")) % 6 % 9; CHECK_XPATH_NODESET(doc, STR("//*[/**4]")) % 6 % 9; CHECK_XPATH_FAIL(STR("//*[/***4]")); } TEST(xpath_miscellaneous) { CHECK_XPATH_FAIL(STR("/root/child[a=3]/substring(child::text())")); CHECK_XPATH_NODESET(xml_node(), STR("foo/@FOO/@bar")); } TEST_XML(xpath_context_node, "5") { CHECK_XPATH_NODESET(doc, STR("node")) % 2; CHECK_XPATH_BOOLEAN(doc, STR("node"), true); CHECK_XPATH_NUMBER(doc, STR("node"), 5); CHECK_XPATH_STRING(doc, STR("node"), STR("5")); } TEST_XML(xpath_context_position, "5") { CHECK_XPATH_NODESET(doc, STR("id(position() + last())")); CHECK_XPATH_BOOLEAN(doc, STR("position() + last() = 2"), true); CHECK_XPATH_NUMBER(doc, STR("position() + last()"), 2); CHECK_XPATH_STRING(doc, STR("position() + last()"), STR("2")); } TEST(xpath_lexer_unknown_lexeme) { CHECK_XPATH_FAIL(STR("(^3))")); CHECK_XPATH_FAIL(STR("(!3))")); } TEST(xpath_large_node_set) { xml_document doc; CHECK(doc.load_file("tests/data/large.xml")); xpath_node_set ns = doc.select_nodes(STR("//*")); CHECK(ns.size() == 10001); } TEST(xpath_out_of_memory_query) { test_runner::_memory_fail_threshold = 1; CHECK_ALLOC_FAIL(xpath_query q(STR("node"))); } TEST_XML(xpath_out_of_memory_evaluate, "") { test_runner::_memory_fail_threshold = 4196 * sizeof(char_t) + 4096 * 2 + 32768; std::basic_string query = STR("*[concat(\"a\", \""); query.resize(4196, 'a'); query += STR("\")]"); xpath_query q(query.c_str()); CHECK_ALLOC_FAIL(CHECK(q.evaluate_boolean(doc) == false)); CHECK_ALLOC_FAIL(CHECK_DOUBLE_NAN(q.evaluate_number(doc))); #ifndef PUGIXML_NO_STL CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(doc).empty())); #endif CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, doc) == 1)); CHECK_ALLOC_FAIL(CHECK(q.evaluate_node(doc) == xpath_node())); CHECK_ALLOC_FAIL(CHECK(q.evaluate_node_set(doc).empty())); } TEST(xpath_out_of_memory_evaluate_concat) { test_runner::_memory_fail_threshold = 4196 * sizeof(char_t) + 4096 * 2; std::basic_string query = STR("concat(\"a\", \""); query.resize(4196, 'a'); query += STR("\")"); xpath_query q(query.c_str()); CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, xml_node()) == 1)); } TEST(xpath_out_of_memory_evaluate_concat_list) { std::basic_string query = STR("concat("); for (size_t i = 0; i < 500; ++i) query += STR("\"\","); query += STR("\"\")"); xpath_query q(query.c_str()); test_runner::_memory_fail_threshold = 1; CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, xml_node()) == 1)); } TEST(xpath_out_of_memory_evaluate_substring) { test_runner::_memory_fail_threshold = 4196 * sizeof(char_t) + 4096 * 2; std::basic_string query = STR("substring(\""); query.resize(4196, 'a'); query += STR("\", 1, 4097)"); xpath_query q(query.c_str()); CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, xml_node()) == 1)); } TEST_XML(xpath_out_of_memory_evaluate_union, "") { // left hand side: size * sizeof(xpath_node) (8 on 32-bit, 16 on 64-bit) // right hand side: same // to make sure that when we append right hand side to left hand side, we run out of an XPath stack page (4K), we need slightly more than 2K/8 = 256 nodes on 32-bit, 128 nodes on 64-bit size_t count = sizeof(void*) == 4 ? 300 : 150; for (size_t i = 0; i < count; ++i) doc.first_child().append_child(STR("a")); xpath_query q(STR("a|a")); test_runner::_memory_fail_threshold = 1; CHECK_ALLOC_FAIL(CHECK(q.evaluate_node_set(doc.child(STR("node"))).empty())); } TEST_XML(xpath_out_of_memory_evaluate_union_hash, "") { // left hand side: size * sizeof(xpath_node) (8 on 32-bit, 16 on 64-bit) // right hand side: same // hash table: size * 1.5 * sizeof(void*) // to make sure that when we append right hand side to left hand side, we do *not* run out of an XPath stack page (4K), we need slightly less than 2K/8 = 256 nodes on 32-bit, 128 nodes on 64-bit size_t count = sizeof(void*) == 4 ? 200 : 100; for (size_t i = 0; i < count; ++i) doc.first_child().append_child(STR("a")); xpath_query q(STR("a|a")); test_runner::_memory_fail_threshold = 1; CHECK_ALLOC_FAIL(CHECK(q.evaluate_node_set(doc.child(STR("node"))).empty())); } TEST_XML(xpath_out_of_memory_evaluate_predicate, "") { test_runner::_memory_fail_threshold = 32768 + 4096 * 2; xpath_query q(STR("//a[//a[//a[//a[true()]]]]")); CHECK_ALLOC_FAIL(CHECK(q.evaluate_node_set(doc).empty())); } TEST_XML(xpath_out_of_memory_evaluate_normalize_space_0, " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z ") { test_runner::_memory_fail_threshold = 32768 + 4096 * 2; xpath_query q(STR("concat(normalize-space(), normalize-space(), normalize-space(), normalize-space(), normalize-space(), normalize-space(), normalize-space(), normalize-space())")); CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, doc.first_child()) == 1)); } TEST_XML(xpath_out_of_memory_evaluate_normalize_space_1, " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z ") { test_runner::_memory_fail_threshold = 32768 + 4096 * 2; xpath_query q(STR("concat(normalize-space(node), normalize-space(node), normalize-space(node), normalize-space(node), normalize-space(node), normalize-space(node), normalize-space(node), normalize-space(node))")); CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, doc) == 1)); } TEST_XML(xpath_out_of_memory_evaluate_translate, " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z ") { test_runner::_memory_fail_threshold = 32768 + 4096 * 2; xpath_query q(STR("concat(translate(node, 'a', '\xe9'), translate(node, 'a', '\xe9'), translate(node, 'a', '\xe9'), translate(node, 'a', '\xe9'), translate(node, 'a', '\xe9'), translate(node, 'a', '\xe9'), translate(node, 'a', '\xe9'), translate(node, 'a', '\xe9'))")); CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, doc) == 1)); } TEST_XML(xpath_out_of_memory_evaluate_translate_table, " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z ") { test_runner::_memory_fail_threshold = 32768 + 4096 * 2; xpath_query q(STR("concat(translate(node, 'a', 'A'), translate(node, 'a', 'A'), translate(node, 'a', 'A'), translate(node, 'a', 'A'), translate(node, 'a', 'A'), translate(node, 'a', 'A'), translate(node, 'a', 'A'), translate(node, 'a', 'A'))")); CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, doc) == 1)); } TEST(xpath_out_of_memory_evaluate_string_append) { test_runner::_memory_fail_threshold = 32768 + 4096 * 2; std::basic_string literal(5000, 'a'); std::basic_string buf; buf += STR("text"); buf += literal; buf += STR(""); xml_document doc; CHECK(doc.load_buffer_inplace(&buf[0], buf.size() * sizeof(char_t))); xpath_query q(STR("string(n)")); CHECK(q); CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, doc) == 1)); } TEST(xpath_out_of_memory_evaluate_number_to_string) { test_runner::_memory_fail_threshold = 4096 + 128; xpath_variable_set vars; vars.set(STR("x"), 1e+308); xpath_query q(STR("concat($x, $x, $x, $x, $x, $x, $x, $x, $x, $x, $x, $x, $x, $x, $x, $x, $x)"), &vars); CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, xml_node()) == 1)); } TEST(xpath_memory_concat_massive) { xml_document doc; xml_node node = doc.append_child(STR("node")); for (int i = 0; i < 5000; ++i) node.append_child(STR("c")).text().set(i % 10); xpath_query q(STR("/")); size_t size = q.evaluate_string(0, 0, node); CHECK(size == 5001); } TEST_XML(xpath_memory_translate_table, "a") { test_runner::_memory_fail_threshold = 32768 + 4096 + 128; // 128b per table => we need 32+ translate calls to exhaust a page std::basic_string query = STR("concat("); for (int i = 0; i < 64; ++i) query += STR("translate(.,'a','A'),"); query += STR("'')"); CHECK_ALLOC_FAIL(CHECK(!xpath_query(query.c_str()))); } TEST_XML(xpath_sort_copy_share, "test") { // copy sharing shares the name/value data for nodes that can potentially make document order optimization invalid (silently) xml_node node = doc.child(STR("node")); xml_node child1 = node.child(STR("child1")); xml_node child2 = node.child(STR("child2")); // swap child1 & child2 node.prepend_copy(child2); node.append_copy(child1); node.remove_child(child1); node.remove_child(child2); // just some random union order, it should not matter probably? xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child1 | child2 | child1/@* | . | child2/@* | child2/text()")); ns.sort(false); xpath_node_set sorted = ns; ns.sort(true); xpath_node_set reverse_sorted = ns; xpath_node_set_tester(sorted, "sorted order failed") % 2 % 3 % 4 % 5 % 6 % 7 % 8; xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 8 % 7 % 6 % 5 % 4 % 3 % 2; } TEST_XML(xpath_sort_move_share, "test") { // moving changes the relation between name/value data and document order, this can potentially make document order optimization invalid (silently) xml_node node = doc.child(STR("node")); xml_node child1 = node.child(STR("child1")); xml_node child2 = node.child(STR("child2")); // swap child1 & child2 node.prepend_move(child2); node.append_move(child1); // just some random union order, it should not matter probably? xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child1 | child2 | child1/@* | . | child2/@* | child2/text()")); ns.sort(false); xpath_node_set sorted = ns; ns.sort(true); xpath_node_set reverse_sorted = ns; xpath_node_set_tester(sorted, "sorted order failed") % 2 % 3 % 4 % 5 % 6 % 7 % 8; xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 8 % 7 % 6 % 5 % 4 % 3 % 2; } TEST_XML(xpath_sort_append_buffer, "") { // append_buffer changes the relation between name/value data and document order, this can potentially make document order optimization invalid (silently) const char* child1 = ""; const char* child2 = "test "; doc.last_child().append_buffer(child2, strlen(child2)); doc.first_child().append_buffer(child1, strlen(child1)); // just some random union order, it should not matter probably? xpath_node_set ns = doc.select_nodes(STR("node/child1 | node/child2 | node/child1/@* | node/. | node/child2/@* | node/child2/text()")); ns.sort(false); xpath_node_set sorted = ns; ns.sort(true); xpath_node_set reverse_sorted = ns; xpath_node_set_tester(sorted, "sorted order failed") % 2 % 3 % 4 % 5 % 6 % 7 % 8 % 9; xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 9 % 8 % 7 % 6 % 5 % 4 % 3 % 2; } TEST(xpath_sort_crossdoc) { xml_document doc1; CHECK(doc1.load_string(STR(""))); xml_document doc2; CHECK(doc2.load_string(STR(""))); xpath_node_set ns1 = doc1.select_nodes(STR("*")); CHECK(ns1.size() == 1); xpath_node_set ns2 = doc2.select_nodes(STR("*")); CHECK(ns2.size() == 1); xpath_variable_set set; set.set(STR("ns1"), ns1); set.set(STR("ns2"), ns2); xpath_node_set ns = xpath_query(STR("$ns1 | $ns2"), &set).evaluate_node_set(xpath_node()); ns.sort(); CHECK(ns.size() == 2); CHECK((ns[0] == ns1[0] && ns[1] == ns2[0]) || (ns[0] == ns2[0] && ns[1] == ns1[0])); } TEST(xpath_sort_crossdoc_dynamic) { xml_document doc1; doc1.append_child(STR("node")); xml_document doc2; doc2.append_child(STR("node")); xpath_node_set ns1 = doc1.select_nodes(STR("*")); CHECK(ns1.size() == 1); xpath_node_set ns2 = doc2.select_nodes(STR("*")); CHECK(ns2.size() == 1); xpath_variable_set set; set.set(STR("ns1"), ns1); set.set(STR("ns2"), ns2); xpath_node_set ns = xpath_query(STR("$ns1 | $ns2"), &set).evaluate_node_set(xpath_node()); ns.sort(); CHECK(ns.size() == 2); CHECK((ns[0] == ns1[0] && ns[1] == ns2[0]) || (ns[0] == ns2[0] && ns[1] == ns1[0])); } TEST(xpath_sort_crossdoc_different_depth) { xml_document doc1; doc1.append_child(STR("node")); xml_document doc2; doc2.append_child(STR("node")).append_child(STR("node")); xpath_node_set ns1 = doc1.select_nodes(STR("*")); CHECK(ns1.size() == 1); xpath_node_set ns2 = doc2.select_nodes(STR("*/*")); CHECK(ns2.size() == 1); xpath_variable_set set; set.set(STR("ns1"), ns1); set.set(STR("ns2"), ns2); xpath_node_set ns = xpath_query(STR("$ns1 | $ns2"), &set).evaluate_node_set(xpath_node()); ns.sort(); CHECK(ns.size() == 2); CHECK((ns[0] == ns1[0] && ns[1] == ns2[0]) || (ns[0] == ns2[0] && ns[1] == ns1[0])); } TEST_XML(xpath_sort_empty_node, "") { xml_node n = doc.child(STR("node")); xpath_node nodes[] = { n.child(STR("child2")), xml_node(), n.child(STR("child1")), xml_node() }; xpath_node_set ns(nodes, nodes + sizeof(nodes) / sizeof(nodes[0])); ns.sort(); CHECK(!ns[0] && !ns[1] && ns[2] == nodes[2] && ns[3] == nodes[0]); } TEST(xpath_allocate_string_out_of_memory) { std::basic_string query; for (int i = 0; i < 1024; ++i) query += STR("abcdefgh"); test_runner::_memory_fail_threshold = 8*1024; #ifndef __DMC__ // DigitalMars exception handling crashes instead of catching the exception... CHECK_ALLOC_FAIL(CHECK(!xpath_query(query.c_str()))); #endif } TEST(xpath_remove_duplicates) { xml_document doc; for (int i = 0; i < 20; ++i) { doc.append_child(STR("node2")); doc.prepend_child(STR("node1")); } xpath_node_set ns = doc.select_nodes(STR("/node2/preceding::* | //node1 | /node() | /* | /node1/following-sibling::*")); ns.sort(); { xpath_node_set_tester tester(ns, "sorted order failed"); for (int i = 0; i < 40; ++i) tester % (2 + i); } } TEST(xpath_anonymous_nodes) { xml_document doc; doc.append_child(node_element); doc.append_child(node_pi); CHECK_XPATH_NODESET(doc, STR("/name")); CHECK_XPATH_NODESET(doc, STR("/processing-instruction('a')")); CHECK_XPATH_NODESET(doc, STR("/ns:*")); } #endif