18 #include "tut_exception.hpp"
19 #include "tut_result.hpp"
20 #include "tut_posix.hpp"
21 #include "tut_assert.hpp"
22 #include "tut_runner.hpp"
24 #if defined(TUT_USE_SEH)
30 * Template Unit Tests Framework for C++.
33 * @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com
39 * Test object. Contains data test run upon and default test method
40 * implementation. Inherited from Data to allow tests to
41 * access test data as members.
44 class test_object : public Data, public test_object_posix
55 void set_test_name(const std::string& current_test_name)
57 current_test_name_ = current_test_name;
60 const std::string& get_test_name() const
62 return current_test_name_;
65 void set_test_id(int current_test_id)
67 current_test_id_ = current_test_id;
70 int get_test_id() const
72 return current_test_id_;
76 * Default do-nothing test.
81 called_method_was_a_dummy_test_ = true;
85 * The flag is set to true by default (dummy) test.
86 * Used to detect usused test numbers and avoid unnecessary
87 * test object creation which may be time-consuming depending
88 * on operations described in Data::Data() and Data::~Data().
89 * TODO: replace with throwing special exception from default test.
91 bool called_method_was_a_dummy_test_;
95 std::string current_test_name_;
100 * Walks through test tree and stores address of each
101 * test method in group. Instantiation stops at 0.
103 template <class Test, class Group, int n>
104 struct tests_registerer
106 static void reg(Group& group)
108 group.reg(n, &Test::template test<n>);
109 tests_registerer<Test, Group, n - 1>::reg(group);
113 template <class Test, class Group>
114 struct tests_registerer<Test, Group, 0>
116 static void reg(Group&)
122 * Test group; used to recreate test object instance for
123 * each new test since we have to have reinitialized
126 template <class Data, int MaxTestsInGroup = 50>
127 class test_group : public group_base, public test_group_posix
131 typedef void (test_object<Data>::*testmethod)();
132 typedef std::map<int, testmethod> tests;
133 typedef typename tests::iterator tests_iterator;
134 typedef typename tests::const_iterator tests_const_iterator;
135 typedef typename tests::const_reverse_iterator
136 tests_const_reverse_iterator;
137 typedef typename tests::size_type size_type;
140 tests_iterator current_test_;
151 * Exception-in-destructor-safe smart-pointer class.
157 bool permit_throw_in_dtor;
159 safe_holder(const safe_holder&);
160 safe_holder& operator=(const safe_holder&);
165 permit_throw_in_dtor(false)
174 T* operator->() const
185 * Tell ptr it can throw from destructor. Right way is to
186 * use std::uncaught_exception(), but some compilers lack
187 * correct implementation of the function.
191 permit_throw_in_dtor = true;
195 * Specially treats exceptions in test object destructor;
196 * if test itself failed, exceptions in destructor
197 * are ignored; if test was successful and destructor failed,
198 * warning exception throwed.
204 if (delete_obj() == false)
206 throw warning("destructor of test object raised"
207 " an SEH exception");
210 catch (const std::exception& ex)
212 if (permit_throw_in_dtor)
214 std::string msg = "destructor of test object raised"
222 if (permit_throw_in_dtor)
224 throw warning("destructor of test object raised an"
231 * Re-init holder to get brand new object.
236 permit_throw_in_dtor = false;
242 #if defined(TUT_USE_SEH)
249 #if defined(TUT_USE_SEH)
251 __except(handle_seh_(::GetExceptionCode()))
253 if (permit_throw_in_dtor)
265 typedef test_object<Data> object;
268 * Creates and registers test group with specified name.
270 test_group(const char* name)
274 runner.get().register_group(name_,this);
276 // register all tests
277 tests_registerer<object, test_group, MaxTestsInGroup>::reg(*this);
281 * This constructor is used in self-test run only.
283 test_group(const char* name, test_runner& another_runner)
287 another_runner.register_group(name_, this);
289 // register all tests
290 tests_registerer<test_object<Data>, test_group,
291 MaxTestsInGroup>::reg(*this);
295 * Registers test method under given number.
297 void reg(int n, testmethod tm)
303 * Reset test position before first test.
307 current_test_ = tests_.begin();
313 bool run_next(test_result &tr)
315 if (current_test_ == tests_.end())
320 // find next user-specialized test
321 safe_holder<object> obj;
322 while (current_test_ != tests_.end())
324 tests_iterator current_test = current_test_++;
326 if(run_test_(current_test, obj, tr) && tr.result != test_result::dummy)
336 * Runs one test by position.
338 bool run_test(int n, test_result &tr)
340 if (tests_.rbegin() == tests_.rend() ||
341 tests_.rbegin()->first < n)
346 // withing scope; check if given test exists
347 tests_iterator ti = tests_.find(n);
348 if (ti == tests_.end())
353 safe_holder<object> obj;
354 return run_test_(ti, obj, tr);
358 * VC allows only one exception handling type per function,
359 * so I have to split the method.
361 bool run_test_(const tests_iterator& ti, safe_holder<object>& obj, test_result &tr)
363 std::string current_test_name;
365 tr = test_result(name_, ti->first, current_test_name, test_result::ok);
369 switch (run_test_seh_(ti->second, obj, current_test_name, ti->first))
372 throw bad_ctor("seh");
380 tr.result = test_result::dummy;
388 catch (const rethrown& ex)
391 tr.result = test_result::rethrown;
393 catch (const tut_error& ex)
395 tr.result = ex.result();
396 tr.exception_typeid = typeid(ex).name();
397 tr.message = ex.what();
399 catch (const std::exception& ex)
401 tr.result = test_result::ex;
402 tr.exception_typeid = typeid(ex).name();
403 tr.message = ex.what();
407 // test failed with unknown exception
408 tr.result = test_result::ex;
413 tr.name = obj->get_test_name();
415 // try to report to parent, if exists
416 send_result_(obj.get(), tr);
420 tr.name = current_test_name;
427 * Runs one under SEH if platform supports it.
429 seh_result run_test_seh_(testmethod tm, safe_holder<object>& obj,
430 std::string& current_test_name, int current_test_id)
432 #if defined(TUT_USE_SEH)
441 obj->called_method_was_a_dummy_test_ = false;
443 #if defined(TUT_USE_SEH)
448 obj.get()->set_test_id(current_test_id);
450 #if defined(TUT_USE_SEH)
452 __except(handle_seh_(::GetExceptionCode()))
454 current_test_name = obj->get_test_name();
459 if (obj->called_method_was_a_dummy_test_)
461 // do not call obj.release(); reuse object
465 current_test_name = obj->get_test_name();
468 #if defined(TUT_USE_SEH)
470 __except(handle_seh_(::GetExceptionCode()))
478 void reset_holder_(safe_holder<object>& obj)
484 catch (const std::exception& ex)
486 throw bad_ctor(ex.what());
490 throw bad_ctor("test constructor has generated an exception;"
491 " group execution is terminated");
496 #if defined(TUT_USE_SEH)
498 * Decides should we execute handler or ignore SE.
500 inline int handle_seh_(DWORD excode)
504 case EXCEPTION_ACCESS_VIOLATION:
505 case EXCEPTION_DATATYPE_MISALIGNMENT:
506 case EXCEPTION_BREAKPOINT:
507 case EXCEPTION_SINGLE_STEP:
508 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
509 case EXCEPTION_FLT_DENORMAL_OPERAND:
510 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
511 case EXCEPTION_FLT_INEXACT_RESULT:
512 case EXCEPTION_FLT_INVALID_OPERATION:
513 case EXCEPTION_FLT_OVERFLOW:
514 case EXCEPTION_FLT_STACK_CHECK:
515 case EXCEPTION_FLT_UNDERFLOW:
516 case EXCEPTION_INT_DIVIDE_BY_ZERO:
517 case EXCEPTION_INT_OVERFLOW:
518 case EXCEPTION_PRIV_INSTRUCTION:
519 case EXCEPTION_IN_PAGE_ERROR:
520 case EXCEPTION_ILLEGAL_INSTRUCTION:
521 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
522 case EXCEPTION_STACK_OVERFLOW:
523 case EXCEPTION_INVALID_DISPOSITION:
524 case EXCEPTION_GUARD_PAGE:
525 case EXCEPTION_INVALID_HANDLE:
526 return EXCEPTION_EXECUTE_HANDLER;
529 return EXCEPTION_CONTINUE_SEARCH;