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