src/tut/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 
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 

Generated on Fri Oct 26 13:35:13 2007 for FEMAXX (Finite Element Maxwell Eigensolver) by  doxygen 1.4.7