diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 25d1cde4..e06b569f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -92,4 +92,5 @@ test-suite unordered-exception [ run exception/rehash_exception_tests.cpp framework ] [ run exception/swap_exception_tests.cpp framework : : : BOOST_UNORDERED_SWAP_METHOD=2 ] + [ run exception/merge_exception_tests.cpp framework ] ; diff --git a/test/exception/merge_exception_tests.cpp b/test/exception/merge_exception_tests.cpp new file mode 100644 index 00000000..496c01e7 --- /dev/null +++ b/test/exception/merge_exception_tests.cpp @@ -0,0 +1,108 @@ +#include "../helpers/exception_test.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/metafunctions.hpp" +#include "../helpers/random_values.hpp" +#include "./containers.hpp" + +template void merge_exception_test(T1 x, T2 y) +{ + std::size_t size = x.size() + y.size(); + + try { + ENABLE_EXCEPTIONS; + x.merge(y); + } catch (...) { + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); + throw; + } + + // Not a full check, just want to make sure the merge completed. + BOOST_TEST(size == x.size() + y.size()); + if (y.size()) { + BOOST_TEST(test::has_unique_keys::value); + for (typename T2::iterator it = y.begin(); it != y.end(); ++it) { + BOOST_TEST(x.find(test::get_key(*it)) != x.end()); + } + } + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); +} + +template +void merge_exception_test(T1 const*, T2 const*, std::size_t count1, + std::size_t count2, int tag1, int tag2, test::random_generator gen1, + test::random_generator gen2) +{ + test::random_values v1(count1, gen1); + test::random_values v2(count2, gen2); + T1 x(v1.begin(), v1.end(), 0, test::exception::hash(tag1), + test::exception::equal_to(tag1)); + T2 y(v2.begin(), v2.end(), 0, test::exception::hash(tag2), + test::exception::equal_to(tag2)); + + EXCEPTION_LOOP(merge_exception_test(x, y)) +} + +boost::unordered_set >* test_set_; +boost::unordered_multiset >* test_multiset_; +boost::unordered_map >* test_map_; +boost::unordered_multimap >* test_multimap_; + +using test::default_generator; +using test::generate_collisions; +using test::limited_range; + +// clang-format off +UNORDERED_TEST(merge_exception_test, + ((test_set_)(test_multiset_)) + ((test_set_)(test_multiset_)) + ((0)(10)(100)) + ((0)(10)(100)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(limited_range)) + ((default_generator)(limited_range)) +) +UNORDERED_TEST(merge_exception_test, + ((test_map_)(test_multimap_)) + ((test_map_)(test_multimap_)) + ((0)(10)(100)) + ((0)(10)(100)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(limited_range)) + ((default_generator)(limited_range)) +) +// Run fewer generate_collisions tests, as they're slow. +UNORDERED_TEST(merge_exception_test, + ((test_set_)(test_multiset_)) + ((test_set_)(test_multiset_)) + ((10)) + ((10)) + ((0)(1)(2)) + ((0)(1)(2)) + ((generate_collisions)) + ((generate_collisions)) +) +UNORDERED_TEST(merge_exception_test, + ((test_map_)(test_multimap_)) + ((test_map_)(test_multimap_)) + ((10)) + ((10)) + ((0)(1)(2)) + ((0)(1)(2)) + ((generate_collisions)) + ((generate_collisions)) +) +// clang-format on + +RUN_TESTS() diff --git a/test/helpers/exception_test.hpp b/test/helpers/exception_test.hpp index 842de8b1..7edf8848 100644 --- a/test/helpers/exception_test.hpp +++ b/test/helpers/exception_test.hpp @@ -275,6 +275,77 @@ template void exception_safety(Test const& f, char const* /*name*/) } runner.end(); } + +// +// An alternative way to run exception tests. +// See merge_exception_tests.cpp for an example. + +struct exception_looper +{ + bool success; + unsigned int failure_count; + char const* error_msg; + int error_count; + + exception_looper() : success(false), failure_count(0), error_msg(0) {} + + void start() { iteration = 0; } + + bool loop_condition() const + { + return !error_msg && !success && failure_count < 5; + } + + void start_iteration() + { + error_count = boost::detail::test_errors(); + ++iteration; + count = 0; + } + + void successful_run() { success = true; } + + void test_failure_caught(test_failure const&) + { + error_msg = "test_failure caught."; + } + + void test_exception_caught(test_exception const& e) + { + if (error_count != boost::detail::test_errors()) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << "Iteration: " << iteration + << " Error found for epoint: " << e.name << std::endl; + } + } + + void unexpected_exception_caught() { error_msg = "Unexpected exception."; } + + void end() + { + if (error_msg) { + BOOST_ERROR(error_msg); + } + } +}; + +#define EXCEPTION_LOOP(op) \ + test::lightweight::exception_looper looper; \ + looper.start(); \ + while (looper.loop_condition()) { \ + looper.start_iteration(); \ + try { \ + op; \ + looper.successful_run(); \ + } catch (test::lightweight::test_failure e) { \ + looper.test_failure_caught(e); \ + } catch (test::lightweight::test_exception e) { \ + looper.test_exception_caught(e); \ + } catch (...) { \ + looper.unexpected_exception_caught(); \ + } \ + } \ + looper.end(); } } diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index 8471bd29..01b39ee3 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -10,8 +10,9 @@ #include "../helpers/count.hpp" #include "../helpers/fwd.hpp" +#include "../helpers/generators.hpp" #include "../helpers/memory.hpp" -#include "../objects/fwd.hpp" +#include "./fwd.hpp" #include #include #include