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
00160 virtual void rewind() = 0;
00161 virtual test_result run_next() = 0;
00162
00163
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& ){};
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
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 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
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
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
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
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
00654 runner.get().register_group(name_,this);
00655
00656
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
00667 another_runner.register_group(name_,this);
00668
00669
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
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
00723 if( tests_.rbegin() == tests_.rend() ) throw beyond_last_test();
00724 if( tests_.rbegin()->first < n ) throw beyond_last_test();
00725
00726
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
00753 test_result tr(name_,ti->first,test_result::warn,ex);
00754 return tr;
00755 }
00756 catch(const failure& ex)
00757 {
00758
00759 test_result tr(name_,ti->first,test_result::fail,ex);
00760 return tr;
00761 }
00762 catch(const seh& ex)
00763 {
00764
00765 test_result tr(name_,ti->first,test_result::term,ex);
00766 return tr;
00767 }
00768 catch(const std::exception& ex)
00769 {
00770
00771 test_result tr(name_,ti->first,test_result::ex,ex);
00772 return tr;
00773 }
00774 catch(...)
00775 {
00776
00777 test_result tr(name_,ti->first,test_result::ex);
00778 return tr;
00779 }
00780
00781
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
00808 return false;
00809 }
00810 #endif
00811
00812 if( obj->called_method_was_a_dummy_test_ )
00813 {
00814
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
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