+++ /dev/null
-#ifndef TUT_RESTARTABLE_H_GUARD
-#define TUT_RESTARTABLE_H_GUARD
-
-#include <tut/tut.hpp>
-#include <fstream>
-#include <iostream>
-#include <stdexcept>
-#include <cassert>
-
-/**
- * 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
-