src/tut/tut_restartable.h

Go to the documentation of this file.
00001 #ifndef TUT_RESTARTABLE_H_GUARD
00002 #define TUT_RESTARTABLE_H_GUARD
00003 
00004 #include "tut.h"
00005 #include <fstream>
00006 #include <iostream>
00007 
00019 namespace tut
00020 {
00021   namespace util
00022   {
00026     std::string escape(const std::string& orig)
00027     {
00028       std::string rc;
00029       std::string::const_iterator i,e;
00030       i = orig.begin();
00031       e = orig.end();
00032 
00033       while( i != e )
00034       {
00035         if( (*i >= 'a' && *i <= 'z') ||
00036             (*i >= 'A' && *i <= 'Z') ||
00037             (*i >= '0' && *i <= '9') )
00038         {
00039           rc += *i;
00040         }
00041         else
00042         {
00043           rc += '\\';
00044           rc += ('a'+(((unsigned int)*i)>>4));
00045           rc += ('a'+(((unsigned int)*i)&0xF));
00046         }
00047 
00048         ++i;         
00049       }
00050       return rc;
00051     }
00052 
00056     std::string unescape(const std::string& orig)
00057     {
00058       std::string rc;
00059       std::string::const_iterator i,e;
00060       i = orig.begin();
00061       e = orig.end();
00062 
00063       while( i != e )
00064       {
00065         if( *i != '\\' )
00066         {
00067           rc += *i;
00068         }
00069         else
00070         {
00071           ++i; if( i == e ) throw std::invalid_argument("unexpected end of string");
00072           unsigned int c1 = *i;
00073           ++i; if( i == e ) throw std::invalid_argument("unexpected end of string");
00074           unsigned int c2 = *i;
00075           rc += (((c1-'a')<<4) + (c2-'a'));
00076         }
00077  
00078         ++i;
00079       }
00080       return rc;         
00081     }
00082 
00086     void serialize(std::ostream& os,const tut::test_result& tr)
00087     {
00088       os << escape(tr.group) << std::endl;
00089       os << tr.test << ' ';
00090       switch(tr.result)
00091       {
00092         case test_result::ok: os << 0; break;
00093         case test_result::fail: os << 1; break;
00094         case test_result::ex: os << 2; break;
00095         case test_result::warn: os << 3; break;
00096         case test_result::term: os << 4; break;
00097         default: throw std::logic_error("operator << : bad result_type");
00098       }  
00099       os << ' ' << escape(tr.message) << std::endl;
00100     }
00101 
00105     void deserialize(std::istream& is,tut::test_result& tr)
00106     {
00107       std::getline(is,tr.group);
00108       if( is.eof() ) throw tut::no_more_tests();
00109       tr.group = unescape(tr.group);
00110 
00111       tr.test = -1;
00112       is >> tr.test;
00113       if( tr.test < 0 ) throw std::logic_error("operator >> : bad test number");
00114 
00115       int n = -1; is >> n;
00116       switch(n)
00117       {
00118         case 0: tr.result = test_result::ok; break;
00119         case 1: tr.result = test_result::fail; break;
00120         case 2: tr.result = test_result::ex; break;
00121         case 3: tr.result = test_result::warn; break;
00122         case 4: tr.result = test_result::term; break;
00123         default: throw std::logic_error("operator >> : bad result_type");
00124       }  
00125  
00126       is.ignore(1); // space
00127       std::getline(is,tr.message);
00128       tr.message = unescape(tr.message);
00129       if( !is.good() ) throw std::logic_error("malformed test result");
00130     }
00131   };
00132 
00136   class restartable_wrapper
00137   {
00138     test_runner& runner_;
00139     callback* callback_;
00140 
00141     std::string dir_;
00142     std::string log_; // log file: last test being executed
00143     std::string jrn_; // journal file: results of all executed tests
00144 
00145     public:
00150     restartable_wrapper(const std::string& dir = ".") 
00151       : runner_(runner.get()), callback_(0), dir_(dir)
00152     {
00153       // dozen: it works, but it would be better to use system path separator
00154       jrn_ = dir_+'/'+"journal.tut";
00155       log_ = dir_+'/'+"log.tut";
00156     }
00157 
00161     void register_group(const std::string& name,group_base* gr)
00162     {
00163       runner_.register_group(name,gr);
00164     }
00165 
00169     void set_callback(callback* cb)
00170     {
00171       callback_ = cb;
00172     }
00173 
00177     callback& get_callback() const
00178     {
00179       return runner_.get_callback();
00180     }
00181 
00185     groupnames list_groups() const
00186     {
00187       return runner_.list_groups();
00188     }
00189 
00193     void run_tests() const
00194     {
00195       // where last run was failed
00196       std::string fail_group;
00197       int fail_test;
00198       read_log_(fail_group,fail_test);
00199       bool fail_group_reached = (fail_group == "");
00200 
00201       // iterate over groups
00202       tut::groupnames gn = list_groups();
00203       tut::groupnames::const_iterator gni,gne;
00204       gni = gn.begin();
00205       gne = gn.end();
00206       while( gni != gne )
00207       {
00208         // skip all groups before one that failed
00209         if( !fail_group_reached )
00210         {
00211           if( *gni != fail_group )
00212           {
00213             ++gni;
00214             continue;
00215           }
00216           fail_group_reached = true;
00217         }
00218 
00219         // first or restarted run
00220         int test = (*gni == fail_group && fail_test>=0)? fail_test+1:1;
00221         while(true)
00222         {
00223           // last executed test pos
00224           register_execution_(*gni,test);
00225 
00226           try
00227           {
00228             tut::test_result tr = runner_.run_test(*gni,test);
00229             register_test_(tr);
00230           }
00231           catch( const tut::beyond_last_test& ex )
00232           {
00233             break;
00234           }
00235           catch( const tut::no_such_test& ex )
00236           {
00237             // it's ok
00238           }
00239 
00240           ++test;
00241         }
00242 
00243         ++gni;      
00244       }
00245 
00246       // show final results to user
00247       invoke_callback_();
00248 
00249       // truncate files as mark of successful finish
00250       truncate_();
00251     }
00252 
00253   private:
00257     void invoke_callback_() const
00258     {
00259       runner_.set_callback(callback_);
00260       runner_.get_callback().run_started();
00261 
00262       std::string current_group;
00263       std::ifstream ijournal(jrn_.c_str());
00264       while( ijournal.good() )
00265       {
00266         // read next test result
00267         try
00268         {
00269           tut::test_result tr;
00270           util::deserialize(ijournal,tr);
00271           runner_.get_callback().test_completed(tr);
00272         }
00273         catch( const no_more_tests& )
00274         {
00275           break;
00276         }
00277       }
00278 
00279       runner_.get_callback().run_completed();
00280     }
00281 
00285     void register_test_(const test_result& tr) const
00286     {
00287       std::ofstream ojournal(jrn_.c_str(),std::ios::app);
00288       util::serialize(ojournal,tr);
00289       ojournal << std::flush;
00290       if( !ojournal.good() ) throw std::runtime_error("unable to register test result in file "+jrn_);
00291     }
00292 
00296     void register_execution_(const std::string& grp,int test) const
00297     {
00298       // last executed test pos
00299       std::ofstream olog(log_.c_str());
00300       olog << util::escape(grp) << std::endl << test << std::endl << std::flush;
00301       if( !olog.good() ) throw std::runtime_error("unable to register execution in file "+log_);
00302     }
00303 
00307     void truncate_() const
00308     {
00309       std::ofstream olog(log_.c_str());
00310       std::ofstream ojournal(jrn_.c_str());
00311     }
00312 
00316     void read_log_(std::string& fail_group,int& fail_test) const
00317     {
00318       // read failure point, if any
00319       std::ifstream ilog(log_.c_str());
00320       std::getline(ilog,fail_group);
00321       fail_group = util::unescape(fail_group);
00322       ilog >> fail_test;
00323       if( !ilog.good() )
00324       { 
00325         fail_group = ""; fail_test = -1; 
00326         truncate_();
00327       }
00328       else
00329       {
00330         // test was terminated...
00331         tut::test_result tr(fail_group,fail_test,tut::test_result::term);
00332         register_test_(tr);
00333       }
00334     }
00335   };
00336 }
00337 
00338 #endif

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