libdballe 4.0.18
|
00001 #ifndef TUT_H_GUARD 00002 #define TUT_H_GUARD 00003 00004 #include <iostream> 00005 #include <map> 00006 #include <vector> 00007 #include <string> 00008 #include <sstream> 00009 #include <stdexcept> 00010 #include <typeinfo> 00011 00012 #if defined(TUT_USE_SEH) 00013 #include <windows.h> 00014 #include <winbase.h> 00015 #endif 00016 00023 namespace tut 00024 { 00029 struct no_such_test : public std::logic_error 00030 { 00031 no_such_test() : std::logic_error("no such test"){}; 00032 }; 00033 00039 struct beyond_last_test : public no_such_test 00040 { 00041 beyond_last_test(){}; 00042 }; 00043 00047 struct no_such_group : public std::logic_error 00048 { 00049 no_such_group(const std::string& grp) : 00050 std::logic_error(grp){}; 00051 }; 00052 00057 struct no_more_tests 00058 { 00059 no_more_tests(){}; 00060 }; 00061 00065 class failure : public std::logic_error 00066 { 00067 public: 00068 failure(const std::string& msg) : std::logic_error(msg){}; 00069 }; 00070 00074 class warning : public std::logic_error 00075 { 00076 public: 00077 warning(const std::string& msg) : std::logic_error(msg){}; 00078 }; 00079 00083 class seh : public std::logic_error 00084 { 00085 public: 00086 seh(const std::string& msg) : std::logic_error(msg){}; 00087 }; 00088 00095 struct test_result 00096 { 00100 std::string group; 00101 00105 int test; 00106 00114 typedef enum { ok, fail, ex, warn, term } result_type; 00115 result_type result; 00116 00120 std::string message; 00121 std::string exception_typeid; 00122 00126 test_result() 00127 : test(0),result(ok) 00128 { 00129 } 00130 00134 test_result( const std::string& grp,int pos,result_type res) 00135 : group(grp),test(pos),result(res) 00136 { 00137 } 00138 00142 test_result( const std::string& grp,int pos, 00143 result_type res, 00144 const std::exception& ex) 00145 : group(grp),test(pos),result(res), 00146 message(ex.what()),exception_typeid(typeid(ex).name()) 00147 { 00148 } 00149 }; 00150 00155 struct group_base 00156 { 00157 virtual ~group_base(){}; 00158 00159 // execute tests iteratively 00160 virtual void rewind() = 0; 00161 virtual test_result run_next() = 0; 00162 00163 // execute one test 00164 virtual test_result run_test(int n) = 0; 00165 }; 00166 00174 struct callback 00175 { 00179 virtual ~callback(){}; 00180 00184 virtual void run_started(){}; 00185 00190 virtual void test_completed(const test_result& /*tr*/){}; 00191 00195 virtual void run_completed(){}; 00196 }; 00197 00201 typedef std::vector<std::string> groupnames; 00202 00206 class test_runner 00207 { 00208 protected: 00209 typedef std::map<std::string,group_base*> groups; 00210 typedef groups::iterator iterator; 00211 typedef groups::const_iterator const_iterator; 00212 groups groups_; 00213 00214 callback default_callback_; 00215 callback* callback_; 00216 00217 public: 00221 test_runner() : callback_(&default_callback_) 00222 { 00223 } 00224 00228 void register_group(const std::string& name,group_base* gr) 00229 { 00230 if( gr == 0 ) 00231 { 00232 throw std::invalid_argument("group shall be non-null"); 00233 } 00234 00235 groups::iterator found = groups_.find(name); 00236 if( found != groups_.end() ) 00237 { 00238 std::string msg("attempt to add already existent group "+name); 00239 // this exception terminates application so we use cerr also 00240 std::cerr << msg << std::endl; 00241 throw std::logic_error(msg); 00242 } 00243 00244 groups_[name] = gr; 00245 } 00246 00250 void set_callback(callback* cb) 00251 { 00252 callback_ = cb==0? &default_callback_:cb; 00253 } 00254 00258 callback& get_callback() const 00259 { 00260 return *callback_; 00261 } 00262 00266 const groupnames list_groups() const 00267 { 00268 groupnames ret; 00269 const_iterator i = groups_.begin(); 00270 const_iterator e = groups_.end(); 00271 while( i != e ) 00272 { 00273 ret.push_back(i->first); 00274 ++i; 00275 } 00276 return ret; 00277 } 00278 00283 void run_tests() const 00284 { 00285 callback_->run_started(); 00286 00287 const_iterator i = groups_.begin(); 00288 const_iterator e = groups_.end(); 00289 while( i != e ) 00290 { 00291 try 00292 { 00293 // iterate all tests 00294 i->second->rewind(); 00295 for( ;; ) 00296 { 00297 test_result tr = i->second->run_next(); 00298 callback_->test_completed(tr); 00299 } 00300 } 00301 catch( const no_more_tests& ) 00302 { 00303 // ok 00304 } 00305 00306 ++i; 00307 } 00308 00309 callback_->run_completed(); 00310 } 00311 00315 void run_tests(const std::string& group_name) const 00316 { 00317 callback_->run_started(); 00318 00319 const_iterator i = groups_.find(group_name); 00320 if( i == groups_.end() ) 00321 { 00322 throw no_such_group(group_name); 00323 } 00324 00325 try 00326 { 00327 // iterate all tests 00328 i->second->rewind(); 00329 for(;;) 00330 { 00331 test_result tr = i->second->run_next(); 00332 callback_->test_completed(tr); 00333 } 00334 } 00335 catch( const no_more_tests& ) 00336 { 00337 // ok 00338 } 00339 00340 callback_->run_completed(); 00341 } 00342 00346 test_result run_test(const std::string& group_name,int n) const 00347 { 00348 callback_->run_started(); 00349 00350 const_iterator i = groups_.find(group_name); 00351 if( i == groups_.end() ) 00352 { 00353 throw no_such_group(group_name); 00354 } 00355 00356 try 00357 { 00358 test_result tr = i->second->run_test(n); 00359 callback_->test_completed(tr); 00360 callback_->run_completed(); 00361 return tr; 00362 } 00363 catch( const beyond_last_test& ) 00364 { 00365 callback_->run_completed(); 00366 throw; 00367 } 00368 catch( const no_such_test& ) 00369 { 00370 callback_->run_completed(); 00371 throw; 00372 } 00373 } 00374 }; 00375 00381 class test_runner_singleton 00382 { 00383 public: 00384 static test_runner& get() 00385 { 00386 static test_runner tr; 00387 return tr; 00388 } 00389 }; 00390 extern test_runner_singleton runner; 00391 00397 template <class Data> 00398 class test_object : public Data 00399 { 00400 public: 00404 test_object(){}; 00405 00413 bool called_method_was_a_dummy_test_; 00414 00418 template <int n> 00419 void test() 00420 { 00421 called_method_was_a_dummy_test_ = true; 00422 } 00423 }; 00424 00425 namespace 00426 { 00431 void ensure(bool cond) 00432 { 00433 if( !cond ) throw failure(""); 00434 } 00435 00440 void ensure(const char* msg,bool cond) 00441 { 00442 if( !cond ) throw failure(msg); 00443 } 00444 00452 template <class T,class Q> 00453 void ensure_equals(const char* msg,const Q& actual,const T& expected) 00454 { 00455 if( expected != actual ) 00456 { 00457 std::stringstream ss; 00458 ss << (msg?msg:"") << (msg?": ":"") << "expected " << expected << " actual " << actual; 00459 throw failure(ss.str().c_str()); 00460 } 00461 } 00462 00463 template <class T,class Q> 00464 void ensure_equals(const Q& actual,const T& expected) 00465 { 00466 ensure_equals<>(0,actual,expected); 00467 } 00468 00478 template <class T> 00479 void ensure_distance(const char* msg,const T& actual,const T& expected,const T& distance) 00480 { 00481 if( expected-distance >= actual || expected+distance <= actual ) 00482 { 00483 std::stringstream ss; 00484 ss << (msg?msg:"") << (msg?": ":"") << "expected [" << expected-distance << ";" 00485 << expected+distance << "] actual " << actual; 00486 throw failure(ss.str().c_str()); 00487 } 00488 } 00489 00490 template <class T> 00491 void ensure_distance(const T& actual,const T& expected,const T& distance) 00492 { 00493 ensure_distance<>(0,actual,expected,distance); 00494 } 00495 00499 void fail(const char* msg="") 00500 { 00501 throw failure(msg); 00502 } 00503 } 00504 00509 template <class Test,class Group,int n> 00510 struct tests_registerer 00511 { 00512 static void reg(Group& group) 00513 { 00514 group.reg(n,&Test::template test<n>); 00515 tests_registerer<Test,Group,n-1>::reg(group); 00516 } 00517 }; 00518 00519 template<class Test,class Group> 00520 struct tests_registerer<Test,Group,0> 00521 { 00522 static void reg(Group&){}; 00523 }; 00524 00530 template <class Data,int MaxTestsInGroup = 50> 00531 class test_group : public group_base 00532 { 00533 const char* name_; 00534 00535 typedef void (test_object<Data>::*testmethod)(); 00536 typedef std::map<int,testmethod> tests; 00537 typedef typename tests::iterator tests_iterator; 00538 typedef typename tests::const_iterator tests_const_iterator; 00539 typedef typename tests::const_reverse_iterator 00540 tests_const_reverse_iterator; 00541 typedef typename tests::size_type size_type; 00542 00543 tests tests_; 00544 tests_iterator current_test_; 00545 00549 template <class T> 00550 class safe_holder 00551 { 00552 T* p_; 00553 bool permit_throw_in_dtor; 00554 00555 safe_holder(const safe_holder&); 00556 safe_holder& operator = (const safe_holder&); 00557 00558 public: 00559 safe_holder() : p_(0),permit_throw_in_dtor(false) 00560 { 00561 } 00562 00563 ~safe_holder() 00564 { 00565 release(); 00566 } 00567 00568 T* operator -> () const { return p_; }; 00569 T* get() const { return p_; }; 00570 00576 void permit_throw(){ permit_throw_in_dtor = true; } 00577 00584 void release() 00585 { 00586 try 00587 { 00588 if( delete_obj() == false ) 00589 { 00590 throw warning("destructor of test object raised an SEH exception"); 00591 } 00592 } 00593 catch( const std::exception& ex ) 00594 { 00595 if( permit_throw_in_dtor ) 00596 { 00597 std::string msg = "destructor of test object raised exception: "; 00598 msg += ex.what(); 00599 throw warning(msg); 00600 } 00601 } 00602 catch( ... ) 00603 { 00604 if( permit_throw_in_dtor ) 00605 { 00606 throw warning("destructor of test object raised an exception"); 00607 } 00608 } 00609 } 00610 00614 void reset() 00615 { 00616 release(); 00617 permit_throw_in_dtor = false; 00618 p_ = new T(); 00619 } 00620 00621 bool delete_obj() 00622 { 00623 #if defined(TUT_USE_SEH) 00624 __try 00625 { 00626 #endif 00627 T* p = p_; 00628 p_ = 0; 00629 delete p; 00630 #if defined(TUT_USE_SEH) 00631 } 00632 __except(handle_seh_(::GetExceptionCode())) 00633 { 00634 if( permit_throw_in_dtor ) 00635 { 00636 return false; 00637 } 00638 } 00639 #endif 00640 return true; 00641 } 00642 }; 00643 00644 public: 00645 typedef test_object<Data> object; 00646 00650 test_group(const char* name) 00651 : name_(name) 00652 { 00653 // register itself 00654 runner.get().register_group(name_,this); 00655 00656 // register all tests 00657 tests_registerer<object,test_group,MaxTestsInGroup>::reg(*this); 00658 }; 00659 00663 test_group(const char* name,test_runner& another_runner) 00664 : name_(name) 00665 { 00666 // register itself 00667 another_runner.register_group(name_,this); 00668 00669 // register all tests 00670 tests_registerer<test_object<Data>, 00671 test_group,MaxTestsInGroup>::reg(*this); 00672 }; 00673 00677 void reg(int n,testmethod tm) 00678 { 00679 tests_[n] = tm; 00680 } 00681 00685 void rewind() 00686 { 00687 current_test_ = tests_.begin(); 00688 } 00689 00693 test_result run_next() 00694 { 00695 if( current_test_ == tests_.end() ) 00696 { 00697 throw no_more_tests(); 00698 } 00699 00700 // find next user-specialized test 00701 safe_holder<object> obj; 00702 while( current_test_ != tests_.end() ) 00703 { 00704 try 00705 { 00706 return run_test_(current_test_++,obj); 00707 } 00708 catch( const no_such_test& ) 00709 { 00710 continue; 00711 } 00712 } 00713 00714 throw no_more_tests(); 00715 } 00716 00720 test_result run_test(int n) 00721 { 00722 // beyond tests is special case to discover upper limit 00723 if( tests_.rbegin() == tests_.rend() ) throw beyond_last_test(); 00724 if( tests_.rbegin()->first < n ) throw beyond_last_test(); 00725 00726 // withing scope; check if given test exists 00727 tests_iterator ti = tests_.find(n); 00728 if( ti == tests_.end() ) throw no_such_test(); 00729 00730 safe_holder<object> obj; 00731 return run_test_(ti,obj); 00732 } 00733 00734 private: 00739 test_result run_test_(const tests_iterator& ti,safe_holder<object>& obj) 00740 { 00741 try 00742 { 00743 if( run_test_seh_(ti->second,obj) == false ) 00744 throw seh("seh"); 00745 } 00746 catch(const no_such_test&) 00747 { 00748 throw; 00749 } 00750 catch(const warning& ex) 00751 { 00752 // test ok, but destructor failed 00753 test_result tr(name_,ti->first,test_result::warn,ex); 00754 return tr; 00755 } 00756 catch(const failure& ex) 00757 { 00758 // test failed because of ensure() or similar method 00759 test_result tr(name_,ti->first,test_result::fail,ex); 00760 return tr; 00761 } 00762 catch(const seh& ex) 00763 { 00764 // test failed with sigsegv, divide by zero, etc 00765 test_result tr(name_,ti->first,test_result::term,ex); 00766 return tr; 00767 } 00768 catch(const std::exception& ex) 00769 { 00770 // test failed with std::exception 00771 test_result tr(name_,ti->first,test_result::ex,ex); 00772 return tr; 00773 } 00774 catch(...) 00775 { 00776 // test failed with unknown exception 00777 test_result tr(name_,ti->first,test_result::ex); 00778 return tr; 00779 } 00780 00781 // test passed 00782 test_result tr(name_,ti->first,test_result::ok); 00783 return tr; 00784 } 00785 00789 bool run_test_seh_(testmethod tm,safe_holder<object>& obj) 00790 { 00791 #if defined(TUT_USE_SEH) 00792 __try 00793 { 00794 #endif 00795 if( obj.get() == 0 ) obj.reset(); 00796 obj->called_method_was_a_dummy_test_ = false; 00797 00798 #if defined(TUT_USE_SEH) 00799 __try 00800 { 00801 #endif 00802 (obj.get()->*tm)(); 00803 #if defined(TUT_USE_SEH) 00804 } 00805 __except(handle_seh_(::GetExceptionCode())) 00806 { 00807 // throw seh("SEH"); 00808 return false; 00809 } 00810 #endif 00811 00812 if( obj->called_method_was_a_dummy_test_ ) 00813 { 00814 // do not call obj.release(); reuse object 00815 throw no_such_test(); 00816 } 00817 00818 obj.permit_throw(); 00819 obj.release(); 00820 #if defined(TUT_USE_SEH) 00821 } 00822 __except(handle_seh_(::GetExceptionCode())) 00823 { 00824 // throw seh("SEH"); 00825 return false; 00826 } 00827 #endif 00828 return true; 00829 } 00830 }; 00831 00832 00833 #if defined(TUT_USE_SEH) 00834 00837 inline int handle_seh_(DWORD excode) 00838 { 00839 switch(excode) 00840 { 00841 case EXCEPTION_ACCESS_VIOLATION: 00842 case EXCEPTION_DATATYPE_MISALIGNMENT: 00843 case EXCEPTION_BREAKPOINT: 00844 case EXCEPTION_SINGLE_STEP: 00845 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 00846 case EXCEPTION_FLT_DENORMAL_OPERAND: 00847 case EXCEPTION_FLT_DIVIDE_BY_ZERO: 00848 case EXCEPTION_FLT_INEXACT_RESULT: 00849 case EXCEPTION_FLT_INVALID_OPERATION: 00850 case EXCEPTION_FLT_OVERFLOW: 00851 case EXCEPTION_FLT_STACK_CHECK: 00852 case EXCEPTION_FLT_UNDERFLOW: 00853 case EXCEPTION_INT_DIVIDE_BY_ZERO: 00854 case EXCEPTION_INT_OVERFLOW: 00855 case EXCEPTION_PRIV_INSTRUCTION: 00856 case EXCEPTION_IN_PAGE_ERROR: 00857 case EXCEPTION_ILLEGAL_INSTRUCTION: 00858 case EXCEPTION_NONCONTINUABLE_EXCEPTION: 00859 case EXCEPTION_STACK_OVERFLOW: 00860 case EXCEPTION_INVALID_DISPOSITION: 00861 case EXCEPTION_GUARD_PAGE: 00862 case EXCEPTION_INVALID_HANDLE: 00863 return EXCEPTION_EXECUTE_HANDLER; 00864 }; 00865 00866 return EXCEPTION_CONTINUE_SEARCH; 00867 } 00868 #endif 00869 } 00870 00871 #endif 00872