tut_restartable.h
Go to the documentation of this file.
#ifndef TUT_RESTARTABLE_H_GUARD
#define TUT_RESTARTABLE_H_GUARD
#include <wibble/tests/tut.h>
#include <fstream>
#include <iostream>
namespace tut
{
namespace util
{
std::string escape(const std::string& orig)
{
std::string rc;
std::string::const_iterator i,e;
i = orig.begin();
e = orig.end();
while( i != e )
{
if( (*i >= 'a' && *i <= 'z') ||
(*i >= 'A' && *i <= 'Z') ||
(*i >= '0' && *i <= '9') )
{
rc += *i;
}
else
{
rc += '\\';
rc += ('a'+(((unsigned int)*i)>>4));
rc += ('a'+(((unsigned int)*i)&0xF));
}
++i;
}
return rc;
}
std::string unescape(const std::string& orig)
{
std::string rc;
std::string::const_iterator i,e;
i = orig.begin();
e = orig.end();
while( i != e )
{
if( *i != '\\' )
{
rc += *i;
}
else
{
++i; if( i == e ) throw std::invalid_argument("unexpected end of string");
unsigned int c1 = *i;
++i; if( i == e ) throw std::invalid_argument("unexpected end of string");
unsigned int c2 = *i;
rc += (((c1-'a')<<4) + (c2-'a'));
}
++i;
}
return rc;
}
void serialize(std::ostream& os,const tut::test_result& tr)
{
os << escape(tr.group) << std::endl;
os << tr.test << ' ';
switch(tr.result)
{
case test_result::ok: os << 0; break;
case test_result::fail: os << 1; break;
case test_result::ex: os << 2; break;
case test_result::warn: os << 3; break;
case test_result::term: os << 4; break;
default: throw std::logic_error("operator << : bad result_type");
}
os << ' ' << escape(tr.message) << std::endl;
}
void deserialize(std::istream& is,tut::test_result& tr)
{
std::getline(is,tr.group);
if( is.eof() ) throw tut::no_more_tests();
tr.group = unescape(tr.group);
tr.test = -1;
is >> tr.test;
if( tr.test < 0 ) throw std::logic_error("operator >> : bad test number");
int n = -1; is >> n;
switch(n)
{
case 0: tr.result = test_result::ok; break;
case 1: tr.result = test_result::fail; break;
case 2: tr.result = test_result::ex; break;
case 3: tr.result = test_result::warn; break;
case 4: tr.result = test_result::term; break;
default: throw std::logic_error("operator >> : bad result_type");
}
is.ignore(1); // space
std::getline(is,tr.message);
tr.message = unescape(tr.message);
if( !is.good() ) throw std::logic_error("malformed test result");
}
};
class restartable_wrapper
{
test_runner& runner_;
callback* callback_;
std::string dir_;
std::string log_; // log file: last test being executed
std::string jrn_; // journal file: results of all executed tests
public:
restartable_wrapper(const std::string& dir = ".")
: runner_(runner.get()), callback_(0), dir_(dir)
{
// dozen: it works, but it would be better to use system path separator
jrn_ = dir_+'/'+"journal.tut";
log_ = dir_+'/'+"log.tut";
}
void register_group(const std::string& name,group_base* gr)
{
runner_.register_group(name,gr);
}
void set_callback(callback* cb)
{
callback_ = cb;
}
callback& get_callback() const
{
return runner_.get_callback();
}
groupnames list_groups() const
{
return runner_.list_groups();
}
void run_tests() const
{
// where last run was failed
std::string fail_group;
int fail_test;
read_log_(fail_group,fail_test);
bool fail_group_reached = (fail_group == "");
// iterate over groups
tut::groupnames gn = list_groups();
tut::groupnames::const_iterator gni,gne;
gni = gn.begin();
gne = gn.end();
while( gni != gne )
{
// skip all groups before one that failed
if( !fail_group_reached )
{
if( *gni != fail_group )
{
++gni;
continue;
}
fail_group_reached = true;
}
// first or restarted run
int test = (*gni == fail_group && fail_test>=0)? fail_test+1:1;
while(true)
{
// last executed test pos
register_execution_(*gni,test);
try
{
tut::test_result tr = runner_.run_test(*gni,test);
register_test_(tr);
}
catch( const tut::beyond_last_test& ex )
{
break;
}
catch( const tut::no_such_test& ex )
{
// it's ok
}
++test;
}
++gni;
}
// show final results to user
invoke_callback_();
// truncate files as mark of successful finish
truncate_();
}
private:
void invoke_callback_() const
{
runner_.set_callback(callback_);
runner_.get_callback().run_started();
std::string current_group;
std::ifstream ijournal(jrn_.c_str());
while( ijournal.good() )
{
// read next test result
try
{
tut::test_result tr;
util::deserialize(ijournal,tr);
runner_.get_callback().test_completed(tr);
}
catch( const no_more_tests& )
{
break;
}
}
runner_.get_callback().run_completed();
}
void register_test_(const test_result& tr) const
{
std::ofstream ojournal(jrn_.c_str(),std::ios::app);
util::serialize(ojournal,tr);
ojournal << std::flush;
if( !ojournal.good() ) throw std::runtime_error("unable to register test result in file "+jrn_);
}
void register_execution_(const std::string& grp,int test) const
{
// last executed test pos
std::ofstream olog(log_.c_str());
olog << util::escape(grp) << std::endl << test << std::endl << std::flush;
if( !olog.good() ) throw std::runtime_error("unable to register execution in file "+log_);
}
void truncate_() const
{
std::ofstream olog(log_.c_str());
std::ofstream ojournal(jrn_.c_str());
}
void read_log_(std::string& fail_group,int& fail_test) const
{
// read failure point, if any
std::ifstream ilog(log_.c_str());
std::getline(ilog,fail_group);
fail_group = util::unescape(fail_group);
ilog >> fail_test;
if( !ilog.good() )
{
fail_group = ""; fail_test = -1;
truncate_();
}
else
{
// test was terminated...
tut::test_result tr(fail_group,fail_test,tut::test_result::term);
register_test_(tr);
}
}
};
}
#endif
tut::restartable_wrapper
Restartable test runner wrapper.
Definition: tut_restartable.h:137
tut::restartable_wrapper::list_groups
groupnames list_groups() const
Returns list of known test groups.
Definition: tut_restartable.h:185
tut::restartable_wrapper::restartable_wrapper
restartable_wrapper(const std::string &dir=".")
Default constructor.
Definition: tut_restartable.h:150
tut::restartable_wrapper::set_callback
void set_callback(callback *cb)
Stores callback object.
Definition: tut_restartable.h:169
tut::restartable_wrapper::register_group
void register_group(const std::string &name, group_base *gr)
Stores another group for getting by name.
Definition: tut_restartable.h:161
tut::restartable_wrapper::run_tests
void run_tests() const
Runs all tests in all groups.
Definition: tut_restartable.h:193
tut::restartable_wrapper::get_callback
callback & get_callback() const
Returns callback object.
Definition: tut_restartable.h:177
tut::test_runner
Test runner.
Definition: tut.h:229
tut::test_runner::set_callback
void set_callback(callback *cb)
Stores callback object.
Definition: tut.h:272
tut::test_runner::register_group
void register_group(const std::string &name, group_base *gr)
Stores another group for getting by name.
Definition: tut.h:250
tut::test_runner::run_test
test_result run_test(const std::string &group_name, int n) const
Runs one test in specified group.
Definition: tut.h:360
tut::test_runner::get_callback
callback & get_callback() const
Returns callback object.
Definition: tut.h:280
tut::test_runner::list_groups
const groupnames list_groups() const
Returns list of known test groups.
Definition: tut.h:288
tut::util::unescape
std::string unescape(const std::string &orig)
Un-escapes string.
Definition: tut_restartable.h:56
tut::util::deserialize
void deserialize(std::istream &is, tut::test_result &tr)
deserialization for test_result
Definition: tut_restartable.h:105
tut::util::escape
std::string escape(const std::string &orig)
Escapes non-alphabetical characters in string.
Definition: tut_restartable.h:26
tut::util::serialize
void serialize(std::ostream &os, const tut::test_result &tr)
Serialize test_result avoiding interfering with operator <<.
Definition: tut_restartable.h:86
tut
Template Unit Tests Framework for C++.
Definition: tut-main.cpp:7
tut::groupnames
std::vector< std::string > groupnames
Typedef for runner::list_groups()
Definition: tut.h:223
tut::runner
test_runner_singleton runner
Definition: tut-main.cpp:8
tut::beyond_last_test
No such test and passed test number is higher than any test number in current group.
Definition: tut.h:40
tut::callback
Test runner callback interface.
Definition: tut.h:185
tut::callback::run_started
virtual void run_started()
Called when new test run started.
Definition: tut.h:194
tut::callback::test_completed
virtual void test_completed(const test_result &)
Called when a test finished.
Definition: tut.h:206
tut::callback::run_completed
virtual void run_completed()
Called when all tests in run completed.
Definition: tut.h:217
tut::group_base
Interface.
Definition: tut.h:166
tut::no_more_tests
Internal exception to be throwed when no more tests left in group or journal.
Definition: tut.h:58
tut::no_such_test
Exception to be throwed when attempted to execute missed test by number.
Definition: tut.h:30
tut::test_result
Return type of runned test/test group.
Definition: tut.h:106
tut::test_result::result
result_type result
Definition: tut.h:125
tut::test_result::group
std::string group
Test group name.
Definition: tut.h:110
tut::test_result::message
std::string message
Exception message for failed test.
Definition: tut.h:130
tut::test_result::term
@ term
Definition: tut.h:124
tut::test_result::fail
@ fail
Definition: tut.h:124
tut::test_result::ok
@ ok
Definition: tut.h:124
tut::test_result::warn
@ warn
Definition: tut.h:124
tut::test_result::ex
@ ex
Definition: tut.h:124
tut::test_result::test
int test
Test number in group.
Definition: tut.h:115
tut.h