From 5edeaf67658a3ab27e9ace87ccff37aba8352607 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 12 Apr 2015 21:27:12 -0700 Subject: tests: Add more out of memory tests Also add tests that verify save_file for absence of FILE leaks. --- tests/test.hpp | 4 ++-- tests/test_document.cpp | 37 +++++++++++++++++++++++++++++++++++-- tests/test_parse.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/test.hpp b/tests/test.hpp index 46c3330..d0fd0ca 100644 --- a/tests/test.hpp +++ b/tests/test.hpp @@ -143,9 +143,9 @@ struct dummy_fixture {}; #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 +#define CHECK_ALLOC_FAIL(code) do { CHECK(!test_runner::_memory_fail_triggered); code; CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false; } while (test_runner::_memory_fail_triggered) #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 +#define CHECK_ALLOC_FAIL(code) do { CHECK(!test_runner::_memory_fail_triggered); try { code; } catch (std::bad_alloc&) {} CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false; } while (test_runner::_memory_fail_triggered) #endif #define STR(text) PUGIXML_TEXT(text) diff --git a/tests/test_document.cpp b/tests/test_document.cpp index 1545e19..9c8c860 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -319,9 +319,7 @@ TEST(document_load_file_out_of_memory_file_leak) 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; @@ -329,6 +327,21 @@ TEST(document_load_file_out_of_memory_file_leak) CHECK_NODE(doc, STR("")); } +TEST(document_load_file_wide_out_of_memory_file_leak) +{ + test_runner::_memory_fail_threshold = 256; + + pugi::xml_document doc; + + for (int i = 0; i < 256; ++i) + CHECK_ALLOC_FAIL(CHECK(doc.load_file(L"tests/data/small.xml").status == status_out_of_memory)); + + test_runner::_memory_fail_threshold = 0; + + CHECK(doc.load_file(L"tests/data/small.xml")); + CHECK_NODE(doc, STR("")); +} + TEST(document_load_file_error_previous) { pugi::xml_document doc; @@ -556,6 +569,26 @@ TEST_XML(document_save_file_wide_text, "") CHECK(test_file_contents(f.path, "\n", 9)); } +TEST_XML(document_save_file_leak, "") +{ + temp_file f; + + for (int i = 0; i < 256; ++i) + CHECK(doc.save_file(f.path)); +} + +TEST_XML(document_save_file_wide_leak, "") +{ + temp_file f; + + // widen the path + wchar_t wpath[sizeof(f.path)]; + std::copy(f.path, f.path + strlen(f.path) + 1, wpath + 0); + + for (int i = 0; i < 256; ++i) + CHECK(doc.save_file(wpath)); +} + TEST(document_load_buffer) { const pugi::char_t text[] = STR(""); diff --git a/tests/test_parse.cpp b/tests/test_parse.cpp index 08ddee4..393e14c 100644 --- a/tests/test_parse.cpp +++ b/tests/test_parse.cpp @@ -935,6 +935,31 @@ TEST(parse_out_of_memory_conversion) CHECK(!doc.first_child()); } +TEST(parse_out_of_memory_allocator_state_sync) +{ + const unsigned int count = 10000; + static char_t text[count * 4]; + + for (unsigned int i = 0; i < count; ++i) + { + text[4*i + 0] = '<'; + text[4*i + 1] = 'n'; + text[4*i + 2] = '/'; + text[4*i + 3] = '>'; + } + + test_runner::_memory_fail_threshold = 65536; + + xml_document doc; + CHECK_ALLOC_FAIL(CHECK(doc.load_buffer_inplace(text, count * 4).status == status_out_of_memory)); + CHECK_NODE(doc.first_child(), STR("")); + + test_runner::_memory_fail_threshold = 0; + + for (unsigned int i = 0; i < count; ++i) + CHECK(doc.append_child(STR("n"))); +} + 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 f04b56e178a93960c89c5ca1b7d6ebdd19416cb8 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 12 Apr 2015 21:46:48 -0700 Subject: Permit custom allocation function to throw Ensure that all the necessary cleanup is performed in case the allocation fails with an exception - files are closed, buffers are reclaimed, etc. Any test that triggers a simulated out-of-memory condition is ran once again with a throwing allocation function. Unobserved std::bad_alloc count as test failures and require CHECK_ALLOC_FAIL macro. Fixes #17. --- tests/main.cpp | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/main.cpp b/tests/main.cpp index 6996eb9..00016a6 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -50,6 +50,18 @@ static void* custom_allocate(size_t size) } } +#ifndef PUGIXML_NO_EXCEPTIONS +static void* custom_allocate_throw(size_t size) +{ + void* result = custom_allocate(size); + + if (!result) + throw std::bad_alloc(); + + return result; +} +#endif + static void custom_deallocate(void* ptr) { assert(ptr); @@ -82,7 +94,7 @@ namespace std } #endif -static bool run_test(test_runner* test) +static bool run_test(test_runner* test, const char* test_name, pugi::allocation_function allocate) { #ifndef PUGIXML_NO_EXCEPTIONS try @@ -94,7 +106,7 @@ static bool run_test(test_runner* test) test_runner::_memory_fail_threshold = 0; test_runner::_memory_fail_triggered = false; - pugi::set_memory_management_functions(custom_allocate, custom_deallocate); + pugi::set_memory_management_functions(allocate, custom_deallocate); #ifdef _MSC_VER # pragma warning(push) @@ -110,7 +122,7 @@ static bool run_test(test_runner* test) if (result) { - printf("Test %s failed: %s\n", test->_name, test_runner::_failure_message); + printf("Test %s failed: %s\n", test_name, test_runner::_failure_message); return false; } @@ -118,13 +130,13 @@ static bool run_test(test_runner* test) if (test_runner::_memory_fail_triggered) { - printf("Test %s failed: unguarded memory fail triggered\n", test->_name); + 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)); + 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)); return false; } @@ -133,12 +145,12 @@ static bool run_test(test_runner* test) } catch (const std::exception& e) { - printf("Test %s failed: exception %s\n", test->_name, e.what()); + printf("Test %s failed: exception %s\n", test_name, e.what()); return false; } catch (...) { - printf("Test %s failed for unknown reason\n", test->_name); + printf("Test %s failed for unknown reason\n", test_name); return false; } #endif @@ -176,7 +188,15 @@ int main(int, char** argv) for (test = test_runner::_tests; test; test = test->_next) { total++; - passed += run_test(test); + passed += run_test(test, test->_name, custom_allocate); + + #ifndef PUGIXML_NO_EXCEPTIONS + if (g_memory_fail_triggered) + { + total++; + passed += run_test(test, (test->_name + std::string(" (throw)")).c_str(), custom_allocate_throw); + } + #endif } unsigned int failed = total - passed; -- cgit v1.2.3 From 9539c488c29e7c2c06afa63abce70785cb5d15c5 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 12 Apr 2015 22:06:17 -0700 Subject: Fix unused variable warning Also fix test in wchar_t mode. --- tests/test_dom_modify.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index 41120e5..f2877ff 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -905,7 +905,8 @@ TEST(dom_node_out_of_memory) xml_attribute a = n.append_attribute(STR("a")); CHECK(a); - CHECK_ALLOC_FAIL(while (n.append_child(node_comment) || n.append_attribute(STR("b"))) { /* nop */ }); + CHECK_ALLOC_FAIL(while (n.append_child(node_comment)) { /* nop */ }); + CHECK_ALLOC_FAIL(while (n.append_attribute(STR("b"))) { /* nop */ }); // verify all node modification operations CHECK_ALLOC_FAIL(CHECK(!n.append_child())); -- cgit v1.2.3