diff options
Diffstat (limited to 'boost/test/impl/test_tools.ipp')
-rw-r--r-- | boost/test/impl/test_tools.ipp | 156 |
1 files changed, 135 insertions, 21 deletions
diff --git a/boost/test/impl/test_tools.ipp b/boost/test/impl/test_tools.ipp index ed94da3a5b..a6b20a7729 100644 --- a/boost/test/impl/test_tools.ipp +++ b/boost/test/impl/test_tools.ipp @@ -29,6 +29,8 @@ #include <boost/test/detail/throw_exception.hpp> +#include <boost/test/utils/algorithm.hpp> + // Boost #include <boost/config.hpp> @@ -505,7 +507,7 @@ output_test_stream::output_test_stream( const_string pattern_file_name, bool mat m_pimpl->m_pattern.open( pattern_file_name.begin(), m ); if( !m_pimpl->m_pattern.is_open() ) - BOOST_TEST_MESSAGE( "Can't open pattern file " << pattern_file_name << " for " << (match_or_save ? "reading" : "writing") ); + 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; @@ -572,57 +574,163 @@ output_test_stream::is_equal( const_string arg, bool flush_stream ) //____________________________________________________________________________// +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 ) { - for ( std::string::size_type i = 0; i < m_pimpl->m_synced_string.length(); ++i ) { + + 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(); - result = !m_pimpl->m_pattern.fail() && + 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() && - (m_pimpl->m_synced_string[i] == c); + (stream_string_repr[i+offset] == c); - if( !result ) { - std::string::size_type suffix_size = (std::min)( m_pimpl->m_synced_string.length() - i, - static_cast<std::string::size_type>(5) ); + if( !is_same ) { - // try to log area around the mismatch - result.message() << "Mismatch at position " << i << '\n' - << "..." << m_pimpl->m_synced_string.substr( i, suffix_size ) << "..." << '\n' - << "..." << c; + result = false; - std::string::size_type counter = suffix_size; - while( --counter ) { + 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() << c2; + 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"; - result.message() << "..."; + // 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; - // skip rest of the bytes. May help for further matching - m_pimpl->m_pattern.ignore( - static_cast<std::streamsize>( m_pimpl->m_synced_string.length() - i - suffix_size) ); - break; } + + } + + // not needed anymore + /* + if(offset > 0 && false) { + m_pimpl->m_pattern.ignore( + static_cast<std::streamsize>( offset )); } + */ } else { - m_pimpl->m_pattern.write( m_pimpl->m_synced_string.c_str(), - static_cast<std::streamsize>( m_pimpl->m_synced_string.length() ) ); + m_pimpl->m_pattern.write( stream_string_repr.c_str(), + static_cast<std::streamsize>( stream_string_repr.length() ) ); m_pimpl->m_pattern.flush(); } } @@ -647,6 +755,12 @@ output_test_stream::flush() #endif } + +std::string +output_test_stream::get_stream_string_representation() const { + return m_pimpl->m_synced_string; +} + //____________________________________________________________________________// std::size_t |