2013-05-09 11:13:16 -04:00

282 lines
6.4 KiB
C++

// ---
//
// $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 <algorithm>
#include <cassert>
#include <cstring>
#include <functional>
#include <numeric>
#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 <class FwdIter>
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(Suite* suite)
{
_suites.push_back(suite);
}
/// 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