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 |
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: |
78 | scope_guard(char const* name) |
79 | : old_scope_(scope), scope_(name), dismissed_(false) |
80 | { |
81 | scope = scope_; |
82 | } |
83 | |
84 | ~scope_guard() |
85 | { |
86 | if (dismissed_) |
87 | scope = old_scope_; |
88 | } |
89 | |
90 | void dismiss() { dismissed_ = true; } |
91 | |
92 | 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: |
104 | exceptions_enable(bool enable) |
105 | : old_value_(exceptions_enabled), released_(false) |
106 | { |
107 | exceptions_enabled = enable; |
108 | } |
109 | |
110 | ~exceptions_enable() |
111 | { |
112 | if (!released_) { |
113 | exceptions_enabled = old_value_; |
114 | released_ = true; |
115 | } |
116 | } |
117 | |
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 | { |
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 ( |
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 ( |
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 ( |
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 | 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 | } |
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; |
215 | test_exception(char const* n) : name(n) {} |
216 | }; |
217 | |
218 | struct test_failure |
219 | { |
220 | }; |
221 | |
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> |
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 | |
283 | exception_looper() : success(false), failure_count(0), error_msg(0) {} |
284 | |
285 | void start() { iteration = 0; } |
286 | |
287 | bool loop_condition() const |
288 | { |
289 | return !error_msg && !success && failure_count < 5; |
290 | } |
291 | |
292 | void start_iteration() |
293 | { |
294 | error_count = boost::detail::test_errors(); |
295 | ++iteration; |
296 | count = 0; |
297 | } |
298 | |
299 | void successful_run() { success = true; } |
300 | |
301 | void test_failure_caught(test_failure const&) |
302 | { |
303 | error_msg = "test_failure caught." ; |
304 | } |
305 | |
306 | 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 | |
315 | void unexpected_exception_caught() |
316 | { |
317 | error_msg = "Unexpected exception." ; |
318 | } |
319 | |
320 | 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 | |