1//  (C) Copyright Gennadiy Rozental 2005-2008.
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: 54633 $
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#include <boost/test/unit_test_suite_impl.hpp>
25
26// Boost
27#include <boost/config.hpp>
28
29// STL
30#include <fstream>
31#include <string>
32#include <cstring>
33#include <cctype>
34#include <cwchar>
35#include <stdexcept>
36#include <ios>
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
56// ************************************************************************** //
57// **************                print_log_value               ************** //
58// ************************************************************************** //
59
60void
61print_log_value<char>::operator()( std::ostream& ostr, char t )
62{
63    if( (std::isprint)( static_cast<unsigned char>(t) ) )
64        ostr << '\'' << t << '\'';
65    else
66        ostr << std::hex
67#if BOOST_TEST_USE_STD_LOCALE
68        << std::showbase
69#else
70        << "0x"
71#endif
72        << static_cast<int>(t);
73}
74
75//____________________________________________________________________________//
76
77void
78print_log_value<unsigned char>::operator()( std::ostream& ostr, unsigned char t )
79{
80    ostr << std::hex
81        // showbase is only available for new style streams:
82#if BOOST_TEST_USE_STD_LOCALE
83        << std::showbase
84#else
85        << "0x"
86#endif
87        << static_cast<int>(t);
88}
89
90//____________________________________________________________________________//
91
92void
93print_log_value<char const*>::operator()( std::ostream& ostr, char const* t )
94{
95    ostr << ( t ? t : "null string" );
96}
97
98//____________________________________________________________________________//
99
100void
101print_log_value<wchar_t const*>::operator()( std::ostream& ostr, wchar_t const* t )
102{
103    ostr << ( t ? t : L"null string" );
104}
105
106//____________________________________________________________________________//
107
108namespace tt_detail {
109
110// ************************************************************************** //
111// **************            TOOL BOX Implementation           ************** //
112// ************************************************************************** //
113
114using ::boost::unit_test::lazy_ostream;
115
116bool
117check_impl( predicate_result const& pr, lazy_ostream const& check_descr,
118            const_string file_name, std::size_t line_num,
119            tool_level tl, check_type ct,
120            std::size_t num_of_args, ... )
121{
122    using namespace unit_test;
123
124    if( !framework::is_initialized() )
125        throw std::runtime_error( "can't use testing tools before framework is initialized" );
126
127    if( !!pr )
128        tl = PASS;
129
130    log_level    ll;
131    char const*  prefix;
132    char const*  suffix;
133
134    switch( tl ) {
135    case PASS:
136        ll      = log_successful_tests;
137        prefix  = "check ";
138        suffix  = " passed";
139        break;
140    case WARN:
141        ll      = log_warnings;
142        prefix  = "condition ";
143        suffix  = " is not satisfied";
144        break;
145    case CHECK:
146        ll      = log_all_errors;
147        prefix  = "check ";
148        suffix  = " failed";
149        break;
150    case REQUIRE:
151        ll      = log_fatal_errors;
152        prefix  = "critical check ";
153        suffix  = " failed";
154        break;
155    default:
156        return true;
157    }
158
159    switch( ct ) {
160    case CHECK_PRED:
161        unit_test_log << unit_test::log::begin( file_name, line_num )
162                      << ll << prefix << check_descr << suffix;
163
164        if( !pr.has_empty_message() )
165            unit_test_log << ". " << pr.message();
166
167        unit_test_log << unit_test::log::end();
168        break;
169
170    case CHECK_MSG:
171        unit_test_log << unit_test::log::begin( file_name, line_num ) << ll;
172
173        if( tl == PASS )
174            unit_test_log << prefix << "'" << check_descr << "'" << suffix;
175        else
176            unit_test_log << check_descr;
177
178        if( !pr.has_empty_message() )
179            unit_test_log << ". " << pr.message();
180
181        unit_test_log << unit_test::log::end();
182        break;
183
184    case CHECK_EQUAL:
185    case CHECK_NE:
186    case CHECK_LT:
187    case CHECK_LE:
188    case CHECK_GT:
189    case CHECK_GE: {
190        static char const* check_str [] = { " == ", " != ", " < " , " <= ", " > " , " >= " };
191        static char const* rever_str [] = { " != ", " == ", " >= ", " > " , " <= ", " < "  };
192
193        va_list args;
194
195        va_start( args, num_of_args );
196        char const*         arg1_descr  = va_arg( args, char const* );
197        lazy_ostream const* arg1_val    = va_arg( args, lazy_ostream const* );
198        char const*         arg2_descr  = va_arg( args, char const* );
199        lazy_ostream const* arg2_val    = va_arg( args, lazy_ostream const* );
200
201        unit_test_log << unit_test::log::begin( file_name, line_num )
202                      << ll << prefix << arg1_descr << check_str[ct-CHECK_EQUAL] << arg2_descr << suffix;
203
204        if( tl != PASS )
205            unit_test_log << " [" << *arg1_val << rever_str[ct-CHECK_EQUAL] << *arg2_val << "]" ;
206
207        va_end( args );
208
209        if( !pr.has_empty_message() )
210            unit_test_log << ". " << pr.message();
211
212        unit_test_log << unit_test::log::end();
213        break;
214    }
215
216    case CHECK_CLOSE:
217    case CHECK_CLOSE_FRACTION: {
218        va_list args;
219
220        va_start( args, num_of_args );
221        char const*         arg1_descr  = va_arg( args, char const* );
222        lazy_ostream const* arg1_val    = va_arg( args, lazy_ostream const* );
223        char const*         arg2_descr  = va_arg( args, char const* );
224        lazy_ostream const* arg2_val    = va_arg( args, lazy_ostream const* );
225        /* toler_descr = */               va_arg( args, char const* );
226        lazy_ostream const* toler_val   = va_arg( args, lazy_ostream const* );
227
228        unit_test_log << unit_test::log::begin( file_name, line_num ) << ll;
229
230        unit_test_log << "difference{" << pr.message() << (ct == CHECK_CLOSE ? "%" : "")
231                      << "} between " << arg1_descr << "{" << *arg1_val
232                      << "} and "               << arg2_descr << "{" << *arg2_val
233                      << ( tl == PASS ? "} doesn't exceed " : "} exceeds " )
234                      << *toler_val;
235        if( ct == CHECK_CLOSE )
236            unit_test_log << "%";
237
238        va_end( args );
239
240        unit_test_log << unit_test::log::end();
241        break;
242    }
243    case CHECK_SMALL: {
244        va_list args;
245
246        va_start( args, num_of_args );
247        char const*         arg1_descr  = va_arg( args, char const* );
248        lazy_ostream const* arg1_val    = va_arg( args, lazy_ostream const* );
249        /* toler_descr = */               va_arg( args, char const* );
250        lazy_ostream const* toler_val   = va_arg( args, lazy_ostream const* );
251
252        unit_test_log << unit_test::log::begin( file_name, line_num ) << ll;
253
254        unit_test_log << "absolute value of " << arg1_descr << "{" << *arg1_val << "}"
255                      << ( tl == PASS ? " doesn't exceed " : " exceeds " )
256                      << *toler_val;
257
258        va_end( args );
259
260        if( !pr.has_empty_message() )
261            unit_test_log << ". " << pr.message();
262
263        unit_test_log << unit_test::log::end();
264        break;
265    }
266
267    case CHECK_PRED_WITH_ARGS: {
268        unit_test_log << unit_test::log::begin( file_name, line_num )
269                      << ll << prefix << check_descr;
270
271        // print predicate call description
272        {
273            va_list args;
274            va_start( args, num_of_args );
275
276            unit_test_log << "( ";
277            for( std::size_t i = 0; i < num_of_args; ++i ) {
278                unit_test_log << va_arg( args, char const* );
279                va_arg( args, lazy_ostream const* ); // skip argument value;
280
281                if( i != num_of_args-1 )
282                    unit_test_log << ", ";
283            }
284            unit_test_log << " )" << suffix;
285            va_end( args );
286        }
287
288        if( tl != PASS ) {
289            va_list args;
290            va_start( args, num_of_args );
291
292            unit_test_log << " for ( ";
293            for( std::size_t i = 0; i < num_of_args; ++i ) {
294                va_arg( args, char const* ); // skip argument description;
295                unit_test_log << *va_arg( args, lazy_ostream const* );
296
297                if( i != num_of_args-1 )
298                    unit_test_log << ", ";
299            }
300            unit_test_log << " )";
301            va_end( args );
302        }
303
304        if( !pr.has_empty_message() )
305            unit_test_log << ". " << pr.message();
306
307        unit_test_log << unit_test::log::end();
308        break;
309    }
310
311    case CHECK_EQUAL_COLL: {
312        va_list args;
313
314        va_start( args, num_of_args );
315        char const* left_begin_descr    = va_arg( args, char const* );
316        char const* left_end_descr      = va_arg( args, char const* );
317        char const* right_begin_descr   = va_arg( args, char const* );
318        char const* right_end_descr     = va_arg( args, char const* );
319
320        unit_test_log << unit_test::log::begin( file_name, line_num )
321                      << ll << prefix
322                      << "{ " << left_begin_descr  << ", " << left_end_descr  << " } == { "
323                              << right_begin_descr << ", " << right_end_descr << " }"
324                      << suffix;
325
326        va_end( args );
327
328        if( !pr.has_empty_message() )
329            unit_test_log << ". " << pr.message();
330
331        unit_test_log << unit_test::log::end();
332        break;
333    }
334
335    case CHECK_BITWISE_EQUAL: {
336        va_list args;
337
338        va_start( args, num_of_args );
339        char const* left_descr    = va_arg( args, char const* );
340        char const* right_descr   = va_arg( args, char const* );
341
342        unit_test_log << unit_test::log::begin( file_name, line_num )
343                      << ll << prefix << left_descr  << " =.= " << right_descr << suffix;
344
345        va_end( args );
346
347        if( !pr.has_empty_message() )
348            unit_test_log << ". " << pr.message();
349
350        unit_test_log << unit_test::log::end();
351        break;
352    }
353    }
354
355    switch( tl ) {
356    case PASS:
357        framework::assertion_result( true );
358        return true;
359
360    case WARN:
361        return false;
362
363    case CHECK:
364        framework::assertion_result( false );
365        return false;
366
367    case REQUIRE:
368        framework::assertion_result( false );
369
370        framework::test_unit_aborted( framework::current_test_case() );
371
372        throw execution_aborted();
373    }
374
375    return true;
376}
377
378//____________________________________________________________________________//
379
380predicate_result
381equal_impl( char const* left, char const* right )
382{
383    return (left && right) ? std::strcmp( left, right ) == 0 : (left == right);
384}
385
386//____________________________________________________________________________//
387
388#if !defined( BOOST_NO_CWCHAR )
389
390predicate_result
391equal_impl( wchar_t const* left, wchar_t const* right )
392{
393    return (left && right) ? std::wcscmp( left, right ) == 0 : (left == right);
394}
395
396#endif // !defined( BOOST_NO_CWCHAR )
397
398//____________________________________________________________________________//
399
400bool
401is_defined_impl( const_string symbol_name, const_string symbol_value )
402{
403    symbol_value.trim_left( 2 );
404    return symbol_name != symbol_value;
405}
406
407//____________________________________________________________________________//
408
409} // namespace tt_detail
410
411// ************************************************************************** //
412// **************               output_test_stream             ************** //
413// ************************************************************************** //
414
415struct output_test_stream::Impl
416{
417    std::fstream    m_pattern;
418    bool            m_match_or_save;
419    bool            m_text_or_binary;
420    std::string     m_synced_string;
421
422    char            get_char()
423    {
424        char res;
425        do {
426            m_pattern.get( res );
427        } while( m_text_or_binary && res == '\r' && !m_pattern.fail() && !m_pattern.eof() );
428
429        return res;
430    }
431
432    void            check_and_fill( predicate_result& res )
433    {
434        if( !res.p_predicate_value )
435            res.message() << "Output content: \"" << m_synced_string << '\"';
436    }
437};
438
439//____________________________________________________________________________//
440
441output_test_stream::output_test_stream( const_string pattern_file_name, bool match_or_save, bool text_or_binary )
442: m_pimpl( new Impl )
443{
444    if( !pattern_file_name.is_empty() ) {
445        std::ios::openmode m = match_or_save ? std::ios::in : std::ios::out;
446        if( !text_or_binary )
447            m |= std::ios::binary;
448
449        m_pimpl->m_pattern.open( pattern_file_name.begin(), m );
450
451        BOOST_WARN_MESSAGE( m_pimpl->m_pattern.is_open(),
452                             "Can't open pattern file " << pattern_file_name
453                                << " for " << (match_or_save ? "reading" : "writing") );
454    }
455
456    m_pimpl->m_match_or_save    = match_or_save;
457    m_pimpl->m_text_or_binary   = text_or_binary;
458}
459
460//____________________________________________________________________________//
461
462output_test_stream::~output_test_stream()
463{
464    delete m_pimpl;
465}
466
467//____________________________________________________________________________//
468
469predicate_result
470output_test_stream::is_empty( bool flush_stream )
471{
472    sync();
473
474    result_type res( m_pimpl->m_synced_string.empty() );
475
476    m_pimpl->check_and_fill( res );
477
478    if( flush_stream )
479        flush();
480
481    return res;
482}
483
484//____________________________________________________________________________//
485
486predicate_result
487output_test_stream::check_length( std::size_t length_, bool flush_stream )
488{
489    sync();
490
491    result_type res( m_pimpl->m_synced_string.length() == length_ );
492
493    m_pimpl->check_and_fill( res );
494
495    if( flush_stream )
496        flush();
497
498    return res;
499}
500
501//____________________________________________________________________________//
502
503predicate_result
504output_test_stream::is_equal( const_string arg, bool flush_stream )
505{
506    sync();
507
508    result_type res( const_string( m_pimpl->m_synced_string ) == arg );
509
510    m_pimpl->check_and_fill( res );
511
512    if( flush_stream )
513        flush();
514
515    return res;
516}
517
518//____________________________________________________________________________//
519
520predicate_result
521output_test_stream::match_pattern( bool flush_stream )
522{
523    sync();
524
525    result_type result( true );
526
527    if( !m_pimpl->m_pattern.is_open() ) {
528        result = false;
529        result.message() << "Pattern file can't be opened!";
530    }
531    else {
532        if( m_pimpl->m_match_or_save ) {
533            for ( std::string::size_type i = 0; i < m_pimpl->m_synced_string.length(); ++i ) {
534                char c = m_pimpl->get_char();
535
536                result = !m_pimpl->m_pattern.fail() &&
537                         !m_pimpl->m_pattern.eof()  &&
538                         (m_pimpl->m_synced_string[i] == c);
539
540                if( !result ) {
541                    std::string::size_type suffix_size  = (std::min)( m_pimpl->m_synced_string.length() - i,
542                                                                    static_cast<std::string::size_type>(5) );
543
544                    // try to log area around the mismatch
545                    result.message() << "Mismatch at position " << i << '\n'
546                        << "..." << m_pimpl->m_synced_string.substr( i, suffix_size ) << "..." << '\n'
547                        << "..." << c;
548
549                    std::string::size_type counter = suffix_size;
550                    while( --counter ) {
551                        char c = m_pimpl->get_char();
552
553                        if( m_pimpl->m_pattern.fail() || m_pimpl->m_pattern.eof() )
554                            break;
555
556                        result.message() << c;
557                    }
558
559                    result.message() << "...";
560
561                    // skip rest of the bytes. May help for further matching
562                    m_pimpl->m_pattern.ignore(
563                        static_cast<std::streamsize>( m_pimpl->m_synced_string.length() - i - suffix_size) );
564                    break;
565                }
566            }
567        }
568        else {
569            m_pimpl->m_pattern.write( m_pimpl->m_synced_string.c_str(),
570                                      static_cast<std::streamsize>( m_pimpl->m_synced_string.length() ) );
571            m_pimpl->m_pattern.flush();
572        }
573    }
574
575    if( flush_stream )
576        flush();
577
578    return result;
579}
580
581//____________________________________________________________________________//
582
583void
584output_test_stream::flush()
585{
586    m_pimpl->m_synced_string.erase();
587
588#ifndef BOOST_NO_STRINGSTREAM
589    str( std::string() );
590#else
591    seekp( 0, std::ios::beg );
592#endif
593}
594
595//____________________________________________________________________________//
596
597std::size_t
598output_test_stream::length()
599{
600    sync();
601
602    return m_pimpl->m_synced_string.length();
603}
604
605//____________________________________________________________________________//
606
607void
608output_test_stream::sync()
609{
610#ifdef BOOST_NO_STRINGSTREAM
611    m_pimpl->m_synced_string.assign( str(), pcount() );
612    freeze( false );
613#else
614    m_pimpl->m_synced_string = str();
615#endif
616}
617
618//____________________________________________________________________________//
619
620} // namespace test_tools
621
622} // namespace boost
623
624//____________________________________________________________________________//
625
626#include <boost/test/detail/enable_warnings.hpp>
627
628#endif // BOOST_TEST_TEST_TOOLS_IPP_012205GER
629