1//  (C) Copyright Gennadiy Rozental 2005.
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: test_tools.ipp,v $
9//
10//  Version     : $Revision: 1.1.1.1 $
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/output_test_stream.hpp>
22#include <boost/test/framework.hpp>
23#include <boost/test/execution_monitor.hpp> // execution_aborted
24
25// Boost
26#include <boost/config.hpp>
27
28// STL
29#include <fstream>
30#include <string>
31#include <cstring>
32#include <cctype>
33#include <cwchar>
34#ifdef BOOST_STANDARD_IOSTREAMS
35#include <ios>
36#endif
37
38// !! should we use #include <cstdarg>
39#include <stdarg.h>
40
41#include <boost/test/detail/suppress_warnings.hpp>
42
43//____________________________________________________________________________//
44
45# ifdef BOOST_NO_STDC_NAMESPACE
46namespace std { using ::strcmp; using ::strlen; using ::isprint; }
47#if !defined( BOOST_NO_CWCHAR )
48namespace std { using ::wcscmp; }
49#endif
50# endif
51
52namespace boost {
53
54namespace test_tools {
55
56namespace tt_detail {
57
58// ************************************************************************** //
59// **************            TOOL BOX Implementation           ************** //
60// ************************************************************************** //
61
62void
63check_impl( predicate_result const& pr, wrap_stringstream& check_descr,
64            const_string file_name, std::size_t line_num,
65            tool_level tl, check_type ct,
66            std::size_t num_of_args, ... )
67{
68    using namespace unit_test;
69
70    if( !!pr )
71        tl = PASS;
72
73    log_level    ll;
74    char const*  prefix;
75    char const*  suffix;
76
77    if( tl == PASS )
78        framework::assertion_result( true );
79    else if( tl != WARN )
80        framework::assertion_result( false );
81
82    switch( tl ) {
83    case PASS:
84        ll      = log_successful_tests;
85        prefix  = "check ";
86        suffix  = " passed";
87        break;
88    case WARN:
89        ll      = log_warnings;
90        prefix  = "condition ";
91        suffix  = " is not satisfied";
92        break;
93    case CHECK:
94        ll      = log_all_errors;
95        prefix  = "check ";
96        suffix  = " failed";
97        break;
98    case REQUIRE:
99        ll      = log_fatal_errors;
100        prefix  = "critical test ";
101        suffix  = " failed";
102        break;
103    default:
104        return;
105    }
106
107    switch( ct ) {
108    case CHECK_PRED:
109        unit_test_log << unit_test::log::begin() << unit_test::log::file( file_name ) << unit_test::log::line( line_num )
110                      << ll << prefix << check_descr.str() << suffix;
111
112        if( !pr.has_empty_message() )
113            unit_test_log << ". " << pr.message();
114
115        unit_test_log << unit_test::log::end();
116        break;
117    case CHECK_MSG:
118        unit_test_log << unit_test::log::begin() << unit_test::log::file( file_name ) << unit_test::log::line( line_num ) << ll;
119
120        if( tl == PASS )
121            unit_test_log << prefix << "'" << check_descr.str() << "'" << suffix;
122        else
123            unit_test_log << check_descr.str();
124
125        if( !pr.has_empty_message() )
126            unit_test_log << ". " << pr.message();
127
128        unit_test_log << unit_test::log::end();
129        break;
130    case MSG_ONLY:
131        unit_test_log << unit_test::log::begin() << unit_test::log::file( file_name ) << unit_test::log::line( line_num )
132                      << log_messages << check_descr.str() << unit_test::log::end();
133        break;
134    case SET_CHECKPOINT:
135        unit_test_log << unit_test::log::file( file_name ) << unit_test::log::line( line_num ) << unit_test::log::checkpoint( check_descr.str() );
136        break;
137    case CHECK_EQUAL: {
138        va_list args;
139
140        va_start( args, num_of_args );
141        char const* arg1_descr  = va_arg( args, char const* );
142        char const* arg1_val    = va_arg( args, char const* );
143        char const* arg2_descr  = va_arg( args, char const* );
144        char const* arg2_val    = va_arg( args, char const* );
145
146        unit_test_log << unit_test::log::begin() << unit_test::log::file( file_name ) << unit_test::log::line( line_num )
147                      << ll << prefix << arg1_descr << " == " << arg2_descr << suffix;
148
149        if( tl != PASS )
150            unit_test_log << " [" << arg1_val << " != " << arg2_val << "]" ;
151
152        va_end( args );
153
154        if( !pr.has_empty_message() )
155            unit_test_log << ". " << pr.message();
156
157        unit_test_log << unit_test::log::end();
158        break;
159    }
160    case CHECK_CLOSE: {
161        va_list args;
162
163        va_start( args, num_of_args );
164        char const* arg1_descr  = va_arg( args, char const* );
165        char const* arg1_val    = va_arg( args, char const* );
166        char const* arg2_descr  = va_arg( args, char const* );
167        char const* arg2_val    = va_arg( args, char const* );
168        /* toler_descr = */       va_arg( args, char const* );
169        char const* toler_val   = va_arg( args, char const* );
170
171        unit_test_log << unit_test::log::begin() << unit_test::log::file( file_name ) << unit_test::log::line( line_num ) << ll;
172
173        unit_test_log << "difference between " << arg1_descr << "{" << arg1_val << "}"
174                      << " and "               << arg2_descr << "{" << arg2_val << "}"
175                      << ( tl == PASS ? " doesn't exceed " : " exceeds " )
176                      << toler_val << "%",
177
178        va_end( args );
179
180        if( !pr.has_empty_message() )
181            unit_test_log << ". " << pr.message();
182
183        unit_test_log << unit_test::log::end();
184        break;
185    }
186    case CHECK_SMALL: {
187        va_list args;
188
189        va_start( args, num_of_args );
190        char const* arg1_descr  = va_arg( args, char const* );
191        char const* arg1_val    = va_arg( args, char const* );
192        /* toler_descr = */       va_arg( args, char const* );
193        char const* toler_val   = va_arg( args, char const* );
194
195        unit_test_log << unit_test::log::begin() << unit_test::log::file( file_name ) << unit_test::log::line( line_num ) << ll;
196
197        unit_test_log << "absolute value of " << arg1_descr << "{" << arg1_val << "}"
198                      << ( tl == PASS ? " doesn't exceed " : " exceeds " )
199                      << toler_val,
200
201        va_end( args );
202
203        if( !pr.has_empty_message() )
204            unit_test_log << ". " << pr.message();
205
206        unit_test_log << unit_test::log::end();
207        break;
208    }
209    case CHECK_PRED_WITH_ARGS: {
210        unit_test_log << unit_test::log::begin() << unit_test::log::file( file_name ) << unit_test::log::line( line_num )
211                      << ll << prefix << check_descr.str();
212
213        // print predicate call description
214        {
215            va_list args;
216            va_start( args, num_of_args );
217
218            unit_test_log << "( ";
219            for( std::size_t i = 0; i < num_of_args; ++i ) {
220                unit_test_log << va_arg( args, char const* );
221                va_arg( args, char const* ); // skip argument value;
222
223                if( i != num_of_args-1 )
224                    unit_test_log << ", ";
225            }
226            unit_test_log << " )" << suffix;
227            va_end( args );
228        }
229
230        if( tl != PASS ) {
231            va_list args;
232            va_start( args, num_of_args );
233
234            unit_test_log << " for ( ";
235            for( std::size_t i = 0; i < num_of_args; ++i ) {
236                va_arg( args, char const* ); // skip argument description;
237                unit_test_log << va_arg( args, char const* );
238
239                if( i != num_of_args-1 )
240                    unit_test_log << ", ";
241            }
242            unit_test_log << " )";
243            va_end( args );
244        }
245
246        if( !pr.has_empty_message() )
247            unit_test_log << ". " << pr.message();
248
249        unit_test_log << unit_test::log::end();
250        break;
251    }
252    case CHECK_EQUAL_COLL: {
253        va_list args;
254
255        va_start( args, num_of_args );
256        char const* left_begin_descr    = va_arg( args, char const* );
257        char const* left_end_descr      = va_arg( args, char const* );
258        char const* right_begin_descr   = va_arg( args, char const* );
259        char const* right_end_descr     = va_arg( args, char const* );
260
261        unit_test_log << unit_test::log::begin() << unit_test::log::file( file_name ) << unit_test::log::line( line_num )
262                      << ll << prefix
263                      << "{ " << left_begin_descr  << ", " << left_end_descr  << " } == { "
264                              << right_begin_descr << ", " << right_end_descr << " }"
265                      << suffix;
266
267        va_end( args );
268
269        if( !pr.has_empty_message() )
270            unit_test_log << ". " << pr.message();
271
272        unit_test_log << unit_test::log::end();
273        break;
274    }
275    case CHECK_BITWISE_EQUAL: {
276        va_list args;
277
278        va_start( args, num_of_args );
279        char const* left_descr    = va_arg( args, char const* );
280        char const* right_descr   = va_arg( args, char const* );
281
282        unit_test_log << unit_test::log::begin() << unit_test::log::file( file_name ) << unit_test::log::line( line_num )
283                      << ll << prefix << left_descr  << " =.= " << right_descr << suffix;
284
285        va_end( args );
286
287        if( !pr.has_empty_message() )
288            unit_test_log << ". " << pr.message();
289
290        unit_test_log << unit_test::log::end();
291        break;
292    }
293    }
294
295    if( tl == REQUIRE ) {
296        framework::test_unit_aborted();
297
298        throw execution_aborted();
299    }
300}
301
302//____________________________________________________________________________//
303
304predicate_result
305equal_impl( char const* left, char const* right )
306{
307    return (left && right) ? std::strcmp( left, right ) == 0 : (left == right);
308}
309
310//____________________________________________________________________________//
311
312#if !defined( BOOST_NO_CWCHAR )
313
314predicate_result
315equal_impl( wchar_t const* left, wchar_t const* right )
316{
317    return (left && right) ? std::wcscmp( left, right ) == 0 : (left == right);
318}
319
320#endif // !defined( BOOST_NO_CWCHAR )
321
322//____________________________________________________________________________//
323
324bool
325is_defined_impl( const_string symbol_name, const_string symbol_value )
326{
327    symbol_value.trim_left( 2 );
328    return symbol_name != symbol_value;
329}
330
331//____________________________________________________________________________//
332
333// ************************************************************************** //
334// **************               log print helper               ************** //
335// ************************************************************************** //
336
337void
338print_log_value<char>::operator()( std::ostream& ostr, char t )
339{
340    if( (std::isprint)( t ) )
341        ostr << '\'' << t << '\'';
342    else
343        ostr << std::hex
344        // showbase is only available for new style streams:
345#ifndef BOOST_NO_STD_LOCALE
346        << std::showbase
347#else
348        << "0x"
349#endif
350        << (int)t;
351}
352
353//____________________________________________________________________________//
354
355void
356print_log_value<unsigned char>::operator()( std::ostream& ostr, unsigned char t )
357{
358    ostr << std::hex
359        // showbase is only available for new style streams:
360#ifndef BOOST_NO_STD_LOCALE
361        << std::showbase
362#else
363        << "0x"
364#endif
365       << (int)t;
366}
367
368//____________________________________________________________________________//
369
370void
371print_log_value<char const*>::operator()( std::ostream& ostr, char const* t )
372{
373    ostr << ( t ? t : "null string" );
374}
375
376//____________________________________________________________________________//
377
378void
379print_log_value<wchar_t const*>::operator()( std::ostream& ostr, wchar_t const* t )
380{
381    ostr << ( t ? t : L"null string" );
382}
383
384//____________________________________________________________________________//
385
386} // namespace tt_detail
387
388// ************************************************************************** //
389// **************               output_test_stream             ************** //
390// ************************************************************************** //
391
392struct output_test_stream::Impl
393{
394    std::fstream    m_pattern;
395    bool            m_match_or_save;
396    std::string     m_synced_string;
397
398    char            get_char()
399    {
400        char res;
401        do {
402            m_pattern.get( res );
403        } while( res == '\r' && !m_pattern.fail() && !m_pattern.eof() );
404
405        return res;
406    }
407
408    void            check_and_fill( predicate_result& res )
409    {
410        if( !res.p_predicate_value )
411            res.message() << "Output content: \"" << m_synced_string << '\"';
412    }
413};
414
415//____________________________________________________________________________//
416
417output_test_stream::output_test_stream( const_string pattern_file_name, bool match_or_save )
418: m_pimpl( new Impl )
419{
420    if( !pattern_file_name.is_empty() ) {
421        m_pimpl->m_pattern.open( pattern_file_name.begin(), match_or_save ? std::ios::in : std::ios::out );
422
423        BOOST_WARN_MESSAGE( m_pimpl->m_pattern.is_open(),
424                             "Couldn't open pattern file " << pattern_file_name
425                                << " for " << ( m_pimpl->m_match_or_save ? "reading" : "writing") );
426    }
427
428    m_pimpl->m_match_or_save = match_or_save;
429}
430
431//____________________________________________________________________________//
432
433output_test_stream::~output_test_stream()
434{
435    delete m_pimpl;
436}
437
438//____________________________________________________________________________//
439
440predicate_result
441output_test_stream::is_empty( bool flush_stream )
442{
443    sync();
444
445    result_type res( m_pimpl->m_synced_string.empty() );
446
447    m_pimpl->check_and_fill( res );
448
449    if( flush_stream )
450        flush();
451
452    return res;
453}
454
455//____________________________________________________________________________//
456
457predicate_result
458output_test_stream::check_length( std::size_t length_, bool flush_stream )
459{
460    sync();
461
462    result_type res( m_pimpl->m_synced_string.length() == length_ );
463
464    m_pimpl->check_and_fill( res );
465
466    if( flush_stream )
467        flush();
468
469    return res;
470}
471
472//____________________________________________________________________________//
473
474predicate_result
475output_test_stream::is_equal( const_string arg, bool flush_stream )
476{
477    sync();
478
479    result_type res( const_string( m_pimpl->m_synced_string ) == arg );
480
481    m_pimpl->check_and_fill( res );
482
483    if( flush_stream )
484        flush();
485
486    return res;
487}
488
489//____________________________________________________________________________//
490
491predicate_result
492output_test_stream::match_pattern( bool flush_stream )
493{
494    sync();
495
496    result_type result( true );
497
498    if( !m_pimpl->m_pattern.is_open() ) {
499        result = false;
500        result.message() << "I/O failure";
501    }
502    else {
503        if( m_pimpl->m_match_or_save ) {
504            for ( std::string::size_type i = 0; i < m_pimpl->m_synced_string.length(); ++i ) {
505                char c = m_pimpl->get_char();
506
507                result = !m_pimpl->m_pattern.fail() &&
508                         !m_pimpl->m_pattern.eof()  &&
509                         (m_pimpl->m_synced_string[i] == c);
510
511                if( !result ) {
512                    std::string::size_type suffix_size  = (std::min)( m_pimpl->m_synced_string.length() - i,
513                                                                    static_cast<std::string::size_type>(5) );
514
515                    // try to log area around the mismatch
516                    result.message() << "Mismatch at position " << i << '\n'
517                        << "..." << m_pimpl->m_synced_string.substr( i, suffix_size ) << "..." << '\n'
518                        << "..." << c;
519
520                    std::string::size_type counter = suffix_size;
521                    while( --counter ) {
522                        char c = m_pimpl->get_char();
523
524                        if( m_pimpl->m_pattern.fail() || m_pimpl->m_pattern.eof() )
525                            break;
526
527                        result.message() << c;
528                    }
529
530                    result.message() << "...";
531
532                    // skip rest of the bytes. May help for further matching
533                    m_pimpl->m_pattern.ignore( m_pimpl->m_synced_string.length() - i - suffix_size );
534                    break;
535                }
536            }
537        }
538        else {
539            m_pimpl->m_pattern.write( m_pimpl->m_synced_string.c_str(),
540                                      static_cast<std::streamsize>( m_pimpl->m_synced_string.length() ) );
541            m_pimpl->m_pattern.flush();
542        }
543    }
544
545    if( flush_stream )
546        flush();
547
548    return result;
549}
550
551//____________________________________________________________________________//
552
553void
554output_test_stream::flush()
555{
556    m_pimpl->m_synced_string.erase();
557
558#ifndef BOOST_NO_STRINGSTREAM
559    str( std::string() );
560#else
561    seekp( 0, std::ios::beg );
562#endif
563}
564
565//____________________________________________________________________________//
566
567std::size_t
568output_test_stream::length()
569{
570    sync();
571
572    return m_pimpl->m_synced_string.length();
573}
574
575//____________________________________________________________________________//
576
577void
578output_test_stream::sync()
579{
580#ifdef BOOST_NO_STRINGSTREAM
581    m_pimpl->m_synced_string.assign( str(), pcount() );
582    freeze( false );
583#elif BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x530) )
584    m_pimpl->m_synced_string.assign( str().c_str(), tellp() );
585#else
586    m_pimpl->m_synced_string = str();
587#endif
588}
589
590//____________________________________________________________________________//
591
592} // namespace test_tools
593
594} // namespace boost
595
596//____________________________________________________________________________//
597
598#include <boost/test/detail/enable_warnings.hpp>
599
600// ***************************************************************************
601//  Revision History :
602//
603//  $Log: test_tools.ipp,v $
604//  Revision 1.1.1.1  2006/03/20 20:15:27  ewalkup
605//  boost libraries
606//
607//  Revision 1.9  2005/06/22 22:03:05  dgregor
608//  More explicit scoping needed for GCC 2.95.3
609//
610//  Revision 1.8  2005/04/30 17:56:31  rogeeff
611//  switch to stdarg.h to workarround como issues
612//
613//  Revision 1.7  2005/03/23 21:02:23  rogeeff
614//  Sunpro CC 5.3 fixes
615//
616//  Revision 1.6  2005/02/21 10:14:04  rogeeff
617//  CHECK_SMALL tool implemented
618//
619//  Revision 1.5  2005/02/20 08:27:07  rogeeff
620//  This a major update for Boost.Test framework. See release docs for complete list of fixes/updates
621//
622//  Revision 1.4  2005/02/02 12:08:14  rogeeff
623//  namespace log added for log manipulators
624//
625//  Revision 1.3  2005/02/01 06:40:07  rogeeff
626//  copyright update
627//  old log entries removed
628//  minor stilistic changes
629//  depricated tools removed
630//
631//  Revision 1.2  2005/01/30 03:18:27  rogeeff
632//  test tools implementation completely reworked. All tools inplemented through single vararg function
633//
634//  Revision 1.1  2005/01/22 19:22:12  rogeeff
635//  implementation moved into headers section to eliminate dependency of included/minimal component on src directory
636//
637//  Revision 1.43  2005/01/19 06:40:05  vawjr
638//  deleted redundant \r in many \r\r\n sequences of the source.  VC8.0 doesn't like them
639//
640//  Revision 1.42  2005/01/18 08:30:08  rogeeff
641//  unit_test_log rework:
642//     eliminated need for ::instance()
643//     eliminated need for << end and ...END macro
644//     straitend interface between log and formatters
645//     change compiler like formatter name
646//     minimized unit_test_log interface and reworked to use explicit calls
647//
648// ***************************************************************************
649
650#endif // BOOST_TEST_TEST_TOOLS_IPP_012205GER
651