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 "./test.hpp"
10 
11 #include <boost/preprocessor/seq/for_each_product.hpp>
12 #include <boost/preprocessor/seq/elem.hpp>
13 #include <boost/preprocessor/cat.hpp>
14 
15 #   define UNORDERED_EXCEPTION_TEST_CASE(name, test_func, type)             \
16         UNORDERED_AUTO_TEST(name)                                           \
17         {                                                                   \
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         {                                                                   \
26             for (unsigned i = 0; i < n; ++i) {                              \
27                 test_func< type > fixture;                                  \
28                 ::test::lightweight::exception_safety(                      \
29                     fixture, BOOST_STRINGIZE(test_func<type>));             \
30             }                                                               \
31         }                                                                   \
32 
33 
34 #    define UNORDERED_EPOINT_IMPL ::test::lightweight::epoint
35 
36 #define UNORDERED_EXCEPTION_TEST_POSTFIX RUN_TESTS()
37 
38 #define EXCEPTION_TESTS(test_seq, param_seq)                                \
39     BOOST_PP_SEQ_FOR_EACH_PRODUCT(EXCEPTION_TESTS_OP,                       \
40         (test_seq)((1))(param_seq))
41 
42 #define EXCEPTION_TESTS_REPEAT(n, test_seq, param_seq)                      \
43     BOOST_PP_SEQ_FOR_EACH_PRODUCT(EXCEPTION_TESTS_OP,                       \
44         (test_seq)((n))(param_seq))
45 
46 #define EXCEPTION_TESTS_OP(r, product)                                      \
47     UNORDERED_EXCEPTION_TEST_CASE_REPEAT(                                   \
48         BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product),                         \
49             BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(2, product))                  \
50         ),                                                                  \
51         BOOST_PP_SEQ_ELEM(0, product),                                      \
52         BOOST_PP_SEQ_ELEM(1, product),                                      \
53         BOOST_PP_SEQ_ELEM(2, product)                                       \
54     )                                                                       \
55 
56 #define UNORDERED_SCOPE(scope_name)                                         \
57     for(::test::scope_guard unordered_test_guard(                           \
58             BOOST_STRINGIZE(scope_name));                                   \
59         !unordered_test_guard.dismissed();                                  \
60         unordered_test_guard.dismiss())                                     \
61 
62 #define UNORDERED_EPOINT(name)                                              \
63     if(::test::exceptions_enabled) {                                        \
64         UNORDERED_EPOINT_IMPL(name);                                        \
65     }                                                                       \
66 
67 #define ENABLE_EXCEPTIONS                                                   \
68     ::test::exceptions_enable BOOST_PP_CAT(                                 \
69         ENABLE_EXCEPTIONS_, __LINE__)(true)                                 \
70 
71 #define DISABLE_EXCEPTIONS                                                  \
72     ::test::exceptions_enable BOOST_PP_CAT(                                 \
73         ENABLE_EXCEPTIONS_, __LINE__)(false)                                \
74 
75 namespace test {
76     static char const* scope = "";
77     bool exceptions_enabled = false;
78 
79     class scope_guard {
80         scope_guard& operator=(scope_guard const&);
81         scope_guard(scope_guard const&);
82 
83         char const* old_scope_;
84         char const* scope_;
85         bool dismissed_;
86     public:
scope_guard(char const * name)87         scope_guard(char const* name)
88             : old_scope_(scope),
89             scope_(name),
90             dismissed_(false)
91         {
92             scope = scope_;
93         }
94 
~scope_guard()95         ~scope_guard() {
96             if(dismissed_) scope = old_scope_;
97         }
98 
dismiss()99         void dismiss() {
100             dismissed_ = true;
101         }
102 
dismissed() const103         bool dismissed() const {
104             return dismissed_;
105         }
106     };
107 
108     class exceptions_enable
109     {
110         exceptions_enable& operator=(exceptions_enable const&);
111         exceptions_enable(exceptions_enable const&);
112 
113         bool old_value_;
114     public:
exceptions_enable(bool enable)115         exceptions_enable(bool enable)
116             : old_value_(exceptions_enabled)
117         {
118             exceptions_enabled = enable;
119         }
120 
~exceptions_enable()121         ~exceptions_enable()
122         {
123             exceptions_enabled = old_value_;
124         }
125     };
126 
127     struct exception_base {
128         struct data_type {};
129         struct strong_type {
storetest::exception_base::strong_type130             template <class T> void store(T const&) {}
testtest::exception_base::strong_type131             template <class T> void test(T const&) const {}
132         };
inittest::exception_base133         data_type init() const { return data_type(); }
BOOST_PREVENT_MACRO_SUBSTITUTIONtest::exception_base134         void check BOOST_PREVENT_MACRO_SUBSTITUTION() const {}
135     };
136 
137     template <class T, class P1, class P2, class T2>
call_ignore_extra_parameters(void (T::* fn)()const,T2 const & obj,P1 &,P2 &)138     inline void call_ignore_extra_parameters(
139             void (T::*fn)() const, T2 const& obj,
140             P1&, P2&)
141     {
142         (obj.*fn)();
143     }
144 
145     template <class T, class P1, class P2, class T2>
call_ignore_extra_parameters(void (T::* fn)(P1 &)const,T2 const & obj,P1 & p1,P2 &)146     inline void call_ignore_extra_parameters(
147             void (T::*fn)(P1&) const, T2 const& obj,
148             P1& p1, P2&)
149     {
150         (obj.*fn)(p1);
151     }
152 
153     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)154     inline void call_ignore_extra_parameters(
155             void (T::*fn)(P1&, P2&) const, T2 const& obj,
156             P1& p1, P2& p2)
157     {
158         (obj.*fn)(p1, p2);
159     }
160 
161     template <class T>
constant(T const & x)162     T const& constant(T const& x) {
163         return x;
164     }
165 
166     template <class Test>
167     class test_runner
168     {
169         Test const& test_;
170 
171         test_runner(test_runner const&);
172         test_runner& operator=(test_runner const&);
173     public:
test_runner(Test const & t)174         test_runner(Test const& t) : test_(t) {}
operator ()() const175         void operator()() const {
176             DISABLE_EXCEPTIONS;
177             test::scope = "";
178             BOOST_DEDUCED_TYPENAME Test::data_type x(test_.init());
179             BOOST_DEDUCED_TYPENAME Test::strong_type strong;
180             strong.store(x);
181             try {
182                 ENABLE_EXCEPTIONS;
183                 call_ignore_extra_parameters<
184                     Test,
185                     BOOST_DEDUCED_TYPENAME Test::data_type,
186                     BOOST_DEDUCED_TYPENAME Test::strong_type
187                 >(&Test::run, test_, x, strong);
188             }
189             catch(...) {
190                 call_ignore_extra_parameters<
191                     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                 throw;
196             }
197         }
198     };
199 
200     // Quick exception testing based on lightweight test
201 
202     namespace lightweight {
203         static int iteration;
204         static int count;
205 
206         struct test_exception {
207             char const* name;
test_exceptiontest::lightweight::test_exception208             test_exception(char const* n) : name(n) {}
209         };
210 
211         struct test_failure {
212         };
213 
epoint(char const * name)214         void epoint(char const* name) {
215             ++count;
216             if(count == iteration) {
217                 throw test_exception(name);
218             }
219         }
220 
221         template <class Test>
exception_safety(Test const & f,char const *)222         void exception_safety(Test const& f, char const* /*name*/) {
223             test_runner<Test> runner(f);
224 
225             iteration = 0;
226             bool success = false;
227             do {
228                 ++iteration;
229                 count = 0;
230 
231                 try {
232                     runner();
233                     success = true;
234                 }
235                 catch(test_failure) {
236                     BOOST_ERROR("test_failure caught.");
237                     break;
238                 }
239                 catch(test_exception) {
240                     continue;
241                 }
242                 catch(...) {
243                     BOOST_ERROR("Unexpected exception.");
244                     break;
245                 }
246             } while(!success);
247         }
248     }
249 }
250 
251 #endif
252