1
#ifndef TUT_RESTARTABLE_H_GUARD
2
#define TUT_RESTARTABLE_H_GUARD
11
* Optional restartable wrapper for test_runner.
13
* Allows to restart test runs finished due to abnormal
14
* test application termination (such as segmentation
15
* fault or math error).
17
* @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com
27
* Escapes non-alphabetical characters in string.
29
std::string escape(const std::string& orig)
32
std::string::const_iterator i,e;
38
if ((*i >= 'a' && *i <= 'z') ||
39
(*i >= 'A' && *i <= 'Z') ||
40
(*i >= '0' && *i <= '9') )
47
rc += ('a'+(((unsigned int)*i) >> 4));
48
rc += ('a'+(((unsigned int)*i) & 0xF));
59
std::string unescape(const std::string& orig)
62
std::string::const_iterator i,e;
77
throw std::invalid_argument("unexpected end of string");
83
throw std::invalid_argument("unexpected end of string");
86
rc += (((c1 - 'a') << 4) + (c2 - 'a'));
95
* Serialize test_result avoiding interfering with operator <<.
97
void serialize(std::ostream& os, const tut::test_result& tr)
99
os << escape(tr.group) << std::endl;
100
os << tr.test << ' ';
103
case test_result::ok:
106
case test_result::fail:
109
case test_result::ex:
112
case test_result::warn:
115
case test_result::term:
118
case test_result::rethrown:
121
case test_result::ex_ctor:
124
case test_result::dummy:
125
assert(!"Should never be called");
127
throw std::logic_error("operator << : bad result_type");
129
os << ' ' << escape(tr.message) << std::endl;
133
* deserialization for test_result
135
bool deserialize(std::istream& is, tut::test_result& tr)
137
std::getline(is,tr.group);
142
tr.group = unescape(tr.group);
148
throw std::logic_error("operator >> : bad test number");
156
tr.result = test_result::ok;
159
tr.result = test_result::fail;
162
tr.result = test_result::ex;
165
tr.result = test_result::warn;
168
tr.result = test_result::term;
171
tr.result = test_result::rethrown;
174
tr.result = test_result::ex_ctor;
177
throw std::logic_error("operator >> : bad result_type");
180
is.ignore(1); // space
181
std::getline(is,tr.message);
182
tr.message = unescape(tr.message);
185
throw std::logic_error("malformed test result");
192
* Restartable test runner wrapper.
194
class restartable_wrapper
196
test_runner& runner_;
197
callbacks callbacks_;
200
std::string log_; // log file: last test being executed
201
std::string jrn_; // journal file: results of all executed tests
205
* Default constructor.
206
* @param dir Directory where to search/put log and journal files
208
restartable_wrapper(const std::string& dir = ".")
209
: runner_(runner.get()),
212
log_( dir + '/' + "log.tut" ),
213
jrn_( dir + '/' + "journal.tut" )
215
// dozen: it works, but it would be better to use system path separator
219
* Stores another group for getting by name.
221
void register_group(const std::string& name, group_base* gr)
223
runner_.register_group(name,gr);
227
* Stores callback object.
229
void set_callback(callback* cb)
232
callbacks_.insert(cb);
235
void insert_callback(callback* cb)
237
callbacks_.insert(cb);
240
void erase_callback(callback* cb)
242
callbacks_.erase(cb);
245
void set_callbacks(const callbacks& cb)
250
const callbacks& get_callbacks() const
252
return runner_.get_callbacks();
256
* Returns list of known test groups.
258
groupnames list_groups() const
260
return runner_.list_groups();
264
* Runs all tests in all groups.
266
void run_tests() const
268
// where last run was failed
269
std::string fail_group;
271
read_log_(fail_group,fail_test);
272
bool fail_group_reached = (fail_group == "");
274
// iterate over groups
275
tut::groupnames gn = list_groups();
276
tut::groupnames::const_iterator gni,gne;
281
// skip all groups before one that failed
282
if (!fail_group_reached)
284
if (*gni != fail_group)
289
fail_group_reached = true;
292
// first or restarted run
293
int test = (*gni == fail_group && fail_test >= 0) ? fail_test + 1 : 1;
296
// last executed test pos
297
register_execution_(*gni,test);
300
if( !runner_.run_test(*gni,test, tr) || tr.result == test_result::dummy )
312
// show final results to user
315
// truncate files as mark of successful finish
321
* Shows results from journal file.
323
void invoke_callback_() const
325
runner_.set_callbacks(callbacks_);
326
runner_.cb_run_started_();
328
std::string current_group;
329
std::ifstream ijournal(jrn_.c_str());
330
while (ijournal.good())
333
if( !util::deserialize(ijournal,tr) )
337
runner_.cb_test_completed_(tr);
340
runner_.cb_run_completed_();
344
* Register test into journal.
346
void register_test_(const test_result& tr) const
348
std::ofstream ojournal(jrn_.c_str(), std::ios::app);
349
util::serialize(ojournal, tr);
350
ojournal << std::flush;
351
if (!ojournal.good())
353
throw std::runtime_error("unable to register test result in file "
359
* Mark the fact test going to be executed
361
void register_execution_(const std::string& grp, int test) const
363
// last executed test pos
364
std::ofstream olog(log_.c_str());
365
olog << util::escape(grp) << std::endl << test << std::endl << std::flush;
368
throw std::runtime_error("unable to register execution in file "
376
void truncate_() const
378
std::ofstream olog(log_.c_str());
379
std::ofstream ojournal(jrn_.c_str());
385
void read_log_(std::string& fail_group, int& fail_test) const
387
// read failure point, if any
388
std::ifstream ilog(log_.c_str());
389
std::getline(ilog,fail_group);
390
fail_group = util::unescape(fail_group);
400
// test was terminated...
401
tut::test_result tr(fail_group, fail_test, "", tut::test_result::term);