#include "test.hpp" #include "allocator.hpp" #include #include #include #include #include #ifndef PUGIXML_NO_EXCEPTIONS # include #endif #ifdef _WIN32_WCE # undef DebugBreak # pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union # include #endif 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; 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; } else { void* ptr = memory_allocate(size); if (!ptr) return 0; g_memory_total_size += memory_size(ptr); g_memory_total_count++; return ptr; } } #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); g_memory_total_size -= memory_size(ptr); g_memory_total_count--; memory_deallocate(ptr); } static void replace_memory_management() { // create some document to touch original functions { pugi::xml_document doc; doc.append_child().set_name(STR("node")); } // replace functions pugi::set_memory_management_functions(custom_allocate, custom_deallocate); } #if defined(_MSC_VER) && _MSC_VER > 1200 && _MSC_VER < 1400 && !defined(__INTEL_COMPILER) && !defined(__DMC__) #include namespace std { _CRTIMP2 _Prhand _Raise_handler; _CRTIMP2 void __cdecl _Throw(const exception&) {} } #endif static bool run_test(test_runner* test, const char* test_name, pugi::allocation_function allocate) { #ifndef PUGIXML_NO_EXCEPTIONS try { #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; pugi::set_memory_management_functions(allocate, custom_deallocate); #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4611) // interaction between _setjmp and C++ object destruction is non-portable # pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged #endif volatile int result = setjmp(test_runner::_failure_buffer); #ifdef _MSC_VER # pragma warning(pop) #endif if (result) { printf("Test %s failed: %s\n", test_name, test_runner::_failure_message); return false; } 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)); return false; } return true; #ifndef PUGIXML_NO_EXCEPTIONS } catch (const std::exception& e) { printf("Test %s failed: exception %s\n", test_name, e.what()); return false; } catch (...) { printf("Test %s failed for unknown reason\n", test_name); return false; } #endif } #if defined(__CELLOS_LV2__) && defined(PUGIXML_NO_EXCEPTIONS) && !defined(__SNC__) #include void std::exception::_Raise() const { abort(); } #endif int main(int, char** argv) { #ifdef __BORLANDC__ _control87(MCW_EM | PC_53, MCW_EM | MCW_PC); #endif // setup temp path as the executable folder std::string temp = argv[0]; std::string::size_type slash = temp.find_last_of("\\/"); temp.erase((slash != std::string::npos) ? slash + 1 : 0); test_runner::_temp_path = temp.c_str(); replace_memory_management(); unsigned int total = 0; unsigned int passed = 0; test_runner* test = 0; // gcc3 "variable might be used uninitialized in this function" bug workaround for (test = test_runner::_tests; test; test = test->_next) { total++; passed += run_test(test, test->_name, custom_allocate); if (g_memory_fail_triggered) { // run tests that trigger memory failures twice - with an allocator that returns NULL and with an allocator that throws #ifndef PUGIXML_NO_EXCEPTIONS total++; passed += run_test(test, (test->_name + std::string(" (throw)")).c_str(), custom_allocate_throw); #endif } } unsigned int failed = total - passed; if (failed != 0) printf("FAILURE: %u out of %u tests failed.\n", failed, total); else printf("Success: %u tests passed.\n", total); return failed; } #ifdef _WIN32_WCE int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { return main(0, NULL); } #endif