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
00170 virtual void rewind() = 0;
00171 virtual test_result run_next() = 0;
00172
00173
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& ){};
00201
00206 virtual void test_completed(const test_result& ){};
00207
00212 virtual void group_completed(const std::string& ){};
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
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
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
00690 runner.get().register_group(name_,this);
00691
00692
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
00703 another_runner.register_group(name_,this);
00704
00705
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
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
00759 if( tests_.rbegin() == tests_.rend() ) throw beyond_last_test();
00760 if( tests_.rbegin()->first < n ) throw beyond_last_test();
00761
00762
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
00789 test_result tr(name_,ti->first,test_result::warn,ex);
00790 return tr;
00791 }
00792 catch(const failure& ex)
00793 {
00794
00795 test_result tr(name_,ti->first,test_result::fail,ex);
00796 return tr;
00797 }
00798 catch(const seh& ex)
00799 {
00800
00801 test_result tr(name_,ti->first,test_result::term,ex);
00802 return tr;
00803 }
00804 catch(const bad_ctor& ex)
00805 {
00806
00807 test_result tr(name_,ti->first,test_result::ex_ctor,ex);
00808 return tr;
00809 }
00810 catch(const std::exception& ex)
00811 {
00812
00813 test_result tr(name_,ti->first,test_result::ex,ex);
00814 return tr;
00815 }
00816 catch(...)
00817 {
00818
00819 test_result tr(name_,ti->first,test_result::ex);
00820 return tr;
00821 }
00822
00823
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
00853 return false;
00854 }
00855 #endif
00856
00857 if( obj->called_method_was_a_dummy_test_ )
00858 {
00859
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