1 2 // Copyright 2006-2009 Daniel James. 3 // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 #if !defined(BOOST_UNORDERED_EXCEPTION_TEST_HEADER) 7 #define BOOST_UNORDERED_EXCEPTION_TEST_HEADER 8 9 #include "./count.hpp" 10 #include "./test.hpp" 11 12 #include <boost/preprocessor/cat.hpp> 13 #include <boost/preprocessor/seq/elem.hpp> 14 #include <boost/preprocessor/seq/for_each_product.hpp> 15 16 #define UNORDERED_EXCEPTION_TEST_CASE(name, test_func, type) \ 17 UNORDERED_AUTO_TEST (name) { \ 18 test_func<type> fixture; \ 19 ::test::lightweight::exception_safety( \ 20 fixture, BOOST_STRINGIZE(test_func<type>)); \ 21 } 22 23 #define UNORDERED_EXCEPTION_TEST_CASE_REPEAT(name, test_func, n, type) \ 24 UNORDERED_AUTO_TEST (name) { \ 25 for (unsigned i = 0; i < n; ++i) { \ 26 test_func<type> fixture; \ 27 ::test::lightweight::exception_safety( \ 28 fixture, BOOST_STRINGIZE(test_func<type>)); \ 29 } \ 30 } 31 32 #define UNORDERED_EPOINT_IMPL ::test::lightweight::epoint 33 34 #define UNORDERED_EXCEPTION_TEST_POSTFIX RUN_TESTS() 35 36 #define EXCEPTION_TESTS(test_seq, param_seq) \ 37 BOOST_PP_SEQ_FOR_EACH_PRODUCT(EXCEPTION_TESTS_OP, (test_seq)((1))(param_seq)) 38 39 #define EXCEPTION_TESTS_REPEAT(n, test_seq, param_seq) \ 40 BOOST_PP_SEQ_FOR_EACH_PRODUCT(EXCEPTION_TESTS_OP, (test_seq)((n))(param_seq)) 41 42 #define EXCEPTION_TESTS_OP(r, product) \ 43 UNORDERED_EXCEPTION_TEST_CASE_REPEAT( \ 44 BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product), \ 45 BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(2, product))), \ 46 BOOST_PP_SEQ_ELEM(0, product), BOOST_PP_SEQ_ELEM(1, product), \ 47 BOOST_PP_SEQ_ELEM(2, product)) 48 49 #define UNORDERED_SCOPE(scope_name) \ 50 for (::test::scope_guard unordered_test_guard(BOOST_STRINGIZE(scope_name)); \ 51 !unordered_test_guard.dismissed(); unordered_test_guard.dismiss()) 52 53 #define UNORDERED_EPOINT(name) \ 54 if (::test::exceptions_enabled) { \ 55 UNORDERED_EPOINT_IMPL(name); \ 56 } 57 58 #define ENABLE_EXCEPTIONS \ 59 ::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(true) 60 61 #define DISABLE_EXCEPTIONS \ 62 ::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(false) 63 64 namespace test { 65 static char const* scope = ""; 66 bool exceptions_enabled = false; 67 68 class scope_guard 69 { 70 scope_guard& operator=(scope_guard const&); 71 scope_guard(scope_guard const&); 72 73 char const* old_scope_; 74 char const* scope_; 75 bool dismissed_; 76 77 public: scope_guard(char const * name)78 scope_guard(char const* name) 79 : old_scope_(scope), scope_(name), dismissed_(false) 80 { 81 scope = scope_; 82 } 83 ~scope_guard()84 ~scope_guard() 85 { 86 if (dismissed_) 87 scope = old_scope_; 88 } 89 dismiss()90 void dismiss() { dismissed_ = true; } 91 dismissed() const92 bool dismissed() const { return dismissed_; } 93 }; 94 95 class exceptions_enable 96 { 97 exceptions_enable& operator=(exceptions_enable const&); 98 exceptions_enable(exceptions_enable const&); 99 100 bool old_value_; 101 bool released_; 102 103 public: exceptions_enable(bool enable)104 exceptions_enable(bool enable) 105 : old_value_(exceptions_enabled), released_(false) 106 { 107 exceptions_enabled = enable; 108 } 109 ~exceptions_enable()110 ~exceptions_enable() 111 { 112 if (!released_) { 113 exceptions_enabled = old_value_; 114 released_ = true; 115 } 116 } 117 release()118 void release() 119 { 120 if (!released_) { 121 exceptions_enabled = old_value_; 122 released_ = true; 123 } 124 } 125 }; 126 127 struct exception_base 128 { 129 struct data_type 130 { 131 }; 132 struct strong_type 133 { storetest::exception_base::strong_type134 template <class T> void store(T const&) {} testtest::exception_base::strong_type135 template <class T> void test(T const&) const {} 136 }; inittest::exception_base137 data_type init() const { return data_type(); } BOOST_PREVENT_MACRO_SUBSTITUTIONtest::exception_base138 void check BOOST_PREVENT_MACRO_SUBSTITUTION() const {} 139 }; 140 141 template <class T, class P1, class P2, class T2> call_ignore_extra_parameters(void (T::* fn)()const,T2 const & obj,P1 &,P2 &)142 inline void call_ignore_extra_parameters( 143 void (T::*fn)() const, T2 const& obj, P1&, P2&) 144 { 145 (obj.*fn)(); 146 } 147 148 template <class T, class P1, class P2, class T2> call_ignore_extra_parameters(void (T::* fn)(P1 &)const,T2 const & obj,P1 & p1,P2 &)149 inline void call_ignore_extra_parameters( 150 void (T::*fn)(P1&) const, T2 const& obj, P1& p1, P2&) 151 { 152 (obj.*fn)(p1); 153 } 154 155 template <class T, class P1, class P2, class T2> call_ignore_extra_parameters(void (T::* fn)(P1 &,P2 &)const,T2 const & obj,P1 & p1,P2 & p2)156 inline void call_ignore_extra_parameters( 157 void (T::*fn)(P1&, P2&) const, T2 const& obj, P1& p1, P2& p2) 158 { 159 (obj.*fn)(p1, p2); 160 } 161 constant(T const & x)162 template <class T> T const& constant(T const& x) { return x; } 163 164 template <class Test> class test_runner 165 { 166 Test const& test_; 167 bool exception_in_check_; 168 169 test_runner(test_runner const&); 170 test_runner& operator=(test_runner const&); 171 172 public: test_runner(Test const & t)173 test_runner(Test const& t) : test_(t), exception_in_check_(false) {} run()174 void run() 175 { 176 DISABLE_EXCEPTIONS; 177 test::check_instances check; 178 test::scope = ""; 179 typename Test::data_type x(test_.init()); 180 typename Test::strong_type strong; 181 strong.store(x); 182 try { 183 ENABLE_EXCEPTIONS; 184 call_ignore_extra_parameters<Test, typename Test::data_type, 185 typename Test::strong_type>(&Test::run, test_, x, strong); 186 } catch (...) { 187 try { 188 DISABLE_EXCEPTIONS; 189 call_ignore_extra_parameters<Test, typename Test::data_type const, 190 typename Test::strong_type const>( 191 &Test::check, test_, constant(x), constant(strong)); 192 } catch (...) { 193 exception_in_check_ = true; 194 } 195 throw; 196 } 197 } end()198 void end() 199 { 200 if (exception_in_check_) { 201 BOOST_ERROR("Unexcpected exception in test_runner check call."); 202 } 203 } 204 }; 205 206 // Quick exception testing based on lightweight test 207 208 namespace lightweight { 209 static int iteration; 210 static int count; 211 212 struct test_exception 213 { 214 char const* name; test_exceptiontest::lightweight::test_exception215 test_exception(char const* n) : name(n) {} 216 }; 217 218 struct test_failure 219 { 220 }; 221 epoint(char const * name)222 void epoint(char const* name) 223 { 224 ++count; 225 if (count == iteration) { 226 throw test_exception(name); 227 } 228 } 229 230 template <class Test> exception_safety(Test const & f,char const *)231 void exception_safety(Test const& f, char const* /*name*/) 232 { 233 test_runner<Test> runner(f); 234 235 iteration = 0; 236 bool success = false; 237 unsigned int failure_count = 0; 238 char const* error_msg = 0; 239 do { 240 int error_count = boost::detail::test_errors(); 241 ++iteration; 242 count = 0; 243 244 try { 245 runner.run(); 246 success = true; 247 } catch (test_failure) { 248 error_msg = "test_failure caught."; 249 break; 250 } catch (test_exception e) { 251 if (error_count != boost::detail::test_errors()) { 252 BOOST_LIGHTWEIGHT_TEST_OSTREAM 253 << "Iteration: " << iteration 254 << " Error found for epoint: " << e.name << std::endl; 255 } 256 } catch (...) { 257 error_msg = "Unexpected exception."; 258 break; 259 } 260 261 if (error_count != boost::detail::test_errors()) { 262 ++failure_count; 263 } 264 } while (!success && failure_count < 5); 265 266 if (error_msg) { 267 BOOST_ERROR(error_msg); 268 } 269 runner.end(); 270 } 271 272 // 273 // An alternative way to run exception tests. 274 // See merge_exception_tests.cpp for an example. 275 276 struct exception_looper 277 { 278 bool success; 279 unsigned int failure_count; 280 char const* error_msg; 281 int error_count; 282 exception_loopertest::lightweight::exception_looper283 exception_looper() : success(false), failure_count(0), error_msg(0) {} 284 starttest::lightweight::exception_looper285 void start() { iteration = 0; } 286 loop_conditiontest::lightweight::exception_looper287 bool loop_condition() const 288 { 289 return !error_msg && !success && failure_count < 5; 290 } 291 start_iterationtest::lightweight::exception_looper292 void start_iteration() 293 { 294 error_count = boost::detail::test_errors(); 295 ++iteration; 296 count = 0; 297 } 298 successful_runtest::lightweight::exception_looper299 void successful_run() { success = true; } 300 test_failure_caughttest::lightweight::exception_looper301 void test_failure_caught(test_failure const&) 302 { 303 error_msg = "test_failure caught."; 304 } 305 test_exception_caughttest::lightweight::exception_looper306 void test_exception_caught(test_exception const& e) 307 { 308 if (error_count != boost::detail::test_errors()) { 309 BOOST_LIGHTWEIGHT_TEST_OSTREAM 310 << "Iteration: " << iteration 311 << " Error found for epoint: " << e.name << std::endl; 312 } 313 } 314 unexpected_exception_caughttest::lightweight::exception_looper315 void unexpected_exception_caught() 316 { 317 error_msg = "Unexpected exception."; 318 } 319 endtest::lightweight::exception_looper320 void end() 321 { 322 if (error_msg) { 323 BOOST_ERROR(error_msg); 324 } 325 } 326 }; 327 328 #define EXCEPTION_LOOP(op) \ 329 test::lightweight::exception_looper looper; \ 330 looper.start(); \ 331 while (looper.loop_condition()) { \ 332 looper.start_iteration(); \ 333 try { \ 334 op; \ 335 looper.successful_run(); \ 336 } catch (test::lightweight::test_failure e) { \ 337 looper.test_failure_caught(e); \ 338 } catch (test::lightweight::test_exception e) { \ 339 looper.test_exception_caught(e); \ 340 } catch (...) { \ 341 looper.unexpected_exception_caught(); \ 342 } \ 343 } \ 344 looper.end(); 345 } 346 } 347 348 #endif 349