• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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