Chris@16: // (C) Copyright Gennadiy Rozental 2005-2008. Chris@16: // Distributed under the Boost Software License, Version 1.0. Chris@16: // (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: // See http://www.boost.org/libs/test for the library home page. Chris@16: // Chris@16: // File : $RCSfile$ Chris@16: // Chris@101: // Version : $Revision$ Chris@16: // Chris@16: // Description : implements framework API - main driver for the test Chris@16: // *************************************************************************** Chris@16: Chris@16: #ifndef BOOST_TEST_FRAMEWORK_IPP_021005GER Chris@16: #define BOOST_TEST_FRAMEWORK_IPP_021005GER Chris@16: Chris@16: // Boost.Test Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: // Boost Chris@16: #include Chris@16: Chris@16: // STL Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #ifdef BOOST_NO_STDC_NAMESPACE Chris@16: namespace std { using ::time; using ::srand; } Chris@16: #endif Chris@16: Chris@16: #include Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: namespace boost { Chris@16: Chris@16: namespace unit_test { Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** test_start calls wrapper ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: namespace ut_detail { Chris@16: Chris@16: struct test_start_caller { Chris@16: test_start_caller( test_observer* to, counter_t tc_amount ) Chris@16: : m_to( to ) Chris@16: , m_tc_amount( tc_amount ) Chris@16: {} Chris@16: Chris@16: int operator()() Chris@16: { Chris@16: m_to->test_start( m_tc_amount ); Chris@16: return 0; Chris@16: } Chris@16: Chris@16: private: Chris@16: // Data members Chris@16: test_observer* m_to; Chris@16: counter_t m_tc_amount; Chris@16: }; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: struct test_init_caller { Chris@16: explicit test_init_caller( init_unit_test_func init_func ) Chris@16: : m_init_func( init_func ) Chris@16: {} Chris@16: int operator()() Chris@16: { Chris@16: #ifdef BOOST_TEST_ALTERNATIVE_INIT_API Chris@16: if( !(*m_init_func)() ) Chris@16: throw std::runtime_error( "test module initialization failed" ); Chris@16: #else Chris@16: test_suite* manual_test_units = (*m_init_func)( framework::master_test_suite().argc, framework::master_test_suite().argv ); Chris@16: Chris@16: if( manual_test_units ) Chris@16: framework::master_test_suite().add( manual_test_units ); Chris@16: #endif Chris@16: return 0; Chris@16: } Chris@16: Chris@16: // Data members Chris@16: init_unit_test_func m_init_func; Chris@16: }; Chris@16: Chris@16: } Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** framework ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: class framework_impl : public test_tree_visitor { Chris@16: public: Chris@16: framework_impl() Chris@16: : m_master_test_suite( 0 ) Chris@16: , m_curr_test_case( INV_TEST_UNIT_ID ) Chris@16: , m_next_test_case_id( MIN_TEST_CASE_ID ) Chris@16: , m_next_test_suite_id( MIN_TEST_SUITE_ID ) Chris@16: , m_is_initialized( false ) Chris@16: , m_test_in_progress( false ) Chris@16: {} Chris@16: Chris@16: ~framework_impl() { clear(); } Chris@16: Chris@16: void clear() Chris@16: { Chris@16: while( !m_test_units.empty() ) { Chris@16: test_unit_store::value_type const& tu = *m_test_units.begin(); Chris@16: test_unit* tu_ptr = tu.second; Chris@16: Chris@16: // the delete will erase this element from map Chris@16: if( ut_detail::test_id_2_unit_type( tu.second->p_id ) == tut_suite ) Chris@16: delete (test_suite const*)tu_ptr; Chris@16: else Chris@16: delete (test_case const*)tu_ptr; Chris@16: } Chris@16: } Chris@16: Chris@16: void set_tu_id( test_unit& tu, test_unit_id id ) { tu.p_id.value = id; } Chris@16: Chris@16: // test_tree_visitor interface implementation Chris@16: void visit( test_case const& tc ) Chris@16: { Chris@16: if( !tc.check_dependencies() ) { Chris@16: BOOST_TEST_FOREACH( test_observer*, to, m_observers ) Chris@16: to->test_unit_skipped( tc ); Chris@16: Chris@16: return; Chris@16: } Chris@16: Chris@16: BOOST_TEST_FOREACH( test_observer*, to, m_observers ) Chris@16: to->test_unit_start( tc ); Chris@16: Chris@16: boost::timer tc_timer; Chris@16: test_unit_id bkup = m_curr_test_case; Chris@16: m_curr_test_case = tc.p_id; Chris@16: unit_test_monitor_t::error_level run_result = unit_test_monitor.execute_and_translate( tc ); Chris@16: Chris@16: unsigned long elapsed = static_cast( tc_timer.elapsed() * 1e6 ); Chris@16: Chris@16: if( unit_test_monitor.is_critical_error( run_result ) ) { Chris@16: BOOST_TEST_FOREACH( test_observer*, to, m_observers ) Chris@16: to->test_aborted(); Chris@16: } Chris@16: Chris@16: BOOST_TEST_FOREACH( test_observer*, to, m_observers ) Chris@16: to->test_unit_finish( tc, elapsed ); Chris@16: Chris@16: m_curr_test_case = bkup; Chris@16: Chris@16: if( unit_test_monitor.is_critical_error( run_result ) ) Chris@16: throw test_being_aborted(); Chris@16: } Chris@16: Chris@16: bool test_suite_start( test_suite const& ts ) Chris@16: { Chris@16: if( !ts.check_dependencies() ) { Chris@16: BOOST_TEST_FOREACH( test_observer*, to, m_observers ) Chris@16: to->test_unit_skipped( ts ); Chris@16: Chris@16: return false; Chris@16: } Chris@16: Chris@16: BOOST_TEST_FOREACH( test_observer*, to, m_observers ) Chris@16: to->test_unit_start( ts ); Chris@16: Chris@16: return true; Chris@16: } Chris@16: Chris@16: void test_suite_finish( test_suite const& ts ) Chris@16: { Chris@16: BOOST_TEST_FOREACH( test_observer*, to, m_observers ) Chris@16: to->test_unit_finish( ts, 0 ); Chris@16: } Chris@16: Chris@16: ////////////////////////////////////////////////////////////////// Chris@16: struct priority_order { Chris@16: bool operator()( test_observer* lhs, test_observer* rhs ) const Chris@16: { Chris@16: return (lhs->priority() < rhs->priority()) || ((lhs->priority() == rhs->priority()) && (lhs < rhs)); Chris@16: } Chris@16: }; Chris@16: Chris@16: typedef std::map test_unit_store; Chris@16: typedef std::set observer_store; Chris@16: Chris@16: master_test_suite_t* m_master_test_suite; Chris@16: test_unit_id m_curr_test_case; Chris@16: test_unit_store m_test_units; Chris@16: Chris@16: test_unit_id m_next_test_case_id; Chris@16: test_unit_id m_next_test_suite_id; Chris@16: Chris@16: bool m_is_initialized; Chris@16: bool m_test_in_progress; Chris@16: Chris@16: observer_store m_observers; Chris@16: }; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: namespace { Chris@16: Chris@16: #if defined(__CYGWIN__) Chris@16: framework_impl& s_frk_impl() { static framework_impl* the_inst = 0; if(!the_inst) the_inst = new framework_impl; return *the_inst; } Chris@16: #else Chris@16: framework_impl& s_frk_impl() { static framework_impl the_inst; return the_inst; } Chris@16: #endif Chris@16: Chris@16: } // local namespace Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: namespace framework { Chris@16: Chris@16: void Chris@16: init( init_unit_test_func init_func, int argc, char* argv[] ) Chris@16: { Chris@16: runtime_config::init( argc, argv ); Chris@16: Chris@16: // set the log level and format Chris@16: unit_test_log.set_threshold_level( runtime_config::log_level() ); Chris@16: unit_test_log.set_format( runtime_config::log_format() ); Chris@16: Chris@16: // set the report level and format Chris@16: results_reporter::set_level( runtime_config::report_level() ); Chris@16: results_reporter::set_format( runtime_config::report_format() ); Chris@16: Chris@16: register_observer( results_collector ); Chris@16: register_observer( unit_test_log ); Chris@16: Chris@16: if( runtime_config::show_progress() ) Chris@16: register_observer( progress_monitor ); Chris@16: Chris@16: if( runtime_config::detect_memory_leaks() > 0 ) { Chris@16: debug::detect_memory_leaks( true ); Chris@16: debug::break_memory_alloc( runtime_config::detect_memory_leaks() ); Chris@16: } Chris@16: Chris@16: // init master unit test suite Chris@16: master_test_suite().argc = argc; Chris@16: master_test_suite().argv = argv; Chris@16: Chris@16: try { Chris@16: boost::execution_monitor em; Chris@16: Chris@16: ut_detail::test_init_caller tic( init_func ); Chris@16: Chris@16: em.execute( tic ); Chris@16: } Chris@16: catch( execution_exception const& ex ) { Chris@16: throw setup_error( ex.what() ); Chris@16: } Chris@16: Chris@16: s_frk_impl().m_is_initialized = true; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: bool Chris@16: is_initialized() Chris@16: { Chris@16: return s_frk_impl().m_is_initialized; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: register_test_unit( test_case* tc ) Chris@16: { Chris@16: BOOST_TEST_SETUP_ASSERT( tc->p_id == INV_TEST_UNIT_ID, BOOST_TEST_L( "test case already registered" ) ); Chris@16: Chris@16: test_unit_id new_id = s_frk_impl().m_next_test_case_id; Chris@16: Chris@16: BOOST_TEST_SETUP_ASSERT( new_id != MAX_TEST_CASE_ID, BOOST_TEST_L( "too many test cases" ) ); Chris@16: Chris@16: typedef framework_impl::test_unit_store::value_type map_value_type; Chris@16: Chris@16: s_frk_impl().m_test_units.insert( map_value_type( new_id, tc ) ); Chris@16: s_frk_impl().m_next_test_case_id++; Chris@16: Chris@16: s_frk_impl().set_tu_id( *tc, new_id ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: register_test_unit( test_suite* ts ) Chris@16: { Chris@16: BOOST_TEST_SETUP_ASSERT( ts->p_id == INV_TEST_UNIT_ID, BOOST_TEST_L( "test suite already registered" ) ); Chris@16: Chris@16: test_unit_id new_id = s_frk_impl().m_next_test_suite_id; Chris@16: Chris@16: BOOST_TEST_SETUP_ASSERT( new_id != MAX_TEST_SUITE_ID, BOOST_TEST_L( "too many test suites" ) ); Chris@16: Chris@16: typedef framework_impl::test_unit_store::value_type map_value_type; Chris@16: s_frk_impl().m_test_units.insert( map_value_type( new_id, ts ) ); Chris@16: s_frk_impl().m_next_test_suite_id++; Chris@16: Chris@16: s_frk_impl().set_tu_id( *ts, new_id ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: deregister_test_unit( test_unit* tu ) Chris@16: { Chris@16: s_frk_impl().m_test_units.erase( tu->p_id ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: clear() Chris@16: { Chris@16: s_frk_impl().clear(); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: register_observer( test_observer& to ) Chris@16: { Chris@16: s_frk_impl().m_observers.insert( &to ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: deregister_observer( test_observer& to ) Chris@16: { Chris@16: s_frk_impl().m_observers.erase( &to ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: reset_observers() Chris@16: { Chris@16: s_frk_impl().m_observers.clear(); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: master_test_suite_t& Chris@16: master_test_suite() Chris@16: { Chris@16: if( !s_frk_impl().m_master_test_suite ) Chris@16: s_frk_impl().m_master_test_suite = new master_test_suite_t; Chris@16: Chris@16: return *s_frk_impl().m_master_test_suite; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: test_case const& Chris@16: current_test_case() Chris@16: { Chris@16: return get( s_frk_impl().m_curr_test_case ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: test_unit& Chris@16: get( test_unit_id id, test_unit_type t ) Chris@16: { Chris@16: test_unit* res = s_frk_impl().m_test_units[id]; Chris@16: Chris@16: if( (res->p_type & t) == 0 ) Chris@16: throw internal_error( "Invalid test unit type" ); Chris@16: Chris@16: return *res; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: run( test_unit_id id, bool continue_test ) Chris@16: { Chris@16: if( id == INV_TEST_UNIT_ID ) Chris@16: id = master_test_suite().p_id; Chris@16: Chris@16: test_case_counter tcc; Chris@16: traverse_test_tree( id, tcc ); Chris@16: Chris@16: BOOST_TEST_SETUP_ASSERT( tcc.p_count != 0 , runtime_config::test_to_run().is_empty() Chris@16: ? BOOST_TEST_L( "test tree is empty" ) Chris@16: : BOOST_TEST_L( "no test cases matching filter" ) ); Chris@16: Chris@16: bool call_start_finish = !continue_test || !s_frk_impl().m_test_in_progress; Chris@16: bool was_in_progress = s_frk_impl().m_test_in_progress; Chris@16: Chris@16: s_frk_impl().m_test_in_progress = true; Chris@16: Chris@16: if( call_start_finish ) { Chris@16: BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) { Chris@16: boost::execution_monitor em; Chris@16: Chris@16: try { Chris@16: em.execute( ut_detail::test_start_caller( to, tcc.p_count ) ); Chris@16: } Chris@16: catch( execution_exception const& ex ) { Chris@16: throw setup_error( ex.what() ); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: switch( runtime_config::random_seed() ) { Chris@16: case 0: Chris@16: break; Chris@16: case 1: { Chris@16: unsigned int seed = static_cast( std::time( 0 ) ); Chris@16: BOOST_TEST_MESSAGE( "Test cases order is shuffled using seed: " << seed ); Chris@16: std::srand( seed ); Chris@16: break; Chris@16: } Chris@16: default: Chris@16: BOOST_TEST_MESSAGE( "Test cases order is shuffled using seed: " << runtime_config::random_seed() ); Chris@16: std::srand( runtime_config::random_seed() ); Chris@16: } Chris@16: Chris@16: try { Chris@16: traverse_test_tree( id, s_frk_impl() ); Chris@16: } Chris@16: catch( test_being_aborted const& ) { Chris@16: // abort already reported Chris@16: } Chris@16: Chris@16: if( call_start_finish ) { Chris@16: BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) Chris@16: to->test_finish(); Chris@16: } Chris@16: Chris@16: s_frk_impl().m_test_in_progress = was_in_progress; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: run( test_unit const* tu, bool continue_test ) Chris@16: { Chris@16: run( tu->p_id, continue_test ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: assertion_result( bool passed ) Chris@16: { Chris@16: BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) Chris@16: to->assertion_result( passed ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: exception_caught( execution_exception const& ex ) Chris@16: { Chris@16: BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) Chris@16: to->exception_caught( ex ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: test_unit_aborted( test_unit const& tu ) Chris@16: { Chris@16: BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) Chris@16: to->test_unit_aborted( tu ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: } // namespace framework Chris@16: Chris@16: } // namespace unit_test Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // BOOST_TEST_FRAMEWORK_IPP_021005GER