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
23namespace 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
106 {
110 std::string group;
111
115 int test;
116
124 typedef enum { ok, fail, ex, warn, term, ex_ctor } result_type;
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
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;
232 typedef groups::iterator iterator;
233 typedef groups::const_iterator const_iterator;
235
238
239 public:
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
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 {
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
327 }
328
332 void run_tests(const std::string& group_name) const
333 {
335
336 const_iterator i = groups_.find(group_name);
337 if( i == groups_.end() )
338 {
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);
355 }
356
360 test_result run_test(const std::string& group_name,int n) const
361 {
363
364 const_iterator i = groups_.find(group_name);
365 if( i == groups_.end() )
366 {
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);
376 callback_->group_completed(group_name);
378 return tr;
379 }
380 catch( const beyond_last_test& )
381 {
382 callback_->group_completed(group_name);
384 throw;
385 }
386 catch( const no_such_test& )
387 {
388 callback_->group_completed(group_name);
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();
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 };
425 extern test_runner_singleton runner;
426
432 template <class Data>
433 class test_object : public Data
434 {
435 public:
440
449
453 template <int n>
454 void test()
455 {
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)
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
Exception to be throwed when ensure() fails or fail() called.
Definition tut.h:76
failure(const std::string &msg)
Definition tut.h:78
Exception to be throwed when test issued SEH (Win32)
Definition tut.h:94
seh(const std::string &msg)
Definition tut.h:96
Test group; used to recreate test object instance for each new test since we have to have reinitializ...
Definition tut.h:568
test_result run_test(int n)
Runs one test by position.
Definition tut.h:756
test_group(const char *name)
Creates and registers test group with specified name.
Definition tut.h:686
void reg(int n, testmethod tm)
Registers test method under given number.
Definition tut.h:713
test_group(const char *name, test_runner &another_runner)
This constructor is used in self-test run only.
Definition tut.h:699
test_object< Data > object
Definition tut.h:681
void rewind()
Reset test position before first test.
Definition tut.h:721
test_result run_next()
Runs next test.
Definition tut.h:729
Test object.
Definition tut.h:434
test_object()
Default constructor.
Definition tut.h:439
bool called_method_was_a_dummy_test_
The flag is set to true by default (dummy) test.
Definition tut.h:448
void test()
Default do-nothing test.
Definition tut.h:454
Singleton for test_runner implementation.
Definition tut.h:417
static test_runner & get()
Definition tut.h:419
Test runner.
Definition tut.h:229
groups groups_
Definition tut.h:234
void set_callback(callback *cb)
Stores callback object.
Definition tut.h:272
void run_tests() const
Runs all tests in all groups.
Definition tut.h:305
groups::const_iterator const_iterator
Definition tut.h:233
callback & get_callback() const
Returns callback object.
Definition tut.h:280
groups::iterator iterator
Definition tut.h:232
void register_group(const std::string &name, group_base *gr)
Stores another group for getting by name.
Definition tut.h:250
std::map< std::string, group_base * > groups
Definition tut.h:231
callback * callback_
Definition tut.h:237
test_result run_test(const std::string &group_name, int n) const
Runs one test in specified group.
Definition tut.h:360
test_runner()
Constructor.
Definition tut.h:243
void run_tests(const std::string &group_name) const
Runs all tests in specified group.
Definition tut.h:332
callback default_callback_
Definition tut.h:236
const groupnames list_groups() const
Returns list of known test groups.
Definition tut.h:288
Exception to be throwed when test desctructor throwed an exception.
Definition tut.h:85
warning(const std::string &msg)
Definition tut.h:87
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
Actual< A > actual(const A &actual)
Definition tests.h:384
Internal exception to be throwed when test constructor has failed.
Definition tut.h:67
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:40
beyond_last_test()
Definition tut.h:41
Test runner callback interface.
Definition tut.h:185
virtual void run_started()
Called when new test run started.
Definition tut.h:194
virtual ~callback()
Virtual destructor is a must for subclassed types.
Definition tut.h:189
virtual void test_completed(const test_result &)
Called when a test finished.
Definition tut.h:206
virtual void group_completed(const std::string &)
Called when a group is completed.
Definition tut.h:212
virtual void run_completed()
Called when all tests in run completed.
Definition tut.h:217
virtual void group_started(const std::string &)
Called when a group started.
Definition tut.h:200
Interface.
Definition tut.h:166
virtual void rewind()=0
virtual test_result run_test(int n)=0
virtual ~group_base()
Definition tut.h:167
virtual test_result run_next()=0
Internal exception to be throwed when no more tests left in group or journal.
Definition tut.h:58
no_more_tests()
Definition tut.h:59
Group not found exception.
Definition tut.h:48
no_such_group(const std::string &grp)
Definition tut.h:49
Exception to be throwed when attempted to execute missed test by number.
Definition tut.h:30
no_such_test()
Definition tut.h:31
Return type of runned test/test group.
Definition tut.h:106
std::string exception_typeid
Definition tut.h:131
result_type result
Definition tut.h:125
std::string group
Test group name.
Definition tut.h:110
test_result(const std::string &grp, int pos, result_type res, const std::exception &ex)
Constructor with exception.
Definition tut.h:152
std::string message
Exception message for failed test.
Definition tut.h:130
result_type
ok - test finished successfully fail - test failed with ensure() or fail() methods ex - test throwed ...
Definition tut.h:124
@ term
Definition tut.h:124
@ fail
Definition tut.h:124
@ ex_ctor
Definition tut.h:124
@ ok
Definition tut.h:124
@ warn
Definition tut.h:124
@ ex
Definition tut.h:124
test_result()
Default constructor.
Definition tut.h:136
test_result(const std::string &grp, int pos, result_type res)
Constructor.
Definition tut.h:144
int test
Test number in group.
Definition tut.h:115
static void reg(Group &)
Definition tut.h:558
Walks through test tree and stores address of each test method in group.
Definition tut.h:547
static void reg(Group &group)
Definition tut.h:548
Definition amorph.h:30
#define ensure(x)
Definition tests.h:101
#define ensure_equals(x, y)
Definition tests.h:105