00001 #ifndef TUT_H_GUARD
00002 #define TUT_H_GUARD
00003 #include <tut/tut_config.hpp>
00004
00005 #include <iostream>
00006 #include <map>
00007 #include <vector>
00008 #include <set>
00009 #include <string>
00010 #include <sstream>
00011 #include <iterator>
00012 #include <algorithm>
00013
00014 #include "tut_exception.hpp"
00015 #include "tut_result.hpp"
00016 #include "tut_posix.hpp"
00017 #include "tut_assert.hpp"
00018 #include "tut_runner.hpp"
00019
00020 #if defined(TUT_USE_SEH)
00021 #include <windows.h>
00022 #include <winbase.h>
00023 #endif
00024
00031 namespace tut
00032 {
00033
00034 template <class, int>
00035 class test_group;
00036
00042 template <class Data>
00043 class test_object : public Data, public test_object_posix
00044 {
00045 template<class D, int M>
00046 friend class test_group;
00047
00048 void set_test_group(const char *group)
00049 {
00050 current_test_group_ = group;
00051 }
00052
00053 void set_test_id(int current_test_id)
00054 {
00055 current_test_id_ = current_test_id;
00056 }
00057
00058 public:
00059
00063 test_object()
00064 : called_method_was_a_dummy_test_(false),
00065 current_test_id_(0),
00066 current_test_name_(),
00067 current_test_group_()
00068 {
00069 }
00070
00071 void set_test_name(const std::string& current_test_name)
00072 {
00073 current_test_name_ = current_test_name;
00074 }
00075
00076 const std::string& get_test_name() const
00077 {
00078 return current_test_name_;
00079 }
00080
00081 const std::string& get_test_group() const
00082 {
00083 return current_test_group_;
00084 }
00085
00086 int get_test_id() const
00087 {
00088 return current_test_id_;
00089 }
00090
00094 template <int n>
00095 void test()
00096 {
00097 called_method_was_a_dummy_test_ = true;
00098 }
00099
00106 bool called_method_was_a_dummy_test_;
00107
00108 virtual ~test_object()
00109 {
00110 }
00111
00112 private:
00113 int current_test_id_;
00114 std::string current_test_name_;
00115 std::string current_test_group_;
00116 };
00117
00118
00123 template <class Test, class Group, int n>
00124 struct tests_registerer
00125 {
00126 static void reg(Group& group)
00127 {
00128 group.reg(n, &Test::template test<n>);
00129 tests_registerer<Test, Group, n - 1>::reg(group);
00130 }
00131 };
00132
00133 template <class Test, class Group>
00134 struct tests_registerer<Test, Group, 0>
00135 {
00136 static void reg(Group&)
00137 {
00138 }
00139 };
00140
00146 template <class Data, int MaxTestsInGroup = 50>
00147 class test_group : public group_base, public test_group_posix
00148 {
00149 test_group(const test_group&);
00150 void operator=(const test_group&);
00151
00152 const char* name_;
00153
00154 typedef void (test_object<Data>::*testmethod)();
00155 typedef std::map<int, testmethod> tests;
00156 typedef typename tests::iterator tests_iterator;
00157 typedef typename tests::const_iterator tests_const_iterator;
00158 typedef typename tests::const_reverse_iterator
00159 tests_const_reverse_iterator;
00160 typedef typename tests::size_type size_type;
00161
00162 tests tests_;
00163 tests_iterator current_test_;
00164
00165 enum seh_result
00166 {
00167 SEH_OK,
00168 #if defined(TUT_USE_SEH)
00169 SEH_CTOR,
00170 SEH_TEST,
00171 #endif
00172 SEH_DUMMY
00173 };
00174
00178 template <class T>
00179 class safe_holder
00180 {
00181 T* p_;
00182 bool permit_throw_in_dtor;
00183
00184 safe_holder(const safe_holder&);
00185 safe_holder& operator=(const safe_holder&);
00186
00187 public:
00188 safe_holder()
00189 : p_(0),
00190 permit_throw_in_dtor(false)
00191 {
00192 }
00193
00194 ~safe_holder()
00195 {
00196 release();
00197 }
00198
00199 T* operator->() const
00200 {
00201 return p_;
00202 }
00203
00204 T* get() const
00205 {
00206 return p_;
00207 }
00208
00214 void permit_throw()
00215 {
00216 permit_throw_in_dtor = true;
00217 }
00218
00225 void release()
00226 {
00227 try
00228 {
00229 #if defined(TUT_USE_SEH)
00230 if (delete_obj() == false)
00231 {
00232 throw warning("destructor of test object raised"
00233 " an SEH exception");
00234 }
00235 #else
00236 bool d = delete_obj();
00237 assert(d && "delete failed with SEH disabled: runtime bug?");
00238 #endif
00239 }
00240 catch (const std::exception& ex)
00241 {
00242 if (permit_throw_in_dtor)
00243 {
00244 std::string msg = "destructor of test object raised"
00245 " exception: ";
00246 msg += ex.what();
00247 throw warning(msg);
00248 }
00249 }
00250 catch( ... )
00251 {
00252 if (permit_throw_in_dtor)
00253 {
00254 throw warning("destructor of test object raised an"
00255 " exception");
00256 }
00257 }
00258 }
00259
00263 void reset()
00264 {
00265 release();
00266 permit_throw_in_dtor = false;
00267 p_ = new T();
00268 }
00269
00270 bool delete_obj()
00271 {
00272 #if defined(TUT_USE_SEH)
00273 __try
00274 {
00275 #endif
00276 T* p = p_;
00277 p_ = 0;
00278 delete p;
00279 #if defined(TUT_USE_SEH)
00280 }
00281 __except(handle_seh_(::GetExceptionCode()))
00282 {
00283 if (permit_throw_in_dtor)
00284 {
00285 return false;
00286 }
00287 }
00288 #endif
00289 return true;
00290 }
00291 };
00292
00293 public:
00294
00295 typedef test_object<Data> object;
00296
00300 test_group(const char* name)
00301 : name_(name),
00302 tests_(),
00303 current_test_()
00304 {
00305
00306 runner.get().register_group(name_,this);
00307
00308
00309 tests_registerer<object, test_group, MaxTestsInGroup>::reg(*this);
00310 }
00311
00315 test_group(const char* name, test_runner& another_runner)
00316 : name_(name),
00317 tests_(),
00318 current_test_()
00319 {
00320
00321 another_runner.register_group(name_, this);
00322
00323
00324 tests_registerer<test_object<Data>, test_group,
00325 MaxTestsInGroup>::reg(*this);
00326 };
00327
00331 void reg(int n, testmethod tm)
00332 {
00333 tests_[n] = tm;
00334 }
00335
00339 void rewind()
00340 {
00341 current_test_ = tests_.begin();
00342 }
00343
00347 bool run_next(test_result &tr)
00348 {
00349 if (current_test_ == tests_.end())
00350 {
00351 return false;
00352 }
00353
00354
00355 safe_holder<object> obj;
00356 while (current_test_ != tests_.end())
00357 {
00358 tests_iterator current_test = current_test_++;
00359
00360 if(run_test_(current_test, obj, tr) && tr.result != test_result::dummy)
00361 {
00362 return true;
00363 }
00364 }
00365
00366 return false;
00367 }
00368
00372 bool run_test(int n, test_result &tr)
00373 {
00374 if (tests_.rbegin() == tests_.rend() ||
00375 tests_.rbegin()->first < n)
00376 {
00377 return false;
00378 }
00379
00380
00381 tests_iterator ti = tests_.find(n);
00382 if (ti == tests_.end())
00383 {
00384 return false;
00385 }
00386
00387 safe_holder<object> obj;
00388 return run_test_(ti, obj, tr);
00389 }
00390
00395 bool run_test_(const tests_iterator& ti, safe_holder<object>& obj, test_result &tr)
00396 {
00397 std::string current_test_name;
00398
00399 tr = test_result(name_, ti->first, current_test_name, test_result::ok);
00400
00401 try
00402 {
00403 switch (run_test_seh_(ti->second, obj, current_test_name, ti->first))
00404 {
00405 #if defined(TUT_USE_SEH)
00406 case SEH_CTOR:
00407 throw bad_ctor("seh");
00408 break;
00409
00410 case SEH_TEST:
00411 throw seh("seh");
00412 break;
00413 #endif
00414 case SEH_DUMMY:
00415 tr.result = test_result::dummy;
00416 break;
00417
00418 case SEH_OK:
00419
00420 break;
00421 }
00422 }
00423 catch (const rethrown& ex)
00424 {
00425 tr = ex.tr;
00426 tr.result = test_result::rethrown;
00427 }
00428 catch (const tut_error& ex)
00429 {
00430 tr.result = ex.result();
00431 tr.exception_typeid = ex.type();
00432 tr.message = ex.what();
00433 }
00434 catch (const std::exception& ex)
00435 {
00436 tr.result = test_result::ex;
00437 tr.exception_typeid = type_name(ex);
00438 tr.message = ex.what();
00439 }
00440 catch (...)
00441 {
00442
00443 tr.result = test_result::ex;
00444 }
00445
00446 if (obj.get())
00447 {
00448 tr.name = obj->get_test_name();
00449
00450
00451 send_result_(obj.get(), tr);
00452 }
00453 else
00454 {
00455 tr.name = current_test_name;
00456 }
00457
00458 return true;
00459 }
00460
00464 seh_result run_test_seh_(testmethod tm, safe_holder<object>& obj,
00465 std::string& current_test_name, int current_test_id)
00466 {
00467 #if defined(TUT_USE_SEH)
00468 __try
00469 {
00470 #endif
00471 if (obj.get() == 0)
00472 {
00473 reset_holder_(obj);
00474 }
00475
00476 obj->called_method_was_a_dummy_test_ = false;
00477
00478 #if defined(TUT_USE_SEH)
00479
00480 __try
00481 {
00482 #endif
00483 obj.get()->set_test_id(current_test_id);
00484 obj.get()->set_test_group(name_);
00485 (obj.get()->*tm)();
00486 #if defined(TUT_USE_SEH)
00487 }
00488 __except(handle_seh_(::GetExceptionCode()))
00489 {
00490 current_test_name = obj->get_test_name();
00491 return SEH_TEST;
00492 }
00493 #endif
00494
00495 if (obj->called_method_was_a_dummy_test_)
00496 {
00497
00498 return SEH_DUMMY;
00499 }
00500
00501 current_test_name = obj->get_test_name();
00502 obj.permit_throw();
00503 obj.release();
00504 #if defined(TUT_USE_SEH)
00505 }
00506 __except(handle_seh_(::GetExceptionCode()))
00507 {
00508 return SEH_CTOR;
00509 }
00510 #endif
00511 return SEH_OK;
00512 }
00513
00514 void reset_holder_(safe_holder<object>& obj)
00515 {
00516 try
00517 {
00518 obj.reset();
00519 }
00520 catch (const std::exception& ex)
00521 {
00522 throw bad_ctor(ex.what());
00523 }
00524 catch (...)
00525 {
00526 throw bad_ctor("test constructor has generated an exception;"
00527 " group execution is terminated");
00528 }
00529 }
00530 };
00531
00532 #if defined(TUT_USE_SEH)
00533
00536 inline int handle_seh_(DWORD excode)
00537 {
00538 switch(excode)
00539 {
00540 case EXCEPTION_ACCESS_VIOLATION:
00541 case EXCEPTION_DATATYPE_MISALIGNMENT:
00542 case EXCEPTION_BREAKPOINT:
00543 case EXCEPTION_SINGLE_STEP:
00544 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
00545 case EXCEPTION_FLT_DENORMAL_OPERAND:
00546 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
00547 case EXCEPTION_FLT_INEXACT_RESULT:
00548 case EXCEPTION_FLT_INVALID_OPERATION:
00549 case EXCEPTION_FLT_OVERFLOW:
00550 case EXCEPTION_FLT_STACK_CHECK:
00551 case EXCEPTION_FLT_UNDERFLOW:
00552 case EXCEPTION_INT_DIVIDE_BY_ZERO:
00553 case EXCEPTION_INT_OVERFLOW:
00554 case EXCEPTION_PRIV_INSTRUCTION:
00555 case EXCEPTION_IN_PAGE_ERROR:
00556 case EXCEPTION_ILLEGAL_INSTRUCTION:
00557 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
00558 case EXCEPTION_STACK_OVERFLOW:
00559 case EXCEPTION_INVALID_DISPOSITION:
00560 case EXCEPTION_GUARD_PAGE:
00561 case EXCEPTION_INVALID_HANDLE:
00562 return EXCEPTION_EXECUTE_HANDLER;
00563 };
00564
00565 return EXCEPTION_CONTINUE_SEARCH;
00566 }
00567 #endif
00568 }
00569
00570 #endif // TUT_H_GUARD
00571