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 // dozen: it works, but it would be better to use system path separator
213 jrn_ = dir_ + '/' + "journal.tut";
214 log_ = dir_ + '/' + "log.tut";
218 * Stores another group for getting by name.
220 void register_group(const std::string& name, group_base* gr)
222 runner_.register_group(name,gr);
226 * Stores callback object.
228 void set_callback(callback* cb)
231 callbacks_.insert(cb);
234 void insert_callback(callback* cb)
236 callbacks_.insert(cb);
239 void erase_callback(callback* cb)
241 callbacks_.erase(cb);
244 void set_callbacks(const callbacks& cb)
249 const callbacks& get_callbacks() const
251 return runner_.get_callbacks();
255 * Returns list of known test groups.
257 groupnames list_groups() const
259 return runner_.list_groups();
263 * Runs all tests in all groups.
265 void run_tests() const
267 // where last run was failed
268 std::string fail_group;
270 read_log_(fail_group,fail_test);
271 bool fail_group_reached = (fail_group == "");
273 // iterate over groups
274 tut::groupnames gn = list_groups();
275 tut::groupnames::const_iterator gni,gne;
280 // skip all groups before one that failed
281 if (!fail_group_reached)
283 if (*gni != fail_group)
288 fail_group_reached = true;
291 // first or restarted run
292 int test = (*gni == fail_group && fail_test >= 0) ? fail_test + 1 : 1;
295 // last executed test pos
296 register_execution_(*gni,test);
299 if( !runner_.run_test(*gni,test, tr) || tr.result == test_result::dummy )
311 // show final results to user
314 // truncate files as mark of successful finish
320 * Shows results from journal file.
322 void invoke_callback_() const
324 runner_.set_callbacks(callbacks_);
325 runner_.cb_run_started_();
327 std::string current_group;
328 std::ifstream ijournal(jrn_.c_str());
329 while (ijournal.good())
332 if( !util::deserialize(ijournal,tr) )
336 runner_.cb_test_completed_(tr);
339 runner_.cb_run_completed_();
343 * Register test into journal.
345 void register_test_(const test_result& tr) const
347 std::ofstream ojournal(jrn_.c_str(), std::ios::app);
348 util::serialize(ojournal, tr);
349 ojournal << std::flush;
350 if (!ojournal.good())
352 throw std::runtime_error("unable to register test result in file "
358 * Mark the fact test going to be executed
360 void register_execution_(const std::string& grp, int test) const
362 // last executed test pos
363 std::ofstream olog(log_.c_str());
364 olog << util::escape(grp) << std::endl << test << std::endl << std::flush;
367 throw std::runtime_error("unable to register execution in file "
375 void truncate_() const
377 std::ofstream olog(log_.c_str());
378 std::ofstream ojournal(jrn_.c_str());
384 void read_log_(std::string& fail_group, int& fail_test) const
386 // read failure point, if any
387 std::ifstream ilog(log_.c_str());
388 std::getline(ilog,fail_group);
389 fail_group = util::unescape(fail_group);
399 // test was terminated...
400 tut::test_result tr(fail_group, fail_test, "", tut::test_result::term);