3 #include <tut/tut_config.hpp>
14 #include "tut_exception.hpp"
15 #include "tut_result.hpp"
16 #include "tut_posix.hpp"
17 #include "tut_assert.hpp"
18 #include "tut_runner.hpp"
20 #if defined(TUT_USE_SEH)
26 * Template Unit Tests Framework for C++.
29 * @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com
38 * Test object. Contains data test run upon and default test method
39 * implementation. Inherited from Data to allow tests to
40 * access test data as members.
43 class test_object : public Data, public test_object_posix
45 template<class D, int M>
46 friend class test_group;
48 void set_test_group(const char *group)
50 current_test_group_ = group;
53 void set_test_id(int current_test_id)
55 current_test_id_ = current_test_id;
64 : called_method_was_a_dummy_test_(false),
71 void set_test_name(const std::string& current_test_name)
73 current_test_name_ = current_test_name;
76 const std::string& get_test_name() const
78 return current_test_name_;
81 const std::string& get_test_group() const
83 return current_test_group_;
86 int get_test_id() const
88 return current_test_id_;
92 * Default do-nothing test.
97 called_method_was_a_dummy_test_ = true;
101 * The flag is set to true by default (dummy) test.
102 * Used to detect usused test numbers and avoid unnecessary
103 * test object creation which may be time-consuming depending
104 * on operations described in Data::Data() and Data::~Data().
106 bool called_method_was_a_dummy_test_;
108 virtual ~test_object()
113 int current_test_id_;
114 std::string current_test_name_;
115 std::string current_test_group_;
120 * Walks through test tree and stores address of each
121 * test method in group. Instantiation stops at 0.
123 template <class Test, class Group, int n>
124 struct tests_registerer
126 static void reg(Group& group)
128 group.reg(n, &Test::template test<n>);
129 tests_registerer<Test, Group, n - 1>::reg(group);
133 template <class Test, class Group>
134 struct tests_registerer<Test, Group, 0>
136 static void reg(Group&)
142 * Test group; used to recreate test object instance for
143 * each new test since we have to have reinitialized
146 template <class Data, int MaxTestsInGroup = 50>
147 class test_group : public group_base, public test_group_posix
149 test_group(const test_group&);
150 void operator=(const test_group&);
154 typedef void (test_object<Data>::*testmethod)();
155 typedef std::map<int, testmethod> tests;
156 typedef typename tests::iterator tests_iterator;
157 typedef typename tests::const_iterator tests_const_iterator;
158 typedef typename tests::const_reverse_iterator
159 tests_const_reverse_iterator;
160 typedef typename tests::size_type size_type;
163 tests_iterator current_test_;
168 #if defined(TUT_USE_SEH)
176 * Exception-in-destructor-safe smart-pointer class.
182 bool permit_throw_in_dtor;
184 safe_holder(const safe_holder&);
185 safe_holder& operator=(const safe_holder&);
190 permit_throw_in_dtor(false)
199 T* operator->() const
210 * Tell ptr it can throw from destructor. Right way is to
211 * use std::uncaught_exception(), but some compilers lack
212 * correct implementation of the function.
216 permit_throw_in_dtor = true;
220 * Specially treats exceptions in test object destructor;
221 * if test itself failed, exceptions in destructor
222 * are ignored; if test was successful and destructor failed,
223 * warning exception throwed.
229 #if defined(TUT_USE_SEH)
230 if (delete_obj() == false)
232 throw warning("destructor of test object raised"
233 " an SEH exception");
236 bool d = delete_obj();
237 assert(d && "delete failed with SEH disabled: runtime bug?");
240 catch (const std::exception& ex)
242 if (permit_throw_in_dtor)
244 std::string msg = "destructor of test object raised"
252 if (permit_throw_in_dtor)
254 throw warning("destructor of test object raised an"
261 * Re-init holder to get brand new object.
266 permit_throw_in_dtor = false;
272 #if defined(TUT_USE_SEH)
279 #if defined(TUT_USE_SEH)
281 __except(handle_seh_(::GetExceptionCode()))
283 if (permit_throw_in_dtor)
295 typedef test_object<Data> object;
298 * Creates and registers test group with specified name.
300 test_group(const char* name)
306 runner.get().register_group(name_,this);
308 // register all tests
309 tests_registerer<object, test_group, MaxTestsInGroup>::reg(*this);
313 * This constructor is used in self-test run only.
315 test_group(const char* name, test_runner& another_runner)
321 another_runner.register_group(name_, this);
323 // register all tests
324 tests_registerer<test_object<Data>, test_group,
325 MaxTestsInGroup>::reg(*this);
329 * Registers test method under given number.
331 void reg(int n, testmethod tm)
337 * Reset test position before first test.
341 current_test_ = tests_.begin();
347 bool run_next(test_result &tr)
349 if (current_test_ == tests_.end())
354 // find next user-specialized test
355 safe_holder<object> obj;
356 while (current_test_ != tests_.end())
358 tests_iterator current_test = current_test_++;
360 if(run_test_(current_test, obj, tr) && tr.result != test_result::dummy)
370 * Runs one test by position.
372 bool run_test(int n, test_result &tr)
374 if (tests_.rbegin() == tests_.rend() ||
375 tests_.rbegin()->first < n)
380 // withing scope; check if given test exists
381 tests_iterator ti = tests_.find(n);
382 if (ti == tests_.end())
387 safe_holder<object> obj;
388 return run_test_(ti, obj, tr);
392 * VC allows only one exception handling type per function,
393 * so I have to split the method.
395 bool run_test_(const tests_iterator& ti, safe_holder<object>& obj, test_result &tr)
397 std::string current_test_name;
399 tr = test_result(name_, ti->first, current_test_name, test_result::ok);
403 switch (run_test_seh_(ti->second, obj, current_test_name, ti->first))
405 #if defined(TUT_USE_SEH)
407 throw bad_ctor("seh");
415 tr.result = test_result::dummy;
423 catch (const rethrown& ex)
426 tr.result = test_result::rethrown;
428 catch (const tut_error& ex)
430 tr.result = ex.result();
431 tr.exception_typeid = ex.type();
432 tr.message = ex.what();
434 catch (const std::exception& ex)
436 tr.result = test_result::ex;
437 tr.exception_typeid = type_name(ex);
438 tr.message = ex.what();
442 // test failed with unknown exception
443 tr.result = test_result::ex;
448 tr.name = obj->get_test_name();
450 // try to report to parent, if exists
451 send_result_(obj.get(), tr);
455 tr.name = current_test_name;
462 * Runs one under SEH if platform supports it.
464 seh_result run_test_seh_(testmethod tm, safe_holder<object>& obj,
465 std::string& current_test_name, int current_test_id)
467 #if defined(TUT_USE_SEH)
476 obj->called_method_was_a_dummy_test_ = false;
478 #if defined(TUT_USE_SEH)
483 obj.get()->set_test_id(current_test_id);
484 obj.get()->set_test_group(name_);
486 #if defined(TUT_USE_SEH)
488 __except(handle_seh_(::GetExceptionCode()))
490 current_test_name = obj->get_test_name();
495 if (obj->called_method_was_a_dummy_test_)
497 // do not call obj.release(); reuse object
501 current_test_name = obj->get_test_name();
504 #if defined(TUT_USE_SEH)
506 __except(handle_seh_(::GetExceptionCode()))
514 void reset_holder_(safe_holder<object>& obj)
520 catch (const std::exception& ex)
522 throw bad_ctor(ex.what());
526 throw bad_ctor("test constructor has generated an exception;"
527 " group execution is terminated");
532 #if defined(TUT_USE_SEH)
534 * Decides should we execute handler or ignore SE.
536 inline int handle_seh_(DWORD excode)
540 case EXCEPTION_ACCESS_VIOLATION:
541 case EXCEPTION_DATATYPE_MISALIGNMENT:
542 case EXCEPTION_BREAKPOINT:
543 case EXCEPTION_SINGLE_STEP:
544 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
545 case EXCEPTION_FLT_DENORMAL_OPERAND:
546 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
547 case EXCEPTION_FLT_INEXACT_RESULT:
548 case EXCEPTION_FLT_INVALID_OPERATION:
549 case EXCEPTION_FLT_OVERFLOW:
550 case EXCEPTION_FLT_STACK_CHECK:
551 case EXCEPTION_FLT_UNDERFLOW:
552 case EXCEPTION_INT_DIVIDE_BY_ZERO:
553 case EXCEPTION_INT_OVERFLOW:
554 case EXCEPTION_PRIV_INSTRUCTION:
555 case EXCEPTION_IN_PAGE_ERROR:
556 case EXCEPTION_ILLEGAL_INSTRUCTION:
557 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
558 case EXCEPTION_STACK_OVERFLOW:
559 case EXCEPTION_INVALID_DISPOSITION:
560 case EXCEPTION_GUARD_PAGE:
561 case EXCEPTION_INVALID_HANDLE:
562 return EXCEPTION_EXECUTE_HANDLER;
565 return EXCEPTION_CONTINUE_SEARCH;
570 #endif // TUT_H_GUARD