📄 tut.hpp
字号:
private: std::string current_test_name_;};namespace{/** * Tests provided condition. * Throws if false. */void ensure(bool cond){ if (!cond) { // TODO: default ctor? throw failure(""); }}/** * Tests provided condition. * Throws if true. */void ensure_not(bool cond){ ensure(!cond);}/** * Tests provided condition. * Throws if false. */template <typename T>void ensure(const T msg, bool cond){ if (!cond) { throw failure(msg); }}/** * Tests provided condition. * Throws if true. */template <typename T>void ensure_not(const T msg, bool cond){ ensure(msg, !cond);}/** * Tests two objects for being equal. * Throws if false. * * NB: both T and Q must have operator << defined somewhere, or * client code will not compile at all! */template <class T, class Q>void ensure_equals(const char* msg, const Q& actual, const T& expected){ if (expected != actual) { std::stringstream ss; ss << (msg ? msg : "") << (msg ? ":" : "") << " expected '" << expected << "' actual '" << actual << '\''; throw failure(ss.str().c_str()); }}template <class T, class Q>void ensure_equals(const Q& actual, const T& expected){ ensure_equals<>(0, actual, expected);}/** * Tests two objects for being at most in given distance one from another. * Borders are excluded. * Throws if false. * * NB: T must have operator << defined somewhere, or * client code will not compile at all! Also, T shall have * operators + and -, and be comparable. */template <class T>void ensure_distance(const char* msg, const T& actual, const T& expected, const T& distance){ if (expected-distance >= actual || expected+distance <= actual) { std::stringstream ss; ss << (msg ? msg : "") << (msg? ":" : "") << " expected (" << expected-distance << " - " << expected+distance << ") actual '" << actual << '\''; throw failure(ss.str().c_str()); }}template <class T>void ensure_distance(const T& actual, const T& expected, const T& distance){ ensure_distance<>(0, actual, expected, distance);}/** * Unconditionally fails with message. */void fail(const char* msg = ""){ throw failure(msg);}} // end of namespace/** * Walks through test tree and stores address of each * test method in group. Instantiation stops at 0. */template <class Test, class Group, int n>struct tests_registerer{ static void reg(Group& group) { group.reg(n, &Test::template test<n>); tests_registerer<Test, Group, n - 1>::reg(group); }};template <class Test, class Group>struct tests_registerer<Test, Group, 0>{ static void reg(Group&) { }};/** * Test group; used to recreate test object instance for * each new test since we have to have reinitialized * Data base class. */template <class Data, int MaxTestsInGroup = 50>class test_group : public group_base{ const char* name_; typedef void (test_object<Data>::*testmethod)(); typedef std::map<int, testmethod> tests; typedef typename tests::iterator tests_iterator; typedef typename tests::const_iterator tests_const_iterator; typedef typename tests::const_reverse_iterator tests_const_reverse_iterator; typedef typename tests::size_type size_type; tests tests_; tests_iterator current_test_; /** * Exception-in-destructor-safe smart-pointer class. */ template <class T> class safe_holder { T* p_; bool permit_throw_in_dtor; safe_holder(const safe_holder&); safe_holder& operator=(const safe_holder&); public: safe_holder() : p_(0), permit_throw_in_dtor(false) { } ~safe_holder() { release(); } T* operator->() const { return p_; } T* get() const { return p_; } /** * Tell ptr it can throw from destructor. Right way is to * use std::uncaught_exception(), but some compilers lack * correct implementation of the function. */ void permit_throw() { permit_throw_in_dtor = true; } /** * Specially treats exceptions in test object destructor; * if test itself failed, exceptions in destructor * are ignored; if test was successful and destructor failed, * warning exception throwed. */ void release() { try { if (delete_obj() == false) { throw warning("destructor of test object raised" " an SEH exception"); } } catch (const std::exception& ex) { if (permit_throw_in_dtor) { std::string msg = "destructor of test object raised" " exception: "; msg += ex.what(); throw warning(msg); } } catch( ... ) { if (permit_throw_in_dtor) { throw warning("destructor of test object raised an" " exception"); } } } /** * Re-init holder to get brand new object. */ void reset() { release(); permit_throw_in_dtor = false; p_ = new T(); } bool delete_obj() {#if defined(TUT_USE_SEH) __try {#endif T* p = p_; p_ = 0; delete p;#if defined(TUT_USE_SEH) } __except(handle_seh_(::GetExceptionCode())) { if (permit_throw_in_dtor) { return false; } }#endif return true; } };public: typedef test_object<Data> object; /** * Creates and registers test group with specified name. */ test_group(const char* name) : name_(name) { // register itself runner.get().register_group(name_,this); // register all tests tests_registerer<object, test_group, MaxTestsInGroup>::reg(*this); } /** * This constructor is used in self-test run only. */ test_group(const char* name, test_runner& another_runner) : name_(name) { // register itself another_runner.register_group(name_, this); // register all tests tests_registerer<test_object<Data>, test_group, MaxTestsInGroup>::reg(*this); }; /** * Registers test method under given number. */ void reg(int n, testmethod tm) { tests_[n] = tm; } /** * Reset test position before first test. */ void rewind() { current_test_ = tests_.begin(); } /** * Runs next test. */ test_result run_next() { if (current_test_ == tests_.end()) { throw no_more_tests(); } // find next user-specialized test safe_holder<object> obj; while (current_test_ != tests_.end()) { try { return run_test_(current_test_++, obj); } catch (const no_such_test&) { continue; } } throw no_more_tests(); } /** * Runs one test by position. */ test_result run_test(int n) { // beyond tests is special case to discover upper limit if (tests_.rbegin() == tests_.rend()) { throw beyond_last_test(); } if (tests_.rbegin()->first < n) { throw beyond_last_test(); } // withing scope; check if given test exists tests_iterator ti = tests_.find(n); if (ti == tests_.end()) { throw no_such_test(); } safe_holder<object> obj; return run_test_(ti, obj); }private: /** * VC allows only one exception handling type per function, * so I have to split the method. * * TODO: refactoring needed! */ test_result run_test_(const tests_iterator& ti, safe_holder<object>& obj) { std::string current_test_name; try { if (run_test_seh_(ti->second,obj, current_test_name) == false) { throw seh("seh"); } } catch (const no_such_test&) { throw; } catch (const warning& ex) { // test ok, but destructor failed if (obj.get()) { current_test_name = obj->get_test_name(); } test_result tr(name_,ti->first, current_test_name, test_result::warn, ex); return tr; } catch (const failure& ex) { // test failed because of ensure() or similar method if (obj.get()) { current_test_name = obj->get_test_name(); } test_result tr(name_,ti->first, current_test_name, test_result::fail, ex); return tr; } catch (const seh& ex) { // test failed with sigsegv, divide by zero, etc if (obj.get()) { current_test_name = obj->get_test_name(); } test_result tr(name_, ti->first, current_test_name, test_result::term, ex); return tr; } catch (const bad_ctor& ex) { // test failed because test ctor failed; stop the whole group if (obj.get()) { current_test_name = obj->get_test_name(); } test_result tr(name_, ti->first, current_test_name, test_result::ex_ctor, ex); return tr; } catch (const std::exception& ex) { // test failed with std::exception if (obj.get()) { current_test_name = obj->get_test_name(); } test_result tr(name_, ti->first, current_test_name, test_result::ex, ex); return tr; } catch (...) { // test failed with unknown exception if (obj.get()) { current_test_name = obj->get_test_name(); } test_result tr(name_, ti->first, current_test_name, test_result::ex); return tr; } // test passed test_result tr(name_,ti->first, current_test_name, test_result::ok); return tr; } /** * Runs one under SEH if platform supports it. */ bool run_test_seh_(testmethod tm, safe_holder<object>& obj, std::string& current_test_name) {#if defined(TUT_USE_SEH) __try {#endif if (obj.get() == 0) { reset_holder_(obj); } obj->called_method_was_a_dummy_test_ = false;#if defined(TUT_USE_SEH) __try {#endif (obj.get()->*tm)();#if defined(TUT_USE_SEH) } __except(handle_seh_(::GetExceptionCode())) { // throw seh("SEH"); current_test_name = obj->get_test_name(); return false; }#endif if (obj->called_method_was_a_dummy_test_) { // do not call obj.release(); reuse object throw no_such_test(); } current_test_name = obj->get_test_name(); obj.permit_throw(); obj.release();#if defined(TUT_USE_SEH) } __except(handle_seh_(::GetExceptionCode())) { return false; }#endif return true; } void reset_holder_(safe_holder<object>& obj) { try { obj.reset(); } catch (const std::exception& ex) { throw bad_ctor(ex.what()); } catch (...) { throw bad_ctor("test constructor has generated an exception;" " group execution is terminated"); } }};#if defined(TUT_USE_SEH)/** * Decides should we execute handler or ignore SE. */inline int handle_seh_(DWORD excode){ switch(excode) { case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_DATATYPE_MISALIGNMENT: case EXCEPTION_BREAKPOINT: case EXCEPTION_SINGLE_STEP: case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_INEXACT_RESULT: case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_UNDERFLOW: case EXCEPTION_INT_DIVIDE_BY_ZERO: case EXCEPTION_INT_OVERFLOW: case EXCEPTION_PRIV_INSTRUCTION: case EXCEPTION_IN_PAGE_ERROR: case EXCEPTION_ILLEGAL_INSTRUCTION: case EXCEPTION_NONCONTINUABLE_EXCEPTION: case EXCEPTION_STACK_OVERFLOW: case EXCEPTION_INVALID_DISPOSITION: case EXCEPTION_GUARD_PAGE: case EXCEPTION_INVALID_HANDLE: return EXCEPTION_EXECUTE_HANDLER; }; return EXCEPTION_CONTINUE_SEARCH;}#endif}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -