]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
b32b8144 | 9 | #include "./count.hpp" |
7c673cae FG |
10 | #include "./test.hpp" |
11 | ||
7c673cae | 12 | #include <boost/preprocessor/cat.hpp> |
b32b8144 FG |
13 | #include <boost/preprocessor/seq/elem.hpp> |
14 | #include <boost/preprocessor/seq/for_each_product.hpp> | |
7c673cae | 15 | |
b32b8144 FG |
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 | } | |
7c673cae | 22 | |
b32b8144 FG |
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 | } | |
7c673cae | 31 | |
b32b8144 | 32 | #define UNORDERED_EPOINT_IMPL ::test::lightweight::epoint |
7c673cae FG |
33 | |
34 | #define UNORDERED_EXCEPTION_TEST_POSTFIX RUN_TESTS() | |
35 | ||
b32b8144 FG |
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) | |
7c673cae FG |
63 | |
64 | namespace test { | |
b32b8144 FG |
65 | static char const* scope = ""; |
66 | bool exceptions_enabled = false; | |
7c673cae | 67 | |
b32b8144 FG |
68 | class scope_guard |
69 | { | |
70 | scope_guard& operator=(scope_guard const&); | |
71 | scope_guard(scope_guard const&); | |
7c673cae | 72 | |
b32b8144 FG |
73 | char const* old_scope_; |
74 | char const* scope_; | |
75 | bool dismissed_; | |
7c673cae | 76 | |
b32b8144 FG |
77 | public: |
78 | scope_guard(char const* name) | |
79 | : old_scope_(scope), scope_(name), dismissed_(false) | |
80 | { | |
81 | scope = scope_; | |
82 | } | |
7c673cae | 83 | |
b32b8144 | 84 | ~scope_guard() |
7c673cae | 85 | { |
b32b8144 FG |
86 | if (dismissed_) |
87 | scope = old_scope_; | |
88 | } | |
7c673cae | 89 | |
b32b8144 | 90 | void dismiss() { dismissed_ = true; } |
7c673cae | 91 | |
b32b8144 FG |
92 | bool dismissed() const { return dismissed_; } |
93 | }; | |
7c673cae | 94 | |
b32b8144 FG |
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: | |
104 | exceptions_enable(bool enable) | |
105 | : old_value_(exceptions_enabled), released_(false) | |
106 | { | |
107 | exceptions_enabled = enable; | |
108 | } | |
7c673cae | 109 | |
b32b8144 | 110 | ~exceptions_enable() |
7c673cae | 111 | { |
b32b8144 FG |
112 | if (!released_) { |
113 | exceptions_enabled = old_value_; | |
114 | released_ = true; | |
115 | } | |
7c673cae FG |
116 | } |
117 | ||
b32b8144 | 118 | void release() |
7c673cae | 119 | { |
b32b8144 FG |
120 | if (!released_) { |
121 | exceptions_enabled = old_value_; | |
122 | released_ = true; | |
123 | } | |
7c673cae | 124 | } |
b32b8144 | 125 | }; |
7c673cae | 126 | |
b32b8144 FG |
127 | struct exception_base |
128 | { | |
129 | struct data_type | |
130 | { | |
131 | }; | |
132 | struct strong_type | |
7c673cae | 133 | { |
b32b8144 FG |
134 | template <class T> void store(T const&) {} |
135 | template <class T> void test(T const&) const {} | |
136 | }; | |
137 | data_type init() const { return data_type(); } | |
138 | void check BOOST_PREVENT_MACRO_SUBSTITUTION() const {} | |
139 | }; | |
140 | ||
141 | template <class T, class P1, class P2, class T2> | |
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> | |
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> | |
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 | ||
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: | |
173 | test_runner(Test const& t) : test_(t), exception_in_check_(false) {} | |
174 | void run() | |
175 | { | |
176 | DISABLE_EXCEPTIONS; | |
177 | test::check_instances check; | |
178 | test::scope = ""; | |
179 | BOOST_DEDUCED_TYPENAME Test::data_type x(test_.init()); | |
180 | BOOST_DEDUCED_TYPENAME Test::strong_type strong; | |
181 | strong.store(x); | |
182 | try { | |
183 | ENABLE_EXCEPTIONS; | |
184 | call_ignore_extra_parameters<Test, | |
185 | BOOST_DEDUCED_TYPENAME Test::data_type, | |
186 | BOOST_DEDUCED_TYPENAME Test::strong_type>( | |
187 | &Test::run, test_, x, strong); | |
188 | } catch (...) { | |
189 | try { | |
190 | DISABLE_EXCEPTIONS; | |
191 | call_ignore_extra_parameters<Test, | |
192 | BOOST_DEDUCED_TYPENAME Test::data_type const, | |
193 | BOOST_DEDUCED_TYPENAME Test::strong_type const>( | |
194 | &Test::check, test_, constant(x), constant(strong)); | |
195 | } catch (...) { | |
196 | exception_in_check_ = true; | |
197 | } | |
198 | throw; | |
199 | } | |
7c673cae | 200 | } |
b32b8144 FG |
201 | void end() |
202 | { | |
203 | if (exception_in_check_) { | |
204 | BOOST_ERROR("Unexcpected exception in test_runner check call."); | |
205 | } | |
206 | } | |
207 | }; | |
208 | ||
209 | // Quick exception testing based on lightweight test | |
210 | ||
211 | namespace lightweight { | |
212 | static int iteration; | |
213 | static int count; | |
214 | ||
215 | struct test_exception | |
216 | { | |
217 | char const* name; | |
218 | test_exception(char const* n) : name(n) {} | |
219 | }; | |
220 | ||
221 | struct test_failure | |
222 | { | |
223 | }; | |
7c673cae | 224 | |
b32b8144 FG |
225 | void epoint(char const* name) |
226 | { | |
227 | ++count; | |
228 | if (count == iteration) { | |
229 | throw test_exception(name); | |
230 | } | |
7c673cae FG |
231 | } |
232 | ||
233 | template <class Test> | |
b32b8144 | 234 | void exception_safety(Test const& f, char const* /*name*/) |
7c673cae | 235 | { |
b32b8144 FG |
236 | test_runner<Test> runner(f); |
237 | ||
238 | iteration = 0; | |
239 | bool success = false; | |
240 | unsigned int failure_count = 0; | |
241 | char const* error_msg = 0; | |
242 | do { | |
243 | int error_count = boost::detail::test_errors(); | |
244 | ++iteration; | |
245 | count = 0; | |
246 | ||
247 | try { | |
248 | runner.run(); | |
249 | success = true; | |
250 | } catch (test_failure) { | |
251 | error_msg = "test_failure caught."; | |
252 | break; | |
253 | } catch (test_exception e) { | |
254 | if (error_count != boost::detail::test_errors()) { | |
255 | BOOST_LIGHTWEIGHT_TEST_OSTREAM | |
256 | << "Iteration: " << iteration | |
257 | << " Error found for epoint: " << e.name << std::endl; | |
258 | } | |
259 | } catch (...) { | |
260 | error_msg = "Unexpected exception."; | |
261 | break; | |
7c673cae | 262 | } |
b32b8144 FG |
263 | |
264 | if (error_count != boost::detail::test_errors()) { | |
265 | ++failure_count; | |
7c673cae | 266 | } |
b32b8144 | 267 | } while (!success && failure_count < 5); |
7c673cae | 268 | |
b32b8144 FG |
269 | if (error_msg) { |
270 | BOOST_ERROR(error_msg); | |
271 | } | |
272 | runner.end(); | |
273 | } | |
7c673cae | 274 | |
b32b8144 FG |
275 | // |
276 | // An alternative way to run exception tests. | |
277 | // See merge_exception_tests.cpp for an example. | |
7c673cae | 278 | |
b32b8144 FG |
279 | struct exception_looper |
280 | { | |
281 | bool success; | |
282 | unsigned int failure_count; | |
283 | char const* error_msg; | |
284 | int error_count; | |
285 | ||
286 | exception_looper() : success(false), failure_count(0), error_msg(0) {} | |
287 | ||
288 | void start() { iteration = 0; } | |
289 | ||
290 | bool loop_condition() const | |
291 | { | |
292 | return !error_msg && !success && failure_count < 5; | |
293 | } | |
294 | ||
295 | void start_iteration() | |
296 | { | |
297 | error_count = boost::detail::test_errors(); | |
298 | ++iteration; | |
299 | count = 0; | |
300 | } | |
301 | ||
302 | void successful_run() { success = true; } | |
303 | ||
304 | void test_failure_caught(test_failure const&) | |
305 | { | |
306 | error_msg = "test_failure caught."; | |
307 | } | |
308 | ||
309 | void test_exception_caught(test_exception const& e) | |
310 | { | |
311 | if (error_count != boost::detail::test_errors()) { | |
312 | BOOST_LIGHTWEIGHT_TEST_OSTREAM | |
313 | << "Iteration: " << iteration | |
314 | << " Error found for epoint: " << e.name << std::endl; | |
315 | } | |
316 | } | |
7c673cae | 317 | |
b32b8144 FG |
318 | void unexpected_exception_caught() |
319 | { | |
320 | error_msg = "Unexpected exception."; | |
321 | } | |
7c673cae | 322 | |
b32b8144 FG |
323 | void end() |
324 | { | |
325 | if (error_msg) { | |
326 | BOOST_ERROR(error_msg); | |
7c673cae | 327 | } |
b32b8144 FG |
328 | } |
329 | }; | |
7c673cae | 330 | |
b32b8144 FG |
331 | #define EXCEPTION_LOOP(op) \ |
332 | test::lightweight::exception_looper looper; \ | |
333 | looper.start(); \ | |
334 | while (looper.loop_condition()) { \ | |
335 | looper.start_iteration(); \ | |
336 | try { \ | |
337 | op; \ | |
338 | looper.successful_run(); \ | |
339 | } catch (test::lightweight::test_failure e) { \ | |
340 | looper.test_failure_caught(e); \ | |
341 | } catch (test::lightweight::test_exception e) { \ | |
342 | looper.test_exception_caught(e); \ | |
343 | } catch (...) { \ | |
344 | looper.unexpected_exception_caught(); \ | |
345 | } \ | |
346 | } \ | |
347 | looper.end(); | |
348 | } | |
7c673cae FG |
349 | } |
350 | ||
351 | #endif |