wibble  0.1.28
tut.h
Go to the documentation of this file.
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