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