X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/e3e2d6326db86d7ca22d2cba1193aa64a8e33b2d..08dd72f2d8d3d7766e4fa87f01840c3ed8211091:/tests/tut/tut_restartable.hpp diff --git a/tests/tut/tut_restartable.hpp b/tests/tut/tut_restartable.hpp deleted file mode 100644 index 9f929234..00000000 --- a/tests/tut/tut_restartable.hpp +++ /dev/null @@ -1,410 +0,0 @@ -#ifndef TUT_RESTARTABLE_H_GUARD -#define TUT_RESTARTABLE_H_GUARD - -#include -#include -#include -#include -#include - -/** - * Optional restartable wrapper for test_runner. - * - * Allows to restart test runs finished due to abnormal - * test application termination (such as segmentation - * fault or math error). - * - * @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com - */ - -namespace tut -{ - -namespace util -{ - -/** - * Escapes non-alphabetical characters in string. - */ -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; -} - -/** - * Un-escapes string. - */ -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; -} - -/** - * Serialize test_result avoiding interfering with operator <<. - */ -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; - case test_result::rethrown: - os << 5; - break; - case test_result::ex_ctor: - os << 6; - break; - case test_result::dummy: - assert(!"Should never be called"); - default: - throw std::logic_error("operator << : bad result_type"); - } - os << ' ' << escape(tr.message) << std::endl; -} - -/** - * deserialization for test_result - */ -bool deserialize(std::istream& is, tut::test_result& tr) -{ - std::getline(is,tr.group); - if (is.eof()) - { - return false; - } - 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; - case 5: - tr.result = test_result::rethrown; - break; - case 6: - tr.result = test_result::ex_ctor; - 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"); - } - return true; -} -} - -/** - * Restartable test runner wrapper. - */ -class restartable_wrapper -{ - test_runner& runner_; - callbacks callbacks_; - - std::string dir_; - std::string log_; // log file: last test being executed - std::string jrn_; // journal file: results of all executed tests - -public: - /** - * Default constructor. - * @param dir Directory where to search/put log and journal files - */ - restartable_wrapper(const std::string& dir = ".") - : runner_(runner.get()), - callbacks_(), - dir_(dir), - log_( dir + '/' + "log.tut" ), - jrn_( dir + '/' + "journal.tut" ) - { - // dozen: it works, but it would be better to use system path separator - } - - /** - * Stores another group for getting by name. - */ - void register_group(const std::string& name, group_base* gr) - { - runner_.register_group(name,gr); - } - - /** - * Stores callback object. - */ - void set_callback(callback* cb) - { - callbacks_.clear(); - callbacks_.insert(cb); - } - - void insert_callback(callback* cb) - { - callbacks_.insert(cb); - } - - void erase_callback(callback* cb) - { - callbacks_.erase(cb); - } - - void set_callbacks(const callbacks& cb) - { - callbacks_ = cb; - } - - const callbacks& get_callbacks() const - { - return runner_.get_callbacks(); - } - - /** - * Returns list of known test groups. - */ - groupnames list_groups() const - { - return runner_.list_groups(); - } - - /** - * Runs all tests in all 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); - - tut::test_result tr; - if( !runner_.run_test(*gni,test, tr) || tr.result == test_result::dummy ) - { - break; - } - register_test_(tr); - - ++test; - } - - ++gni; - } - - // show final results to user - invoke_callback_(); - - // truncate files as mark of successful finish - truncate_(); - } - -private: - /** - * Shows results from journal file. - */ - void invoke_callback_() const - { - runner_.set_callbacks(callbacks_); - runner_.cb_run_started_(); - - std::string current_group; - std::ifstream ijournal(jrn_.c_str()); - while (ijournal.good()) - { - tut::test_result tr; - if( !util::deserialize(ijournal,tr) ) - { - break; - } - runner_.cb_test_completed_(tr); - } - - runner_.cb_run_completed_(); - } - - /** - * Register test into journal. - */ - 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_); - } - } - - /** - * Mark the fact test going to be executed - */ - 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_); - } - } - - /** - * Truncate tests. - */ - void truncate_() const - { - std::ofstream olog(log_.c_str()); - std::ofstream ojournal(jrn_.c_str()); - } - - /** - * Read log file - */ - 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 -