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