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 : supplies offline implementation for the Test Tools Chris@16: // *************************************************************************** Chris@16: Chris@16: #ifndef BOOST_TEST_TEST_TOOLS_IPP_012205GER Chris@16: #define BOOST_TEST_TEST_TOOLS_IPP_012205GER Chris@16: Chris@16: // Boost.Test Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include // execution_aborted 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: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: // !! should we use #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: # ifdef BOOST_NO_STDC_NAMESPACE Chris@16: namespace std { using ::strcmp; using ::strlen; using ::isprint; } Chris@16: #if !defined( BOOST_NO_CWCHAR ) Chris@16: namespace std { using ::wcscmp; } Chris@16: #endif Chris@16: # endif Chris@16: Chris@16: namespace boost { Chris@16: Chris@16: namespace test_tools { Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** print_log_value ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: void Chris@16: print_log_value::operator()( std::ostream& ostr, char t ) Chris@16: { Chris@16: if( (std::isprint)( static_cast(t) ) ) Chris@16: ostr << '\'' << t << '\''; Chris@16: else Chris@16: ostr << std::hex Chris@16: #if BOOST_TEST_USE_STD_LOCALE Chris@16: << std::showbase Chris@16: #else Chris@16: << "0x" Chris@16: #endif Chris@16: << static_cast(t); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: print_log_value::operator()( std::ostream& ostr, unsigned char t ) Chris@16: { Chris@16: ostr << std::hex Chris@16: // showbase is only available for new style streams: Chris@16: #if BOOST_TEST_USE_STD_LOCALE Chris@16: << std::showbase Chris@16: #else Chris@16: << "0x" Chris@16: #endif Chris@16: << static_cast(t); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: print_log_value::operator()( std::ostream& ostr, char const* t ) Chris@16: { Chris@16: ostr << ( t ? t : "null string" ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: print_log_value::operator()( std::ostream& ostr, wchar_t const* t ) Chris@16: { Chris@16: ostr << ( t ? t : L"null string" ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: namespace tt_detail { Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** TOOL BOX Implementation ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: using ::boost::unit_test::lazy_ostream; Chris@16: Chris@16: bool Chris@16: check_impl( predicate_result const& pr, lazy_ostream const& check_descr, Chris@16: const_string file_name, std::size_t line_num, Chris@16: tool_level tl, check_type ct, Chris@16: std::size_t num_of_args, ... ) Chris@16: { Chris@16: using namespace unit_test; Chris@16: Chris@16: if( !framework::is_initialized() ) Chris@16: throw std::runtime_error( "can't use testing tools before framework is initialized" ); Chris@16: Chris@16: if( !!pr ) Chris@16: tl = PASS; Chris@16: Chris@16: log_level ll; Chris@16: char const* prefix; Chris@16: char const* suffix; Chris@16: Chris@16: switch( tl ) { Chris@16: case PASS: Chris@16: ll = log_successful_tests; Chris@16: prefix = "check "; Chris@16: suffix = " passed"; Chris@16: break; Chris@16: case WARN: Chris@16: ll = log_warnings; Chris@16: prefix = "condition "; Chris@16: suffix = " is not satisfied"; Chris@16: break; Chris@16: case CHECK: Chris@16: ll = log_all_errors; Chris@16: prefix = "check "; Chris@16: suffix = " failed"; Chris@16: break; Chris@16: case REQUIRE: Chris@16: ll = log_fatal_errors; Chris@16: prefix = "critical check "; Chris@16: suffix = " failed"; Chris@16: break; Chris@16: default: Chris@16: return true; Chris@16: } Chris@16: Chris@16: switch( ct ) { Chris@16: case CHECK_PRED: Chris@16: unit_test_log << unit_test::log::begin( file_name, line_num ) Chris@16: << ll << prefix << check_descr << suffix; Chris@16: Chris@16: if( !pr.has_empty_message() ) Chris@16: unit_test_log << ". " << pr.message(); Chris@16: Chris@16: unit_test_log << unit_test::log::end(); Chris@16: break; Chris@16: Chris@16: case CHECK_MSG: Chris@16: unit_test_log << unit_test::log::begin( file_name, line_num ) << ll; Chris@16: Chris@16: if( tl == PASS ) Chris@16: unit_test_log << prefix << "'" << check_descr << "'" << suffix; Chris@16: else Chris@16: unit_test_log << check_descr; Chris@16: Chris@16: if( !pr.has_empty_message() ) Chris@16: unit_test_log << ". " << pr.message(); Chris@16: Chris@16: unit_test_log << unit_test::log::end(); Chris@16: break; Chris@16: Chris@16: case CHECK_EQUAL: Chris@16: case CHECK_NE: Chris@16: case CHECK_LT: Chris@16: case CHECK_LE: Chris@16: case CHECK_GT: Chris@16: case CHECK_GE: { Chris@16: static char const* check_str [] = { " == ", " != ", " < " , " <= ", " > " , " >= " }; Chris@16: static char const* rever_str [] = { " != ", " == ", " >= ", " > " , " <= ", " < " }; Chris@16: Chris@16: va_list args; Chris@16: Chris@16: va_start( args, num_of_args ); Chris@16: char const* arg1_descr = va_arg( args, char const* ); Chris@16: lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); Chris@16: char const* arg2_descr = va_arg( args, char const* ); Chris@16: lazy_ostream const* arg2_val = va_arg( args, lazy_ostream const* ); Chris@16: Chris@16: unit_test_log << unit_test::log::begin( file_name, line_num ) Chris@16: << ll << prefix << arg1_descr << check_str[ct-CHECK_EQUAL] << arg2_descr << suffix; Chris@16: Chris@16: if( tl != PASS ) Chris@16: unit_test_log << " [" << *arg1_val << rever_str[ct-CHECK_EQUAL] << *arg2_val << "]" ; Chris@16: Chris@16: va_end( args ); Chris@16: Chris@16: if( !pr.has_empty_message() ) Chris@16: unit_test_log << ". " << pr.message(); Chris@16: Chris@16: unit_test_log << unit_test::log::end(); Chris@16: break; Chris@16: } Chris@16: Chris@16: case CHECK_CLOSE: Chris@16: case CHECK_CLOSE_FRACTION: { Chris@16: va_list args; Chris@16: Chris@16: va_start( args, num_of_args ); Chris@16: char const* arg1_descr = va_arg( args, char const* ); Chris@16: lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); Chris@16: char const* arg2_descr = va_arg( args, char const* ); Chris@16: lazy_ostream const* arg2_val = va_arg( args, lazy_ostream const* ); Chris@16: /* toler_descr = */ va_arg( args, char const* ); Chris@16: lazy_ostream const* toler_val = va_arg( args, lazy_ostream const* ); Chris@16: Chris@16: unit_test_log << unit_test::log::begin( file_name, line_num ) << ll; Chris@16: Chris@16: unit_test_log << "difference{" << pr.message() << (ct == CHECK_CLOSE ? "%" : "") Chris@16: << "} between " << arg1_descr << "{" << *arg1_val Chris@16: << "} and " << arg2_descr << "{" << *arg2_val Chris@16: << ( tl == PASS ? "} doesn't exceed " : "} exceeds " ) Chris@16: << *toler_val; Chris@16: if( ct == CHECK_CLOSE ) Chris@16: unit_test_log << "%"; Chris@16: Chris@16: va_end( args ); Chris@16: Chris@16: unit_test_log << unit_test::log::end(); Chris@16: break; Chris@16: } Chris@16: case CHECK_SMALL: { Chris@16: va_list args; Chris@16: Chris@16: va_start( args, num_of_args ); Chris@16: char const* arg1_descr = va_arg( args, char const* ); Chris@16: lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); Chris@16: /* toler_descr = */ va_arg( args, char const* ); Chris@16: lazy_ostream const* toler_val = va_arg( args, lazy_ostream const* ); Chris@16: Chris@16: unit_test_log << unit_test::log::begin( file_name, line_num ) << ll; Chris@16: Chris@16: unit_test_log << "absolute value of " << arg1_descr << "{" << *arg1_val << "}" Chris@16: << ( tl == PASS ? " doesn't exceed " : " exceeds " ) Chris@16: << *toler_val; Chris@16: Chris@16: va_end( args ); Chris@16: Chris@16: if( !pr.has_empty_message() ) Chris@16: unit_test_log << ". " << pr.message(); Chris@16: Chris@16: unit_test_log << unit_test::log::end(); Chris@16: break; Chris@16: } Chris@16: Chris@16: case CHECK_PRED_WITH_ARGS: { Chris@16: unit_test_log << unit_test::log::begin( file_name, line_num ) Chris@16: << ll << prefix << check_descr; Chris@16: Chris@16: // print predicate call description Chris@16: { Chris@16: va_list args; Chris@16: va_start( args, num_of_args ); Chris@16: Chris@16: unit_test_log << "( "; Chris@16: for( std::size_t i = 0; i < num_of_args; ++i ) { Chris@16: unit_test_log << va_arg( args, char const* ); Chris@16: va_arg( args, lazy_ostream const* ); // skip argument value; Chris@16: Chris@16: if( i != num_of_args-1 ) Chris@16: unit_test_log << ", "; Chris@16: } Chris@16: unit_test_log << " )" << suffix; Chris@16: va_end( args ); Chris@16: } Chris@16: Chris@16: if( tl != PASS ) { Chris@16: va_list args; Chris@16: va_start( args, num_of_args ); Chris@16: Chris@16: unit_test_log << " for ( "; Chris@16: for( std::size_t i = 0; i < num_of_args; ++i ) { Chris@16: va_arg( args, char const* ); // skip argument description; Chris@16: unit_test_log << *va_arg( args, lazy_ostream const* ); Chris@16: Chris@16: if( i != num_of_args-1 ) Chris@16: unit_test_log << ", "; Chris@16: } Chris@16: unit_test_log << " )"; Chris@16: va_end( args ); Chris@16: } Chris@16: Chris@16: if( !pr.has_empty_message() ) Chris@16: unit_test_log << ". " << pr.message(); Chris@16: Chris@16: unit_test_log << unit_test::log::end(); Chris@16: break; Chris@16: } Chris@16: Chris@16: case CHECK_EQUAL_COLL: { Chris@16: va_list args; Chris@16: Chris@16: va_start( args, num_of_args ); Chris@16: char const* left_begin_descr = va_arg( args, char const* ); Chris@16: char const* left_end_descr = va_arg( args, char const* ); Chris@16: char const* right_begin_descr = va_arg( args, char const* ); Chris@16: char const* right_end_descr = va_arg( args, char const* ); Chris@16: Chris@16: unit_test_log << unit_test::log::begin( file_name, line_num ) Chris@16: << ll << prefix Chris@16: << "{ " << left_begin_descr << ", " << left_end_descr << " } == { " Chris@16: << right_begin_descr << ", " << right_end_descr << " }" Chris@16: << suffix; Chris@16: Chris@16: va_end( args ); Chris@16: Chris@16: if( !pr.has_empty_message() ) Chris@16: unit_test_log << ". " << pr.message(); Chris@16: Chris@16: unit_test_log << unit_test::log::end(); Chris@16: break; Chris@16: } Chris@16: Chris@16: case CHECK_BITWISE_EQUAL: { Chris@16: va_list args; Chris@16: Chris@16: va_start( args, num_of_args ); Chris@16: char const* left_descr = va_arg( args, char const* ); Chris@16: char const* right_descr = va_arg( args, char const* ); Chris@16: Chris@16: unit_test_log << unit_test::log::begin( file_name, line_num ) Chris@16: << ll << prefix << left_descr << " =.= " << right_descr << suffix; Chris@16: Chris@16: va_end( args ); Chris@16: Chris@16: if( !pr.has_empty_message() ) Chris@16: unit_test_log << ". " << pr.message(); Chris@16: Chris@16: unit_test_log << unit_test::log::end(); Chris@16: break; Chris@16: } Chris@16: } Chris@16: Chris@16: switch( tl ) { Chris@16: case PASS: Chris@16: framework::assertion_result( true ); Chris@16: return true; Chris@16: Chris@16: case WARN: Chris@16: return false; Chris@16: Chris@16: case CHECK: Chris@16: framework::assertion_result( false ); Chris@16: return false; Chris@16: Chris@16: case REQUIRE: Chris@16: framework::assertion_result( false ); Chris@16: Chris@16: framework::test_unit_aborted( framework::current_test_case() ); Chris@16: Chris@16: throw execution_aborted(); Chris@16: } Chris@16: Chris@16: return true; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: predicate_result Chris@16: equal_impl( char const* left, char const* right ) Chris@16: { Chris@16: return (left && right) ? std::strcmp( left, right ) == 0 : (left == right); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: #if !defined( BOOST_NO_CWCHAR ) Chris@16: Chris@16: predicate_result Chris@16: equal_impl( wchar_t const* left, wchar_t const* right ) Chris@16: { Chris@16: return (left && right) ? std::wcscmp( left, right ) == 0 : (left == right); Chris@16: } Chris@16: Chris@16: #endif // !defined( BOOST_NO_CWCHAR ) Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: bool Chris@16: is_defined_impl( const_string symbol_name, const_string symbol_value ) Chris@16: { Chris@16: symbol_value.trim_left( 2 ); Chris@16: return symbol_name != symbol_value; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: } // namespace tt_detail Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** output_test_stream ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: struct output_test_stream::Impl Chris@16: { Chris@16: std::fstream m_pattern; Chris@16: bool m_match_or_save; Chris@16: bool m_text_or_binary; Chris@16: std::string m_synced_string; Chris@16: Chris@16: char get_char() Chris@16: { Chris@16: char res; Chris@16: do { Chris@16: m_pattern.get( res ); Chris@16: } while( m_text_or_binary && res == '\r' && !m_pattern.fail() && !m_pattern.eof() ); Chris@16: Chris@16: return res; Chris@16: } Chris@16: Chris@16: void check_and_fill( predicate_result& res ) Chris@16: { Chris@16: if( !res.p_predicate_value ) Chris@16: res.message() << "Output content: \"" << m_synced_string << '\"'; Chris@16: } Chris@16: }; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: output_test_stream::output_test_stream( const_string pattern_file_name, bool match_or_save, bool text_or_binary ) Chris@16: : m_pimpl( new Impl ) Chris@16: { Chris@16: if( !pattern_file_name.is_empty() ) { Chris@16: std::ios::openmode m = match_or_save ? std::ios::in : std::ios::out; Chris@16: if( !text_or_binary ) Chris@16: m |= std::ios::binary; Chris@16: Chris@16: m_pimpl->m_pattern.open( pattern_file_name.begin(), m ); Chris@16: Chris@16: BOOST_WARN_MESSAGE( m_pimpl->m_pattern.is_open(), Chris@16: "Can't open pattern file " << pattern_file_name Chris@16: << " for " << (match_or_save ? "reading" : "writing") ); Chris@16: } Chris@16: Chris@16: m_pimpl->m_match_or_save = match_or_save; Chris@16: m_pimpl->m_text_or_binary = text_or_binary; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: output_test_stream::~output_test_stream() Chris@16: { Chris@16: delete m_pimpl; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: predicate_result Chris@16: output_test_stream::is_empty( bool flush_stream ) Chris@16: { Chris@16: sync(); Chris@16: Chris@16: result_type res( m_pimpl->m_synced_string.empty() ); Chris@16: Chris@16: m_pimpl->check_and_fill( res ); Chris@16: Chris@16: if( flush_stream ) Chris@16: flush(); Chris@16: Chris@16: return res; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: predicate_result Chris@16: output_test_stream::check_length( std::size_t length_, bool flush_stream ) Chris@16: { Chris@16: sync(); Chris@16: Chris@16: result_type res( m_pimpl->m_synced_string.length() == length_ ); Chris@16: Chris@16: m_pimpl->check_and_fill( res ); Chris@16: Chris@16: if( flush_stream ) Chris@16: flush(); Chris@16: Chris@16: return res; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: predicate_result Chris@16: output_test_stream::is_equal( const_string arg, bool flush_stream ) Chris@16: { Chris@16: sync(); Chris@16: Chris@16: result_type res( const_string( m_pimpl->m_synced_string ) == arg ); Chris@16: Chris@16: m_pimpl->check_and_fill( res ); Chris@16: Chris@16: if( flush_stream ) Chris@16: flush(); Chris@16: Chris@16: return res; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: predicate_result Chris@16: output_test_stream::match_pattern( bool flush_stream ) Chris@16: { Chris@16: sync(); Chris@16: Chris@16: result_type result( true ); Chris@16: Chris@16: if( !m_pimpl->m_pattern.is_open() ) { Chris@16: result = false; Chris@16: result.message() << "Pattern file can't be opened!"; Chris@16: } Chris@16: else { Chris@16: if( m_pimpl->m_match_or_save ) { Chris@16: for ( std::string::size_type i = 0; i < m_pimpl->m_synced_string.length(); ++i ) { Chris@16: char c = m_pimpl->get_char(); Chris@16: Chris@16: result = !m_pimpl->m_pattern.fail() && Chris@16: !m_pimpl->m_pattern.eof() && Chris@16: (m_pimpl->m_synced_string[i] == c); Chris@16: Chris@16: if( !result ) { Chris@16: std::string::size_type suffix_size = (std::min)( m_pimpl->m_synced_string.length() - i, Chris@16: static_cast(5) ); Chris@16: Chris@16: // try to log area around the mismatch Chris@16: result.message() << "Mismatch at position " << i << '\n' Chris@16: << "..." << m_pimpl->m_synced_string.substr( i, suffix_size ) << "..." << '\n' Chris@16: << "..." << c; Chris@16: Chris@16: std::string::size_type counter = suffix_size; Chris@16: while( --counter ) { Chris@16: char c = m_pimpl->get_char(); Chris@16: Chris@16: if( m_pimpl->m_pattern.fail() || m_pimpl->m_pattern.eof() ) Chris@16: break; Chris@16: Chris@16: result.message() << c; Chris@16: } Chris@16: Chris@16: result.message() << "..."; Chris@16: Chris@16: // skip rest of the bytes. May help for further matching Chris@16: m_pimpl->m_pattern.ignore( Chris@16: static_cast( m_pimpl->m_synced_string.length() - i - suffix_size) ); Chris@16: break; Chris@16: } Chris@16: } Chris@16: } Chris@16: else { Chris@16: m_pimpl->m_pattern.write( m_pimpl->m_synced_string.c_str(), Chris@16: static_cast( m_pimpl->m_synced_string.length() ) ); Chris@16: m_pimpl->m_pattern.flush(); Chris@16: } Chris@16: } Chris@16: Chris@16: if( flush_stream ) Chris@16: flush(); Chris@16: Chris@16: return result; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: output_test_stream::flush() Chris@16: { Chris@16: m_pimpl->m_synced_string.erase(); Chris@16: Chris@16: #ifndef BOOST_NO_STRINGSTREAM Chris@16: str( std::string() ); Chris@16: #else Chris@16: seekp( 0, std::ios::beg ); Chris@16: #endif Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: std::size_t Chris@16: output_test_stream::length() Chris@16: { Chris@16: sync(); Chris@16: Chris@16: return m_pimpl->m_synced_string.length(); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: void Chris@16: output_test_stream::sync() Chris@16: { Chris@16: #ifdef BOOST_NO_STRINGSTREAM Chris@16: m_pimpl->m_synced_string.assign( str(), pcount() ); Chris@16: freeze( false ); Chris@16: #else Chris@16: m_pimpl->m_synced_string = str(); Chris@16: #endif Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: } // namespace test_tools Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // BOOST_TEST_TEST_TOOLS_IPP_012205GER