wibble  1.1
tut_restartable.h
Go to the documentation of this file.
1 #ifndef TUT_RESTARTABLE_H_GUARD
2 #define TUT_RESTARTABLE_H_GUARD
3 
4 #include <wibble/tests/tut.h>
5 #include <fstream>
6 #include <iostream>
7 
19 namespace tut
20 {
21  namespace util
22  {
26  std::string escape(const std::string& orig)
27  {
28  std::string rc;
29  std::string::const_iterator i,e;
30  i = orig.begin();
31  e = orig.end();
32 
33  while( i != e )
34  {
35  if( (*i >= 'a' && *i <= 'z') ||
36  (*i >= 'A' && *i <= 'Z') ||
37  (*i >= '0' && *i <= '9') )
38  {
39  rc += *i;
40  }
41  else
42  {
43  rc += '\\';
44  rc += ('a'+(((unsigned int)*i)>>4));
45  rc += ('a'+(((unsigned int)*i)&0xF));
46  }
47 
48  ++i;
49  }
50  return rc;
51  }
52 
56  std::string unescape(const std::string& orig)
57  {
58  std::string rc;
59  std::string::const_iterator i,e;
60  i = orig.begin();
61  e = orig.end();
62 
63  while( i != e )
64  {
65  if( *i != '\\' )
66  {
67  rc += *i;
68  }
69  else
70  {
71  ++i; if( i == e ) throw std::invalid_argument("unexpected end of string");
72  unsigned int c1 = *i;
73  ++i; if( i == e ) throw std::invalid_argument("unexpected end of string");
74  unsigned int c2 = *i;
75  rc += (((c1-'a')<<4) + (c2-'a'));
76  }
77 
78  ++i;
79  }
80  return rc;
81  }
82 
86  void serialize(std::ostream& os,const tut::test_result& tr)
87  {
88  os << escape(tr.group) << std::endl;
89  os << tr.test << ' ';
90  switch(tr.result)
91  {
92  case test_result::ok: os << 0; break;
93  case test_result::fail: os << 1; break;
94  case test_result::ex: os << 2; break;
95  case test_result::warn: os << 3; break;
96  case test_result::term: os << 4; break;
97  default: throw std::logic_error("operator << : bad result_type");
98  }
99  os << ' ' << escape(tr.message) << std::endl;
100  }
101 
105  void deserialize(std::istream& is,tut::test_result& tr)
106  {
107  std::getline(is,tr.group);
108  if( is.eof() ) throw tut::no_more_tests();
109  tr.group = unescape(tr.group);
110 
111  tr.test = -1;
112  is >> tr.test;
113  if( tr.test < 0 ) throw std::logic_error("operator >> : bad test number");
114 
115  int n = -1; is >> n;
116  switch(n)
117  {
118  case 0: tr.result = test_result::ok; break;
119  case 1: tr.result = test_result::fail; break;
120  case 2: tr.result = test_result::ex; break;
121  case 3: tr.result = test_result::warn; break;
122  case 4: tr.result = test_result::term; break;
123  default: throw std::logic_error("operator >> : bad result_type");
124  }
125 
126  is.ignore(1); // space
127  std::getline(is,tr.message);
128  tr.message = unescape(tr.message);
129  if( !is.good() ) throw std::logic_error("malformed test result");
130  }
131  };
132 
137  {
138  test_runner& runner_;
139  callback* callback_;
140 
141  std::string dir_;
142  std::string log_; // log file: last test being executed
143  std::string jrn_; // journal file: results of all executed tests
144 
145  public:
150  restartable_wrapper(const std::string& dir = ".")
151  : runner_(runner.get()), callback_(0), dir_(dir)
152  {
153  // dozen: it works, but it would be better to use system path separator
154  jrn_ = dir_+'/'+"journal.tut";
155  log_ = dir_+'/'+"log.tut";
156  }
157 
161  void register_group(const std::string& name,group_base* gr)
162  {
163  runner_.register_group(name,gr);
164  }
165 
170  {
171  callback_ = cb;
172  }
173 
178  {
179  return runner_.get_callback();
180  }
181 
186  {
187  return runner_.list_groups();
188  }
189 
193  void run_tests() const
194  {
195  // where last run was failed
196  std::string fail_group;
197  int fail_test;
198  read_log_(fail_group,fail_test);
199  bool fail_group_reached = (fail_group == "");
200 
201  // iterate over groups
203  tut::groupnames::const_iterator gni,gne;
204  gni = gn.begin();
205  gne = gn.end();
206  while( gni != gne )
207  {
208  // skip all groups before one that failed
209  if( !fail_group_reached )
210  {
211  if( *gni != fail_group )
212  {
213  ++gni;
214  continue;
215  }
216  fail_group_reached = true;
217  }
218 
219  // first or restarted run
220  int test = (*gni == fail_group && fail_test>=0)? fail_test+1:1;
221  while(true)
222  {
223  // last executed test pos
224  register_execution_(*gni,test);
225 
226  try
227  {
228  tut::test_result tr = runner_.run_test(*gni,test);
229  register_test_(tr);
230  }
231  catch( const tut::beyond_last_test& ex )
232  {
233  break;
234  }
235  catch( const tut::no_such_test& ex )
236  {
237  // it's ok
238  }
239 
240  ++test;
241  }
242 
243  ++gni;
244  }
245 
246  // show final results to user
247  invoke_callback_();
248 
249  // truncate files as mark of successful finish
250  truncate_();
251  }
252 
253  private:
257  void invoke_callback_() const
258  {
259  runner_.set_callback(callback_);
260  runner_.get_callback().run_started();
261 
262  std::string current_group;
263  std::ifstream ijournal(jrn_.c_str());
264  while( ijournal.good() )
265  {
266  // read next test result
267  try
268  {
269  tut::test_result tr;
270  util::deserialize(ijournal,tr);
271  runner_.get_callback().test_completed(tr);
272  }
273  catch( const no_more_tests& )
274  {
275  break;
276  }
277  }
278 
279  runner_.get_callback().run_completed();
280  }
281 
285  void register_test_(const test_result& tr) const
286  {
287  std::ofstream ojournal(jrn_.c_str(),std::ios::app);
288  util::serialize(ojournal,tr);
289  ojournal << std::flush;
290  if( !ojournal.good() ) throw std::runtime_error("unable to register test result in file "+jrn_);
291  }
292 
296  void register_execution_(const std::string& grp,int test) const
297  {
298  // last executed test pos
299  std::ofstream olog(log_.c_str());
300  olog << util::escape(grp) << std::endl << test << std::endl << std::flush;
301  if( !olog.good() ) throw std::runtime_error("unable to register execution in file "+log_);
302  }
303 
307  void truncate_() const
308  {
309  std::ofstream olog(log_.c_str());
310  std::ofstream ojournal(jrn_.c_str());
311  }
312 
316  void read_log_(std::string& fail_group,int& fail_test) const
317  {
318  // read failure point, if any
319  std::ifstream ilog(log_.c_str());
320  std::getline(ilog,fail_group);
321  fail_group = util::unescape(fail_group);
322  ilog >> fail_test;
323  if( !ilog.good() )
324  {
325  fail_group = ""; fail_test = -1;
326  truncate_();
327  }
328  else
329  {
330  // test was terminated...
331  tut::test_result tr(fail_group,fail_test,tut::test_result::term);
332  register_test_(tr);
333  }
334  }
335  };
336 }
337 
338 #endif
339 
Restartable test runner wrapper.
Definition: tut_restartable.h:137
groupnames list_groups() const
Returns list of known test groups.
Definition: tut_restartable.h:185
restartable_wrapper(const std::string &dir=".")
Default constructor.
Definition: tut_restartable.h:150
void set_callback(callback *cb)
Stores callback object.
Definition: tut_restartable.h:169
void register_group(const std::string &name, group_base *gr)
Stores another group for getting by name.
Definition: tut_restartable.h:161
void run_tests() const
Runs all tests in all groups.
Definition: tut_restartable.h:193
callback & get_callback() const
Returns callback object.
Definition: tut_restartable.h:177
Test runner.
Definition: tut.h:229
void set_callback(callback *cb)
Stores callback object.
Definition: tut.h:272
void register_group(const std::string &name, group_base *gr)
Stores another group for getting by name.
Definition: tut.h:250
test_result run_test(const std::string &group_name, int n) const
Runs one test in specified group.
Definition: tut.h:360
callback & get_callback() const
Returns callback object.
Definition: tut.h:280
const groupnames list_groups() const
Returns list of known test groups.
Definition: tut.h:288
std::string unescape(const std::string &orig)
Un-escapes string.
Definition: tut_restartable.h:56
void deserialize(std::istream &is, tut::test_result &tr)
deserialization for test_result
Definition: tut_restartable.h:105
std::string escape(const std::string &orig)
Escapes non-alphabetical characters in string.
Definition: tut_restartable.h:26
void serialize(std::ostream &os, const tut::test_result &tr)
Serialize test_result avoiding interfering with operator <<.
Definition: tut_restartable.h:86
Template Unit Tests Framework for C++.
Definition: tut-main.cpp:7
std::vector< std::string > groupnames
Typedef for runner::list_groups()
Definition: tut.h:223
test_runner_singleton runner
Definition: tut-main.cpp:8
No such test and passed test number is higher than any test number in current group.
Definition: tut.h:40
Test runner callback interface.
Definition: tut.h:185
virtual void run_started()
Called when new test run started.
Definition: tut.h:194
virtual void test_completed(const test_result &)
Called when a test finished.
Definition: tut.h:206
virtual void run_completed()
Called when all tests in run completed.
Definition: tut.h:217
Interface.
Definition: tut.h:166
Internal exception to be throwed when no more tests left in group or journal.
Definition: tut.h:58
Exception to be throwed when attempted to execute missed test by number.
Definition: tut.h:30
Return type of runned test/test group.
Definition: tut.h:106
result_type result
Definition: tut.h:125
std::string group
Test group name.
Definition: tut.h:110
std::string message
Exception message for failed test.
Definition: tut.h:130
@ term
Definition: tut.h:124
@ fail
Definition: tut.h:124
@ ok
Definition: tut.h:124
@ warn
Definition: tut.h:124
@ ex
Definition: tut.h:124
int test
Test number in group.
Definition: tut.h:115