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// 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 BOOST_TEST_I_ASSRT( framework::current_test_case_id() != INV_TEST_UNIT_ID,
298 std::runtime_error( "Can't use testing tools outside of test case implementation." ) );
299
300 if( !!ar )
301 tl = PASS;
302
303 log_level ll;
304 char const* prefix;
305 char const* suffix;
306
307 switch( tl ) {
308 case PASS:
309 ll = log_successful_tests;
310 prefix = "check ";
311 suffix = " has passed";
312 break;
313 case WARN:
314 ll = log_warnings;
315 prefix = "condition ";
316 suffix = " is not satisfied";
317 break;
318 case CHECK:
319 ll = log_all_errors;
320 prefix = "check ";
321 suffix = " has failed";
322 break;
323 case REQUIRE:
324 ll = log_fatal_errors;
325 prefix = "critical check ";
326 suffix = " has failed";
327 break;
328 default:
329 return true;
330 }
331
332 unit_test_log << unit_test::log::begin( file_name, line_num ) << ll;
333 va_list args;
334 va_start( args, num_args );
335
336 format_report( unit_test_log, ar, assertion_descr, tl, ct, num_args, args, prefix, suffix );
337
338 va_end( args );
339 unit_test_log << unit_test::log::end();
340
341 switch( tl ) {
342 case PASS:
343 framework::assertion_result( AR_PASSED );
344 return true;
345
346 case WARN:
347 framework::assertion_result( AR_TRIGGERED );
348 return false;
349
350 case CHECK:
351 framework::assertion_result( AR_FAILED );
352 return false;
353
354 case REQUIRE:
355 framework::assertion_result( AR_FAILED );
356
357 framework::test_unit_aborted( framework::current_test_case() );
358
359 BOOST_TEST_I_THROW( execution_aborted() );
360 }
361
362 return true;
363}
364
365//____________________________________________________________________________//
366
367assertion_result
368format_assertion_result( const_string expr_val, const_string details )
369{
370 assertion_result res(false);
371
372 bool starts_new_line = first_char( expr_val ) == '\n';
373
374 if( !starts_new_line && !expr_val.is_empty() )
375 res.message().stream() << " [" << expr_val << "]";
376
377 if( !details.is_empty() ) {
378 if( first_char(details) != '[' )
379 res.message().stream() << ". ";
380 else
381 res.message().stream() << " ";
382
383 res.message().stream() << details;
384 }
385
386 if( starts_new_line )
387 res.message().stream() << "." << expr_val;
388
389 return res;
390}
391
392//____________________________________________________________________________//
393
394BOOST_TEST_DECL std::string
395prod_report_format( assertion_result const& ar, unit_test::lazy_ostream const& assertion_descr, check_type ct, std::size_t num_args, ... )
396{
397 std::ostringstream msg_buff;
398
399 va_list args;
400 va_start( args, num_args );
401
402 format_report( msg_buff, ar, assertion_descr, CHECK, ct, num_args, args, "assertion ", " failed" );
403
404 va_end( args );
405
406 return msg_buff.str();
407}
408
409//____________________________________________________________________________//
410
411assertion_result
412equal_impl( char const* left, char const* right )
413{
414 return (left && right) ? std::strcmp( left, right ) == 0 : (left == right);
415}
416
417//____________________________________________________________________________//
418
419#if !defined( BOOST_NO_CWCHAR )
420
421assertion_result
422equal_impl( wchar_t const* left, wchar_t const* right )
423{
424 return (left && right) ? std::wcscmp( left, right ) == 0 : (left == right);
425}
426
427#endif // !defined( BOOST_NO_CWCHAR )
428
429//____________________________________________________________________________//
430
431bool
432is_defined_impl( const_string symbol_name, const_string symbol_value )
433{
434 symbol_value.trim_left( 2 );
435 return symbol_name != symbol_value;
436}
437
438//____________________________________________________________________________//
439
440// ************************************************************************** //
441// ************** context_frame ************** //
442// ************************************************************************** //
443
444context_frame::context_frame( ::boost::unit_test::lazy_ostream const& context_descr )
445: m_frame_id( unit_test::framework::add_context( context_descr, true ) )
446{
447}
448
449//____________________________________________________________________________//
450
451context_frame::~context_frame()
452{
453 unit_test::framework::clear_context( m_frame_id );
454}
455
456//____________________________________________________________________________//
457
458context_frame::operator bool()
459{
460 return true;
461}
462
463//____________________________________________________________________________//
464
465} // namespace tt_detail
466
467// ************************************************************************** //
468// ************** output_test_stream ************** //
469// ************************************************************************** //
470
471struct output_test_stream::Impl
472{
473 std::fstream m_pattern;
474 bool m_match_or_save;
475 bool m_text_or_binary;
476 std::string m_synced_string;
477
478 char get_char()
479 {
480 char res;
481 do {
482 m_pattern.get( res );
483 } while( m_text_or_binary && res == '\r' && !m_pattern.fail() && !m_pattern.eof() );
484
485 return res;
486 }
487
488 void check_and_fill( assertion_result& res )
489 {
490 if( !res.p_predicate_value )
491 res.message() << "Output content: \"" << m_synced_string << '\"';
492 }
493};
494
495//____________________________________________________________________________//
496
497output_test_stream::output_test_stream( const_string pattern_file_name, bool match_or_save, bool text_or_binary )
498: m_pimpl( new Impl )
499{
500 if( !pattern_file_name.is_empty() ) {
501 std::ios::openmode m = match_or_save ? std::ios::in : std::ios::out;
502 if( !text_or_binary )
503 m |= std::ios::binary;
504
505 m_pimpl->m_pattern.open( pattern_file_name.begin(), m );
506
507 if( !m_pimpl->m_pattern.is_open() )
508 BOOST_TEST_MESSAGE( "Can't open pattern file " << pattern_file_name << " for " << (match_or_save ? "reading" : "writing") );
509 }
510
511 m_pimpl->m_match_or_save = match_or_save;
512 m_pimpl->m_text_or_binary = text_or_binary;
513}
514
515//____________________________________________________________________________//
516
517output_test_stream::~output_test_stream()
518{
519 delete m_pimpl;
520}
521
522//____________________________________________________________________________//
523
524assertion_result
525output_test_stream::is_empty( bool flush_stream )
526{
527 sync();
528
529 assertion_result res( m_pimpl->m_synced_string.empty() );
530
531 m_pimpl->check_and_fill( res );
532
533 if( flush_stream )
534 flush();
535
536 return res;
537}
538
539//____________________________________________________________________________//
540
541assertion_result
542output_test_stream::check_length( std::size_t length_, bool flush_stream )
543{
544 sync();
545
546 assertion_result res( m_pimpl->m_synced_string.length() == length_ );
547
548 m_pimpl->check_and_fill( res );
549
550 if( flush_stream )
551 flush();
552
553 return res;
554}
555
556//____________________________________________________________________________//
557
558assertion_result
559output_test_stream::is_equal( const_string arg, bool flush_stream )
560{
561 sync();
562
563 assertion_result res( const_string( m_pimpl->m_synced_string ) == arg );
564
565 m_pimpl->check_and_fill( res );
566
567 if( flush_stream )
568 flush();
569
570 return res;
571}
572
573//____________________________________________________________________________//
574
575assertion_result
576output_test_stream::match_pattern( bool flush_stream )
577{
578 sync();
579
580 assertion_result result( true );
581
582 if( !m_pimpl->m_pattern.is_open() ) {
583 result = false;
584 result.message() << "Pattern file can't be opened!";
585 }
586 else {
587 if( m_pimpl->m_match_or_save ) {
588 for ( std::string::size_type i = 0; i < m_pimpl->m_synced_string.length(); ++i ) {
589 char c = m_pimpl->get_char();
590
591 result = !m_pimpl->m_pattern.fail() &&
592 !m_pimpl->m_pattern.eof() &&
593 (m_pimpl->m_synced_string[i] == c);
594
595 if( !result ) {
596 std::string::size_type suffix_size = (std::min)( m_pimpl->m_synced_string.length() - i,
597 static_cast<std::string::size_type>(5) );
598
599 // try to log area around the mismatch
600 result.message() << "Mismatch at position " << i << '\n'
601 << "..." << m_pimpl->m_synced_string.substr( i, suffix_size ) << "..." << '\n'
602 << "..." << c;
603
604 std::string::size_type counter = suffix_size;
605 while( --counter ) {
606 char c2 = m_pimpl->get_char();
607
608 if( m_pimpl->m_pattern.fail() || m_pimpl->m_pattern.eof() )
609 break;
610
611 result.message() << c2;
612 }
613
614 result.message() << "...";
615
616 // skip rest of the bytes. May help for further matching
617 m_pimpl->m_pattern.ignore(
618 static_cast<std::streamsize>( m_pimpl->m_synced_string.length() - i - suffix_size) );
619 break;
620 }
621 }
622 }
623 else {
624 m_pimpl->m_pattern.write( m_pimpl->m_synced_string.c_str(),
625 static_cast<std::streamsize>( m_pimpl->m_synced_string.length() ) );
626 m_pimpl->m_pattern.flush();
627 }
628 }
629
630 if( flush_stream )
631 flush();
632
633 return result;
634}
635
636//____________________________________________________________________________//
637
638void
639output_test_stream::flush()
640{
641 m_pimpl->m_synced_string.erase();
642
643#ifndef BOOST_NO_STRINGSTREAM
644 str( std::string() );
645#else
646 seekp( 0, std::ios::beg );
647#endif
648}
649
650//____________________________________________________________________________//
651
652std::size_t
653output_test_stream::length()
654{
655 sync();
656
657 return m_pimpl->m_synced_string.length();
658}
659
660//____________________________________________________________________________//
661
662void
663output_test_stream::sync()
664{
665#ifdef BOOST_NO_STRINGSTREAM
666 m_pimpl->m_synced_string.assign( str(), pcount() );
667 freeze( false );
668#else
669 m_pimpl->m_synced_string = str();
670#endif
671}
672
673//____________________________________________________________________________//
674
675} // namespace test_tools
676} // namespace boost
677
678#include <boost/test/detail/enable_warnings.hpp>
679
680#endif // BOOST_TEST_TEST_TOOLS_IPP_012205GER
681