📄 tut.h
字号:
#ifndef TUT_H_GUARD#define TUT_H_GUARD#include <iostream>#include <map>#include <vector>#include <string>#include <sstream>#include <stdexcept>#include <typeinfo>#if defined(TUT_USE_SEH)#include <windows.h>#include <winbase.h>#endif/** * Template Unit Tests Framework for C++. * http://tut.dozen.ru * * @author dozen, tut@dozen.ru */namespace tut{ /** * Exception to be throwed when attempted to execute * missed test by number. */ struct no_such_test : public std::logic_error { no_such_test() : std::logic_error("no such test"){}; }; /** * No such test and passed test number is higher than * any test number in current group. Used in one-by-one * test running when upper bound is not known. */ struct beyond_last_test : public no_such_test { beyond_last_test(){}; }; /** * Group not found exception. */ struct no_such_group : public std::logic_error { no_such_group(const std::string& grp) : std::logic_error(grp){}; }; /** * Internal exception to be throwed when * no more tests left in group or journal. */ struct no_more_tests { no_more_tests(){}; }; /** * Exception to be throwed when ensure() fails or fail() called. */ class failure : public std::logic_error { public: failure(const std::string& msg) : std::logic_error(msg){}; }; /** * Exception to be throwed when test desctructor throwed an exception. */ class warning : public std::logic_error { public: warning(const std::string& msg) : std::logic_error(msg){}; }; /** * Exception to be throwed when test issued SEH (Win32) */ class seh : public std::logic_error { public: seh(const std::string& msg) : std::logic_error(msg){}; }; /** * Return type of runned test/test group. * * For test: contains result of test and, possible, message * for failure or exception. */ struct test_result { /** * Test group name. */ std::string group; /** * Test number in group. */ int test; /** * ok - test finished successfully * fail - test failed with ensure() or fail() methods * ex - test throwed an exceptions * warn - test finished successfully, but test destructor throwed * term - test forced test application to terminate abnormally */ typedef enum { ok, fail, ex, warn, term } result_type; result_type result; /** * Exception message for failed test. */ std::string message; std::string exception_typeid; /** * Default constructor. */ test_result() : test(0),result(ok) { } /** * Constructor. */ test_result( const std::string& grp,int pos,result_type res) : group(grp),test(pos),result(res) { } /** * Constructor with exception. */ test_result( const std::string& grp,int pos, result_type res, const std::exception& ex) : group(grp),test(pos),result(res), message(ex.what()),exception_typeid(typeid(ex).name()) { } }; /** * Interface. * Test group operations. */ struct group_base { virtual ~group_base(){}; // execute tests iteratively virtual void rewind() = 0; virtual test_result run_next() = 0; // execute one test virtual test_result run_test(int n) = 0; }; /** * Test runner callback interface. * Can be implemented by caller to update * tests results in real-time. User can implement * any of callback methods, and leave unused * in default implementation. */ struct callback { /** * Virtual destructor is a must for subclassed types. */ virtual ~callback(){}; /** * Called when new test run started. */ virtual void run_started(){}; /** * Called when a test finished. * @param tr Test results. */ virtual void test_completed(const test_result& /*tr*/){}; /** * Called when all tests in run completed. */ virtual void run_completed(){}; }; /** * Typedef for runner::list_groups() */ typedef std::vector<std::string> groupnames; /** * Test runner. */ class test_runner { protected: typedef std::map<std::string,group_base*> groups; typedef groups::iterator iterator; typedef groups::const_iterator const_iterator; groups groups_; callback default_callback_; callback* callback_; public: /** * Constructor */ test_runner() : callback_(&default_callback_) { } /** * Stores another group for getting by name. */ void register_group(const std::string& name,group_base* gr) { if( gr == 0 ) { throw std::invalid_argument("group shall be non-null"); } groups::iterator found = groups_.find(name); if( found != groups_.end() ) { std::string msg("attempt to add already existent group "+name); // this exception terminates application so we use cerr also std::cerr << msg << std::endl; throw std::logic_error(msg); } groups_[name] = gr; } /** * Stores callback object. */ void set_callback(callback* cb) { callback_ = cb==0? &default_callback_:cb; } /** * Returns callback object. */ callback& get_callback() const { return *callback_; } /** * Returns list of known test groups. */ groupnames list_groups() const { groupnames ret; const_iterator i = groups_.begin(); const_iterator e = groups_.end(); while( i != e ) { ret.push_back(i->first); ++i; } return ret; } /** * Runs all tests in all groups. * @param callback Callback object if exists; null otherwise */ void run_tests() const { callback_->run_started(); const_iterator i = groups_.begin(); const_iterator e = groups_.end(); while( i != e ) { try { // iterate all tests i->second->rewind(); for( ;; ) { test_result tr = i->second->run_next(); callback_->test_completed(tr); } } catch( const no_more_tests& ) { // ok } ++i; } callback_->run_completed(); } /** * Runs all tests in specified group. */ void run_tests(const std::string& group_name) const { callback_->run_started(); const_iterator i = groups_.find(group_name); if( i == groups_.end() ) { throw no_such_group(group_name); } try { // iterate all tests i->second->rewind(); for(;;) { test_result tr = i->second->run_next(); callback_->test_completed(tr); } } catch( const no_more_tests& ) { // ok } callback_->run_completed(); } /** * Runs one test in specified group. */ test_result run_test(const std::string& group_name,int n) const { callback_->run_started(); const_iterator i = groups_.find(group_name); if( i == groups_.end() ) { throw no_such_group(group_name); } try { test_result tr = i->second->run_test(n); callback_->test_completed(tr); callback_->run_completed(); return tr; } catch( const beyond_last_test& ) { callback_->run_completed(); throw; } catch( const no_such_test& ) { callback_->run_completed(); throw; } } }; /** * Singleton for test_runner implementation. * Instance with name runner_singleton shall be implemented * by user. */ class test_runner_singleton { public: static test_runner& get() { static test_runner tr; return tr; } }; extern test_runner_singleton runner; /** * Test object. Contains data test run upon and default test method * implementation. Inherited from Data to allow tests to * access test data as members. */ template <class Data> class test_object : public Data { public: /** * Default constructor */ test_object(){}; /** * The flag is set to true by default (dummy) test. * Used to detect usused test numbers and avoid unnecessary * test object creation which may be time-consuming depending * on operations described in Data::Data() and Data::~Data(). * TODO: replace with throwing special exception from default test. */ bool called_method_was_a_dummy_test_; /** * Default do-nothing test. */ template <int n> void test() { called_method_was_a_dummy_test_ = true; } }; namespace { /** * Tests provided condition. * Throws if false. */ void ensure(bool cond) { if( !cond ) throw failure(""); } /** * Tests provided condition.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -