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;