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
19namespace 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 {
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
callback & get_callback() const
Returns callback object.
Definition tut_restartable.h:177
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
Test runner.
Definition tut.h:229
void set_callback(callback *cb)
Stores callback object.
Definition tut.h:272
callback & get_callback() const
Returns callback object.
Definition tut.h:280
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
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