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