1 | // (C) Copyright Gennadiy Rozental 2001. |
2 | // Distributed under the Boost Software License, Version 1.0. |
3 | // (See accompanying file LICENSE_1_0.txt or copy at |
4 | // http://www.boost.org/LICENSE_1_0.txt) |
5 | |
6 | // See http://www.boost.org/libs/test for the library home page. |
7 | // |
8 | // File : $RCSfile$ |
9 | // |
10 | // Version : $Revision$ |
11 | // |
12 | // Description : supplies offline implementation for the Test Tools |
13 | // *************************************************************************** |
14 | |
15 | #ifndef BOOST_TEST_TEST_TOOLS_IPP_012205GER |
16 | #define BOOST_TEST_TEST_TOOLS_IPP_012205GER |
17 | |
18 | // Boost.Test |
19 | #include <boost/test/test_tools.hpp> |
20 | #include <boost/test/unit_test_log.hpp> |
21 | #include <boost/test/tools/context.hpp> |
22 | #include <boost/test/tools/output_test_stream.hpp> |
23 | |
24 | #include <boost/test/tools/detail/fwd.hpp> |
25 | #include <boost/test/tools/detail/print_helper.hpp> |
26 | |
27 | #include <boost/test/framework.hpp> |
28 | #include <boost/test/tree/test_unit.hpp> |
29 | #include <boost/test/execution_monitor.hpp> // execution_aborted |
30 | |
31 | #include <boost/test/detail/throw_exception.hpp> |
32 | |
33 | #include <boost/test/utils/algorithm.hpp> |
34 | |
35 | // Boost |
36 | #include <boost/config.hpp> |
37 | |
38 | // STL |
39 | #include <fstream> |
40 | #include <string> |
41 | #include <cstring> |
42 | #include <cctype> |
43 | #include <cwchar> |
44 | #include <stdexcept> |
45 | #include <vector> |
46 | #include <utility> |
47 | #include <ios> |
48 | |
49 | // !! should we use #include <cstdarg> |
50 | #include <stdarg.h> |
51 | |
52 | #include <boost/test/detail/suppress_warnings.hpp> |
53 | |
54 | //____________________________________________________________________________// |
55 | |
56 | # ifdef BOOST_NO_STDC_NAMESPACE |
57 | namespace std { using ::strcmp; using ::strlen; using ::isprint; } |
58 | #if !defined( BOOST_NO_CWCHAR ) |
59 | namespace std { using ::wcscmp; } |
60 | #endif |
61 | # endif |
62 | |
63 | |
64 | namespace boost { |
65 | namespace unit_test { |
66 | // local static variable, needed here for visibility reasons |
67 | lazy_ostream lazy_ostream::inst = lazy_ostream(); |
68 | }} |
69 | |
70 | namespace boost { |
71 | namespace test_tools { |
72 | namespace tt_detail { |
73 | |
74 | // ************************************************************************** // |
75 | // ************** print_log_value ************** // |
76 | // ************************************************************************** // |
77 | |
78 | void |
79 | print_log_value<bool>::operator()( std::ostream& ostr, bool t ) |
80 | { |
81 | ostr << std::boolalpha << t; |
82 | } |
83 | |
84 | void |
85 | print_log_value<char>::operator()( std::ostream& ostr, char t ) |
86 | { |
87 | if( (std::isprint)( static_cast<unsigned char>(t) ) ) |
88 | ostr << '\'' << t << '\''; |
89 | else |
90 | ostr << std::hex |
91 | #if BOOST_TEST_USE_STD_LOCALE |
92 | << std::showbase |
93 | #else |
94 | << "0x" |
95 | #endif |
96 | << static_cast<int>(t); |
97 | } |
98 | |
99 | //____________________________________________________________________________// |
100 | |
101 | void |
102 | print_log_value<unsigned char>::operator()( std::ostream& ostr, unsigned char t ) |
103 | { |
104 | ostr << std::hex |
105 | // showbase is only available for new style streams: |
106 | #if BOOST_TEST_USE_STD_LOCALE |
107 | << std::showbase |
108 | #else |
109 | << "0x" |
110 | #endif |
111 | << static_cast<int>(t); |
112 | } |
113 | |
114 | //____________________________________________________________________________// |
115 | |
116 | void |
117 | print_log_value<char const*>::operator()( std::ostream& ostr, char const* t ) |
118 | { |
119 | ostr << ( t ? t : "null string" ); |
120 | } |
121 | |
122 | //____________________________________________________________________________// |
123 | |
124 | void |
125 | print_log_value<wchar_t const*>::operator()( std::ostream& ostr, wchar_t const* t ) |
126 | { |
127 | if(t) { |
128 | ostr << static_cast<const void*>(t); |
129 | } |
130 | else { |
131 | ostr << "null w-string" ; |
132 | } |
133 | } |
134 | |
135 | //____________________________________________________________________________// |
136 | |
137 | // ************************************************************************** // |
138 | // ************** TOOL BOX Implementation ************** // |
139 | // ************************************************************************** // |
140 | |
141 | using ::boost::unit_test::lazy_ostream; |
142 | |
143 | static char const* check_str [] = { " == " , " != " , " < " , " <= " , " > " , " >= " }; |
144 | static char const* rever_str [] = { " != " , " == " , " >= " , " > " , " <= " , " < " }; |
145 | |
146 | template<typename OutStream> |
147 | void |
148 | format_report( OutStream& os, assertion_result const& pr, unit_test::lazy_ostream const& assertion_descr, |
149 | tool_level tl, check_type ct, |
150 | std::size_t num_args, va_list args, |
151 | char const* prefix, char const* suffix ) |
152 | { |
153 | using namespace unit_test; |
154 | |
155 | switch( ct ) { |
156 | case CHECK_PRED: |
157 | os << prefix << assertion_descr << suffix; |
158 | |
159 | if( !pr.has_empty_message() ) |
160 | os << ". " << pr.message(); |
161 | break; |
162 | |
163 | case CHECK_BUILT_ASSERTION: { |
164 | os << prefix << assertion_descr << suffix; |
165 | |
166 | if( tl != PASS ) { |
167 | const_string details_message = pr.message(); |
168 | |
169 | if( !details_message.is_empty() ) { |
170 | os << details_message; |
171 | } |
172 | } |
173 | break; |
174 | } |
175 | |
176 | case CHECK_MSG: |
177 | if( tl == PASS ) |
178 | os << prefix << "'" << assertion_descr << "'" << suffix; |
179 | else |
180 | os << assertion_descr; |
181 | |
182 | if( !pr.has_empty_message() ) |
183 | os << ". " << pr.message(); |
184 | break; |
185 | |
186 | case CHECK_EQUAL: |
187 | case CHECK_NE: |
188 | case CHECK_LT: |
189 | case CHECK_LE: |
190 | case CHECK_GT: |
191 | case CHECK_GE: { |
192 | char const* arg1_descr = va_arg( args, char const* ); |
193 | lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); |
194 | char const* arg2_descr = va_arg( args, char const* ); |
195 | lazy_ostream const* arg2_val = va_arg( args, lazy_ostream const* ); |
196 | |
197 | os << prefix << arg1_descr << check_str[ct-CHECK_EQUAL] << arg2_descr << suffix; |
198 | |
199 | if( tl != PASS ) |
200 | os << " [" << *arg1_val << rever_str[ct-CHECK_EQUAL] << *arg2_val << "]" ; |
201 | |
202 | if( !pr.has_empty_message() ) |
203 | os << ". " << pr.message(); |
204 | break; |
205 | } |
206 | |
207 | case CHECK_CLOSE: |
208 | case CHECK_CLOSE_FRACTION: { |
209 | char const* arg1_descr = va_arg( args, char const* ); |
210 | lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); |
211 | char const* arg2_descr = va_arg( args, char const* ); |
212 | lazy_ostream const* arg2_val = va_arg( args, lazy_ostream const* ); |
213 | /* toler_descr = */ va_arg( args, char const* ); |
214 | lazy_ostream const* toler_val = va_arg( args, lazy_ostream const* ); |
215 | |
216 | os << "difference{" << pr.message() |
217 | << "} between " << arg1_descr << "{" << *arg1_val |
218 | << "} and " << arg2_descr << "{" << *arg2_val |
219 | << ( tl == PASS ? "} doesn't exceed " : "} exceeds " ) |
220 | << *toler_val; |
221 | if( ct == CHECK_CLOSE ) |
222 | os << "%" ; |
223 | break; |
224 | } |
225 | case CHECK_SMALL: { |
226 | char const* arg1_descr = va_arg( args, char const* ); |
227 | lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); |
228 | /* toler_descr = */ va_arg( args, char const* ); |
229 | lazy_ostream const* toler_val = va_arg( args, lazy_ostream const* ); |
230 | |
231 | os << "absolute value of " << arg1_descr << "{" << *arg1_val << "}" |
232 | << ( tl == PASS ? " doesn't exceed " : " exceeds " ) |
233 | << *toler_val; |
234 | |
235 | if( !pr.has_empty_message() ) |
236 | os << ". " << pr.message(); |
237 | break; |
238 | } |
239 | |
240 | case CHECK_PRED_WITH_ARGS: { |
241 | std::vector< std::pair<char const*, lazy_ostream const*> > args_copy; |
242 | args_copy.reserve( n: num_args ); |
243 | for( std::size_t i = 0; i < num_args; ++i ) { |
244 | char const* desc = va_arg( args, char const* ); |
245 | lazy_ostream const* value = va_arg( args, lazy_ostream const* ); |
246 | args_copy.push_back( x: std::make_pair( x&: desc, y&: value ) ); |
247 | } |
248 | |
249 | os << prefix << assertion_descr; |
250 | |
251 | // print predicate call description |
252 | os << "( " ; |
253 | for( std::size_t i = 0; i < num_args; ++i ) { |
254 | os << args_copy[i].first; |
255 | |
256 | if( i != num_args-1 ) |
257 | os << ", " ; |
258 | } |
259 | os << " )" << suffix; |
260 | |
261 | if( tl != PASS ) { |
262 | os << " for ( " ; |
263 | for( std::size_t i = 0; i < num_args; ++i ) { |
264 | os << *args_copy[i].second; |
265 | |
266 | if( i != num_args-1 ) |
267 | os << ", " ; |
268 | } |
269 | os << " )" ; |
270 | } |
271 | |
272 | if( !pr.has_empty_message() ) |
273 | os << ". " << pr.message(); |
274 | break; |
275 | } |
276 | |
277 | case CHECK_EQUAL_COLL: { |
278 | char const* left_begin_descr = va_arg( args, char const* ); |
279 | char const* left_end_descr = va_arg( args, char const* ); |
280 | char const* right_begin_descr = va_arg( args, char const* ); |
281 | char const* right_end_descr = va_arg( args, char const* ); |
282 | |
283 | os << prefix << "{ " << left_begin_descr << ", " << left_end_descr << " } == { " |
284 | << right_begin_descr << ", " << right_end_descr << " }" |
285 | << suffix; |
286 | |
287 | if( !pr.has_empty_message() ) |
288 | os << ". " << pr.message(); |
289 | break; |
290 | } |
291 | |
292 | case CHECK_BITWISE_EQUAL: { |
293 | char const* left_descr = va_arg( args, char const* ); |
294 | char const* right_descr = va_arg( args, char const* ); |
295 | |
296 | os << prefix << left_descr << " =.= " << right_descr << suffix; |
297 | |
298 | if( !pr.has_empty_message() ) |
299 | os << ". " << pr.message(); |
300 | break; |
301 | } |
302 | } |
303 | } |
304 | |
305 | //____________________________________________________________________________// |
306 | |
307 | bool |
308 | report_assertion( assertion_result const& ar, |
309 | lazy_ostream const& assertion_descr, |
310 | const_string file_name, |
311 | std::size_t line_num, |
312 | tool_level tl, |
313 | check_type ct, |
314 | std::size_t num_args, ... ) |
315 | { |
316 | using namespace unit_test; |
317 | |
318 | if( !framework::test_in_progress() ) { |
319 | // in case no test is in progress, we do not throw anything: |
320 | // raising an exception here may result in raising an exception in a destructor of a global fixture |
321 | // which will abort the process |
322 | // We flag this as aborted instead |
323 | |
324 | //BOOST_TEST_I_ASSRT( framework::current_test_case_id() != INV_TEST_UNIT_ID, |
325 | // std::runtime_error( "Can't use testing tools outside of test case implementation." ) ); |
326 | |
327 | framework::test_aborted(); |
328 | return false; |
329 | } |
330 | |
331 | |
332 | if( !!ar ) |
333 | tl = PASS; |
334 | |
335 | log_level ll; |
336 | char const* prefix; |
337 | char const* suffix; |
338 | |
339 | switch( tl ) { |
340 | case PASS: |
341 | ll = log_successful_tests; |
342 | prefix = "check " ; |
343 | suffix = " has passed" ; |
344 | break; |
345 | case WARN: |
346 | ll = log_warnings; |
347 | prefix = "condition " ; |
348 | suffix = " is not satisfied" ; |
349 | break; |
350 | case CHECK: |
351 | ll = log_all_errors; |
352 | prefix = "check " ; |
353 | suffix = " has failed" ; |
354 | break; |
355 | case REQUIRE: |
356 | ll = log_fatal_errors; |
357 | prefix = "critical check " ; |
358 | suffix = " has failed" ; |
359 | break; |
360 | default: |
361 | return true; |
362 | } |
363 | |
364 | unit_test_log << unit_test::log::begin( file_name, line_num ) << ll; |
365 | va_list args; |
366 | va_start( args, num_args ); |
367 | |
368 | format_report( os&: unit_test_log, pr: ar, assertion_descr, tl, ct, num_args, args, prefix, suffix ); |
369 | |
370 | va_end( args ); |
371 | unit_test_log << unit_test::log::end(); |
372 | |
373 | switch( tl ) { |
374 | case PASS: |
375 | framework::assertion_result( ar: AR_PASSED ); |
376 | return true; |
377 | |
378 | case WARN: |
379 | framework::assertion_result( ar: AR_TRIGGERED ); |
380 | return false; |
381 | |
382 | case CHECK: |
383 | framework::assertion_result( ar: AR_FAILED ); |
384 | return false; |
385 | |
386 | case REQUIRE: |
387 | framework::assertion_result( ar: AR_FAILED ); |
388 | framework::test_unit_aborted( tu: framework::current_test_unit() ); |
389 | BOOST_TEST_I_THROW( execution_aborted() ); |
390 | // the previous line either throws or aborts and the return below is not reached |
391 | // return false; |
392 | BOOST_TEST_UNREACHABLE_RETURN(false); |
393 | } |
394 | |
395 | return true; |
396 | } |
397 | |
398 | //____________________________________________________________________________// |
399 | |
400 | assertion_result |
401 | format_assertion_result( const_string expr_val, const_string details ) |
402 | { |
403 | assertion_result res(false); |
404 | |
405 | bool starts_new_line = first_char( source: expr_val ) == '\n'; |
406 | |
407 | if( !starts_new_line && !expr_val.is_empty() ) |
408 | res.message().stream() << " [" << expr_val << "]" ; |
409 | |
410 | if( !details.is_empty() ) { |
411 | if( first_char(source: details) != '[' ) |
412 | res.message().stream() << ": " ; |
413 | else |
414 | res.message().stream() << " " ; |
415 | |
416 | res.message().stream() << details; |
417 | } |
418 | |
419 | if( starts_new_line ) |
420 | res.message().stream() << "." << expr_val; |
421 | |
422 | return res; |
423 | } |
424 | |
425 | //____________________________________________________________________________// |
426 | |
427 | BOOST_TEST_DECL std::string |
428 | prod_report_format( assertion_result const& ar, unit_test::lazy_ostream const& assertion_descr, check_type ct, std::size_t num_args, ... ) |
429 | { |
430 | std::ostringstream msg_buff; |
431 | |
432 | va_list args; |
433 | va_start( args, num_args ); |
434 | |
435 | format_report( os&: msg_buff, pr: ar, assertion_descr, tl: CHECK, ct, num_args, args, prefix: "assertion " , suffix: " failed" ); |
436 | |
437 | va_end( args ); |
438 | |
439 | return msg_buff.str(); |
440 | } |
441 | |
442 | //____________________________________________________________________________// |
443 | |
444 | assertion_result |
445 | equal_impl( char const* left, char const* right ) |
446 | { |
447 | return (left && right) ? std::strcmp( s1: left, s2: right ) == 0 : (left == right); |
448 | } |
449 | |
450 | //____________________________________________________________________________// |
451 | |
452 | #if !defined( BOOST_NO_CWCHAR ) |
453 | |
454 | assertion_result |
455 | equal_impl( wchar_t const* left, wchar_t const* right ) |
456 | { |
457 | return (left && right) ? std::wcscmp( s1: left, s2: right ) == 0 : (left == right); |
458 | } |
459 | |
460 | #endif // !defined( BOOST_NO_CWCHAR ) |
461 | |
462 | //____________________________________________________________________________// |
463 | |
464 | bool |
465 | is_defined_impl( const_string symbol_name, const_string symbol_value ) |
466 | { |
467 | symbol_value.trim_left( trim_size: 2 ); |
468 | return symbol_name != symbol_value; |
469 | } |
470 | |
471 | //____________________________________________________________________________// |
472 | |
473 | // ************************************************************************** // |
474 | // ************** context_frame ************** // |
475 | // ************************************************************************** // |
476 | |
477 | context_frame::context_frame( ::boost::unit_test::lazy_ostream const& context_descr ) |
478 | : m_frame_id( unit_test::framework::add_context( context_descr, sticky: true ) ) |
479 | { |
480 | } |
481 | |
482 | //____________________________________________________________________________// |
483 | |
484 | context_frame::~context_frame() |
485 | { |
486 | unit_test::framework::clear_context( frame_id: m_frame_id ); |
487 | } |
488 | |
489 | //____________________________________________________________________________// |
490 | |
491 | context_frame::operator bool() |
492 | { |
493 | return true; |
494 | } |
495 | |
496 | //____________________________________________________________________________// |
497 | |
498 | } // namespace tt_detail |
499 | |
500 | // ************************************************************************** // |
501 | // ************** output_test_stream ************** // |
502 | // ************************************************************************** // |
503 | |
504 | struct output_test_stream::Impl |
505 | { |
506 | std::fstream m_pattern; |
507 | bool m_match_or_save; |
508 | bool m_text_or_binary; |
509 | std::string m_synced_string; |
510 | |
511 | char get_char() |
512 | { |
513 | char res = 0; |
514 | do { |
515 | m_pattern.get( c&: res ); |
516 | } while( m_text_or_binary && res == '\r' && !m_pattern.fail() && !m_pattern.eof() ); |
517 | |
518 | return res; |
519 | } |
520 | |
521 | void check_and_fill( assertion_result& res ) |
522 | { |
523 | if( !res.p_predicate_value ) |
524 | res.message() << "Output content: \"" << m_synced_string << '\"'; |
525 | } |
526 | }; |
527 | |
528 | //____________________________________________________________________________// |
529 | |
530 | output_test_stream::output_test_stream( const_string pattern_file_name, bool match_or_save, bool text_or_binary ) |
531 | : m_pimpl( new Impl ) |
532 | { |
533 | if( !pattern_file_name.is_empty() ) { |
534 | std::ios::openmode m = match_or_save ? std::ios::in : std::ios::out; |
535 | if( !text_or_binary ) |
536 | m |= std::ios::binary; |
537 | |
538 | m_pimpl->m_pattern.open( s: pattern_file_name.begin(), mode: m ); |
539 | |
540 | if( !m_pimpl->m_pattern.is_open() ) |
541 | BOOST_TEST_FRAMEWORK_MESSAGE( "Can't open pattern file " << pattern_file_name << " for " << (match_or_save ? "reading" : "writing" ) ); |
542 | } |
543 | |
544 | m_pimpl->m_match_or_save = match_or_save; |
545 | m_pimpl->m_text_or_binary = text_or_binary; |
546 | } |
547 | |
548 | //____________________________________________________________________________// |
549 | |
550 | output_test_stream::~output_test_stream() |
551 | { |
552 | delete m_pimpl; |
553 | } |
554 | |
555 | //____________________________________________________________________________// |
556 | |
557 | assertion_result |
558 | output_test_stream::is_empty( bool flush_stream ) |
559 | { |
560 | sync(); |
561 | |
562 | assertion_result res( m_pimpl->m_synced_string.empty() ); |
563 | |
564 | m_pimpl->check_and_fill( res ); |
565 | |
566 | if( flush_stream ) |
567 | flush(); |
568 | |
569 | return res; |
570 | } |
571 | |
572 | //____________________________________________________________________________// |
573 | |
574 | assertion_result |
575 | output_test_stream::check_length( std::size_t length_, bool flush_stream ) |
576 | { |
577 | sync(); |
578 | |
579 | assertion_result res( m_pimpl->m_synced_string.length() == length_ ); |
580 | |
581 | m_pimpl->check_and_fill( res ); |
582 | |
583 | if( flush_stream ) |
584 | flush(); |
585 | |
586 | return res; |
587 | } |
588 | |
589 | //____________________________________________________________________________// |
590 | |
591 | assertion_result |
592 | output_test_stream::is_equal( const_string arg, bool flush_stream ) |
593 | { |
594 | sync(); |
595 | |
596 | assertion_result res( const_string( m_pimpl->m_synced_string ) == arg ); |
597 | |
598 | m_pimpl->check_and_fill( res ); |
599 | |
600 | if( flush_stream ) |
601 | flush(); |
602 | |
603 | return res; |
604 | } |
605 | |
606 | //____________________________________________________________________________// |
607 | |
608 | std::string pretty_print_log(std::string str) { |
609 | |
610 | static const std::string to_replace[] = { "\r" , "\n" }; |
611 | static const std::string replacement[] = { "\\r" , "\\n" }; |
612 | |
613 | return unit_test::utils::replace_all_occurrences_of( |
614 | str, |
615 | first1: to_replace, last1: to_replace + sizeof(to_replace)/sizeof(to_replace[0]), |
616 | first2: replacement, last2: replacement + sizeof(replacement)/sizeof(replacement[0])); |
617 | } |
618 | |
619 | assertion_result |
620 | output_test_stream::match_pattern( bool flush_stream ) |
621 | { |
622 | const std::string::size_type n_chars_presuffix = 10; |
623 | sync(); |
624 | |
625 | assertion_result result( true ); |
626 | |
627 | const std::string stream_string_repr = get_stream_string_representation(); |
628 | |
629 | if( !m_pimpl->m_pattern.is_open() ) { |
630 | result = false; |
631 | result.message() << "Pattern file can't be opened!" ; |
632 | } |
633 | else { |
634 | if( m_pimpl->m_match_or_save ) { |
635 | |
636 | int offset = 0; |
637 | std::vector<char> last_elements; |
638 | for ( std::string::size_type i = 0; static_cast<int>(i + offset) < static_cast<int>(stream_string_repr.length()); ++i ) { |
639 | |
640 | char c = m_pimpl->get_char(); |
641 | |
642 | if( last_elements.size() <= n_chars_presuffix ) { |
643 | last_elements.push_back( x: c ); |
644 | } |
645 | else { |
646 | last_elements[ i % last_elements.size() ] = c; |
647 | } |
648 | |
649 | bool is_same = !m_pimpl->m_pattern.fail() && |
650 | !m_pimpl->m_pattern.eof() && |
651 | (stream_string_repr[i+offset] == c); |
652 | |
653 | if( !is_same ) { |
654 | |
655 | result = false; |
656 | |
657 | std::string::size_type prefix_size = (std::min)( a: i + offset, b: n_chars_presuffix ); |
658 | |
659 | std::string::size_type suffix_size = (std::min)( a: stream_string_repr.length() - i - offset, |
660 | b: n_chars_presuffix ); |
661 | |
662 | // try to log area around the mismatch |
663 | std::string substr = stream_string_repr.substr(pos: 0, n: i+offset); |
664 | std::size_t line = std::count(first: substr.begin(), last: substr.end(), value: '\n'); |
665 | std::size_t column = i + offset - substr.rfind(c: '\n'); |
666 | |
667 | result.message() |
668 | << "Mismatch at position " << i |
669 | << " (line " << line |
670 | << ", column " << column |
671 | << "): '" << pretty_print_log(str: std::string(1, stream_string_repr[i+offset])) << "' != '" << pretty_print_log(str: std::string(1, c)) << "' :\n" ; |
672 | |
673 | // we already escape this substring because we need its actual size for the pretty print |
674 | // of the difference location. |
675 | std::string sub_str_prefix(pretty_print_log(str: stream_string_repr.substr( pos: i + offset - prefix_size, n: prefix_size ))); |
676 | |
677 | // we need this substring as is because we compute the best matching substrings on it. |
678 | std::string sub_str_suffix(stream_string_repr.substr( pos: i + offset, n: suffix_size)); |
679 | result.message() << "... " << sub_str_prefix + pretty_print_log(str: sub_str_suffix) << " ..." << '\n'; |
680 | |
681 | result.message() << "... " ; |
682 | for( std::size_t j = 0; j < last_elements.size() ; j++ ) |
683 | result.message() << pretty_print_log(str: std::string(1, last_elements[(i + j + 1) % last_elements.size()])); |
684 | |
685 | std::vector<char> last_elements_ordered; |
686 | last_elements_ordered.push_back(x: c); |
687 | for( std::string::size_type counter = 0; counter < suffix_size - 1 ; counter++ ) { |
688 | char c2 = m_pimpl->get_char(); |
689 | |
690 | if( m_pimpl->m_pattern.fail() || m_pimpl->m_pattern.eof() ) |
691 | break; |
692 | |
693 | result.message() << pretty_print_log(str: std::string(1, c2)); |
694 | |
695 | last_elements_ordered.push_back(x: c2); |
696 | } |
697 | |
698 | // tries to find the best substring matching in the remainder of the |
699 | // two strings |
700 | std::size_t max_nb_char_in_common = 0; |
701 | std::size_t best_pattern_start_index = 0; |
702 | std::size_t best_stream_start_index = 0; |
703 | for( std::size_t pattern_start_index = best_pattern_start_index; |
704 | pattern_start_index < last_elements_ordered.size(); |
705 | pattern_start_index++ ) { |
706 | for( std::size_t stream_start_index = best_stream_start_index; |
707 | stream_start_index < sub_str_suffix.size(); |
708 | stream_start_index++ ) { |
709 | |
710 | std::size_t max_size = (std::min)( a: last_elements_ordered.size() - pattern_start_index, b: sub_str_suffix.size() - stream_start_index ); |
711 | if( max_nb_char_in_common > max_size ) |
712 | break; // safely break to go to the outer loop |
713 | |
714 | std::size_t nb_char_in_common = 0; |
715 | for( std::size_t k = 0; k < max_size; k++) { |
716 | if( last_elements_ordered[pattern_start_index + k] == sub_str_suffix[stream_start_index + k] ) |
717 | nb_char_in_common ++; |
718 | else |
719 | break; // we take fully matching substring only |
720 | } |
721 | |
722 | if( nb_char_in_common > max_nb_char_in_common ) { |
723 | max_nb_char_in_common = nb_char_in_common; |
724 | best_pattern_start_index = pattern_start_index; |
725 | best_stream_start_index = stream_start_index; |
726 | } |
727 | } |
728 | } |
729 | |
730 | // indicates with more precision the location of the mismatchs in "ascii arts" ... |
731 | result.message() << " ...\n... " ; |
732 | for( std::string::size_type j = 0; j < sub_str_prefix.size(); j++) { |
733 | result.message() << ' '; |
734 | } |
735 | |
736 | result.message() << '~'; // places the first tilde at the current char that mismatches |
737 | |
738 | for( std::size_t k = 1; k < (std::max)(a: best_pattern_start_index, b: best_stream_start_index); k++ ) { // 1 is for the current char c |
739 | std::string s1(pretty_print_log(str: std::string(1, last_elements_ordered[(std::min)(a: k, b: best_pattern_start_index)]))); |
740 | std::string s2(pretty_print_log(str: std::string(1, sub_str_suffix[(std::min)(a: k, b: best_stream_start_index)]))); |
741 | for( int h = static_cast<int>((std::max)(a: s1.size(), b: s2.size())); h > 0; h--) |
742 | result.message() << "~" ; |
743 | } |
744 | |
745 | if( m_pimpl->m_pattern.eof() ) { |
746 | result.message() << " (reference string shorter than current stream)" ; |
747 | } |
748 | |
749 | result.message() << "\n" ; |
750 | |
751 | // no need to continue if the EOF is reached |
752 | if( m_pimpl->m_pattern.eof() ) { |
753 | break; |
754 | } |
755 | |
756 | // first char is a replicat of c, so we do not copy it. |
757 | for(std::string::size_type counter = 0; counter < last_elements_ordered.size() - 1 ; counter++) |
758 | last_elements[ (i + 1 + counter) % last_elements.size() ] = last_elements_ordered[counter + 1]; |
759 | |
760 | i += last_elements_ordered.size()-1; |
761 | offset += best_stream_start_index - best_pattern_start_index; |
762 | |
763 | } |
764 | |
765 | } |
766 | |
767 | // not needed anymore |
768 | /* |
769 | if(offset > 0 && false) { |
770 | m_pimpl->m_pattern.ignore( |
771 | static_cast<std::streamsize>( offset )); |
772 | } |
773 | */ |
774 | } |
775 | else { |
776 | m_pimpl->m_pattern.write( s: stream_string_repr.c_str(), |
777 | n: static_cast<std::streamsize>( stream_string_repr.length() ) ); |
778 | m_pimpl->m_pattern.flush(); |
779 | } |
780 | } |
781 | |
782 | if( flush_stream ) |
783 | flush(); |
784 | |
785 | return result; |
786 | } |
787 | |
788 | //____________________________________________________________________________// |
789 | |
790 | void |
791 | output_test_stream::flush() |
792 | { |
793 | m_pimpl->m_synced_string.erase(); |
794 | |
795 | #ifndef BOOST_NO_STRINGSTREAM |
796 | str( s: std::string() ); |
797 | #else |
798 | seekp( 0, std::ios::beg ); |
799 | #endif |
800 | } |
801 | |
802 | |
803 | std::string |
804 | output_test_stream::get_stream_string_representation() const { |
805 | return m_pimpl->m_synced_string; |
806 | } |
807 | |
808 | //____________________________________________________________________________// |
809 | |
810 | std::size_t |
811 | output_test_stream::length() |
812 | { |
813 | sync(); |
814 | |
815 | return m_pimpl->m_synced_string.length(); |
816 | } |
817 | |
818 | //____________________________________________________________________________// |
819 | |
820 | void |
821 | output_test_stream::sync() |
822 | { |
823 | #ifdef BOOST_NO_STRINGSTREAM |
824 | m_pimpl->m_synced_string.assign( str(), pcount() ); |
825 | freeze( false ); |
826 | #else |
827 | m_pimpl->m_synced_string = str(); |
828 | #endif |
829 | } |
830 | |
831 | //____________________________________________________________________________// |
832 | |
833 | } // namespace test_tools |
834 | } // namespace boost |
835 | |
836 | #include <boost/test/detail/enable_warnings.hpp> |
837 | |
838 | #endif // BOOST_TEST_TEST_TOOLS_IPP_012205GER |
839 | |