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);