📄 tut.hpp
字号:
#ifndef TUT_H_GUARD#define TUT_H_GUARD#include <iostream>#include <map>#include <vector>#include <string>#include <sstream>#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 Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com */namespace tut{/** * The base for all TUT exceptions. */struct tut_error : public std::exception{ tut_error(const std::string& msg) : err_msg(msg) { } ~tut_error() throw() { } const char* what() const throw() { return err_msg.c_str(); } private: std::string err_msg;};/** * Exception to be throwed when attempted to execute * missed test by number. */struct no_such_test : public tut_error{ no_such_test() : tut_error("no such test") { } ~no_such_test() throw() { }};/** * 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() { } ~beyond_last_test() throw() { }};/** * Group not found exception. */struct no_such_group : public tut_error{ no_such_group(const std::string& grp) : tut_error(grp) { } ~no_such_group() throw() { }};/** * Internal exception to be throwed when * no more tests left in group or journal. */struct no_more_tests{ no_more_tests() { } ~no_more_tests() throw() { }};/** * Internal exception to be throwed when * test constructor has failed. */struct bad_ctor : public tut_error{ bad_ctor(const std::string& msg) : tut_error(msg) { } ~bad_ctor() throw() { }};/** * Exception to be throwed when ensure() fails or fail() called. */struct failure : public tut_error{ failure(const std::string& msg) : tut_error(msg) { } ~failure() throw() { }};/** * Exception to be throwed when test desctructor throwed an exception. */struct warning : public tut_error{ warning(const std::string& msg) : tut_error(msg) { } ~warning() throw() { }};/** * Exception to be throwed when test issued SEH (Win32) */struct seh : public tut_error{ seh(const std::string& msg) : tut_error(msg) { } ~seh() throw() { }};/** * 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; /** * Test name (optional) */ std::string name; /** * 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 */ enum result_type { ok, fail, ex, warn, term, ex_ctor }; 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, const std::string& test_name, result_type res) : group(grp), test(pos), name(test_name), result(res) { } /** * Constructor with exception. */ test_result(const std::string& grp,int pos, const std::string& test_name, result_type res, const std::exception& ex) : group(grp), test(pos), name(test_name), 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 group started * @param name Name of the group */ virtual void group_started(const std::string& /*name*/) { } /** * Called when a test finished. * @param tr Test results. */ virtual void test_completed(const test_result& /*tr*/) { } /** * Called when a group is completed * @param name Name of the group */ virtual void group_completed(const std::string& /*name*/) { } /** * 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{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 tut_error("group shall be non-null"); } // TODO: inline variable 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 // TODO: should this message appear in stream? std::cerr << msg << std::endl; throw tut_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. */ const 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) { callback_->group_started(i->first); try { run_all_tests_in_group_(i); } catch (const no_more_tests&) { callback_->group_completed(i->first); } ++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()) { callback_->run_completed(); throw no_such_group(group_name); } callback_->group_started(group_name); try { run_all_tests_in_group_(i); } catch (const no_more_tests&) { // ok } callback_->group_completed(group_name); 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()) { callback_->run_completed(); throw no_such_group(group_name); } callback_->group_started(group_name); try { test_result tr = i->second->run_test(n); callback_->test_completed(tr); callback_->group_completed(group_name); callback_->run_completed(); return tr; } catch (const beyond_last_test&) { callback_->group_completed(group_name); callback_->run_completed(); throw; } catch (const no_such_test&) { callback_->group_completed(group_name); callback_->run_completed(); throw; } }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_;private: void run_all_tests_in_group_(const_iterator i) const { i->second->rewind(); for ( ;; ) { test_result tr = i->second->run_next(); callback_->test_completed(tr); if (tr.result == test_result::ex_ctor) { throw no_more_tests(); } } }};/** * 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() { } void set_test_name(const std::string& current_test_name) { current_test_name_ = current_test_name; } const std::string& get_test_name() const { return current_test_name_; } /** * Default do-nothing test. */ template <int n> void test() { called_method_was_a_dummy_test_ = true; } /** * 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_;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -