summaryrefslogtreecommitdiff
path: root/boost/test/impl/test_tools.ipp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/test/impl/test_tools.ipp')
-rw-r--r--boost/test/impl/test_tools.ipp156
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