795 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			795 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | //  (C) Copyright Gennadiy Rozental 2001. | ||
|  | //  Distributed under 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$ | ||
|  | // | ||
|  | //  Version     : $Revision$ | ||
|  | // | ||
|  | //  Description : supplies offline implementation for the Test Tools | ||
|  | // *************************************************************************** | ||
|  | 
 | ||
|  | #ifndef BOOST_TEST_TEST_TOOLS_IPP_012205GER | ||
|  | #define BOOST_TEST_TEST_TOOLS_IPP_012205GER | ||
|  | 
 | ||
|  | // Boost.Test | ||
|  | #include <boost/test/unit_test_log.hpp> | ||
|  | #include <boost/test/tools/context.hpp> | ||
|  | #include <boost/test/tools/output_test_stream.hpp> | ||
|  | 
 | ||
|  | #include <boost/test/tools/detail/fwd.hpp> | ||
|  | #include <boost/test/tools/detail/print_helper.hpp> | ||
|  | 
 | ||
|  | #include <boost/test/framework.hpp> | ||
|  | #include <boost/test/tree/test_unit.hpp> | ||
|  | #include <boost/test/execution_monitor.hpp> // execution_aborted | ||
|  | 
 | ||
|  | #include <boost/test/detail/throw_exception.hpp> | ||
|  | 
 | ||
|  | #include <boost/test/utils/algorithm.hpp> | ||
|  | 
 | ||
|  | // Boost | ||
|  | #include <boost/config.hpp> | ||
|  | 
 | ||
|  | // STL | ||
|  | #include <fstream> | ||
|  | #include <string> | ||
|  | #include <cstring> | ||
|  | #include <cctype> | ||
|  | #include <cwchar> | ||
|  | #include <stdexcept> | ||
|  | #include <vector> | ||
|  | #include <utility> | ||
|  | #include <ios> | ||
|  | 
 | ||
|  | // !! should we use #include <cstdarg> | ||
|  | #include <stdarg.h> | ||
|  | 
 | ||
|  | #include <boost/test/detail/suppress_warnings.hpp> | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | # ifdef BOOST_NO_STDC_NAMESPACE | ||
|  | namespace std { using ::strcmp; using ::strlen; using ::isprint; } | ||
|  | #if !defined( BOOST_NO_CWCHAR ) | ||
|  | namespace std { using ::wcscmp; } | ||
|  | #endif | ||
|  | # endif | ||
|  | 
 | ||
|  | namespace boost { | ||
|  | namespace test_tools { | ||
|  | namespace tt_detail { | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************                print_log_value               ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | void | ||
|  | print_log_value<char>::operator()( std::ostream& ostr, char t ) | ||
|  | { | ||
|  |     if( (std::isprint)( static_cast<unsigned char>(t) ) ) | ||
|  |         ostr << '\'' << t << '\''; | ||
|  |     else | ||
|  |         ostr << std::hex | ||
|  | #if BOOST_TEST_USE_STD_LOCALE | ||
|  |         << std::showbase | ||
|  | #else | ||
|  |         << "0x" | ||
|  | #endif | ||
|  |         << static_cast<int>(t); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | void | ||
|  | print_log_value<unsigned char>::operator()( std::ostream& ostr, unsigned char t ) | ||
|  | { | ||
|  |     ostr << std::hex | ||
|  |         // showbase is only available for new style streams: | ||
|  | #if BOOST_TEST_USE_STD_LOCALE | ||
|  |         << std::showbase | ||
|  | #else | ||
|  |         << "0x" | ||
|  | #endif | ||
|  |         << static_cast<int>(t); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | void | ||
|  | print_log_value<char const*>::operator()( std::ostream& ostr, char const* t ) | ||
|  | { | ||
|  |     ostr << ( t ? t : "null string" ); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | void | ||
|  | print_log_value<wchar_t const*>::operator()( std::ostream& ostr, wchar_t const* t ) | ||
|  | { | ||
|  |     ostr << ( t ? t : L"null string" ); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************            TOOL BOX Implementation           ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | using ::boost::unit_test::lazy_ostream; | ||
|  | 
 | ||
|  | static char const* check_str [] = { " == ", " != ", " < " , " <= ", " > " , " >= " }; | ||
|  | static char const* rever_str [] = { " != ", " == ", " >= ", " > " , " <= ", " < "  }; | ||
|  | 
 | ||
|  | template<typename OutStream> | ||
|  | void | ||
|  | format_report( OutStream& os, assertion_result const& pr, unit_test::lazy_ostream const& assertion_descr, | ||
|  |                tool_level tl, check_type ct, | ||
|  |                std::size_t num_args, va_list args, | ||
|  |                char const*  prefix, char const*  suffix ) | ||
|  | { | ||
|  |     using namespace unit_test; | ||
|  | 
 | ||
|  |     switch( ct ) { | ||
|  |     case CHECK_PRED: | ||
|  |         os << prefix << assertion_descr << suffix; | ||
|  | 
 | ||
|  |         if( !pr.has_empty_message() ) | ||
|  |             os << ". " << pr.message(); | ||
|  |         break; | ||
|  | 
 | ||
|  |     case CHECK_BUILT_ASSERTION: { | ||
|  |         os << prefix << assertion_descr << suffix; | ||
|  | 
 | ||
|  |         if( tl != PASS ) { | ||
|  |             const_string details_message = pr.message(); | ||
|  | 
 | ||
|  |             if( !details_message.is_empty() ) { | ||
|  |                 os << details_message; | ||
|  |             } | ||
|  |         } | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     case CHECK_MSG: | ||
|  |         if( tl == PASS ) | ||
|  |             os << prefix << "'" << assertion_descr << "'" << suffix; | ||
|  |         else | ||
|  |             os << assertion_descr; | ||
|  | 
 | ||
|  |         if( !pr.has_empty_message() ) | ||
|  |             os << ". " << pr.message(); | ||
|  |         break; | ||
|  | 
 | ||
|  |     case CHECK_EQUAL: | ||
|  |     case CHECK_NE: | ||
|  |     case CHECK_LT: | ||
|  |     case CHECK_LE: | ||
|  |     case CHECK_GT: | ||
|  |     case CHECK_GE: { | ||
|  |         char const*         arg1_descr  = va_arg( args, char const* ); | ||
|  |         lazy_ostream const* arg1_val    = va_arg( args, lazy_ostream const* ); | ||
|  |         char const*         arg2_descr  = va_arg( args, char const* ); | ||
|  |         lazy_ostream const* arg2_val    = va_arg( args, lazy_ostream const* ); | ||
|  | 
 | ||
|  |         os << prefix << arg1_descr << check_str[ct-CHECK_EQUAL] << arg2_descr << suffix; | ||
|  | 
 | ||
|  |         if( tl != PASS ) | ||
|  |             os << " [" << *arg1_val << rever_str[ct-CHECK_EQUAL] << *arg2_val << "]" ; | ||
|  | 
 | ||
|  |         if( !pr.has_empty_message() ) | ||
|  |             os << ". " << pr.message(); | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     case CHECK_CLOSE: | ||
|  |     case CHECK_CLOSE_FRACTION: { | ||
|  |         char const*         arg1_descr  = va_arg( args, char const* ); | ||
|  |         lazy_ostream const* arg1_val    = va_arg( args, lazy_ostream const* ); | ||
|  |         char const*         arg2_descr  = va_arg( args, char const* ); | ||
|  |         lazy_ostream const* arg2_val    = va_arg( args, lazy_ostream const* ); | ||
|  |         /* toler_descr = */               va_arg( args, char const* ); | ||
|  |         lazy_ostream const* toler_val   = va_arg( args, lazy_ostream const* ); | ||
|  | 
 | ||
|  |         os << "difference{" << pr.message() | ||
|  |                             << "} between " << arg1_descr << "{" << *arg1_val | ||
|  |                             << "} and "               << arg2_descr << "{" << *arg2_val | ||
|  |                             << ( tl == PASS ? "} doesn't exceed " : "} exceeds " ) | ||
|  |                             << *toler_val; | ||
|  |         if( ct == CHECK_CLOSE ) | ||
|  |             os << "%"; | ||
|  |         break; | ||
|  |     } | ||
|  |     case CHECK_SMALL: { | ||
|  |         char const*         arg1_descr  = va_arg( args, char const* ); | ||
|  |         lazy_ostream const* arg1_val    = va_arg( args, lazy_ostream const* ); | ||
|  |         /* toler_descr = */               va_arg( args, char const* ); | ||
|  |         lazy_ostream const* toler_val   = va_arg( args, lazy_ostream const* ); | ||
|  | 
 | ||
|  |         os << "absolute value of " << arg1_descr << "{" << *arg1_val << "}" | ||
|  |                                    << ( tl == PASS ? " doesn't exceed " : " exceeds " ) | ||
|  |                                    << *toler_val; | ||
|  | 
 | ||
|  |         if( !pr.has_empty_message() ) | ||
|  |             os << ". " << pr.message(); | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     case CHECK_PRED_WITH_ARGS: { | ||
|  |         std::vector< std::pair<char const*, lazy_ostream const*> > args_copy; | ||
|  |         args_copy.reserve( num_args ); | ||
|  |         for( std::size_t i = 0; i < num_args; ++i ) { | ||
|  |             char const* desc = va_arg( args, char const* ); | ||
|  |             lazy_ostream const* value = va_arg( args, lazy_ostream const* ); | ||
|  |             args_copy.push_back( std::make_pair( desc, value ) ); | ||
|  |         } | ||
|  | 
 | ||
|  |         os << prefix << assertion_descr; | ||
|  | 
 | ||
|  |         // print predicate call description | ||
|  |         os << "( "; | ||
|  |         for( std::size_t i = 0; i < num_args; ++i ) { | ||
|  |             os << args_copy[i].first; | ||
|  | 
 | ||
|  |             if( i != num_args-1 ) | ||
|  |                 os << ", "; | ||
|  |         } | ||
|  |         os << " )" << suffix; | ||
|  | 
 | ||
|  |         if( tl != PASS ) { | ||
|  |             os << " for ( "; | ||
|  |             for( std::size_t i = 0; i < num_args; ++i ) { | ||
|  |                 os << *args_copy[i].second; | ||
|  | 
 | ||
|  |                 if( i != num_args-1 ) | ||
|  |                     os << ", "; | ||
|  |             } | ||
|  |             os << " )"; | ||
|  |         } | ||
|  | 
 | ||
|  |         if( !pr.has_empty_message() ) | ||
|  |             os << ". " << pr.message(); | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     case CHECK_EQUAL_COLL: { | ||
|  |         char const* left_begin_descr    = va_arg( args, char const* ); | ||
|  |         char const* left_end_descr      = va_arg( args, char const* ); | ||
|  |         char const* right_begin_descr   = va_arg( args, char const* ); | ||
|  |         char const* right_end_descr     = va_arg( args, char const* ); | ||
|  | 
 | ||
|  |         os << prefix << "{ " << left_begin_descr  << ", " << left_end_descr  << " } == { " | ||
|  |                              << right_begin_descr << ", " << right_end_descr << " }" | ||
|  |            << suffix; | ||
|  | 
 | ||
|  |         if( !pr.has_empty_message() ) | ||
|  |             os << ". " << pr.message(); | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     case CHECK_BITWISE_EQUAL: { | ||
|  |         char const* left_descr    = va_arg( args, char const* ); | ||
|  |         char const* right_descr   = va_arg( args, char const* ); | ||
|  | 
 | ||
|  |         os << prefix << left_descr  << " =.= " << right_descr << suffix; | ||
|  | 
 | ||
|  |         if( !pr.has_empty_message() ) | ||
|  |             os << ". " << pr.message(); | ||
|  |         break; | ||
|  |     } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | bool | ||
|  | report_assertion( assertion_result const&   ar, | ||
|  |                   lazy_ostream const&       assertion_descr, | ||
|  |                   const_string              file_name, | ||
|  |                   std::size_t               line_num, | ||
|  |                   tool_level                tl, | ||
|  |                   check_type                ct, | ||
|  |                   std::size_t               num_args, ... ) | ||
|  | { | ||
|  |     using namespace unit_test; | ||
|  | 
 | ||
|  |     BOOST_TEST_I_ASSRT( framework::current_test_case_id() != INV_TEST_UNIT_ID, | ||
|  |                         std::runtime_error( "Can't use testing tools outside of test case implementation." ) ); | ||
|  | 
 | ||
|  |     if( !!ar ) | ||
|  |         tl = PASS; | ||
|  | 
 | ||
|  |     log_level    ll; | ||
|  |     char const*  prefix; | ||
|  |     char const*  suffix; | ||
|  | 
 | ||
|  |     switch( tl ) { | ||
|  |     case PASS: | ||
|  |         ll      = log_successful_tests; | ||
|  |         prefix  = "check "; | ||
|  |         suffix  = " has passed"; | ||
|  |         break; | ||
|  |     case WARN: | ||
|  |         ll      = log_warnings; | ||
|  |         prefix  = "condition "; | ||
|  |         suffix  = " is not satisfied"; | ||
|  |         break; | ||
|  |     case CHECK: | ||
|  |         ll      = log_all_errors; | ||
|  |         prefix  = "check "; | ||
|  |         suffix  = " has failed"; | ||
|  |         break; | ||
|  |     case REQUIRE: | ||
|  |         ll      = log_fatal_errors; | ||
|  |         prefix  = "critical check "; | ||
|  |         suffix  = " has failed"; | ||
|  |         break; | ||
|  |     default: | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     unit_test_log << unit_test::log::begin( file_name, line_num ) << ll; | ||
|  |     va_list args; | ||
|  |     va_start( args, num_args ); | ||
|  | 
 | ||
|  |     format_report( unit_test_log, ar, assertion_descr, tl, ct, num_args, args, prefix, suffix ); | ||
|  | 
 | ||
|  |     va_end( args ); | ||
|  |     unit_test_log << unit_test::log::end(); | ||
|  | 
 | ||
|  |     switch( tl ) { | ||
|  |     case PASS: | ||
|  |         framework::assertion_result( AR_PASSED ); | ||
|  |         return true; | ||
|  | 
 | ||
|  |     case WARN: | ||
|  |         framework::assertion_result( AR_TRIGGERED ); | ||
|  |         return false; | ||
|  | 
 | ||
|  |     case CHECK: | ||
|  |         framework::assertion_result( AR_FAILED ); | ||
|  |         return false; | ||
|  | 
 | ||
|  |     case REQUIRE: | ||
|  |         framework::assertion_result( AR_FAILED ); | ||
|  | 
 | ||
|  |         framework::test_unit_aborted( framework::current_test_case() ); | ||
|  | 
 | ||
|  |         BOOST_TEST_I_THROW( execution_aborted() ); | ||
|  |     } | ||
|  | 
 | ||
|  |     return true; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | assertion_result | ||
|  | format_assertion_result( const_string expr_val, const_string details ) | ||
|  | { | ||
|  |     assertion_result res(false); | ||
|  | 
 | ||
|  |     bool starts_new_line = first_char( expr_val ) == '\n'; | ||
|  | 
 | ||
|  |     if( !starts_new_line && !expr_val.is_empty() ) | ||
|  |         res.message().stream() << " [" << expr_val << "]"; | ||
|  | 
 | ||
|  |     if( !details.is_empty() ) { | ||
|  |         if( first_char(details) != '[' ) | ||
|  |             res.message().stream() << ". "; | ||
|  |         else | ||
|  |             res.message().stream() << " "; | ||
|  | 
 | ||
|  |         res.message().stream() << details; | ||
|  |     } | ||
|  | 
 | ||
|  |     if( starts_new_line ) | ||
|  |         res.message().stream() << "." << expr_val; | ||
|  | 
 | ||
|  |     return res; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | BOOST_TEST_DECL std::string | ||
|  | prod_report_format( assertion_result const& ar, unit_test::lazy_ostream const& assertion_descr, check_type ct, std::size_t num_args, ... ) | ||
|  | { | ||
|  |     std::ostringstream msg_buff; | ||
|  | 
 | ||
|  |     va_list args; | ||
|  |     va_start( args, num_args ); | ||
|  | 
 | ||
|  |     format_report( msg_buff, ar, assertion_descr, CHECK, ct, num_args, args, "assertion ", " failed" ); | ||
|  | 
 | ||
|  |     va_end( args ); | ||
|  | 
 | ||
|  |     return msg_buff.str(); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | assertion_result | ||
|  | equal_impl( char const* left, char const* right ) | ||
|  | { | ||
|  |     return (left && right) ? std::strcmp( left, right ) == 0 : (left == right); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | #if !defined( BOOST_NO_CWCHAR ) | ||
|  | 
 | ||
|  | assertion_result | ||
|  | equal_impl( wchar_t const* left, wchar_t const* right ) | ||
|  | { | ||
|  |     return (left && right) ? std::wcscmp( left, right ) == 0 : (left == right); | ||
|  | } | ||
|  | 
 | ||
|  | #endif // !defined( BOOST_NO_CWCHAR ) | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | bool | ||
|  | is_defined_impl( const_string symbol_name, const_string symbol_value ) | ||
|  | { | ||
|  |     symbol_value.trim_left( 2 ); | ||
|  |     return symbol_name != symbol_value; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************                 context_frame                ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | context_frame::context_frame( ::boost::unit_test::lazy_ostream const& context_descr ) | ||
|  | : m_frame_id( unit_test::framework::add_context( context_descr, true ) ) | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | context_frame::~context_frame() | ||
|  | { | ||
|  |     unit_test::framework::clear_context( m_frame_id ); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | context_frame::operator bool() | ||
|  | { | ||
|  |     return true; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | } // namespace tt_detail | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************               output_test_stream             ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | struct output_test_stream::Impl | ||
|  | { | ||
|  |     std::fstream    m_pattern; | ||
|  |     bool            m_match_or_save; | ||
|  |     bool            m_text_or_binary; | ||
|  |     std::string     m_synced_string; | ||
|  | 
 | ||
|  |     char            get_char() | ||
|  |     { | ||
|  |         char res; | ||
|  |         do { | ||
|  |             m_pattern.get( res ); | ||
|  |         } while( m_text_or_binary && res == '\r' && !m_pattern.fail() && !m_pattern.eof() ); | ||
|  | 
 | ||
|  |         return res; | ||
|  |     } | ||
|  | 
 | ||
|  |     void            check_and_fill( assertion_result& res ) | ||
|  |     { | ||
|  |         if( !res.p_predicate_value ) | ||
|  |             res.message() << "Output content: \"" << m_synced_string << '\"'; | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | output_test_stream::output_test_stream( const_string pattern_file_name, bool match_or_save, bool text_or_binary ) | ||
|  | : m_pimpl( new Impl ) | ||
|  | { | ||
|  |     if( !pattern_file_name.is_empty() ) { | ||
|  |         std::ios::openmode m = match_or_save ? std::ios::in : std::ios::out; | ||
|  |         if( !text_or_binary ) | ||
|  |             m |= std::ios::binary; | ||
|  | 
 | ||
|  |         m_pimpl->m_pattern.open( pattern_file_name.begin(), m ); | ||
|  | 
 | ||
|  |         if( !m_pimpl->m_pattern.is_open() ) | ||
|  |             BOOST_TEST_FRAMEWORK_MESSAGE( "Can't open pattern file " << pattern_file_name << " for " << (match_or_save ? "reading" : "writing") ); | ||
|  |     } | ||
|  | 
 | ||
|  |     m_pimpl->m_match_or_save    = match_or_save; | ||
|  |     m_pimpl->m_text_or_binary   = text_or_binary; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | output_test_stream::~output_test_stream() | ||
|  | { | ||
|  |     delete m_pimpl; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | assertion_result | ||
|  | output_test_stream::is_empty( bool flush_stream ) | ||
|  | { | ||
|  |     sync(); | ||
|  | 
 | ||
|  |     assertion_result res( m_pimpl->m_synced_string.empty() ); | ||
|  | 
 | ||
|  |     m_pimpl->check_and_fill( res ); | ||
|  | 
 | ||
|  |     if( flush_stream ) | ||
|  |         flush(); | ||
|  | 
 | ||
|  |     return res; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | assertion_result | ||
|  | output_test_stream::check_length( std::size_t length_, bool flush_stream ) | ||
|  | { | ||
|  |     sync(); | ||
|  | 
 | ||
|  |     assertion_result res( m_pimpl->m_synced_string.length() == length_ ); | ||
|  | 
 | ||
|  |     m_pimpl->check_and_fill( res ); | ||
|  | 
 | ||
|  |     if( flush_stream ) | ||
|  |         flush(); | ||
|  | 
 | ||
|  |     return res; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | assertion_result | ||
|  | output_test_stream::is_equal( const_string arg, bool flush_stream ) | ||
|  | { | ||
|  |     sync(); | ||
|  | 
 | ||
|  |     assertion_result res( const_string( m_pimpl->m_synced_string ) == arg ); | ||
|  | 
 | ||
|  |     m_pimpl->check_and_fill( res ); | ||
|  | 
 | ||
|  |     if( flush_stream ) | ||
|  |         flush(); | ||
|  | 
 | ||
|  |     return res; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | std::string pretty_print_log(std::string str) { | ||
|  | 
 | ||
|  |     static const std::string to_replace[] = { "\r", "\n" }; | ||
|  |     static const std::string replacement[] = { "\\r", "\\n" }; | ||
|  | 
 | ||
|  |     return unit_test::utils::replace_all_occurrences_of( | ||
|  |         str, | ||
|  |         to_replace, to_replace + sizeof(to_replace)/sizeof(to_replace[0]), | ||
|  |         replacement, replacement + sizeof(replacement)/sizeof(replacement[0])); | ||
|  | } | ||
|  | 
 | ||
|  | assertion_result | ||
|  | output_test_stream::match_pattern( bool flush_stream ) | ||
|  | { | ||
|  |     const std::string::size_type n_chars_presuffix = 10; | ||
|  |     sync(); | ||
|  | 
 | ||
|  |     assertion_result result( true ); | ||
|  | 
 | ||
|  |     const std::string stream_string_repr = get_stream_string_representation(); | ||
|  | 
 | ||
|  |     if( !m_pimpl->m_pattern.is_open() ) { | ||
|  |         result = false; | ||
|  |         result.message() << "Pattern file can't be opened!"; | ||
|  |     } | ||
|  |     else { | ||
|  |         if( m_pimpl->m_match_or_save ) { | ||
|  | 
 | ||
|  |             int offset = 0; | ||
|  |             std::vector<char> last_elements; | ||
|  |             for ( std::string::size_type i = 0; static_cast<int>(i + offset) < static_cast<int>(stream_string_repr.length()); ++i ) { | ||
|  |                 char c = m_pimpl->get_char(); | ||
|  | 
 | ||
|  |                 if( last_elements.size() <= n_chars_presuffix ) { | ||
|  |                     last_elements.push_back( c ); | ||
|  |                 } | ||
|  |                 else { | ||
|  |                     last_elements[ i % last_elements.size() ] = c; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 bool is_same = !m_pimpl->m_pattern.fail() && | ||
|  |                          !m_pimpl->m_pattern.eof()  && | ||
|  |                          (stream_string_repr[i+offset] == c); | ||
|  | 
 | ||
|  |                 if( !is_same ) { | ||
|  | 
 | ||
|  |                     result = false; | ||
|  | 
 | ||
|  |                     std::string::size_type prefix_size  = (std::min)( i + offset, n_chars_presuffix ); | ||
|  | 
 | ||
|  |                     std::string::size_type suffix_size  = (std::min)( stream_string_repr.length() - i - offset, | ||
|  |                                                                       n_chars_presuffix ); | ||
|  | 
 | ||
|  |                     // try to log area around the mismatch | ||
|  |                     std::string substr = stream_string_repr.substr(0, i+offset); | ||
|  |                     std::size_t line = std::count(substr.begin(), substr.end(), '\n'); | ||
|  |                     std::size_t column = i + offset - substr.rfind('\n'); | ||
|  | 
 | ||
|  |                     result.message() | ||
|  |                         << "Mismatch at position " << i | ||
|  |                         << " (line " << line | ||
|  |                         << ", column " << column | ||
|  |                         << "): '" << pretty_print_log(std::string(1, stream_string_repr[i+offset])) << "' != '" << pretty_print_log(std::string(1, c)) << "' :\n"; | ||
|  | 
 | ||
|  |                     // we already escape this substring because we need its actual size for the pretty print | ||
|  |                     // of the difference location. | ||
|  |                     std::string sub_str_prefix(pretty_print_log(stream_string_repr.substr( i + offset - prefix_size, prefix_size ))); | ||
|  | 
 | ||
|  |                     // we need this substring as is because we compute the best matching substrings on it. | ||
|  |                     std::string sub_str_suffix(stream_string_repr.substr( i + offset, suffix_size)); | ||
|  |                     result.message() << "... " << sub_str_prefix + pretty_print_log(sub_str_suffix) << " ..." << '\n'; | ||
|  | 
 | ||
|  |                     result.message() << "... "; | ||
|  |                     for( std::size_t j = 0; j < last_elements.size() ; j++ ) | ||
|  |                         result.message() << pretty_print_log(std::string(1, last_elements[(i + j + 1) % last_elements.size()])); | ||
|  | 
 | ||
|  |                     std::vector<char> last_elements_ordered; | ||
|  |                     last_elements_ordered.push_back(c); | ||
|  |                     for( std::string::size_type counter = 0; counter < suffix_size - 1 ; counter++ ) { | ||
|  |                         char c2 = m_pimpl->get_char(); | ||
|  | 
 | ||
|  |                         if( m_pimpl->m_pattern.fail() || m_pimpl->m_pattern.eof() ) | ||
|  |                             break; | ||
|  | 
 | ||
|  |                         result.message() << pretty_print_log(std::string(1, c2)); | ||
|  | 
 | ||
|  |                         last_elements_ordered.push_back(c2); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     // tries to find the best substring matching in the remainder of the | ||
|  |                     // two strings | ||
|  |                     std::size_t max_nb_char_in_common = 0; | ||
|  |                     std::size_t best_pattern_start_index = 0; | ||
|  |                     std::size_t best_stream_start_index = 0; | ||
|  |                     for( std::size_t pattern_start_index = best_pattern_start_index; | ||
|  |                          pattern_start_index < last_elements_ordered.size(); | ||
|  |                          pattern_start_index++ ) { | ||
|  |                         for( std::size_t stream_start_index = best_stream_start_index; | ||
|  |                              stream_start_index < sub_str_suffix.size(); | ||
|  |                              stream_start_index++ ) { | ||
|  | 
 | ||
|  |                             std::size_t max_size = (std::min)( last_elements_ordered.size() - pattern_start_index, sub_str_suffix.size() - stream_start_index ); | ||
|  |                             if( max_nb_char_in_common > max_size ) | ||
|  |                                 break; // safely break to go to the outer loop | ||
|  | 
 | ||
|  |                             std::size_t nb_char_in_common = 0; | ||
|  |                             for( std::size_t k = 0; k < max_size; k++) { | ||
|  |                                 if( last_elements_ordered[pattern_start_index + k] == sub_str_suffix[stream_start_index + k] ) | ||
|  |                                     nb_char_in_common ++; | ||
|  |                                 else | ||
|  |                                     break; // we take fully macthing substring only | ||
|  |                             } | ||
|  | 
 | ||
|  |                             if( nb_char_in_common > max_nb_char_in_common ) { | ||
|  |                                 max_nb_char_in_common = nb_char_in_common; | ||
|  |                                 best_pattern_start_index = pattern_start_index; | ||
|  |                                 best_stream_start_index = stream_start_index; | ||
|  |                             } | ||
|  |                         } | ||
|  |                     } | ||
|  | 
 | ||
|  |                     // indicates with more precision the location of the mismatchs in ascii arts ... | ||
|  |                     result.message() << " ...\n... "; | ||
|  |                     for( std::string::size_type j = 0; j < sub_str_prefix.size(); j++) { | ||
|  |                         result.message() << ' '; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     for( std::size_t k = 0; k < (std::max)(best_pattern_start_index, best_stream_start_index); k++ ) { // 1 is for the current char c | ||
|  |                         std::string s1(pretty_print_log(std::string(1, last_elements_ordered[(std::min)(k, best_pattern_start_index)]))); | ||
|  |                         std::string s2(pretty_print_log(std::string(1, sub_str_suffix[(std::min)(k, best_stream_start_index)]))); | ||
|  |                         for( int h = (std::max)(s1.size(), s2.size()); h > 0; h--) | ||
|  |                           result.message() << "~"; | ||
|  |                     } | ||
|  |                     result.message() << "\n"; | ||
|  | 
 | ||
|  |                     // first char is a replicat of c, so we do not copy it. | ||
|  |                     for(std::string::size_type counter = 0; counter < last_elements_ordered.size() - 1 ; counter++) | ||
|  |                         last_elements[ (i + 1 + counter) % last_elements.size() ] = last_elements_ordered[counter + 1]; | ||
|  | 
 | ||
|  |                     i += last_elements_ordered.size()-1; | ||
|  |                     offset += best_stream_start_index - best_pattern_start_index; | ||
|  | 
 | ||
|  |                 } | ||
|  | 
 | ||
|  |             } | ||
|  | 
 | ||
|  |             // not needed anymore | ||
|  |             /* | ||
|  |             if(offset > 0 && false) { | ||
|  |                 m_pimpl->m_pattern.ignore( | ||
|  |                     static_cast<std::streamsize>( offset )); | ||
|  |             } | ||
|  |             */ | ||
|  |         } | ||
|  |         else { | ||
|  |             m_pimpl->m_pattern.write( stream_string_repr.c_str(), | ||
|  |                                       static_cast<std::streamsize>( stream_string_repr.length() ) ); | ||
|  |             m_pimpl->m_pattern.flush(); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     if( flush_stream ) | ||
|  |         flush(); | ||
|  | 
 | ||
|  |     return result; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | void | ||
|  | output_test_stream::flush() | ||
|  | { | ||
|  |     m_pimpl->m_synced_string.erase(); | ||
|  | 
 | ||
|  | #ifndef BOOST_NO_STRINGSTREAM | ||
|  |     str( std::string() ); | ||
|  | #else | ||
|  |     seekp( 0, std::ios::beg ); | ||
|  | #endif | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | std::string | ||
|  | output_test_stream::get_stream_string_representation() const { | ||
|  |     return m_pimpl->m_synced_string; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | std::size_t | ||
|  | output_test_stream::length() | ||
|  | { | ||
|  |     sync(); | ||
|  | 
 | ||
|  |     return m_pimpl->m_synced_string.length(); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | void | ||
|  | output_test_stream::sync() | ||
|  | { | ||
|  | #ifdef BOOST_NO_STRINGSTREAM | ||
|  |     m_pimpl->m_synced_string.assign( str(), pcount() ); | ||
|  |     freeze( false ); | ||
|  | #else | ||
|  |     m_pimpl->m_synced_string = str(); | ||
|  | #endif | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | } // namespace test_tools | ||
|  | } // namespace boost | ||
|  | 
 | ||
|  | #include <boost/test/detail/enable_warnings.hpp> | ||
|  | 
 | ||
|  | #endif // BOOST_TEST_TEST_TOOLS_IPP_012205GER |