// (C) Copyright Gennadiy Rozental 2001-2003. // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied warranty, // and with no claim as to its suitability for any purpose. // See http://www.boost.org for updates, documentation, and revision history. // // File : $RCSfile$ // // Version : $Revision$ // // Description : privide core implementation for Unit Test Framework. // Extensions could be provided in separate files // *************************************************************************** // Boost.Test #include #include #include // BOOST #include // STL #include #include namespace boost { namespace unit_test_framework { // ************************************************************************** // // ************** test_case ************** // // ************************************************************************** // 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 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 = unit_test_monitor( *this, &test_case::do_init ).execute_and_translate( 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 = unit_test_monitor( *this, &test_case::do_run ).execute_and_translate( 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 = unit_test_monitor( *this, &test_case::do_destroy ).execute_and_translate( 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 m_test_cases; std::list::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( 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$ // Revision 1.8 2003/06/09 09:21:04 rogeeff // dependency check support // simplified testing abort procedures // // *************************************************************************** // EOF