wibble  1.1
tut.h
Go to the documentation of this file.
1 #ifndef TUT_H_GUARD
2 #define TUT_H_GUARD
3 
4 #include <iostream>
5 #include <map>
6 #include <vector>
7 #include <string>
8 #include <sstream>
9 #include <stdexcept>
10 #include <typeinfo>
11 
12 #if defined(TUT_USE_SEH)
13 #include <windows.h>
14 #include <winbase.h>
15 #endif
16 
23 namespace tut
24 {
29  struct no_such_test : public std::logic_error
30  {
31  no_such_test() : std::logic_error("no such test"){};
32  };
33 
40  {
42  };
43 
47  struct no_such_group : public std::logic_error
48  {
49  no_such_group(const std::string& grp) :
50  std::logic_error(grp){};
51  };
52 
58  {
60  };
61 
66  struct bad_ctor : public std::logic_error
67  {
68  bad_ctor(const std::string& msg) :
69  std::logic_error(msg){};
70  };
71 
75  class failure : public std::logic_error
76  {
77  public:
78  failure(const std::string& msg) : std::logic_error(msg){};
79  };
80 
84  class warning : public std::logic_error
85  {
86  public:
87  warning(const std::string& msg) : std::logic_error(msg){};
88  };
89 
93  class seh : public std::logic_error
94  {
95  public:
96  seh(const std::string& msg) : std::logic_error(msg){};
97  };
98 
105  struct test_result
106  {
110  std::string group;
111 
115  int test;
116 
124  typedef enum { ok, fail, ex, warn, term, ex_ctor } result_type;
125  result_type result;
126 
130  std::string message;
131  std::string exception_typeid;
132 
137  : test(0),result(ok)
138  {
139  }
140 
144  test_result( const std::string& grp,int pos,result_type res)
145  : group(grp),test(pos),result(res)
146  {
147  }
148 
152  test_result( const std::string& grp,int pos,
153  result_type res,
154  const std::exception& ex)
155  : group(grp),test(pos),result(res),
156  message(ex.what()),exception_typeid(typeid(ex).name())
157  {
158  }
159  };
160 
165  struct group_base
166  {
167  virtual ~group_base(){};
168 
169  // execute tests iteratively
170  virtual void rewind() = 0;
171  virtual test_result run_next() = 0;
172 
173  // execute one test
174  virtual test_result run_test(int n) = 0;
175  };
176 
184  struct callback
185  {
189  virtual ~callback(){};
190 
194  virtual void run_started(){};
195 
200  virtual void group_started(const std::string& /*name*/){};
201 
206  virtual void test_completed(const test_result& /*tr*/){};
207 
212  virtual void group_completed(const std::string& /*name*/){};
213 
217  virtual void run_completed(){};
218  };
219 
223  typedef std::vector<std::string> groupnames;
224 
229  {
230  protected:
231  typedef std::map<std::string,group_base*> groups;
233  typedef groups::const_iterator const_iterator;
234  groups groups_;
235 
238 
239  public:
243  test_runner() : callback_(&default_callback_)
244  {
245  }
246 
250  void register_group(const std::string& name,group_base* gr)
251  {
252  if( gr == 0 )
253  {
254  throw std::invalid_argument("group shall be non-null");
255  }
256 
257  groups::iterator found = groups_.find(name);
258  if( found != groups_.end() )
259  {
260  std::string msg("attempt to add already existent group "+name);
261  // this exception terminates application so we use cerr also
262  std::cerr << msg << std::endl;
263  throw std::logic_error(msg);
264  }
265 
266  groups_[name] = gr;
267  }
268 
273  {
274  callback_ = cb==0? &default_callback_:cb;
275  }
276 
281  {
282  return *callback_;
283  }
284 
288  const groupnames list_groups() const
289  {
290  groupnames ret;
291  const_iterator i = groups_.begin();
292  const_iterator e = groups_.end();
293  while( i != e )
294  {
295  ret.push_back(i->first);
296  ++i;
297  }
298  return ret;
299  }
300 
305  void run_tests() const
306  {
307  callback_->run_started();
308 
309  const_iterator i = groups_.begin();
310  const_iterator e = groups_.end();
311  while( i != e )
312  {
313  callback_->group_started(i->first);
314  try
315  {
316  run_all_tests_in_group_(i);
317  }
318  catch( const no_more_tests& )
319  {
320  callback_->group_completed(i->first);
321  }
322 
323  ++i;
324  }
325 
326  callback_->run_completed();
327  }
328 
332  void run_tests(const std::string& group_name) const
333  {
334  callback_->run_started();
335 
336  const_iterator i = groups_.find(group_name);
337  if( i == groups_.end() )
338  {
339  callback_->run_completed();
340  throw no_such_group(group_name);
341  }
342 
343  callback_->group_started(group_name);
344  try
345  {
346  run_all_tests_in_group_(i);
347  }
348  catch( const no_more_tests& )
349  {
350  // ok
351  }
352 
353  callback_->group_completed(group_name);
354  callback_->run_completed();
355  }
356 
360  test_result run_test(const std::string& group_name,int n) const
361  {
362  callback_->run_started();
363 
364  const_iterator i = groups_.find(group_name);
365  if( i == groups_.end() )
366  {
367  callback_->run_completed();
368  throw no_such_group(group_name);
369  }
370 
371  callback_->group_started(group_name);
372  try
373  {
374  test_result tr = i->second->run_test(n);
375  callback_->test_completed(tr);
376  callback_->group_completed(group_name);
377  callback_->run_completed();
378  return tr;
379  }
380  catch( const beyond_last_test& )
381  {
382  callback_->group_completed(group_name);
383  callback_->run_completed();
384  throw;
385  }
386  catch( const no_such_test& )
387  {
388  callback_->group_completed(group_name);
389  callback_->run_completed();
390  throw;
391  }
392  }
393 
394  private:
395  void run_all_tests_in_group_(const_iterator i) const
396  {
397  i->second->rewind();
398  for( ;; )
399  {
400  test_result tr = i->second->run_next();
401  callback_->test_completed(tr);
402 
403  if( tr.result == test_result::ex_ctor )
404  {
405  throw no_more_tests();
406  }
407  }
408  }
409  };
410 
417  {
418  public:
419  static test_runner& get()
420  {
421  static test_runner tr;
422  return tr;
423  }
424  };
426 
432  template <class Data>
433  class test_object : public Data
434  {
435  public:
440 
448  bool called_method_was_a_dummy_test_;
449 
453  template <int n>
454  void test()
455  {
456  called_method_was_a_dummy_test_ = true;
457  }
458  };
459 
460  namespace
461  {
466  void ensure(bool cond)
467  {
468  if( !cond ) throw failure("");
469  }
470 
475  template<typename T>
476  void ensure(const T msg,bool cond)
477  {
478  if( !cond ) throw failure(msg);
479  }
480 
488  template <class T,class Q>
489  void ensure_equals(const char* msg,const Q& actual,const T& expected)
490  {
491  if( expected != actual )
492  {
493  std::stringstream ss;
494  ss << (msg?msg:"") << (msg?": ":"") << "expected " << expected << " actual " << actual;
495  throw failure(ss.str().c_str());
496  }
497  }
498 
499  template <class T,class Q>
500  void ensure_equals(const Q& actual,const T& expected)
501  {
502  ensure_equals<>(0,actual,expected);
503  }
504 
514  template <class T>
515  void ensure_distance(const char* msg,const T& actual,const T& expected,const T& distance)
516  {
517  if( expected-distance >= actual || expected+distance <= actual )
518  {
519  std::stringstream ss;
520  ss << (msg?msg:"") << (msg?": ":"") << "expected [" << expected-distance << ";"
521  << expected+distance << "] actual " << actual;
522  throw failure(ss.str().c_str());
523  }
524  }
525 
526  template <class T>
527  void ensure_distance(const T& actual,const T& expected,const T& distance)
528  {
529  ensure_distance<>(0,actual,expected,distance);
530  }
531 
535  void fail(const char* msg="")
536  {
537  throw failure(msg);
538  }
539  }
540 
545  template <class Test,class Group,int n>
547  {
548  static void reg(Group& group)
549  {
550  group.reg(n,&Test::template test<n>);
552  }
553  };
554 
555  template<class Test,class Group>
556  struct tests_registerer<Test,Group,0>
557  {
558  static void reg(Group&){};
559  };
560 
566  template <class Data,int MaxTestsInGroup = 50>
567  class test_group : public group_base
568  {
569  const char* name_;
570 
571  typedef void (test_object<Data>::*testmethod)();
572  typedef std::map<int,testmethod> tests;
573  typedef typename tests::iterator tests_iterator;
574  typedef typename tests::const_iterator tests_const_iterator;
575  typedef typename tests::const_reverse_iterator
576  tests_const_reverse_iterator;
577  typedef typename tests::size_type size_type;
578 
579  tests tests_;
580  tests_iterator current_test_;
581 
585  template <class T>
586  class safe_holder
587  {
588  T* p_;
589  bool permit_throw_in_dtor;
590 
591  safe_holder(const safe_holder&);
592  safe_holder& operator = (const safe_holder&);
593 
594  public:
595  safe_holder() : p_(0),permit_throw_in_dtor(false)
596  {
597  }
598 
599  ~safe_holder()
600  {
601  release();
602  }
603 
604  T* operator -> () const { return p_; };
605  T* get() const { return p_; };
606 
612  void permit_throw(){ permit_throw_in_dtor = true; }
613 
620  void release()
621  {
622  try
623  {
624  if( delete_obj() == false )
625  {
626  throw warning("destructor of test object raised an SEH exception");
627  }
628  }
629  catch( const std::exception& ex )
630  {
631  if( permit_throw_in_dtor )
632  {
633  std::string msg = "destructor of test object raised exception: ";
634  msg += ex.what();
635  throw warning(msg);
636  }
637  }
638  catch( ... )
639  {
640  if( permit_throw_in_dtor )
641  {
642  throw warning("destructor of test object raised an exception");
643  }
644  }
645  }
646 
650  void reset()
651  {
652  release();
653  permit_throw_in_dtor = false;
654  p_ = new T();
655  }
656 
657  bool delete_obj()
658  {
659 #if defined(TUT_USE_SEH)
660  __try
661  {
662 #endif
663  T* p = p_;
664  p_ = 0;
665  delete p;
666 #if defined(TUT_USE_SEH)
667  }
668  __except(handle_seh_(::GetExceptionCode()))
669  {
670  if( permit_throw_in_dtor )
671  {
672  return false;
673  }
674  }
675 #endif
676  return true;
677  }
678  };
679 
680  public:
682 
686  test_group(const char* name)
687  : name_(name)
688  {
689  // register itself
690  runner.get().register_group(name_,this);
691 
692  // register all tests
694  };
695 
699  test_group(const char* name,test_runner& another_runner)
700  : name_(name)
701  {
702  // register itself
703  another_runner.register_group(name_,this);
704 
705  // register all tests
707  test_group,MaxTestsInGroup>::reg(*this);
708  };
709 
713  void reg(int n,testmethod tm)
714  {
715  tests_[n] = tm;
716  }
717 
721  void rewind()
722  {
723  current_test_ = tests_.begin();
724  }
725 
730  {
731  if( current_test_ == tests_.end() )
732  {
733  throw no_more_tests();
734  }
735 
736  // find next user-specialized test
737  safe_holder<object> obj;
738  while( current_test_ != tests_.end() )
739  {
740  try
741  {
742  return run_test_(current_test_++,obj);
743  }
744  catch( const no_such_test& )
745  {
746  continue;
747  }
748  }
749 
750  throw no_more_tests();
751  }
752 
757  {
758  // beyond tests is special case to discover upper limit
759  if( tests_.rbegin() == tests_.rend() ) throw beyond_last_test();
760  if( tests_.rbegin()->first < n ) throw beyond_last_test();
761 
762  // withing scope; check if given test exists
763  tests_iterator ti = tests_.find(n);
764  if( ti == tests_.end() ) throw no_such_test();
765 
766  safe_holder<object> obj;
767  return run_test_(ti,obj);
768  }
769 
770  private:
775  test_result run_test_(const tests_iterator& ti,safe_holder<object>& obj)
776  {
777  try
778  {
779  if( run_test_seh_(ti->second,obj) == false )
780  throw seh("seh");
781  }
782  catch(const no_such_test&)
783  {
784  throw;
785  }
786  catch(const warning& ex)
787  {
788  // test ok, but destructor failed
789  test_result tr(name_,ti->first,test_result::warn,ex);
790  return tr;
791  }
792  catch(const failure& ex)
793  {
794  // test failed because of ensure() or similar method
795  test_result tr(name_,ti->first,test_result::fail,ex);
796  return tr;
797  }
798  catch(const seh& ex)
799  {
800  // test failed with sigsegv, divide by zero, etc
801  test_result tr(name_,ti->first,test_result::term,ex);
802  return tr;
803  }
804  catch(const bad_ctor& ex)
805  {
806  // test failed because test ctor failed; stop the whole group
807  test_result tr(name_,ti->first,test_result::ex_ctor,ex);
808  return tr;
809  }
810  catch(const std::exception& ex)
811  {
812  // test failed with std::exception
813  test_result tr(name_,ti->first,test_result::ex,ex);
814  return tr;
815  }
816  catch(...)
817  {
818  // test failed with unknown exception
819  test_result tr(name_,ti->first,test_result::ex);
820  return tr;
821  }
822 
823  // test passed
824  test_result tr(name_,ti->first,test_result::ok);
825  return tr;
826  }
827 
831  bool run_test_seh_(testmethod tm,safe_holder<object>& obj)
832  {
833 #if defined(TUT_USE_SEH)
834  __try
835  {
836 #endif
837  if( obj.get() == 0 )
838  {
839  reset_holder_(obj);
840  }
841  obj->called_method_was_a_dummy_test_ = false;
842 
843 #if defined(TUT_USE_SEH)
844  __try
845  {
846 #endif
847  (obj.get()->*tm)();
848 #if defined(TUT_USE_SEH)
849  }
850  __except(handle_seh_(::GetExceptionCode()))
851  {
852  // throw seh("SEH");
853  return false;
854  }
855 #endif
856 
857  if( obj->called_method_was_a_dummy_test_ )
858  {
859  // do not call obj.release(); reuse object
860  throw no_such_test();
861  }
862 
863  obj.permit_throw();
864  obj.release();
865 #if defined(TUT_USE_SEH)
866  }
867  __except(handle_seh_(::GetExceptionCode()))
868  {
869  return false;
870  }
871 #endif
872  return true;
873  }
874 
875  void reset_holder_(safe_holder<object>& obj)
876  {
877  try
878  {
879  obj.reset();
880  }
881  catch(const std::exception& ex)
882  {
883  throw bad_ctor(ex.what());
884  }
885  catch(...)
886  {
887  throw bad_ctor("test constructor has generated an exception; group execution is terminated");
888  }
889  }
890  };
891 
892 #if defined(TUT_USE_SEH)
893 
896  inline int handle_seh_(DWORD excode)
897  {
898  switch(excode)
899  {
900  case EXCEPTION_ACCESS_VIOLATION:
901  case EXCEPTION_DATATYPE_MISALIGNMENT:
902  case EXCEPTION_BREAKPOINT:
903  case EXCEPTION_SINGLE_STEP:
904  case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
905  case EXCEPTION_FLT_DENORMAL_OPERAND:
906  case EXCEPTION_FLT_DIVIDE_BY_ZERO:
907  case EXCEPTION_FLT_INEXACT_RESULT:
908  case EXCEPTION_FLT_INVALID_OPERATION:
909  case EXCEPTION_FLT_OVERFLOW:
910  case EXCEPTION_FLT_STACK_CHECK:
911  case EXCEPTION_FLT_UNDERFLOW:
912  case EXCEPTION_INT_DIVIDE_BY_ZERO:
913  case EXCEPTION_INT_OVERFLOW:
914  case EXCEPTION_PRIV_INSTRUCTION:
915  case EXCEPTION_IN_PAGE_ERROR:
916  case EXCEPTION_ILLEGAL_INSTRUCTION:
917  case EXCEPTION_NONCONTINUABLE_EXCEPTION:
918  case EXCEPTION_STACK_OVERFLOW:
919  case EXCEPTION_INVALID_DISPOSITION:
920  case EXCEPTION_GUARD_PAGE:
921  case EXCEPTION_INVALID_HANDLE:
922  return EXCEPTION_EXECUTE_HANDLER;
923  };
924 
925  return EXCEPTION_CONTINUE_SEARCH;
926  }
927 #endif
928 }
929 
930 #endif
931 
static void reg(Group &)
Definition: tut.h:558
Iterator< typename I::value_type > iterator(I i)
Definition: iterator.h:123
test_result()
Default constructor.
Definition: tut.h:136
Test runner.
Definition: tut.h:228
no_such_test()
Definition: tut.h:31
void run_tests() const
Runs all tests in all groups.
Definition: tut.h:305
test_group(const char *name)
Creates and registers test group with specified name.
Definition: tut.h:686
std::string group
Test group name.
Definition: tut.h:110
Test group; used to recreate test object instance for each new test since we have to have reinitializ...
Definition: tut.h:567
Walks through test tree and stores address of each test method in group.
Definition: tut.h:546
Exception to be throwed when attempted to execute missed test by number.
Definition: tut.h:29
static test_runner & get()
Definition: tut.h:419
void Test
Definition: test.h:178
std::string exception_typeid
Definition: tut.h:131
groups groups_
Definition: tut.h:234
Test object.
Definition: tut.h:433
void run_tests(const std::string &group_name) const
Runs all tests in specified group.
Definition: tut.h:332
Group not found exception.
Definition: tut.h:47
virtual void run_completed()
Called when all tests in run completed.
Definition: tut.h:217
static void reg(Group &group)
Definition: tut.h:548
Definition: tut.h:124
failure(const std::string &msg)
Definition: tut.h:78
groups::const_iterator const_iterator
Definition: tut.h:233
test_result(const std::string &grp, int pos, result_type res, const std::exception &ex)
Constructor with exception.
Definition: tut.h:152
result_type
ok - test finished successfully fail - test failed with ensure() or fail() methods ex - test throwed ...
Definition: tut.h:124
const groupnames list_groups() const
Returns list of known test groups.
Definition: tut.h:288
callback & get_callback() const
Returns callback object.
Definition: tut.h:280
Exception to be throwed when ensure() fails or fail() called.
Definition: tut.h:75
Exception to be throwed when test desctructor throwed an exception.
Definition: tut.h:84
beyond_last_test()
Definition: tut.h:41
int test
Test number in group.
Definition: tut.h:115
warning(const std::string &msg)
Definition: tut.h:87
test_result run_test(const std::string &group_name, int n) const
Runs one test in specified group.
Definition: tut.h:360
Definition: tut.h:124
test_result run_test(int n)
Runs one test by position.
Definition: tut.h:756
virtual void test_completed(const test_result &)
Called when a test finished.
Definition: tut.h:206
result_type result
Definition: tut.h:125
#define ensure(x)
Definition: tests.h:101
no_such_group(const std::string &grp)
Definition: tut.h:49
Template Unit Tests Framework for C++.
Definition: tut-main.cpp:6
callback * callback_
Definition: tut.h:237
void rewind()
Reset test position before first test.
Definition: tut.h:721
Definition: tut.h:124
std::map< std::string, group_base * > groups
Definition: tut.h:231
virtual void group_completed(const std::string &)
Called when a group is completed.
Definition: tut.h:212
Internal exception to be throwed when test constructor has failed.
Definition: tut.h:66
test_runner_singleton runner
Definition: tut-main.cpp:8
void set_callback(callback *cb)
Stores callback object.
Definition: tut.h:272
std::vector< std::string > groupnames
Typedef for runner::list_groups()
Definition: tut.h:223
Singleton for test_runner implementation.
Definition: tut.h:416
test_object< Data > object
Definition: tut.h:681
test_group(const char *name, test_runner &another_runner)
This constructor is used in self-test run only.
Definition: tut.h:699
callback default_callback_
Definition: tut.h:236
Internal exception to be throwed when no more tests left in group or journal.
Definition: tut.h:57
void register_group(const std::string &name, group_base *gr)
Stores another group for getting by name.
Definition: tut.h:250
Interface.
Definition: tut.h:165
void reg(int n, testmethod tm)
Registers test method under given number.
Definition: tut.h:713
virtual ~group_base()
Definition: tut.h:167
void test()
Default do-nothing test.
Definition: tut.h:454
Definition: tut.h:124
std::string message
Exception message for failed test.
Definition: tut.h:130
Return type of runned test/test group.
Definition: tut.h:105
test_result(const std::string &grp, int pos, result_type res)
Constructor.
Definition: tut.h:144
Definition: tut.h:124
Definition: tut.h:124
#define ensure_equals(x, y)
Definition: tests.h:105
virtual void group_started(const std::string &)
Called when a group started.
Definition: tut.h:200
Exception to be throwed when test issued SEH (Win32)
Definition: tut.h:93
no_more_tests()
Definition: tut.h:59
test_runner()
Constructor.
Definition: tut.h:243
test_result run_next()
Runs next test.
Definition: tut.h:729
virtual ~callback()
Virtual destructor is a must for subclassed types.
Definition: tut.h:189
groups::iterator iterator
Definition: tut.h:232
Actual< A > actual(const A &actual)
Definition: tests.h:384
bad_ctor(const std::string &msg)
Definition: tut.h:68
No such test and passed test number is higher than any test number in current group.
Definition: tut.h:39
Test runner callback interface.
Definition: tut.h:184
test_object()
Default constructor.
Definition: tut.h:439
virtual void run_started()
Called when new test run started.
Definition: tut.h:194
seh(const std::string &msg)
Definition: tut.h:96