libs/test/src/unit_test_suite.cpp
// (C) Copyright Gennadiy Rozental 2001-2003.
// Use, modification, and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/libs/test for the library home page.
//
// File : $RCSfile: unit_test_suite.cpp,v $
//
// Version : $Revision: 1.12 $
//
// Description : privide core implementation for Unit Test Framework.
// Extensions could be provided in separate files
// ***************************************************************************
// Boost.Test
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/unit_test_log.hpp>
#include <boost/test/unit_test_result.hpp>
// BOOST
#include <boost/mem_fn.hpp>
// STL
#include <list>
#include <algorithm>
namespace boost {
namespace unit_test_framework {
// ************************************************************************** //
// ************** test_case ************** //
// ************************************************************************** //
detail::unit_test_monitor the_monitor;
typedef unit_test_result const* unit_test_result_cptr;
struct test_case::Impl {
Impl( bool monitor_run_ ) : m_monitor_run( monitor_run_ ), m_results_set( unit_test_result_cptr() ) {}
bool m_monitor_run; // true - unit_test_monitor will be user to monitor running
// of implementation function
std::list<test_case const*> m_dependencies_list; // list of test cases this test case depends on. We won't run it until they pass
unit_test_result_cptr m_results_set; // results set instance reference for this test case
static bool s_abort_testing; // used to flag critical error and try gracefully stop testing
bool check_dependencies();
};
bool test_case::Impl::s_abort_testing = false;
//____________________________________________________________________________//
inline bool
test_case::Impl::check_dependencies()
{
return std::find_if( m_dependencies_list.begin(),
m_dependencies_list.end(),
std::not1( boost::mem_fn( &test_case::has_passed ) ) ) == m_dependencies_list.end();
}
//____________________________________________________________________________//
test_case::test_case( std::string const& name_, bool type, unit_test_counter stages_amount_, bool monitor_run_ )
: p_timeout( 0 ), p_expected_failures( 0 ), p_type( type ),
p_name( name_ ), p_compound_stage( false ), p_stages_amount( stages_amount_ ),
m_pimpl( new Impl( monitor_run_ ) )
{
}
//____________________________________________________________________________//
unit_test_counter
test_case::size() const
{
return 1;
}
//____________________________________________________________________________//
void
test_case::depends_on( test_case const* rhs )
{
m_pimpl->m_dependencies_list.push_back( rhs );
}
//____________________________________________________________________________//
bool
test_case::has_passed() const
{
return m_pimpl->m_results_set != unit_test_result_cptr() && m_pimpl->m_results_set->has_passed();
}
//____________________________________________________________________________//
void
test_case::run()
{
using detail::unit_test_monitor;
test_case_scope_tracker scope_tracker( *this );
// 0. Check if we allowed to run this test case
if( !m_pimpl->check_dependencies() )
return;
m_pimpl->s_abort_testing = false;
// 1. Init test results
unit_test_result_tracker result_tracker( p_name, p_expected_failures );
m_pimpl->m_results_set = &unit_test_result::instance();
// 2. Initialize test case
if( m_pimpl->m_monitor_run ) {
error_level_type init_result =
the_monitor.execute_and_translate( this, &test_case::do_init, p_timeout );
if( init_result != unit_test_monitor::test_ok ) {
m_pimpl->s_abort_testing = unit_test_monitor::is_critical_error( init_result );
BOOST_UT_LOG_BEGIN( __FILE__, __LINE__, log_fatal_errors )
"Test case initialization has failed"
BOOST_UT_LOG_END;
return;
}
}
else {
do_init();
}
// 3. Run test case (all stages)
for( unit_test_counter i=0; i != p_stages_amount; ++i ) {
p_compound_stage.value = false; // could be set by do_run to mark compound stage;
// than no need to report progress here
if( m_pimpl->m_monitor_run ) {
error_level_type run_result =
the_monitor.execute_and_translate( this, &test_case::do_run, p_timeout );
if( unit_test_monitor::is_critical_error( run_result ) ) {
m_pimpl->s_abort_testing = true;
BOOST_UT_LOG_BEGIN( __FILE__, __LINE__, log_fatal_errors )
"Testing aborted"
BOOST_UT_LOG_END;
}
if( m_pimpl->s_abort_testing )
return;
}
else {
do_run();
}
if( p_stages_amount != 1 && !p_compound_stage.get() ) // compound test
unit_test_log::instance() << log_progress();
}
// 3. Finalize test case
if( m_pimpl->m_monitor_run ) {
error_level_type destroy_result =
the_monitor.execute_and_translate( this, &test_case::do_destroy, p_timeout );
m_pimpl->s_abort_testing = unit_test_monitor::is_critical_error( destroy_result );
}
else {
do_destroy();
}
}
//____________________________________________________________________________//
// ************************************************************************** //
// ************** test_suite ************** //
// ************************************************************************** //
struct test_suite::Impl {
std::list<test_case*> m_test_cases;
std::list<test_case*>::iterator m_curr_test_case;
unit_test_counter m_cumulative_size;
};
//____________________________________________________________________________//
test_suite::test_suite( std::string const& name )
: test_case( name, false, 0, false ), m_pimpl( new Impl )
{
m_pimpl->m_cumulative_size = 0;
}
//____________________________________________________________________________//
static void safe_delete_test_case( test_case* ptr ) { boost::checked_delete<test_case>( ptr ); }
test_suite::~test_suite()
{
std::for_each( m_pimpl->m_test_cases.begin(), m_pimpl->m_test_cases.end(), &safe_delete_test_case );
}
//____________________________________________________________________________//
void
test_suite::add( test_case* tc, unit_test_counter exp_fail, int timeout )
{
if( exp_fail != 0 ) {
tc->p_expected_failures.value = exp_fail;
}
p_expected_failures.value += tc->p_expected_failures;
if( timeout != 0 )
tc->p_timeout.value = timeout;
m_pimpl->m_test_cases.push_back( tc );
m_pimpl->m_cumulative_size += tc->size();
p_stages_amount.set( p_stages_amount.get()+1 );
}
//____________________________________________________________________________//
unit_test_counter
test_suite::size() const
{
return m_pimpl->m_cumulative_size;
}
//____________________________________________________________________________//
void
test_suite::do_init()
{
m_pimpl->m_curr_test_case = m_pimpl->m_test_cases.begin();
}
//____________________________________________________________________________//
void
test_suite::do_run()
{
if( (*m_pimpl->m_curr_test_case)->size() > 1 )
p_compound_stage.value = true;
(*m_pimpl->m_curr_test_case)->run();
++m_pimpl->m_curr_test_case;
}
//____________________________________________________________________________//
// ************************************************************************** //
// ************** object generators ************** //
// ************************************************************************** //
namespace detail {
std::string const&
normalize_test_case_name( std::string& name_ )
{
if( name_[0] == '&' )
name_.erase( 0, 1 );
return name_;
}
} // namespace detail
} // namespace unit_test_framework
} // namespace boost
// ***************************************************************************
// Revision History :
//
// $Log: unit_test_suite.cpp,v $
// Revision 1.12 2003/12/01 00:42:37 rogeeff
// prerelease cleaning
//
// ***************************************************************************
// EOF