From bbd75fda4618dcbef272c8e5432153992c392588 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 23 Oct 2014 05:46:44 +0000 Subject: tests: Improve test coverage git-svn-id: https://pugixml.googlecode.com/svn/trunk@1074 99668b35-9821-0410-8761-19e4c4f06640 --- tests/test_document.cpp | 34 ++++++++++++++++++++++++++++++++++ tests/test_dom_modify.cpp | 35 +++++++++++++++++++++++++++++++++++ tests/test_parse.cpp | 31 ++++++++++++++++++++++++++++++- tests/test_xpath_paths.cpp | 3 +++ 4 files changed, 102 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test_document.cpp b/tests/test_document.cpp index c75ed8f..c76f671 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -1235,3 +1235,37 @@ TEST(document_alignment) doc->~xml_document(); } } + +TEST(document_convert_out_of_memory) +{ + file_data_t files[] = + { + {"tests/data/utftest_utf16_be_clean.xml", encoding_utf16_be, 0, 0}, + {"tests/data/utftest_utf16_le_clean.xml", encoding_utf16_le, 0, 0}, + {"tests/data/utftest_utf32_be_clean.xml", encoding_utf32_be, 0, 0}, + {"tests/data/utftest_utf32_le_clean.xml", encoding_utf32_le, 0, 0}, + {"tests/data/utftest_utf8_clean.xml", encoding_utf8, 0, 0}, + {"tests/data/latintest_latin1.xml", encoding_latin1, 0, 0} + }; + + // load files in memory + for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i) + { + CHECK(load_file_in_memory(files[i].path, files[i].data, files[i].size)); + } + + // disallow allocations + test_runner::_memory_fail_threshold = 1; + + for (unsigned int src = 0; src < sizeof(files) / sizeof(files[0]); ++src) + { + xml_document doc; + CHECK(doc.load_buffer(files[src].data, files[src].size, parse_default, files[src].encoding).status == status_out_of_memory); + } + + // cleanup + for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j) + { + delete[] files[j].data; + } +} diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index 612b017..5f4e26c 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -1354,3 +1354,38 @@ TEST_XML(dom_node_copy_out_of_memory, "text") +{ + xml_node node = doc.child(STR("node")); + + xml_attribute attr = node.attribute(STR("attr")); + attr.set_name(STR("longattr")); + attr.set_value(STR("longvalue")); + + node.set_name(STR("longnode")); + node.text().set(STR("longtext")); + + node.remove_attribute(attr); + doc.remove_child(node); + + CHECK_NODE(doc, STR("")); +} + +TEST_XML(dom_node_set_deallocate, "text") +{ + xml_node node = doc.child(STR("node")); + + xml_attribute attr = node.attribute(STR("attr")); + + attr.set_name(STR("longattr")); + attr.set_value(STR("longvalue")); + node.set_name(STR("longnode")); + + attr.set_name(STR("")); + attr.set_value(STR("")); + node.set_name(STR("")); + node.text().set(STR("")); + + CHECK_NODE(doc, STR("<:anonymous :anonymous=\"\">")); +} diff --git a/tests/test_parse.cpp b/tests/test_parse.cpp index 2094ef9..444a0fb 100644 --- a/tests/test_parse.cpp +++ b/tests/test_parse.cpp @@ -876,7 +876,7 @@ TEST(parse_out_of_memory) CHECK(!doc.first_child()); } -TEST(parse_out_of_memory_halfway) +TEST(parse_out_of_memory_halfway_node) { const unsigned int count = 10000; static char_t text[count * 4]; @@ -896,6 +896,35 @@ TEST(parse_out_of_memory_halfway) CHECK_NODE(doc.first_child(), STR("")); } +TEST(parse_out_of_memory_halfway_attr) +{ + const unsigned int count = 10000; + static char_t text[count * 5 + 4]; + + text[0] = '<'; + text[1] = 'n'; + + for (unsigned int i = 0; i < count; ++i) + { + text[5*i + 2] = ' '; + text[5*i + 3] = 'a'; + text[5*i + 4] = '='; + text[5*i + 5] = '"'; + text[5*i + 6] = '"'; + } + + text[5 * count + 2] = '/'; + text[5 * count + 3] = '>'; + + test_runner::_memory_fail_threshold = 65536; + + xml_document doc; + CHECK(doc.load_buffer_inplace(text, count * 5 + 4).status == status_out_of_memory); + CHECK_STRING(doc.first_child().name(), STR("n")); + CHECK_STRING(doc.first_child().first_attribute().name(), STR("a")); + CHECK_STRING(doc.first_child().last_attribute().name(), STR("a")); +} + static bool test_offset(const char_t* contents, unsigned int options, pugi::xml_parse_status status, ptrdiff_t offset) { xml_document doc; diff --git a/tests/test_xpath_paths.cpp b/tests/test_xpath_paths.cpp index da8811d..ee2401a 100644 --- a/tests/test_xpath_paths.cpp +++ b/tests/test_xpath_paths.cpp @@ -615,6 +615,9 @@ TEST_XML(xpath_paths_optimize_step_once, "

") -- cgit v1.2.3 From 903db8682a5f14b52adec996584c70ea072619ea Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 23 Oct 2014 07:41:07 +0000 Subject: tests: Add more tests for better coverage More tests for out-of-memory and other edge conditions git-svn-id: https://pugixml.googlecode.com/svn/trunk@1075 99668b35-9821-0410-8761-19e4c4f06640 --- tests/test_document.cpp | 39 +++++++++++++++++++++++++++++++++++++++ tests/test_dom_modify.cpp | 17 ++++++++++++++++- tests/test_parse.cpp | 22 ++++++++++++++++++++++ tests/test_parse_doctype.cpp | 9 +++++++++ tests/test_xpath.cpp | 23 +++++++++++++++++++++++ tests/test_xpath_api.cpp | 5 +++++ tests/test_xpath_functions.cpp | 15 ++++++++++++++- tests/test_xpath_variables.cpp | 3 +++ 8 files changed, 131 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/test_document.cpp b/tests/test_document.cpp index c76f671..2cc39a6 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -227,6 +227,34 @@ TEST(document_load_stream_nonseekable_large) CHECK(doc.load(in)); CHECK_NODE(doc, str.c_str()); } + +TEST(document_load_stream_nonseekable_out_of_memory) +{ + char contents[] = ""; + char_array_buffer buffer(contents, contents + sizeof(contents) / sizeof(contents[0])); + std::istream in(&buffer); + + test_runner::_memory_fail_threshold = 1; + + pugi::xml_document doc; + CHECK(doc.load(in).status == status_out_of_memory); +} + +TEST(document_load_stream_nonseekable_out_of_memory_large) +{ + std::basic_string str; + str += STR(""); + for (int i = 0; i < 10000; ++i) str += STR(""); + str += STR(""); + + char_array_buffer buffer(&str[0], &str[0] + str.length()); + std::basic_istream in(&buffer); + + test_runner::_memory_fail_threshold = 10000 * 8 * 3 / 2; + + pugi::xml_document doc; + CHECK(doc.load(in).status == status_out_of_memory); +} #endif TEST(document_load_string) @@ -295,6 +323,17 @@ TEST(document_load_file_wide_ascii) CHECK_NODE(doc, STR("")); } +TEST(document_load_file_wide_out_of_memory) +{ + test_runner::_memory_fail_threshold = 1; + + pugi::xml_document doc; + + pugi::xml_parse_result result = doc.load_file(L"tests/data/small.xml"); + + CHECK(result.status == status_out_of_memory || result.status == status_file_not_found); +} + TEST_XML(document_save, "") { xml_writer_string writer; diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index 5f4e26c..01b562a 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -27,6 +27,16 @@ TEST_XML(dom_attr_assign, "") CHECK_NODE(node, STR("")); } +TEST_XML(dom_attr_set_name, "") +{ + xml_attribute attr = doc.child(STR("node")).attribute(STR("attr")); + + CHECK(attr.set_name(STR("n"))); + CHECK(!xml_attribute().set_name(STR("n"))); + + CHECK_NODE(doc, STR("")); +} + TEST_XML(dom_attr_set_value, "") { xml_node node = doc.child(STR("node")); @@ -758,8 +768,9 @@ TEST(dom_node_declaration_name) doc.insert_child_after(node_declaration, doc.first_child()); doc.insert_child_before(node_declaration, doc.first_child()); + doc.prepend_child(node_declaration); - CHECK_NODE(doc, STR("")); + CHECK_NODE(doc, STR("")); } TEST(dom_node_declaration_attributes) @@ -867,17 +878,21 @@ TEST(dom_node_out_of_memory) // verify all node modification operations CHECK(!n.append_child()); + CHECK(!n.prepend_child()); CHECK(!n.insert_child_after(node_element, n.first_child())); CHECK(!n.insert_child_before(node_element, n.first_child())); CHECK(!n.append_attribute(STR(""))); + CHECK(!n.prepend_attribute(STR(""))); CHECK(!n.insert_attribute_after(STR(""), a)); CHECK(!n.insert_attribute_before(STR(""), a)); // verify node copy operations CHECK(!n.append_copy(n.first_child())); + CHECK(!n.prepend_copy(n.first_child())); CHECK(!n.insert_copy_after(n.first_child(), n.first_child())); CHECK(!n.insert_copy_before(n.first_child(), n.first_child())); CHECK(!n.append_copy(a)); + CHECK(!n.prepend_copy(a)); CHECK(!n.insert_copy_after(a, a)); CHECK(!n.insert_copy_before(a, a)); } diff --git a/tests/test_parse.cpp b/tests/test_parse.cpp index 444a0fb..c45b783 100644 --- a/tests/test_parse.cpp +++ b/tests/test_parse.cpp @@ -1068,3 +1068,25 @@ TEST(parse_pcdata_gap_fragment) CHECK(doc.load(STR("a&b"), parse_fragment | parse_escapes)); CHECK_STRING(doc.text().get(), STR("a&b")); } + +TEST(parse_name_end_eof) +{ + char_t test[] = STR(""); + + xml_document doc; + CHECK(doc.load_buffer_inplace(test, 6 * sizeof(char_t)).status == status_end_element_mismatch); + CHECK_STRING(doc.first_child().name(), STR("node")); +} + +TEST(parse_close_tag_eof) +{ + char_t test1[] = STR("")).status == status_bad_doctype); CHECK(doc.load(STR(""), parse_doctype).status == status_bad_doctype); } + +TEST(parse_doctype_error_ignore) +{ + xml_document doc; + CHECK(doc.load(STR(" query; + + for (int i = 0; i < 1024; ++i) query += STR("abcdefgh"); + + test_runner::_memory_fail_threshold = 8*1024; + +#ifdef PUGIXML_NO_EXCEPTIONS + CHECK(!xpath_query(query.c_str())); +#else + try + { + xpath_query q(query.c_str()); + + CHECK_FORCE_FAIL("Expected out of memory exception"); + } + catch (const std::bad_alloc&) + { + } +#endif +} #endif diff --git a/tests/test_xpath_api.cpp b/tests/test_xpath_api.cpp index 270f6aa..deb3beb 100644 --- a/tests/test_xpath_api.cpp +++ b/tests/test_xpath_api.cpp @@ -128,6 +128,11 @@ TEST_XML(xpath_api_nodeset_copy, "") copy4 = copy1; CHECK(copy4.size() == 2); CHECK_STRING(copy4[0].node().name(), STR("foo")); + + xpath_node_set copy5; + copy5 = set; + copy5 = xpath_node_set(); + CHECK(copy5.size() == 0); } TEST(xpath_api_nodeset_copy_empty) diff --git a/tests/test_xpath_functions.cpp b/tests/test_xpath_functions.cpp index da820ef..678bc2e 100644 --- a/tests/test_xpath_functions.cpp +++ b/tests/test_xpath_functions.cpp @@ -216,7 +216,7 @@ TEST(xpath_boolean_false) CHECK_XPATH_FAIL(STR("false(1)")); } -TEST_XML(xpath_boolean_lang, "") +TEST_XML(xpath_boolean_lang, "") { xml_node c; @@ -244,6 +244,9 @@ TEST_XML(xpath_boolean_lang, "pcdatafoobar") { CHECK_XPATH_STRING(doc, STR("concat('a', 'b', 'c', translate(node, 'o', 'a'), 'd')"), STR("abcfaabard")); diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp index 70bb4ea..39a4f05 100644 --- a/tests/test_xpath_variables.cpp +++ b/tests/test_xpath_variables.cpp @@ -88,6 +88,9 @@ TEST(xpath_variables_type_string) CHECK_DOUBLE_NAN(var->get_number()); CHECK_STRING(var->get_string(), STR("abc")); CHECK(var->get_node_set().empty()); + + CHECK(var->set(STR("abcdef"))); + CHECK_STRING(var->get_string(), STR("abcdef")); } TEST_XML(xpath_variables_type_node_set, "") -- cgit v1.2.3 From 546997683a9edc1b77b60ac96776668d3f57adad Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Fri, 24 Oct 2014 01:17:57 +0000 Subject: tests: Add even more coverage tests Also fix MSVC6 compilation (make convertions to function pointers explicit). git-svn-id: https://pugixml.googlecode.com/svn/trunk@1076 99668b35-9821-0410-8761-19e4c4f06640 --- tests/test_dom_traverse.cpp | 10 +++++----- tests/test_xpath.cpp | 43 +++++++++++++++++++++++++++++++++++++++++- tests/test_xpath_variables.cpp | 23 ++++++++++++++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/test_dom_traverse.cpp b/tests/test_dom_traverse.cpp index e42846f..83afec8 100644 --- a/tests/test_dom_traverse.cpp +++ b/tests/test_dom_traverse.cpp @@ -1048,14 +1048,14 @@ TEST_XML(dom_unspecified_bool_coverage, "text") { xml_node node = doc.first_child(); - node(0); - node.first_attribute()(0); - node.text()(0); + static_cast(node)(0); + static_cast(node.first_attribute())(0); + static_cast(node.text())(0); #ifndef PUGIXML_NO_XPATH xpath_query q(STR("/node")); - q(0); - q.evaluate_node(doc)(0); + static_cast(q)(0); + static_cast(q.evaluate_node(doc))(0); #endif } diff --git a/tests/test_xpath.cpp b/tests/test_xpath.cpp index 80115b8..a65ee37 100644 --- a/tests/test_xpath.cpp +++ b/tests/test_xpath.cpp @@ -110,7 +110,7 @@ TEST_XML(xpath_sort_attributes, "") n.append_attribute(STR("attr3")); n.insert_attribute_before(STR("attr1"), n.attribute(STR("attr2"))); - xpath_node_set ns = n.select_nodes(STR("@*")); + xpath_node_set ns = n.select_nodes(STR("@* | @*")); ns.sort(true); xpath_node_set reverse_sorted = ns; @@ -122,6 +122,25 @@ TEST_XML(xpath_sort_attributes, "") 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; @@ -629,4 +648,26 @@ TEST(xpath_allocate_string_out_of_memory) } #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); + } +} #endif diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp index 39a4f05..728eaa9 100644 --- a/tests/test_xpath_variables.cpp +++ b/tests/test_xpath_variables.cpp @@ -276,6 +276,29 @@ TEST(xpath_variables_long_name) CHECK_XPATH_BOOLEAN_VAR(xml_node(), STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set, true); } +TEST(xpath_variables_long_name_out_of_memory) +{ + xpath_variable_set set; + set.set(STR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), true); + + test_runner::_memory_fail_threshold = 4096 + 128; + +#ifdef PUGIXML_NO_EXCEPTIONS + xpath_query q(STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set); + CHECK(!q); +#else + try + { + xpath_query q(STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set); + + CHECK_FORCE_FAIL("Expected exception"); + } + catch (const xpath_exception&) + { + } +#endif +} + TEST_XML(xpath_variables_select, "") { xpath_variable_set set; -- cgit v1.2.3 From 7b74531c1badc08b15983a34a85f837c2bd0a8e7 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Fri, 24 Oct 2014 01:37:27 +0000 Subject: tests: Fix test failure in PUGIXML_WCHAR_MODE git-svn-id: https://pugixml.googlecode.com/svn/trunk@1077 99668b35-9821-0410-8761-19e4c4f06640 --- tests/test_xpath_variables.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp index 728eaa9..53b40cf 100644 --- a/tests/test_xpath_variables.cpp +++ b/tests/test_xpath_variables.cpp @@ -281,7 +281,7 @@ TEST(xpath_variables_long_name_out_of_memory) xpath_variable_set set; set.set(STR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), true); - test_runner::_memory_fail_threshold = 4096 + 128; + test_runner::_memory_fail_threshold = 4096 + 64 + 52 * sizeof(char_t); #ifdef PUGIXML_NO_EXCEPTIONS xpath_query q(STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set); -- cgit v1.2.3 From 0b7964917cbfd9d007c8a017eaced636f53ddeb3 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 25 Oct 2014 16:37:16 +0000 Subject: Fix node copying for some out of memory cases A page can fail to allocate during attribute creation; this case was not previously handled. git-svn-id: https://pugixml.googlecode.com/svn/trunk@1080 99668b35-9821-0410-8761-19e4c4f06640 --- tests/test_dom_modify.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index 01b562a..07fe6dc 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -1361,13 +1361,22 @@ TEST_XML(dom_node_copyless_taint, "") CHECK_NODE(doc, STR("")); } -TEST_XML(dom_node_copy_out_of_memory, "text1text2") +TEST_XML(dom_node_copy_out_of_memory_node, "text1text2") { test_runner::_memory_fail_threshold = 32768 * 2 + 4096; - xml_document copy; - for (int i = 0; i < 100; ++i) - copy.append_copy(doc.first_child()); + xml_document copy; + for (int i = 0; i < 1000; ++i) + copy.append_copy(doc.first_child()); +} + +TEST_XML(dom_node_copy_out_of_memory_attr, "") +{ + test_runner::_memory_fail_threshold = 32768 * 2 + 4096; + + xml_document copy; + for (int i = 0; i < 1000; ++i) + copy.append_copy(doc.first_child()); } TEST_XML(dom_node_remove_deallocate, "text") -- cgit v1.2.3 From 0e59ea8234e8365a67cd7c35d9cf6eaaec41996b Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 26 Oct 2014 03:46:28 +0000 Subject: tests: Add a way for tests to verify allocation failure git-svn-id: https://pugixml.googlecode.com/svn/trunk@1081 99668b35-9821-0410-8761-19e4c4f06640 --- tests/main.cpp | 6 ++++++ tests/test.hpp | 1 + 2 files changed, 7 insertions(+) (limited to 'tests') diff --git a/tests/main.cpp b/tests/main.cpp index 75b0108..c4c9341 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -20,6 +20,7 @@ test_runner* test_runner::_tests = 0; size_t test_runner::_memory_fail_threshold = 0; +bool test_runner::_memory_fail_triggered = false; jmp_buf test_runner::_failure_buffer; const char* test_runner::_failure_message; const char* test_runner::_temp_path; @@ -30,7 +31,11 @@ static size_t g_memory_total_count = 0; static void* custom_allocate(size_t size) { if (test_runner::_memory_fail_threshold > 0 && test_runner::_memory_fail_threshold < g_memory_total_size + size) + { + test_runner::_memory_fail_triggered = true; + return 0; + } else { void* ptr = memory_allocate(size); @@ -84,6 +89,7 @@ static bool run_test(test_runner* test) g_memory_total_size = 0; g_memory_total_count = 0; test_runner::_memory_fail_threshold = 0; + test_runner::_memory_fail_triggered = false; pugi::set_memory_management_functions(custom_allocate, custom_deallocate); diff --git a/tests/test.hpp b/tests/test.hpp index 26260ad..509b0cd 100644 --- a/tests/test.hpp +++ b/tests/test.hpp @@ -23,6 +23,7 @@ struct test_runner static test_runner* _tests; static size_t _memory_fail_threshold; + static bool _memory_fail_triggered; static jmp_buf _failure_buffer; static const char* _failure_message; -- cgit v1.2.3 From f31c14e1fdcbd6e406e7e2bea21b303853e028e3 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 26 Oct 2014 02:22:27 -0700 Subject: tests: Remove git2svn helper script! --- tests/gitsvn.sh | 5 ----- 1 file changed, 5 deletions(-) delete mode 100755 tests/gitsvn.sh (limited to 'tests') diff --git a/tests/gitsvn.sh b/tests/gitsvn.sh deleted file mode 100755 index 222a09f..0000000 --- a/tests/gitsvn.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -git svn init https://pugixml.googlecode.com/svn/trunk -git update-ref refs/remotes/git-svn refs/remotes/origin/master -git svn rebase -- cgit v1.2.3 From d5e29292c67c24b5cd7ac4f6afce2f8ec293144a Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 26 Oct 2014 09:37:18 -0700 Subject: XPath: Optimize constant filters/predicates If a filter/predicate expression is a constant, we don't need to evaluate it for every nodeset element - we can evaluate it once and pick the right element or keep/discard the entire collection. If the expression is 1, we can early out on first node when evaluating the node set - queries like following::item[1] are now significantly faster. Additionally this change refactors filters/predicates to have additional metadata describing the expression type in _test field that is filled during optimization. Note that predicate_constant selection right now is very simple (but captures most common use cases except for maybe [last()]). --- tests/test_xpath_paths.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'tests') diff --git a/tests/test_xpath_paths.cpp b/tests/test_xpath_paths.cpp index ee2401a..046592a 100644 --- a/tests/test_xpath_paths.cpp +++ b/tests/test_xpath_paths.cpp @@ -437,6 +437,44 @@ TEST_XML(xpath_paths_predicate_number, "") +{ + CHECK_XPATH_NODESET(doc, STR("node/chapter[0.999999999999999]")); + CHECK_XPATH_NODESET(doc, STR("node/chapter[1]")) % 3; + CHECK_XPATH_NODESET(doc, STR("node/chapter[1.000000000000001]")); + CHECK_XPATH_NODESET(doc, STR("node/chapter[1.999999999999999]")); + CHECK_XPATH_NODESET(doc, STR("node/chapter[2]")) % 4; + CHECK_XPATH_NODESET(doc, STR("node/chapter[2.000000000000001]")); + CHECK_XPATH_NODESET(doc, STR("node/chapter[4.999999999999999]")); + CHECK_XPATH_NODESET(doc, STR("node/chapter[5]")) % 7; + CHECK_XPATH_NODESET(doc, STR("node/chapter[5.000000000000001]")); +} + +TEST_XML(xpath_paths_predicate_number_out_of_range, "") +{ + xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling(); + + CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[0]")); + CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[-1]")); + CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[-1000000000000]")); + CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[-1 div 0]")); + CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[1000000000000]")); + CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[1 div 0]")); + CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[0 div 0]")); +} + +TEST_XML(xpath_paths_predicate_constant_boolean, "") +{ + xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling(); + + xpath_variable_set set; + set.set(STR("true"), true); + set.set(STR("false"), false); + + CHECK_XPATH_NODESET_VAR(n, STR("following-sibling::chapter[$false]"), &set); + CHECK_XPATH_NODESET_VAR(n, STR("following-sibling::chapter[$true]"), &set) % 6 % 7; +} + TEST_XML(xpath_paths_predicate_several, "") { xml_node n = doc.child(STR("node")); -- cgit v1.2.3 From c64d4820b142e6df93ccf612d0e4717159a36591 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 27 Oct 2014 18:50:09 -0700 Subject: XPath: Optimize [position()=expr] and [last()] To get more benefits from constant predicate/filter optimization we rewrite [position()=expr] predicates into [expr] for numeric expressions. Right now the rewrite is only for entire expressions - it may be beneficial to split complex expressions like [position()=constant and expr] into [constant][expr] but that is more complicated. last() does not depend on the node set contents so is "constant" as far as our optimization is concerned so we can evaluate it once. --- tests/test_xpath_paths.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tests') diff --git a/tests/test_xpath_paths.cpp b/tests/test_xpath_paths.cpp index 046592a..df0dfa4 100644 --- a/tests/test_xpath_paths.cpp +++ b/tests/test_xpath_paths.cpp @@ -475,6 +475,14 @@ TEST_XML(xpath_paths_predicate_constant_boolean, "3") +{ + CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=1]")) % 3; + CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=2+2]")) % 7; + CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=last()]")) % 8; + CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=string()]")) % 5; +} + TEST_XML(xpath_paths_predicate_several, "") { xml_node n = doc.child(STR("node")); -- cgit v1.2.3 From 6229138d80380d582f16931d36b279807dcb82dd Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 27 Oct 2014 22:29:14 -0700 Subject: Optimize node printing by using raw pointers This lets us do fewer null pointer checks (making printing 2% faster with -O3) and removes a lot of function calls (making printing 20% faster with -O0). --- tests/test_write.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'tests') diff --git a/tests/test_write.cpp b/tests/test_write.cpp index 98650ac..8fc88e1 100644 --- a/tests/test_write.cpp +++ b/tests/test_write.cpp @@ -51,6 +51,15 @@ TEST_XML(write_cdata_inner, "") CHECK_NODE_EX(doc, STR("\n"), STR(""), 0); } +TEST(write_cdata_null) +{ + xml_document doc; + doc.append_child(node_cdata); + doc.append_child(STR("node")).append_child(node_cdata); + + CHECK_NODE(doc, STR("")); +} + TEST_XML_FLAGS(write_comment, "", parse_comments | parse_fragment) { CHECK_NODE(doc, STR("")); @@ -80,12 +89,32 @@ TEST(write_comment_invalid) CHECK_NODE(doc, STR("")); } +TEST(write_comment_null) +{ + xml_document doc; + doc.append_child(node_comment); + + CHECK_NODE(doc, STR("")); +} + TEST_XML_FLAGS(write_pi, "", parse_pi | parse_fragment) { CHECK_NODE(doc, STR("")); CHECK_NODE_EX(doc, STR("\n"), STR(""), 0); } +TEST(write_pi_null) +{ + xml_document doc; + xml_node node = doc.append_child(node_pi); + + CHECK_NODE(doc, STR("")); + + node.set_value(STR("value")); + + CHECK_NODE(doc, STR("")); +} + TEST_XML_FLAGS(write_declaration, "", parse_declaration | parse_fragment) { CHECK_NODE(doc, STR("")); @@ -98,6 +127,14 @@ TEST_XML_FLAGS(write_doctype, "", parse_doctype | parse_fra CHECK_NODE_EX(doc, STR("\n"), STR(""), 0); } +TEST(write_doctype_null) +{ + xml_document doc; + doc.append_child(node_doctype); + + CHECK_NODE(doc, STR("")); +} + TEST_XML(write_escape, "text") { doc.child(STR("node")).attribute(STR("attr")) = STR("<>'\"&\x04\r\n\t"); @@ -460,3 +497,16 @@ TEST_XML(write_indent_custom, "text\nABCD\nABCDABCDtext\nABCD\n\n"), STR("ABCD"), format_indent); CHECK_NODE_EX(doc, STR("\nABCDE\nABCDEABCDEtext\nABCDE\n\n"), STR("ABCDE"), format_indent); } + +TEST(write_pcdata_null) +{ + xml_document doc; + doc.append_child(STR("node")).append_child(node_pcdata); + + CHECK_NODE(doc, STR("")); + CHECK_NODE_EX(doc, STR("\n"), STR("\t"), format_indent); + + doc.first_child().append_child(node_pcdata); + + CHECK_NODE_EX(doc, STR("\n\t\n\t\n\n"), STR("\t"), format_indent); +} -- cgit v1.2.3