wibble 0.1.28
|
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 00066 struct bad_ctor : public std::logic_error 00067 { 00068 bad_ctor(const std::string& msg) : 00069 std::logic_error(msg){}; 00070 }; 00071 00075 class failure : public std::logic_error 00076 { 00077 public: 00078 failure(const std::string& msg) : std::logic_error(msg){}; 00079 }; 00080 00084 class warning : public std::logic_error 00085 { 00086 public: 00087 warning(const std::string& msg) : std::logic_error(msg){}; 00088 }; 00089 00093 class seh : public std::logic_error 00094 { 00095 public: 00096 seh(const std::string& msg) : std::logic_error(msg){}; 00097 }; 00098 00105 struct test_result 00106 { 00110 std::string group; 00111 00115 int test; 00116 00124 typedef enum { ok, fail, ex, warn, term, ex_ctor } result_type; 00125 result_type result; 00126 00130 std::string message; 00131 std::string exception_typeid; 00132 00136 test_result() 00137 : test(0),result(ok) 00138 { 00139 } 00140 00144 test_result( const std::string& grp,int pos,result_type res) 00145 : group(grp),test(pos),result(res) 00146 { 00147 } 00148 00152 test_result( const std::string& grp,int pos, 00153 result_type res, 00154 const std::exception& ex) 00155 : group(grp),test(pos),result(res), 00156 message(ex.what()),exception_typeid(typeid(ex).name()) 00157 { 00158 } 00159 }; 00160 00165 struct group_base 00166 { 00167 virtual ~group_base(){}; 00168 00169 // execute tests iteratively 00170 virtual void rewind() = 0; 00171 virtual test_result run_next() = 0; 00172 00173 // execute one test 00174 virtual test_result run_test(int n) = 0; 00175 }; 00176 00184 struct callback 00185 { 00189 virtual ~callback(){}; 00190 00194 virtual void run_started(){}; 00195 00200 virtual void group_started(const std::string& /*name*/){}; 00201 00206 virtual void test_completed(const test_result& /*tr*/){}; 00207 00212 virtual void group_completed(const std::string& /*name*/){}; 00213 00217 virtual void run_completed(){}; 00218 }; 00219 00223 typedef std::vector<std::string> groupnames; 00224 00228 class test_runner 00229 { 00230 protected: 00231 typedef std::map<std::string,group_base*> groups; 00232 typedef groups::iterator iterator; 00233 typedef groups::const_iterator const_iterator; 00234 groups groups_; 00235 00236 callback default_callback_; 00237 callback* callback_; 00238 00239 public: 00243 test_runner() : callback_(&default_callback_) 00244 { 00245 } 00246 00250 void register_group(const std::string& name,group_base* gr) 00251 { 00252 if( gr == 0 ) 00253 { 00254 throw std::invalid_argument("group shall be non-null"); 00255 } 00256 00257 groups::iterator found = groups_.find(name); 00258 if( found != groups_.end() ) 00259 { 00260 std::string msg("attempt to add already existent group "+name); 00261 // this exception terminates application so we use cerr also 00262 std::cerr << msg << std::endl; 00263 throw std::logic_error(msg); 00264 } 00265 00266 groups_[name] = gr; 00267 } 00268 00272 void set_callback(callback* cb) 00273 { 00274 callback_ = cb==0? &default_callback_:cb; 00275 } 00276 00280 callback& get_callback() const 00281 { 00282 return *callback_; 00283 } 00284 00288 const groupnames list_groups() const 00289 { 00290 groupnames ret; 00291 const_iterator i = groups_.begin(); 00292 const_iterator e = groups_.end(); 00293 while( i != e ) 00294 { 00295 ret.push_back(i->first); 00296 ++i; 00297 } 00298 return ret; 00299 } 00300 00305 void run_tests() const 00306 { 00307 callback_->run_started(); 00308 00309 const_iterator i = groups_.begin(); 00310 const_iterator e = groups_.end(); 00311 while( i != e ) 00312 { 00313 callback_->group_started(i->first); 00314 try 00315 { 00316 run_all_tests_in_group_(i); 00317 } 00318 catch( const no_more_tests& ) 00319 { 00320 callback_->group_completed(i->first); 00321 } 00322 00323 ++i; 00324 } 00325 00326 callback_->run_completed(); 00327 } 00328 00332 void run_tests(const std::string& group_name) const 00333 { 00334 callback_->run_started(); 00335 00336 const_iterator i = groups_.find(group_name); 00337 if( i == groups_.end() ) 00338 { 00339 callback_->run_completed(); 00340 throw no_such_group(group_name); 00341 } 00342 00343 callback_->group_started(group_name); 00344 try 00345 { 00346 run_all_tests_in_group_(i); 00347 } 00348 catch( const no_more_tests& ) 00349 { 00350 // ok 00351 } 00352 00353 callback_->group_completed(group_name); 00354 callback_->run_completed(); 00355 } 00356 00360 test_result run_test(const std::string& group_name,int n) const 00361 { 00362 callback_->run_started(); 00363 00364 const_iterator i = groups_.find(group_name); 00365 if( i == groups_.end() ) 00366 { 00367 callback_->run_completed(); 00368 throw no_such_group(group_name); 00369 } 00370 00371 callback_->group_started(group_name); 00372 try 00373 { 00374 test_result tr = i->second->run_test(n); 00375 callback_->test_completed(tr); 00376 callback_->group_completed(group_name); 00377 callback_->run_completed(); 00378 return tr; 00379 } 00380 catch( const beyond_last_test& ) 00381 { 00382 callback_->group_completed(group_name); 00383 callback_->run_completed(); 00384 throw; 00385 } 00386 catch( const no_such_test& ) 00387 { 00388 callback_->group_completed(group_name); 00389 callback_->run_completed(); 00390 throw; 00391 } 00392 } 00393 00394 private: 00395 void run_all_tests_in_group_(const_iterator i) const 00396 { 00397 i->second->rewind(); 00398 for( ;; ) 00399 { 00400 test_result tr = i->second->run_next(); 00401 callback_->test_completed(tr); 00402 00403 if( tr.result == test_result::ex_ctor ) 00404 { 00405 throw no_more_tests(); 00406 } 00407 } 00408 } 00409 }; 00410 00416 class test_runner_singleton 00417 { 00418 public: 00419 static test_runner& get() 00420 { 00421 static test_runner tr; 00422 return tr; 00423 } 00424 }; 00425 extern test_runner_singleton runner; 00426 00432 template <class Data> 00433 class test_object : public Data 00434 { 00435 public: 00439 test_object(){}; 00440 00448 bool called_method_was_a_dummy_test_; 00449 00453 template <int n> 00454 void test() 00455 { 00456 called_method_was_a_dummy_test_ = true; 00457 } 00458 }; 00459 00460 namespace 00461 { 00466 void ensure(bool cond) 00467 { 00468 if( !cond ) throw failure(""); 00469 } 00470 00475 template<typename T> 00476 void ensure(const T msg,bool cond) 00477 { 00478 if( !cond ) throw failure(msg); 00479 } 00480 00488 template <class T,class Q> 00489 void ensure_equals(const char* msg,const Q& actual,const T& expected) 00490 { 00491 if( expected != actual ) 00492 { 00493 std::stringstream ss; 00494 ss << (msg?msg:"") << (msg?": ":"") << "expected " << expected << " actual " << actual; 00495 throw failure(ss.str().c_str()); 00496 } 00497 } 00498 00499 template <class T,class Q> 00500 void ensure_equals(const Q& actual,const T& expected) 00501 { 00502 ensure_equals<>(0,actual,expected); 00503 } 00504 00514 template <class T> 00515 void ensure_distance(const char* msg,const T& actual,const T& expected,const T& distance) 00516 { 00517 if( expected-distance >= actual || expected+distance <= actual ) 00518 { 00519 std::stringstream ss; 00520 ss << (msg?msg:"") << (msg?": ":"") << "expected [" << expected-distance << ";" 00521 << expected+distance << "] actual " << actual; 00522 throw failure(ss.str().c_str()); 00523 } 00524 } 00525 00526 template <class T> 00527 void ensure_distance(const T& actual,const T& expected,const T& distance) 00528 { 00529 ensure_distance<>(0,actual,expected,distance); 00530 } 00531 00535 void fail(const char* msg="") 00536 { 00537 throw failure(msg); 00538 } 00539 } 00540 00545 template <class Test,class Group,int n> 00546 struct tests_registerer 00547 { 00548 static void reg(Group& group) 00549 { 00550 group.reg(n,&Test::template test<n>); 00551 tests_registerer<Test,Group,n-1>::reg(group); 00552 } 00553 }; 00554 00555 template<class Test,class Group> 00556 struct tests_registerer<Test,Group,0> 00557 { 00558 static void reg(Group&){}; 00559 }; 00560 00566 template <class Data,int MaxTestsInGroup = 50> 00567 class test_group : public group_base 00568 { 00569 const char* name_; 00570 00571 typedef void (test_object<Data>::*testmethod)(); 00572 typedef std::map<int,testmethod> tests; 00573 typedef typename tests::iterator tests_iterator; 00574 typedef typename tests::const_iterator tests_const_iterator; 00575 typedef typename tests::const_reverse_iterator 00576 tests_const_reverse_iterator; 00577 typedef typename tests::size_type size_type; 00578 00579 tests tests_; 00580 tests_iterator current_test_; 00581 00585 template <class T> 00586 class safe_holder 00587 { 00588 T* p_; 00589 bool permit_throw_in_dtor; 00590 00591 safe_holder(const safe_holder&); 00592 safe_holder& operator = (const safe_holder&); 00593 00594 public: 00595 safe_holder() : p_(0),permit_throw_in_dtor(false) 00596 { 00597 } 00598 00599 ~safe_holder() 00600 { 00601 release(); 00602 } 00603 00604 T* operator -> () const { return p_; }; 00605 T* get() const { return p_; }; 00606 00612 void permit_throw(){ permit_throw_in_dtor = true; } 00613 00620 void release() 00621 { 00622 try 00623 { 00624 if( delete_obj() == false ) 00625 { 00626 throw warning("destructor of test object raised an SEH exception"); 00627 } 00628 } 00629 catch( const std::exception& ex ) 00630 { 00631 if( permit_throw_in_dtor ) 00632 { 00633 std::string msg = "destructor of test object raised exception: "; 00634 msg += ex.what(); 00635 throw warning(msg); 00636 } 00637 } 00638 catch( ... ) 00639 { 00640 if( permit_throw_in_dtor ) 00641 { 00642 throw warning("destructor of test object raised an exception"); 00643 } 00644 } 00645 } 00646 00650 void reset() 00651 { 00652 release(); 00653 permit_throw_in_dtor = false; 00654 p_ = new T(); 00655 } 00656 00657 bool delete_obj() 00658 { 00659 #if defined(TUT_USE_SEH) 00660 __try 00661 { 00662 #endif 00663 T* p = p_; 00664 p_ = 0; 00665 delete p; 00666 #if defined(TUT_USE_SEH) 00667 } 00668 __except(handle_seh_(::GetExceptionCode())) 00669 { 00670 if( permit_throw_in_dtor ) 00671 { 00672 return false; 00673 } 00674 } 00675 #endif 00676 return true; 00677 } 00678 }; 00679 00680 public: 00681 typedef test_object<Data> object; 00682 00686 test_group(const char* name) 00687 : name_(name) 00688 { 00689 // register itself 00690 runner.get().register_group(name_,this); 00691 00692 // register all tests 00693 tests_registerer<object,test_group,MaxTestsInGroup>::reg(*this); 00694 }; 00695 00699 test_group(const char* name,test_runner& another_runner) 00700 : name_(name) 00701 { 00702 // register itself 00703 another_runner.register_group(name_,this); 00704 00705 // register all tests 00706 tests_registerer<test_object<Data>, 00707 test_group,MaxTestsInGroup>::reg(*this); 00708 }; 00709 00713 void reg(int n,testmethod tm) 00714 { 00715 tests_[n] = tm; 00716 } 00717 00721 void rewind() 00722 { 00723 current_test_ = tests_.begin(); 00724 } 00725 00729 test_result run_next() 00730 { 00731 if( current_test_ == tests_.end() ) 00732 { 00733 throw no_more_tests(); 00734 } 00735 00736 // find next user-specialized test 00737 safe_holder<object> obj; 00738 while( current_test_ != tests_.end() ) 00739 { 00740 try 00741 { 00742 return run_test_(current_test_++,obj); 00743 } 00744 catch( const no_such_test& ) 00745 { 00746 continue; 00747 } 00748 } 00749 00750 throw no_more_tests(); 00751 } 00752 00756 test_result run_test(int n) 00757 { 00758 // beyond tests is special case to discover upper limit 00759 if( tests_.rbegin() == tests_.rend() ) throw beyond_last_test(); 00760 if( tests_.rbegin()->first < n ) throw beyond_last_test(); 00761 00762 // withing scope; check if given test exists 00763 tests_iterator ti = tests_.find(n); 00764 if( ti == tests_.end() ) throw no_such_test(); 00765 00766 safe_holder<object> obj; 00767 return run_test_(ti,obj); 00768 } 00769 00770 private: 00775 test_result run_test_(const tests_iterator& ti,safe_holder<object>& obj) 00776 { 00777 try 00778 { 00779 if( run_test_seh_(ti->second,obj) == false ) 00780 throw seh("seh"); 00781 } 00782 catch(const no_such_test&) 00783 { 00784 throw; 00785 } 00786 catch(const warning& ex) 00787 { 00788 // test ok, but destructor failed 00789 test_result tr(name_,ti->first,test_result::warn,ex); 00790 return tr; 00791 } 00792 catch(const failure& ex) 00793 { 00794 // test failed because of ensure() or similar method 00795 test_result tr(name_,ti->first,test_result::fail,ex); 00796 return tr; 00797 } 00798 catch(const seh& ex) 00799 { 00800 // test failed with sigsegv, divide by zero, etc 00801 test_result tr(name_,ti->first,test_result::term,ex); 00802 return tr; 00803 } 00804 catch(const bad_ctor& ex) 00805 { 00806 // test failed because test ctor failed; stop the whole group 00807 test_result tr(name_,ti->first,test_result::ex_ctor,ex); 00808 return tr; 00809 } 00810 catch(const std::exception& ex) 00811 { 00812 // test failed with std::exception 00813 test_result tr(name_,ti->first,test_result::ex,ex); 00814 return tr; 00815 } 00816 catch(...) 00817 { 00818 // test failed with unknown exception 00819 test_result tr(name_,ti->first,test_result::ex); 00820 return tr; 00821 } 00822 00823 // test passed 00824 test_result tr(name_,ti->first,test_result::ok); 00825 return tr; 00826 } 00827 00831 bool run_test_seh_(testmethod tm,safe_holder<object>& obj) 00832 { 00833 #if defined(TUT_USE_SEH) 00834 __try 00835 { 00836 #endif 00837 if( obj.get() == 0 ) 00838 { 00839 reset_holder_(obj); 00840 } 00841 obj->called_method_was_a_dummy_test_ = false; 00842 00843 #if defined(TUT_USE_SEH) 00844 __try 00845 { 00846 #endif 00847 (obj.get()->*tm)(); 00848 #if defined(TUT_USE_SEH) 00849 } 00850 __except(handle_seh_(::GetExceptionCode())) 00851 { 00852 // throw seh("SEH"); 00853 return false; 00854 } 00855 #endif 00856 00857 if( obj->called_method_was_a_dummy_test_ ) 00858 { 00859 // do not call obj.release(); reuse object 00860 throw no_such_test(); 00861 } 00862 00863 obj.permit_throw(); 00864 obj.release(); 00865 #if defined(TUT_USE_SEH) 00866 } 00867 __except(handle_seh_(::GetExceptionCode())) 00868 { 00869 return false; 00870 } 00871 #endif 00872 return true; 00873 } 00874 00875 void reset_holder_(safe_holder<object>& obj) 00876 { 00877 try 00878 { 00879 obj.reset(); 00880 } 00881 catch(const std::exception& ex) 00882 { 00883 throw bad_ctor(ex.what()); 00884 } 00885 catch(...) 00886 { 00887 throw bad_ctor("test constructor has generated an exception; group execution is terminated"); 00888 } 00889 } 00890 }; 00891 00892 #if defined(TUT_USE_SEH) 00893 00896 inline int handle_seh_(DWORD excode) 00897 { 00898 switch(excode) 00899 { 00900 case EXCEPTION_ACCESS_VIOLATION: 00901 case EXCEPTION_DATATYPE_MISALIGNMENT: 00902 case EXCEPTION_BREAKPOINT: 00903 case EXCEPTION_SINGLE_STEP: 00904 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 00905 case EXCEPTION_FLT_DENORMAL_OPERAND: 00906 case EXCEPTION_FLT_DIVIDE_BY_ZERO: 00907 case EXCEPTION_FLT_INEXACT_RESULT: 00908 case EXCEPTION_FLT_INVALID_OPERATION: 00909 case EXCEPTION_FLT_OVERFLOW: 00910 case EXCEPTION_FLT_STACK_CHECK: 00911 case EXCEPTION_FLT_UNDERFLOW: 00912 case EXCEPTION_INT_DIVIDE_BY_ZERO: 00913 case EXCEPTION_INT_OVERFLOW: 00914 case EXCEPTION_PRIV_INSTRUCTION: 00915 case EXCEPTION_IN_PAGE_ERROR: 00916 case EXCEPTION_ILLEGAL_INSTRUCTION: 00917 case EXCEPTION_NONCONTINUABLE_EXCEPTION: 00918 case EXCEPTION_STACK_OVERFLOW: 00919 case EXCEPTION_INVALID_DISPOSITION: 00920 case EXCEPTION_GUARD_PAGE: 00921 case EXCEPTION_INVALID_HANDLE: 00922 return EXCEPTION_EXECUTE_HANDLER; 00923 }; 00924 00925 return EXCEPTION_CONTINUE_SEARCH; 00926 } 00927 #endif 00928 } 00929 00930 #endif 00931