From 29fef9aca2f5832eb721f9d097d2a6f6ebdb0179 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 11 Apr 2015 00:16:39 -0700 Subject: tests: Add more out of memory tests This provides more coverage for #17. --- tests/test_dom_modify.cpp | 36 ++++++++++++++++++++++++++++++++++++ tests/test_parse.cpp | 9 +++++++++ 2 files changed, 45 insertions(+) diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index fa50112..8610a74 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -1100,6 +1100,42 @@ TEST(dom_node_append_buffer_out_of_memory_buffer) CHECK(!doc.first_child()); } +TEST(dom_node_append_buffer_out_of_memory_nodes) +{ + unsigned int count = 4000; + std::basic_string data; + + for (unsigned int i = 0; i < count; ++i) + data += STR(""); + + test_runner::_memory_fail_threshold = 32768 + 128 + data.length() * sizeof(wchar_t) + 32; + + xml_document doc; + CHECK(doc.append_buffer(data.c_str(), data.length() * sizeof(wchar_t), parse_fragment).status == status_out_of_memory); + + unsigned int valid = 0; + + for (xml_node n = doc.first_child(); n; n = n.next_sibling()) + { + CHECK_STRING(n.name(), STR("a")); + valid++; + } + + CHECK(valid > 0 && valid < count); +} + +TEST(dom_node_append_buffer_out_of_memory_name) +{ + test_runner::_memory_fail_threshold = 32768 + 128; + + char data[128] = {0}; + + xml_document doc; + CHECK(doc.append_child(STR("root"))); + CHECK(doc.first_child().append_buffer(data, sizeof(data)).status == status_out_of_memory); + CHECK_STRING(doc.first_child().name(), STR("root")); +} + TEST_XML(dom_node_append_buffer_fragment, "") { xml_node node = doc.child(STR("node")); diff --git a/tests/test_parse.cpp b/tests/test_parse.cpp index 7bb2663..3ae96d1 100644 --- a/tests/test_parse.cpp +++ b/tests/test_parse.cpp @@ -926,6 +926,15 @@ TEST(parse_out_of_memory_halfway_attr) CHECK_STRING(doc.first_child().last_attribute().name(), STR("a")); } +TEST(parse_out_of_memory_conversion) +{ + test_runner::_memory_fail_threshold = 256; + + xml_document doc; + CHECK(doc.load_buffer("", 7, parse_default, encoding_latin1).status == status_out_of_memory); + CHECK(!doc.first_child()); +} + static bool test_offset(const char_t* contents, unsigned int options, pugi::xml_parse_status status, ptrdiff_t offset) { xml_document doc; -- cgit v1.2.3 From 03ea04c32a6c1961d28d3b5dc66dad906dfc8ca6 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 11 Apr 2015 00:33:35 -0700 Subject: tests: Use char_t instead of wchar_t --- tests/test_dom_modify.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index 8610a74..af833a6 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -1108,10 +1108,10 @@ TEST(dom_node_append_buffer_out_of_memory_nodes) for (unsigned int i = 0; i < count; ++i) data += STR(""); - test_runner::_memory_fail_threshold = 32768 + 128 + data.length() * sizeof(wchar_t) + 32; + test_runner::_memory_fail_threshold = 32768 + 128 + data.length() * sizeof(char_t) + 32; xml_document doc; - CHECK(doc.append_buffer(data.c_str(), data.length() * sizeof(wchar_t), parse_fragment).status == status_out_of_memory); + CHECK(doc.append_buffer(data.c_str(), data.length() * sizeof(char_t), parse_fragment).status == status_out_of_memory); unsigned int valid = 0; -- cgit v1.2.3 From 814443b147e6159a0ad2842fabc1288ec6a0ee24 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 11 Apr 2015 22:37:38 -0700 Subject: Fix exception type for out-of-memory for XPath variables When parsing XPath variables, we need to perform a heap allocation; if it fails, an xpath_exception instead of bad_alloc used to be thrown. Now we throw the exception of a correct type so that xpath_exception means 'parsing error'. --- src/pugixml.cpp | 12 +++++++----- tests/test_xpath_variables.cpp | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 5b77a27..5190937 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -7715,7 +7715,7 @@ PUGI__NS_BEGIN } } - PUGI__FN xpath_variable* get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end) + PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) { size_t length = static_cast(end - begin); char_t* scratch = buffer; @@ -7724,19 +7724,19 @@ PUGI__NS_BEGIN { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!scratch) return 0; + if (!scratch) return false; } // copy string to zero-terminated buffer and perform lookup memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; - xpath_variable* result = set->get(scratch); + *out_result = set->get(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); - return result; + return true; } PUGI__NS_END @@ -10309,7 +10309,9 @@ PUGI__NS_BEGIN if (!_variables) throw_error("Unknown variable: variable set is not provided"); - xpath_variable* var = get_variable_scratch(_scratch, _variables, name.begin, name.end); + xpath_variable* var = 0; + if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) + throw_error_oom(); if (!var) throw_error("Unknown variable: variable set does not contain the given name"); diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp index 53b40cf..7a099c4 100644 --- a/tests/test_xpath_variables.cpp +++ b/tests/test_xpath_variables.cpp @@ -293,7 +293,7 @@ TEST(xpath_variables_long_name_out_of_memory) CHECK_FORCE_FAIL("Expected exception"); } - catch (const xpath_exception&) + catch (const std::bad_alloc&) { } #endif -- cgit v1.2.3 From e2e5bc906a06f7937319896d55254e4881888ce4 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 11 Apr 2015 22:41:39 -0700 Subject: Use -fno-exceptions flag for PUGIXML_NO_EXCEPTIONS build This makes sure that no exception handling mechanisms are used if PUGXML_NO_EXCEPTIONS is defined. --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 0e64129..59f055b 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,10 @@ ifneq ($(defines),standard) CXXFLAGS+=-D $(subst $(COMMA), -D ,$(defines)) endif +ifneq ($(findstring PUGIXML_NO_EXCEPTIONS,$(defines)),) + CXXFLAGS+=-fno-exceptions +endif + OBJECTS=$(SOURCES:%=$(BUILD)/%.o) all: $(EXECUTABLE) -- cgit v1.2.3 From 37467c13bfdfbdbee7cc5176a8755e04353a9deb Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 11 Apr 2015 22:44:42 -0700 Subject: tests: Add a test for throwing from xml_writer::write We currently don't allocate/modify any state so there are no issues with this. --- tests/test_write.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test_write.cpp b/tests/test_write.cpp index ad6c409..230d652 100644 --- a/tests/test_write.cpp +++ b/tests/test_write.cpp @@ -574,3 +574,41 @@ TEST_XML_FLAGS(write_mixed, "premid\npostfin\n\n\n"), STR("\t"), 0); CHECK_NODE_EX(doc, STR("\n\t\n\tpremid\n\t\tpostfin\n\t\n\n"), STR("\t"), format_indent); } + +#ifndef PUGIXML_NO_EXCEPTIONS +struct throwing_writer: pugi::xml_writer +{ + virtual void write(const void*, size_t) + { + throw std::runtime_error("write failed"); + } +}; + +TEST_XML(write_throw_simple, "") +{ + try + { + throwing_writer w; + doc.print(w); + + CHECK_FORCE_FAIL("Expected exception"); + } + catch (std::runtime_error&) + { + } +} + +TEST_XML(write_throw_encoding, "") +{ + try + { + throwing_writer w; + doc.print(w, STR("\t"), format_default, encoding_utf32_be); + + CHECK_FORCE_FAIL("Expected exception"); + } + catch (std::runtime_error&) + { + } +} +#endif -- cgit v1.2.3 From 4e004176bacb0160007102742ec62e79a9d958bb Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 11 Apr 2015 22:46:08 -0700 Subject: tests: Improve out-of-memory tests Previously there was no guarantee that the tests that check for out of memory handling behavior are actually correct - e.g. that they correctly simulate out of memory conditions. Now every simulated out of memory condition has to be "guarded" using CHECK_ALLOC_FAIL. It makes sure that every piece of code that is supposed to cause out-of-memory does so, and that no other code runs out of memory unnoticed. --- tests/main.cpp | 9 ++++++ tests/test.hpp | 6 ++++ tests/test_document.cpp | 14 ++++---- tests/test_dom_modify.cpp | 67 ++++++++++++++++++--------------------- tests/test_parse.cpp | 10 +++--- tests/test_xpath.cpp | 72 ++++-------------------------------------- tests/test_xpath_api.cpp | 32 +++---------------- tests/test_xpath_parse.cpp | 25 ++------------- tests/test_xpath_variables.cpp | 20 +++--------- 9 files changed, 76 insertions(+), 179 deletions(-) diff --git a/tests/main.cpp b/tests/main.cpp index c4c9341..6996eb9 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -27,11 +27,13 @@ const char* test_runner::_temp_path; static size_t g_memory_total_size = 0; static size_t g_memory_total_count = 0; +static size_t g_memory_fail_triggered = false; static void* custom_allocate(size_t size) { if (test_runner::_memory_fail_threshold > 0 && test_runner::_memory_fail_threshold < g_memory_total_size + size) { + g_memory_fail_triggered = true; test_runner::_memory_fail_triggered = true; return 0; @@ -88,6 +90,7 @@ static bool run_test(test_runner* test) #endif g_memory_total_size = 0; g_memory_total_count = 0; + g_memory_fail_triggered = false; test_runner::_memory_fail_threshold = 0; test_runner::_memory_fail_triggered = false; @@ -113,6 +116,12 @@ static bool run_test(test_runner* test) test->run(); + if (test_runner::_memory_fail_triggered) + { + printf("Test %s failed: unguarded memory fail triggered\n", test->_name); + return false; + } + if (g_memory_total_size != 0 || g_memory_total_count != 0) { printf("Test %s failed: memory leaks found (%u bytes in %u allocations)\n", test->_name, static_cast(g_memory_total_size), static_cast(g_memory_total_count)); diff --git a/tests/test.hpp b/tests/test.hpp index 4222638..46c3330 100644 --- a/tests/test.hpp +++ b/tests/test.hpp @@ -142,6 +142,12 @@ struct dummy_fixture {}; #define CHECK_XPATH_FAIL(query) CHECK_XPATH_FAIL_VAR(query, 0) #endif +#ifdef PUGIXML_NO_EXCEPTIONS +#define CHECK_ALLOC_FAIL(code) CHECK(!test_runner::_memory_fail_triggered); code; CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false +#else +#define CHECK_ALLOC_FAIL(code) CHECK(!test_runner::_memory_fail_triggered); try { code; } catch (std::bad_alloc&) {} CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false +#endif + #define STR(text) PUGIXML_TEXT(text) #ifdef __DMC__ diff --git a/tests/test_document.cpp b/tests/test_document.cpp index 09d89d7..2c52030 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -110,7 +110,7 @@ TEST(document_load_stream_error) std::istringstream iss(""); test_runner::_memory_fail_threshold = 1; - CHECK(doc.load(iss).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.load(iss).status == status_out_of_memory)); } TEST(document_load_stream_empty) @@ -237,7 +237,7 @@ TEST(document_load_stream_nonseekable_out_of_memory) test_runner::_memory_fail_threshold = 1; pugi::xml_document doc; - CHECK(doc.load(in).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.load(in).status == status_out_of_memory)); } TEST(document_load_stream_nonseekable_out_of_memory_large) @@ -253,7 +253,7 @@ TEST(document_load_stream_nonseekable_out_of_memory_large) test_runner::_memory_fail_threshold = 10000 * 8 * 3 / 2; pugi::xml_document doc; - CHECK(doc.load(in).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.load(in).status == status_out_of_memory)); } #endif @@ -302,7 +302,7 @@ TEST(document_load_file_error) CHECK(doc.load_file("filedoesnotexist").status == status_file_not_found); test_runner::_memory_fail_threshold = 1; - CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory)); } TEST(document_load_file_error_previous) @@ -339,7 +339,9 @@ TEST(document_load_file_wide_out_of_memory) pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(L"tests/data/small.xml"); + pugi::xml_parse_result result; + result.status = status_out_of_memory; + CHECK_ALLOC_FAIL(result = doc.load_file(L"tests/data/small.xml")); CHECK(result.status == status_out_of_memory || result.status == status_file_not_found); } @@ -1320,7 +1322,7 @@ TEST(document_convert_out_of_memory) 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); + CHECK_ALLOC_FAIL(CHECK(doc.load_buffer(files[src].data, files[src].size, parse_default, files[src].encoding).status == status_out_of_memory)); } // cleanup diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index af833a6..ae18171 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -858,10 +858,10 @@ TEST(dom_string_out_of_memory) // no value => long value test_runner::_memory_fail_threshold = 32; - CHECK(!node.set_name(string)); - CHECK(!text.set_value(string)); - CHECK(!attr.set_name(string)); - CHECK(!attr.set_value(string)); + CHECK_ALLOC_FAIL(CHECK(!node.set_name(string))); + CHECK_ALLOC_FAIL(CHECK(!text.set_value(string))); + CHECK_ALLOC_FAIL(CHECK(!attr.set_name(string))); + CHECK_ALLOC_FAIL(CHECK(!attr.set_value(string))); // set some names/values test_runner::_memory_fail_threshold = 0; @@ -873,10 +873,10 @@ TEST(dom_string_out_of_memory) // some value => long value test_runner::_memory_fail_threshold = 32; - CHECK(!node.set_name(string)); - CHECK(!text.set_value(string)); - CHECK(!attr.set_name(string)); - CHECK(!attr.set_value(string)); + CHECK_ALLOC_FAIL(CHECK(!node.set_name(string))); + CHECK_ALLOC_FAIL(CHECK(!text.set_value(string))); + CHECK_ALLOC_FAIL(CHECK(!attr.set_name(string))); + CHECK_ALLOC_FAIL(CHECK(!attr.set_value(string))); // check that original state was preserved test_runner::_memory_fail_threshold = 0; @@ -897,30 +897,27 @@ TEST(dom_node_out_of_memory) xml_attribute a = n.append_attribute(STR("a")); CHECK(a); - while (n.append_child(node_comment) || n.append_attribute(STR("b"))) - { - // nop - } + CHECK_ALLOC_FAIL(while (n.append_child(node_comment) || n.append_attribute(STR("b"))) { /* nop */ }); // 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)); + CHECK_ALLOC_FAIL(CHECK(!n.append_child())); + CHECK_ALLOC_FAIL(CHECK(!n.prepend_child())); + CHECK_ALLOC_FAIL(CHECK(!n.insert_child_after(node_element, n.first_child()))); + CHECK_ALLOC_FAIL(CHECK(!n.insert_child_before(node_element, n.first_child()))); + CHECK_ALLOC_FAIL(CHECK(!n.append_attribute(STR("")))); + CHECK_ALLOC_FAIL(CHECK(!n.prepend_attribute(STR("")))); + CHECK_ALLOC_FAIL(CHECK(!n.insert_attribute_after(STR(""), a))); + CHECK_ALLOC_FAIL(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)); + CHECK_ALLOC_FAIL(CHECK(!n.append_copy(n.first_child()))); + CHECK_ALLOC_FAIL(CHECK(!n.prepend_copy(n.first_child()))); + CHECK_ALLOC_FAIL(CHECK(!n.insert_copy_after(n.first_child(), n.first_child()))); + CHECK_ALLOC_FAIL(CHECK(!n.insert_copy_before(n.first_child(), n.first_child()))); + CHECK_ALLOC_FAIL(CHECK(!n.append_copy(a))); + CHECK_ALLOC_FAIL(CHECK(!n.prepend_copy(a))); + CHECK_ALLOC_FAIL(CHECK(!n.insert_copy_after(a, a))); + CHECK_ALLOC_FAIL(CHECK(!n.insert_copy_before(a, a))); } TEST(dom_node_memory_limit) @@ -1085,7 +1082,7 @@ TEST(dom_node_append_buffer_out_of_memory_extra) test_runner::_memory_fail_threshold = 1; xml_document doc; - CHECK(doc.append_buffer("", 4).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.append_buffer("", 4).status == status_out_of_memory)); CHECK(!doc.first_child()); } @@ -1096,7 +1093,7 @@ TEST(dom_node_append_buffer_out_of_memory_buffer) char data[128] = {0}; xml_document doc; - CHECK(doc.append_buffer(data, sizeof(data)).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.append_buffer(data, sizeof(data)).status == status_out_of_memory)); CHECK(!doc.first_child()); } @@ -1111,7 +1108,7 @@ TEST(dom_node_append_buffer_out_of_memory_nodes) test_runner::_memory_fail_threshold = 32768 + 128 + data.length() * sizeof(char_t) + 32; xml_document doc; - CHECK(doc.append_buffer(data.c_str(), data.length() * sizeof(char_t), parse_fragment).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.append_buffer(data.c_str(), data.length() * sizeof(char_t), parse_fragment).status == status_out_of_memory)); unsigned int valid = 0; @@ -1132,7 +1129,7 @@ TEST(dom_node_append_buffer_out_of_memory_name) xml_document doc; CHECK(doc.append_child(STR("root"))); - CHECK(doc.first_child().append_buffer(data, sizeof(data)).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.first_child().append_buffer(data, sizeof(data)).status == status_out_of_memory)); CHECK_STRING(doc.first_child().name(), STR("root")); } @@ -1441,8 +1438,7 @@ TEST_XML(dom_node_copy_out_of_memory_node, "te test_runner::_memory_fail_threshold = 32768 * 2 + 4096; xml_document copy; - for (int i = 0; i < 1000; ++i) - copy.append_copy(doc.first_child()); + CHECK_ALLOC_FAIL(for (int i = 0; i < 1000; ++i) copy.append_copy(doc.first_child())); } TEST_XML(dom_node_copy_out_of_memory_attr, "") @@ -1450,8 +1446,7 @@ TEST_XML(dom_node_copy_out_of_memory_attr, "text") diff --git a/tests/test_parse.cpp b/tests/test_parse.cpp index 3ae96d1..08ddee4 100644 --- a/tests/test_parse.cpp +++ b/tests/test_parse.cpp @@ -873,7 +873,7 @@ TEST(parse_out_of_memory) test_runner::_memory_fail_threshold = 256; xml_document doc; - CHECK(doc.load_string(STR("")).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.load_string(STR("")).status == status_out_of_memory)); CHECK(!doc.first_child()); } @@ -893,7 +893,7 @@ TEST(parse_out_of_memory_halfway_node) test_runner::_memory_fail_threshold = 65536; xml_document doc; - CHECK(doc.load_buffer_inplace(text, count * 4).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.load_buffer_inplace(text, count * 4).status == status_out_of_memory)); CHECK_NODE(doc.first_child(), STR("")); } @@ -920,7 +920,7 @@ TEST(parse_out_of_memory_halfway_attr) test_runner::_memory_fail_threshold = 65536; xml_document doc; - CHECK(doc.load_buffer_inplace(text, count * 5 + 4).status == status_out_of_memory); + CHECK_ALLOC_FAIL(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")); @@ -931,7 +931,7 @@ TEST(parse_out_of_memory_conversion) test_runner::_memory_fail_threshold = 256; xml_document doc; - CHECK(doc.load_buffer("", 7, parse_default, encoding_latin1).status == status_out_of_memory); + CHECK_ALLOC_FAIL(CHECK(doc.load_buffer("", 7, parse_default, encoding_latin1).status == status_out_of_memory)); CHECK(!doc.first_child()); } @@ -950,7 +950,7 @@ TEST(parse_error_offset) CHECK_OFFSET("", parse_default, status_ok, 0); test_runner::_memory_fail_threshold = 1; - CHECK_OFFSET("", parse_default, status_out_of_memory, 0); + CHECK_ALLOC_FAIL(CHECK_OFFSET("", parse_default, status_out_of_memory, 0)); test_runner::_memory_fail_threshold = 0; CHECK_OFFSET("<3d/>", parse_default, status_unrecognized_tag, 1); diff --git a/tests/test_xpath.cpp b/tests/test_xpath.cpp index f5b4c66..57fa95b 100644 --- a/tests/test_xpath.cpp +++ b/tests/test_xpath.cpp @@ -378,19 +378,7 @@ TEST(xpath_out_of_memory_evaluate_concat) pugi::xpath_query q(query.c_str()); -#ifdef PUGIXML_NO_EXCEPTIONS - CHECK(q.evaluate_string(0, 0, xml_node()) == 1); -#else - try - { - q.evaluate_string(0, 0, xml_node()); - - CHECK_FORCE_FAIL("Expected out of memory exception"); - } - catch (const std::bad_alloc&) - { - } -#endif + CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, xml_node()) == 1)); } TEST(xpath_out_of_memory_evaluate_substring) @@ -404,19 +392,7 @@ TEST(xpath_out_of_memory_evaluate_substring) pugi::xpath_query q(query.c_str()); -#ifdef PUGIXML_NO_EXCEPTIONS - CHECK(q.evaluate_string(0, 0, xml_node()) == 1); -#else - try - { - q.evaluate_string(0, 0, xml_node()); - - CHECK_FORCE_FAIL("Expected out of memory exception"); - } - catch (const std::bad_alloc&) - { - } -#endif + CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, xml_node()) == 1)); } TEST_XML(xpath_out_of_memory_evaluate_union, "") @@ -425,19 +401,7 @@ TEST_XML(xpath_out_of_memory_evaluate_union, " pugi::xpath_query q(STR("a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|(a|a)))))))))))))))))))")); -#ifdef PUGIXML_NO_EXCEPTIONS - CHECK(q.evaluate_node_set(doc.child(STR("node"))).empty()); -#else - try - { - q.evaluate_node_set(doc.child(STR("node"))); - - CHECK_FORCE_FAIL("Expected out of memory exception"); - } - catch (const std::bad_alloc&) - { - } -#endif + CHECK_ALLOC_FAIL(CHECK(q.evaluate_node_set(doc.child(STR("node"))).empty())); } TEST_XML(xpath_out_of_memory_evaluate_predicate, "") @@ -446,19 +410,7 @@ TEST_XML(xpath_out_of_memory_evaluate_predicate, " pugi::xpath_query q(STR("//a[//a[//a[//a[//a[//a[//a[//a[//a[//a[//a[//a[//a[//a[true()]]]]]]]]]]]]]]")); -#ifdef PUGIXML_NO_EXCEPTIONS - CHECK(q.evaluate_node_set(doc).empty()); -#else - try - { - q.evaluate_node_set(doc); - - CHECK_FORCE_FAIL("Expected out of memory exception"); - } - catch (const std::bad_alloc&) - { - } -#endif + CHECK_ALLOC_FAIL(CHECK(q.evaluate_node_set(doc).empty())); } TEST(xpath_memory_concat_massive) @@ -634,20 +586,8 @@ TEST(xpath_allocate_string_out_of_memory) test_runner::_memory_fail_threshold = 8*1024; -#ifdef PUGIXML_NO_EXCEPTIONS - CHECK(!xpath_query(query.c_str())); -#else - try - { - #ifndef __DMC__ // DigitalMars exception handling crashes instead of catching the exception... - xpath_query q(query.c_str()); - - CHECK_FORCE_FAIL("Expected out of memory exception"); - #endif - } - catch (const std::bad_alloc&) - { - } +#ifndef __DMC__ // DigitalMars exception handling crashes instead of catching the exception... + CHECK_ALLOC_FAIL(CHECK(!xpath_query(query.c_str()))); #endif } diff --git a/tests/test_xpath_api.cpp b/tests/test_xpath_api.cpp index deb3beb..df078a0 100644 --- a/tests/test_xpath_api.cpp +++ b/tests/test_xpath_api.cpp @@ -356,6 +356,7 @@ TEST(xpath_api_exception_what) CHECK(e.what()[0] != 0); } } +#endif TEST(xpath_api_node_set_ctor_out_of_memory) { @@ -363,15 +364,7 @@ TEST(xpath_api_node_set_ctor_out_of_memory) xpath_node data[2]; - try - { - xpath_node_set ns(data, data + 2); - - CHECK_FORCE_FAIL("Expected out of memory exception"); - } - catch (const std::bad_alloc&) - { - } + CHECK_ALLOC_FAIL(xpath_node_set ns(data, data + 2)); } TEST(xpath_api_node_set_copy_ctor_out_of_memory) @@ -381,15 +374,7 @@ TEST(xpath_api_node_set_copy_ctor_out_of_memory) test_runner::_memory_fail_threshold = 1; - try - { - xpath_node_set copy = ns; - - CHECK_FORCE_FAIL("Expected out of memory exception"); - } - catch (const std::bad_alloc&) - { - } + CHECK_ALLOC_FAIL(xpath_node_set copy = ns); } TEST_XML(xpath_api_node_set_assign_out_of_memory_preserve, "") @@ -402,19 +387,10 @@ TEST_XML(xpath_api_node_set_assign_out_of_memory_preserve, "") { diff --git a/tests/test_xpath_parse.cpp b/tests/test_xpath_parse.cpp index 2c2af5a..b6de42e 100644 --- a/tests/test_xpath_parse.cpp +++ b/tests/test_xpath_parse.cpp @@ -272,44 +272,25 @@ TEST_XML(xpath_parse_absolute, "
") CHECK_XPATH_NODESET(doc, STR("/*[/]")) % 2; } -#ifdef PUGIXML_NO_EXCEPTIONS -# define CHECK_XPATH_FAIL_OOM(query) CHECK_XPATH_FAIL(query) -#else -static void test_xpath_fail_oom(const char_t* query) -{ - try - { - pugi::xpath_query q(query); - - CHECK_FORCE_FAIL("Expected out of memory exception"); - } - catch (const std::bad_alloc&) - { - } -} - -# define CHECK_XPATH_FAIL_OOM(query) test_xpath_fail_oom(query) -#endif - TEST(xpath_parse_out_of_memory_first_page) { test_runner::_memory_fail_threshold = 1; - CHECK_XPATH_FAIL_OOM(STR("1")); + CHECK_ALLOC_FAIL(CHECK_XPATH_FAIL(STR("1"))); } TEST(xpath_parse_out_of_memory_second_page_node) { test_runner::_memory_fail_threshold = 8192; - CHECK_XPATH_FAIL_OOM(STR("1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1")); + CHECK_ALLOC_FAIL(CHECK_XPATH_FAIL(STR("1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1"))); } TEST(xpath_parse_out_of_memory_string_to_number) { test_runner::_memory_fail_threshold = 4096 + 128; - CHECK_XPATH_FAIL_OOM(STR("0.11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")); + CHECK_ALLOC_FAIL(CHECK_XPATH_FAIL(STR("0.11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"))); } TEST(xpath_parse_qname_error) diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp index 7a099c4..0d33312 100644 --- a/tests/test_xpath_variables.cpp +++ b/tests/test_xpath_variables.cpp @@ -177,7 +177,8 @@ TEST(xpath_variables_set_out_of_memory) xpath_variable_set set; - xpath_variable* var = set.add(STR("target"), xpath_type_number); + xpath_variable* var = 0; + CHECK_ALLOC_FAIL(var = set.add(STR("target"), xpath_type_number)); CHECK(!var); } @@ -190,7 +191,7 @@ TEST(xpath_variables_out_of_memory) xpath_variable* var = set.add(STR("target"), xpath_type_string); CHECK(var); - CHECK(!var->set(STR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"))); + CHECK_ALLOC_FAIL(CHECK(!var->set(STR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")))); } TEST_XML(xpath_variables_evaluate, "") @@ -283,20 +284,7 @@ TEST(xpath_variables_long_name_out_of_memory) test_runner::_memory_fail_threshold = 4096 + 64 + 52 * sizeof(char_t); -#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 std::bad_alloc&) - { - } -#endif + CHECK_ALLOC_FAIL(CHECK(!xpath_query(STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set))); } TEST_XML(xpath_variables_select, "") -- cgit v1.2.3 From 3da7d68617d196d76b56cff27ad565f64f87d476 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 11 Apr 2015 22:52:41 -0700 Subject: Fix Travis CI build. --- tests/test_write.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_write.cpp b/tests/test_write.cpp index 230d652..af4acf4 100644 --- a/tests/test_write.cpp +++ b/tests/test_write.cpp @@ -4,6 +4,7 @@ #include #include +#include TEST_XML(write_simple, "text") { -- cgit v1.2.3 From 99afee183225f6f33f3289030c992738ccaf13fe Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 12 Apr 2015 01:32:25 -0700 Subject: Move zero-termination out of as_utf8_end as_utf8_end was used with std::string, where writing an extra zero-terminating character should *probably* always work (at least if size is positive) but is not ideal. The only place that needed to zero-terminate was convert_path_heap. --- src/pugixml.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 5190937..6b3e87e 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -1691,9 +1691,6 @@ PUGI__NS_BEGIN assert(begin + size == end); (void)!end; - - // zero-terminate - buffer[size] = 0; } #ifndef PUGIXML_NO_STL @@ -4295,6 +4292,9 @@ PUGI__NS_BEGIN // second pass: convert to utf8 as_utf8_end(result, size, str, length); + // zero-terminate + result[size] = 0; + return result; } -- cgit v1.2.3 From d6f7766172bd3dcd6b286888f5bdfdcb1953f3ba Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 12 Apr 2015 02:05:59 -0700 Subject: Optimize xml_node::path() to use 1 allocation Instead of reallocating the string for every tree level just do two passes over the ancestor chain. --- src/pugixml.cpp | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 6b3e87e..619cc7b 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -4056,6 +4056,7 @@ PUGI__NS_BEGIN return status_ok; } + // This function assumes that buffer has extra sizeof(char_t) writable bytes after size PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) { // We only need to zero-terminate if encoding conversion does not do it for us @@ -5328,20 +5329,35 @@ namespace pugi #ifndef PUGIXML_NO_STL PUGI__FN string_t xml_node::path(char_t delimiter) const { - xml_node cursor = *this; // Make a copy. - - string_t result = cursor.name(); + if (!_root) return string_t(); + + size_t offset = 0; - while (cursor.parent()) + for (xml_node_struct* i = _root; i; i = i->parent) { - cursor = cursor.parent(); - - string_t temp = cursor.name(); - temp += delimiter; - temp += result; - result.swap(temp); + offset += (i != _root); + offset += i->name ? impl::strlength(i->name) : 0; + } + + string_t result; + result.resize(offset); + + for (xml_node_struct* j = _root; j; j = j->parent) + { + if (j != _root) + result[--offset] = delimiter; + + if (j->name && *j->name) + { + size_t length = impl::strlength(j->name); + + offset -= length; + memcpy(&result[offset], j->name, length * sizeof(char_t)); + } } + assert(offset == 0); + return result; } #endif @@ -6188,12 +6204,14 @@ namespace pugi PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb"); + return impl::save_file_impl(*this, file, indent, flags, encoding); } PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"); + return impl::save_file_impl(*this, file, indent, flags, encoding); } -- cgit v1.2.3 From 2537cccad34b64086ad2ff47db07c3674b9be07a Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 12 Apr 2015 02:17:20 -0700 Subject: tests: Fix some Coverity issues --- tests/test_document.cpp | 4 +++- tests/test_dom_modify.cpp | 4 ++-- tests/test_dom_traverse.cpp | 12 +++++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/test_document.cpp b/tests/test_document.cpp index 2c52030..d81458a 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -32,9 +32,11 @@ static bool load_file_in_memory(const char* path, char*& data, size_t& size) if (!file) return false; fseek(file, 0, SEEK_END); - size = static_cast(ftell(file)); + long length = ftell(file); fseek(file, 0, SEEK_SET); + CHECK(length >= 0); + size = static_cast(length); data = new char[size]; CHECK(fread(data, 1, size, file) == size); diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index ae18171..fc6dd59 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -644,7 +644,7 @@ TEST_XML(dom_node_remove_child, "") { - doc.child(STR("node")).remove_child(STR("n1")); + CHECK(doc.child(STR("node")).remove_child(STR("n1"))); CHECK_NODE(doc, STR("")); @@ -1040,7 +1040,7 @@ TEST_XML(dom_node_append_buffer_remove, "test") CHECK_NODE(doc, STR("test")); - doc.remove_child(STR("node")); + CHECK(doc.remove_child(STR("node"))); CHECK(!doc.first_child()); } diff --git a/tests/test_dom_traverse.cpp b/tests/test_dom_traverse.cpp index 721a079..4423dbe 100644 --- a/tests/test_dom_traverse.cpp +++ b/tests/test_dom_traverse.cpp @@ -1085,14 +1085,24 @@ TEST_XML(dom_unspecified_bool_coverage, "text") { xml_node node = doc.first_child(); + CHECK(node); static_cast(node)(0); + + CHECK(node.first_attribute()); static_cast(node.first_attribute())(0); + + CHECK(node.text()); static_cast(node.text())(0); #ifndef PUGIXML_NO_XPATH xpath_query q(STR("/node")); + CHECK(q); static_cast(q)(0); - static_cast(q.evaluate_node(doc))(0); + + xpath_node qn = q.evaluate_node(doc); + + CHECK(qn); + static_cast(qn)(0); #endif } -- cgit v1.2.3 From c5d07e2c2825129a37e8d3cac4c19ff3692c11f6 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 12 Apr 2015 02:34:48 -0700 Subject: tests: Add a test that verifies absence of file leaks If an out of memory error happens in load_file there's a danger of leaking the FILE object. Since there is a limited supply of the objects we can easily test that the leak does not happen. --- tests/test_document.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_document.cpp b/tests/test_document.cpp index d81458a..1545e19 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -302,11 +302,33 @@ TEST(document_load_file_error) pugi::xml_document doc; CHECK(doc.load_file("filedoesnotexist").status == status_file_not_found); +} +TEST(document_load_file_out_of_memory) +{ test_runner::_memory_fail_threshold = 1; + + pugi::xml_document doc; CHECK_ALLOC_FAIL(CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory)); } +TEST(document_load_file_out_of_memory_file_leak) +{ + test_runner::_memory_fail_threshold = 1; + + pugi::xml_document doc; + + for (int i = 0; i < 256; ++i) + { + CHECK_ALLOC_FAIL(CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory)); + } + + test_runner::_memory_fail_threshold = 0; + + CHECK(doc.load_file("tests/data/small.xml")); + CHECK_NODE(doc, STR("")); +} + TEST(document_load_file_error_previous) { pugi::xml_document doc; -- cgit v1.2.3 From a0d065cd22d1d43c417f6d3db88a04bf57b67ed0 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 12 Apr 2015 03:03:56 -0700 Subject: Implment copyless copy for attributes Previously attributes that were copied with their node used string sharing, but standalone attributes that were copied using xml_node::*_copy(xml_attribute) were not. --- src/pugixml.cpp | 57 +++++++++++++++++++++++++++++++----------- tests/test_dom_modify.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 16 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 619cc7b..65854e7 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -557,11 +557,11 @@ PUGI__NS_BEGIN xml_extra_buffer* extra_buffers; }; - inline xml_allocator& get_allocator(const xml_node_struct* node) + template inline xml_allocator& get_allocator(const Object* object) { - assert(node); + assert(object); - return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator; + return *reinterpret_cast(object->header & xml_memory_page_pointer_mask)->allocator; } template inline xml_document_struct& get_document(const Object* object) @@ -3824,6 +3824,15 @@ PUGI__NS_BEGIN } } + PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) + { + xml_allocator& alloc = get_allocator(da); + xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0; + + node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); + node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + } + inline bool is_text_node(xml_node_struct* node) { xml_node_type type = PUGI__NODETYPE(node); @@ -4986,41 +4995,59 @@ namespace pugi PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); - xml_attribute result = append_attribute(proto.name()); - result.set_value(proto.value()); + xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); + if (!a) return xml_attribute(); - return result; + impl::append_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; } PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); - xml_attribute result = prepend_attribute(proto.name()); - result.set_value(proto.value()); + xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); + if (!a) return xml_attribute(); - return result; + impl::prepend_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; } PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); - xml_attribute result = insert_attribute_after(proto.name(), attr); - result.set_value(proto.value()); + xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); + if (!a) return xml_attribute(); - return result; + impl::insert_attribute_after(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; } PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); + if (!a) return xml_attribute(); - xml_attribute result = insert_attribute_before(proto.name(), attr); - result.set_value(proto.value()); + impl::insert_attribute_before(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); - return result; + return a; } PUGI__FN xml_node xml_node::append_child(xml_node_type type_) diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index fc6dd59..41120e5 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -771,6 +771,14 @@ TEST_XML(dom_node_copy_crossdoc, "") CHECK_NODE(newdoc, STR("")); } +TEST_XML(dom_node_copy_crossdoc_attribute, "") +{ + xml_document newdoc; + newdoc.append_child(STR("copy")).append_copy(doc.child(STR("node")).attribute(STR("attr"))); + CHECK_NODE(doc, STR("")); + CHECK_NODE(newdoc, STR("")); +} + TEST_XML_FLAGS(dom_node_copy_types, "pcdata", parse_full) { doc.append_copy(doc.child(STR("root"))); @@ -1409,7 +1417,7 @@ TEST(dom_node_copy_copyless_mix) CHECK_NODE(copy2, dataxml.c_str()); } -TEST_XML(dom_node_copyless_taint, "") +TEST_XML(dom_node_copy_copyless_taint, "") { xml_node node = doc.child(STR("node")); xml_node copy = doc.append_copy(node); @@ -1433,6 +1441,59 @@ TEST_XML(dom_node_copyless_taint, "") CHECK_NODE(doc, STR("")); } +TEST(dom_node_copy_attribute_copyless) +{ + std::basic_string data; + data += STR(""); + + std::basic_string datacopy = data; + + // the document is parsed in-place so there should only be 1 page worth of allocations + test_runner::_memory_fail_threshold = 32768 + 128; + + xml_document doc; + CHECK(doc.load_buffer_inplace(&datacopy[0], datacopy.size() * sizeof(char_t), parse_full)); + + // this copy should share all string storage; since there are not a lot of nodes we should not have *any* allocations here (everything will fit in the same page in the document) + xml_node copy1 = doc.append_child(STR("node")); + copy1.append_copy(doc.first_child().first_attribute()); + + xml_node copy2 = doc.append_child(STR("node")); + copy2.append_copy(copy1.first_attribute()); + + CHECK_NODE(copy1, data.c_str()); + CHECK_NODE(copy2, data.c_str()); +} + +TEST_XML(dom_node_copy_attribute_copyless_taint, "") +{ + xml_node node = doc.child(STR("node")); + xml_attribute attr = node.first_attribute(); + + xml_node copy1 = doc.append_child(STR("copy1")); + xml_node copy2 = doc.append_child(STR("copy2")); + xml_node copy3 = doc.append_child(STR("copy3")); + + CHECK_NODE(doc, STR("")); + + copy1.append_copy(attr); + + CHECK_NODE(doc, STR("")); + + attr.set_name(STR("att1")); + copy2.append_copy(attr); + + CHECK_NODE(doc, STR("")); + + copy1.first_attribute().set_value(STR("valu2")); + copy3.append_copy(copy1.first_attribute()); + + CHECK_NODE(doc, STR("")); +} + TEST_XML(dom_node_copy_out_of_memory_node, "text1text2") { test_runner::_memory_fail_threshold = 32768 * 2 + 4096; -- cgit v1.2.3