// --- // // $Id: suite.cpp,v 1.7 2010/03/26 04:38:25 hartwork Exp $ // // CppTest - A C++ Unit Testing Framework // Copyright (c) 2003 Niklas Lundell // // --- // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307, USA. // // --- #include #include #include #include #include #if (defined(__WIN32__) || defined(WIN32)) # include "winconfig.h" #else # include "config.h" #endif #include "cpptest-output.h" #include "cpptest-source.h" #include "cpptest-suite.h" using namespace std; namespace Test { namespace { // Destroys all dynamically allocated objects within the given range. // template void destroy_range(FwdIter first, FwdIter last) { while (first != last) delete *first++; } } // anonymous namespace /// Constructs an empty test suite. /// Suite::Suite() : _cur_test(0), _output(0), _success(true) {} /// Destroys this suite object. /// Suite::~Suite() { destroy_range(_suites.begin(), _suites.end()); } /// Starts the testing. All tests in this suite and embedded suites will /// be executed. /// /// \param output Progress report destination. /// \param cont_after_fail Continue functions despite failures. /// /// \return True if no test failed; false otherwise. /// bool Suite::run(Output& output, bool cont_after_fail) { int ntests = total_tests(); output.initialize(ntests); do_run(&output, cont_after_fail); output.finished(ntests, total_time(true)); return _success; } /// \fn void Suite::setup() /// /// Setups a test fixture. This function is called before each test, /// in this suite, is executed. /// /// This function should be overloaded by derived classes to provide /// specialized behavior. /// /// \see tear_down() /// \fn void Suite::tear_down() /// /// Tears down a test fixture. This function is called after each test, /// in this suite, have been executed. /// /// This function should be overloaded by derived classes to provide /// specialized behavior. /// /// \see setup() /// Adds a suite to this suite. Tests in added suites will be executed /// when run() of the top-level suite is called. /// /// \param suite %Test suite to add. /// void Suite::add(auto_ptr suite) { _suites.push_back(suite.release()); } /// Registers a test function. /// /// \b Note: Do not call this function directly, use the TEST_ADD(func) /// macro instead. /// /// \param func Pointer to a test function. /// \param name Class and function name of the function. The format \b must /// equal \e class::func. /// void Suite::register_test(Func func, const string& name) { string::size_type pos = name.find_first_of(':'); assert(!name.empty() && name[pos + 1] == ':' && name[pos + 2] != '\0'); _name.assign(name, 0, pos); _tests.push_back(Data(func, name.substr(pos + 2))); } /// Issues an assertment to the output handler. /// /// Do not call this function directly, use one of the available assertment /// macros instead, see \ref asserts. /// /// \param s Assert point information. /// void Suite::assertment(Source s) { s._suite = _name; s._test = *_cur_test; _output->assertment(s); _result = _success = false; } // Functor to execute tests for the given suite. // struct Suite::ExecTests { Suite& _suite; ExecTests(Suite& s) : _suite(s) {} void operator()(Data& data) { _suite._cur_test = &data._name; _suite._result = true; // assume success, assert will set to false _suite._output->test_start(data._name); _suite.setup(); Time start(Time::current()); // FIXME Also feedback exception to user try { (_suite.*data._func)(); } catch (...) { _suite._result = _suite._success = false; } Time end(Time::current()); _suite.tear_down(); data._time = end - start; _suite._output->test_end(data._name, _suite._result, data._time); } }; // Functor to execute a suite. // struct Suite::DoRun { bool _continue; Output* _output; DoRun(Output* output, bool cont) : _continue(cont), _output(output) {} void operator()(Suite* suite) { suite->do_run(_output, _continue); } }; // Execute all tests in this and added suites. // void Suite::do_run(Output* os, bool cont_after_fail) { _continue = cont_after_fail; _output = os; _output->suite_start(_tests.size(), _name); for_each(_tests.begin(), _tests.end(), ExecTests(*this)); _output->suite_end(_tests.size(), _name, total_time(false)); for_each(_suites.begin(), _suites.end(), DoRun(_output, _continue)); // FIXME Find a cleaner way Suites::const_iterator iter = _suites.begin(); while (iter != _suites.end()) { if (!(*iter)->_success) { _success = false; break; } iter++; } } // Functor to count all tests in a suite. // struct Suite::SubSuiteTests { int operator()(size_t value, const Suite* s) const { return value + s->total_tests(); } }; // Counts all tests in this and all its embedded suites. // int Suite::total_tests() const { return accumulate(_suites.begin(), _suites.end(), _tests.size(), SubSuiteTests()); } // Functor to accumulate execution time for tests. // struct Suite::SuiteTime { Time operator()(const Time& time, const Data& data) { return time + data._time; } }; // Functor to accumulate execution time for suites. // struct Suite::SubSuiteTime { Time operator()(Time time, const Suite* s) const { return time + s->total_time(true); } }; // Counts time accumulated execution time for all tests in this and all // its embedded suites. // Time Suite::total_time(bool recursive) const { Time time = accumulate(_tests.begin(), _tests.end(), Time(), SuiteTime()); return !recursive ? time : accumulate(_suites.begin(), _suites.end(), time, SubSuiteTime()); } } // namespace Test