3
#include <tut/tut_config.hpp>
14
#include "tut_exception.hpp"
15
#include "tut_result.hpp"
16
#include "tut_posix.hpp"
17
#include "tut_assert.hpp"
18
#include "tut_runner.hpp"
20
#if defined(TUT_USE_SEH)
26
* Template Unit Tests Framework for C++.
29
* @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com
38
* Test object. Contains data test run upon and default test method
39
* implementation. Inherited from Data to allow tests to
40
* access test data as members.
43
class test_object : public Data, public test_object_posix
45
template<class D, int M>
46
friend class test_group;
48
void set_test_group(const char *group)
50
current_test_group_ = group;
53
void set_test_id(int current_test_id)
55
current_test_id_ = current_test_id;
64
: called_method_was_a_dummy_test_(false),
71
void set_test_name(const std::string& current_test_name)
73
current_test_name_ = current_test_name;
76
const std::string& get_test_name() const
78
return current_test_name_;
81
const std::string& get_test_group() const
83
return current_test_group_;
86
int get_test_id() const
88
return current_test_id_;
92
* Default do-nothing test.
97
called_method_was_a_dummy_test_ = true;
101
* The flag is set to true by default (dummy) test.
102
* Used to detect usused test numbers and avoid unnecessary
103
* test object creation which may be time-consuming depending
104
* on operations described in Data::Data() and Data::~Data().
106
bool called_method_was_a_dummy_test_;
108
virtual ~test_object()
113
int current_test_id_;
114
std::string current_test_name_;
115
std::string current_test_group_;
120
* Walks through test tree and stores address of each
121
* test method in group. Instantiation stops at 0.
123
template <class Test, class Group, int n>
124
struct tests_registerer
126
static void reg(Group& group)
128
group.reg(n, &Test::template test<n>);
129
tests_registerer<Test, Group, n - 1>::reg(group);
133
template <class Test, class Group>
134
struct tests_registerer<Test, Group, 0>
136
static void reg(Group&)
142
* Test group; used to recreate test object instance for
143
* each new test since we have to have reinitialized
146
template <class Data, int MaxTestsInGroup = 50>
147
class test_group : public group_base, public test_group_posix
149
test_group(const test_group&);
150
void operator=(const test_group&);
154
typedef void (test_object<Data>::*testmethod)();
155
typedef std::map<int, testmethod> tests;
156
typedef typename tests::iterator tests_iterator;
157
typedef typename tests::const_iterator tests_const_iterator;
158
typedef typename tests::const_reverse_iterator
159
tests_const_reverse_iterator;
160
typedef typename tests::size_type size_type;
163
tests_iterator current_test_;
168
#if defined(TUT_USE_SEH)
176
* Exception-in-destructor-safe smart-pointer class.
182
bool permit_throw_in_dtor;
184
safe_holder(const safe_holder&);
185
safe_holder& operator=(const safe_holder&);
190
permit_throw_in_dtor(false)
199
T* operator->() const
210
* Tell ptr it can throw from destructor. Right way is to
211
* use std::uncaught_exception(), but some compilers lack
212
* correct implementation of the function.
216
permit_throw_in_dtor = true;
220
* Specially treats exceptions in test object destructor;
221
* if test itself failed, exceptions in destructor
222
* are ignored; if test was successful and destructor failed,
223
* warning exception throwed.
229
#if defined(TUT_USE_SEH)
230
if (delete_obj() == false)
232
throw warning("destructor of test object raised"
233
" an SEH exception");
236
bool d = delete_obj();
237
(void)d; // unused variable in Release mode
238
assert(d && "delete failed with SEH disabled: runtime bug?");
241
catch (const std::exception& ex)
243
if (permit_throw_in_dtor)
245
std::string msg = "destructor of test object raised"
253
if (permit_throw_in_dtor)
255
throw warning("destructor of test object raised an"
262
* Re-init holder to get brand new object.
267
permit_throw_in_dtor = false;
273
#if defined(TUT_USE_SEH)
280
#if defined(TUT_USE_SEH)
282
__except(handle_seh_(::GetExceptionCode()))
284
if (permit_throw_in_dtor)
296
typedef test_object<Data> object;
299
* Creates and registers test group with specified name.
301
test_group(const char* name)
307
runner.get().register_group(name_,this);
309
// register all tests
310
tests_registerer<object, test_group, MaxTestsInGroup>::reg(*this);
314
* This constructor is used in self-test run only.
316
test_group(const char* name, test_runner& another_runner)
322
another_runner.register_group(name_, this);
324
// register all tests
325
tests_registerer<test_object<Data>, test_group,
326
MaxTestsInGroup>::reg(*this);
330
* Registers test method under given number.
332
void reg(int n, testmethod tm)
338
* Reset test position before first test.
342
current_test_ = tests_.begin();
348
bool run_next(test_result &tr)
350
if (current_test_ == tests_.end())
355
// find next user-specialized test
356
safe_holder<object> obj;
357
while (current_test_ != tests_.end())
359
tests_iterator current_test = current_test_++;
361
if(run_test_(current_test, obj, tr) && tr.result != test_result::dummy)
371
* Runs one test by position.
373
bool run_test(int n, test_result &tr)
375
if (tests_.rbegin() == tests_.rend() ||
376
tests_.rbegin()->first < n)
381
// withing scope; check if given test exists
382
tests_iterator ti = tests_.find(n);
383
if (ti == tests_.end())
388
safe_holder<object> obj;
389
return run_test_(ti, obj, tr);
393
* VC allows only one exception handling type per function,
394
* so I have to split the method.
396
bool run_test_(const tests_iterator& ti, safe_holder<object>& obj, test_result &tr)
398
std::string current_test_name;
400
tr = test_result(name_, ti->first, current_test_name, test_result::ok);
404
switch (run_test_seh_(ti->second, obj, current_test_name, ti->first))
406
#if defined(TUT_USE_SEH)
408
throw bad_ctor("seh");
416
tr.result = test_result::dummy;
424
catch (const rethrown& ex)
427
tr.result = test_result::rethrown;
429
catch (const tut_error& ex)
431
tr.result = ex.result();
432
tr.exception_typeid = ex.type();
433
tr.message = ex.what();
435
catch (const std::exception& ex)
437
tr.result = test_result::ex;
438
tr.exception_typeid = type_name(ex);
439
tr.message = ex.what();
443
// test failed with unknown exception
444
tr.result = test_result::ex;
449
tr.name = obj->get_test_name();
451
// try to report to parent, if exists
452
send_result_(obj.get(), tr);
456
tr.name = current_test_name;
463
* Runs one under SEH if platform supports it.
465
seh_result run_test_seh_(testmethod tm, safe_holder<object>& obj,
466
std::string& current_test_name, int current_test_id)
468
#if defined(TUT_USE_SEH)
477
obj->called_method_was_a_dummy_test_ = false;
479
#if defined(TUT_USE_SEH)
484
obj.get()->set_test_id(current_test_id);
485
obj.get()->set_test_group(name_);
487
#if defined(TUT_USE_SEH)
489
__except(handle_seh_(::GetExceptionCode()))
491
current_test_name = obj->get_test_name();
496
if (obj->called_method_was_a_dummy_test_)
498
// do not call obj.release(); reuse object
502
current_test_name = obj->get_test_name();
505
#if defined(TUT_USE_SEH)
507
__except(handle_seh_(::GetExceptionCode()))
515
void reset_holder_(safe_holder<object>& obj)
521
catch (const std::exception& ex)
523
throw bad_ctor(ex.what());
527
throw bad_ctor("test constructor has generated an exception;"
528
" group execution is terminated");
533
#if defined(TUT_USE_SEH)
535
* Decides should we execute handler or ignore SE.
537
inline int handle_seh_(DWORD excode)
541
case EXCEPTION_ACCESS_VIOLATION:
542
case EXCEPTION_DATATYPE_MISALIGNMENT:
543
case EXCEPTION_BREAKPOINT:
544
case EXCEPTION_SINGLE_STEP:
545
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
546
case EXCEPTION_FLT_DENORMAL_OPERAND:
547
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
548
case EXCEPTION_FLT_INEXACT_RESULT:
549
case EXCEPTION_FLT_INVALID_OPERATION:
550
case EXCEPTION_FLT_OVERFLOW:
551
case EXCEPTION_FLT_STACK_CHECK:
552
case EXCEPTION_FLT_UNDERFLOW:
553
case EXCEPTION_INT_DIVIDE_BY_ZERO:
554
case EXCEPTION_INT_OVERFLOW:
555
case EXCEPTION_PRIV_INSTRUCTION:
556
case EXCEPTION_IN_PAGE_ERROR:
557
case EXCEPTION_ILLEGAL_INSTRUCTION:
558
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
559
case EXCEPTION_STACK_OVERFLOW:
560
case EXCEPTION_INVALID_DISPOSITION:
561
case EXCEPTION_GUARD_PAGE:
562
case EXCEPTION_INVALID_HANDLE:
563
return EXCEPTION_EXECUTE_HANDLER;
566
return EXCEPTION_CONTINUE_SEARCH;
571
#endif // TUT_H_GUARD