#ifndef TUT_RUNNER_H_GUARD #define TUT_RUNNER_H_GUARD #include #include #include #include "tut_exception.hpp" namespace tut { /** * Interface. * Test group operations. */ struct group_base { virtual ~group_base() { } // execute tests iteratively virtual void rewind() = 0; virtual bool run_next(test_result &) = 0; // execute one test virtual bool run_test(int n, test_result &tr) = 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 { /** * Default constructor. */ 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) { (void)name; } /** * Called when a test finished. * @param tr Test results. */ virtual void test_completed(const test_result& tr) { (void)tr; } /** * Called when a group is completed * @param name Name of the group */ virtual void group_completed(const std::string& name) { (void)name; } /** * Called when all tests in run completed. */ virtual void run_completed() { } virtual bool all_ok() const { return true; } private: callback(const callback &); void operator=(const callback&); }; /** * Typedef for runner::list_groups() */ typedef std::vector groupnames; typedef std::set callbacks; /** * Test runner. */ class test_runner { public: /** * Constructor */ test_runner() : groups_(), callbacks_() { } /** * Stores another group for getting by name. * @param name new group object * @param gr new callback object */ void register_group(const std::string& name, group_base* gr) { if (gr == 0) { throw tut_error("group shall be non-null"); } if (groups_.find(name) != groups_.end()) { std::string msg("attempt to add already existent group " + name); throw tut_error(msg); } groups_.insert( std::make_pair(name, gr) ); } /** * Stores one callback object. * @param cb new callback object */ void set_callback(callback *cb) { clear_callbacks(); insert_callback(cb); } /** * Add callback object. * @param cb new callback object */ void insert_callback(callback* cb) { if(cb != NULL) { callbacks_.insert(cb); } } /** * Remove callback object. * @param cb callback to remove */ void erase_callback(callback* cb) { callbacks_.erase(cb); } /** * Remove all callback objects. */ void clear_callbacks() { callbacks_.clear(); } /** * Returns callback list. * @return callback list */ const callbacks &get_callbacks() const { return callbacks_; } /** * Set callback list. * @param cb new callback list */ void set_callbacks(const callbacks &cb) { callbacks_ = cb; } /** * Returns list of known test groups. * @return groups list */ const groupnames list_groups() const { groupnames ret; for(const_iterator i = groups_.begin(); i != groups_.end(); ++i) { ret.push_back(i->first); } return ret; } /** * Runs all tests in all groups. */ void run_tests() const { cb_run_started_(); const_iterator i = groups_.begin(); const_iterator e = groups_.end(); while (i != e) { cb_group_started_(i->first); run_all_tests_in_group_(i); cb_group_completed_(i->first); ++i; } cb_run_completed_(); } /** * Runs all tests in specified group. * @param group_name group to test */ void run_tests(const std::string& group_name) const { cb_run_started_(); const_iterator i = groups_.find(group_name); if (i == groups_.end()) { cb_run_completed_(); throw no_such_group(group_name); } cb_group_started_(group_name); run_all_tests_in_group_(i); cb_group_completed_(group_name); cb_run_completed_(); } /** * Runs one test in specified group. * @param group_name group to test * @param n run case in test * @param tr result of this case * @return true if test is ok, otherwise false */ bool run_test(const std::string& group_name, int n, test_result &tr) const { cb_run_started_(); const_iterator i = groups_.find(group_name); if (i == groups_.end()) { cb_run_completed_(); throw no_such_group(group_name); } cb_group_started_(group_name); bool t = i->second->run_test(n, tr); if(t && tr.result != test_result::dummy) { cb_test_completed_(tr); } cb_group_completed_(group_name); cb_run_completed_(); return t; } protected: typedef std::map groups; typedef groups::iterator iterator; typedef groups::const_iterator const_iterator; groups groups_; callbacks callbacks_; private: friend class restartable_wrapper; void cb_run_started_() const { for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i) { (*i)->run_started(); } } void cb_run_completed_() const { for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i) { (*i)->run_completed(); } } void cb_group_started_(const std::string &group_name) const { for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i) { (*i)->group_started(group_name); } } void cb_group_completed_(const std::string &group_name) const { for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i) { (*i)->group_completed(group_name); } } void cb_test_completed_(const test_result &tr) const { for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i) { (*i)->test_completed(tr); } } void run_all_tests_in_group_(const_iterator i) const { i->second->rewind(); test_result tr; while(i->second->run_next(tr)) { if(tr.result != test_result::dummy) { cb_test_completed_(tr); } if (tr.result == test_result::ex_ctor) { // test object ctor failed, skip whole group break; } } } }; /** * 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; } #endif // TUT_RUNNER_H_GUARD