annotate DEPENDENCIES/generic/include/boost/test/impl/exception_safety.ipp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents c530137014c0
children
rev   line source
Chris@16 1 // (C) Copyright Gennadiy Rozental 2005-2008.
Chris@16 2 // Use, modification, and distribution are subject to the
Chris@16 3 // Boost Software License, Version 1.0. (See accompanying file
Chris@16 4 // http://www.boost.org/LICENSE_1_0.txt)
Chris@16 5
Chris@16 6 // See http://www.boost.org/libs/test for the library home page.
Chris@16 7 //
Chris@16 8 // File : $RCSfile$
Chris@16 9 //
Chris@101 10 // Version : $Revision$
Chris@16 11 //
Chris@16 12 // Description : Facilities to perform exception safety tests
Chris@16 13 // ***************************************************************************
Chris@16 14
Chris@16 15 #ifndef BOOST_TEST_EXECUTION_SAFETY_IPP_112005GER
Chris@16 16 #define BOOST_TEST_EXECUTION_SAFETY_IPP_112005GER
Chris@16 17
Chris@16 18 // Boost.Test
Chris@16 19 #include <boost/test/detail/config.hpp>
Chris@16 20
Chris@16 21 #if BOOST_TEST_SUPPORT_INTERACTION_TESTING
Chris@16 22
Chris@16 23 #include <boost/test/detail/global_typedef.hpp>
Chris@16 24 #include <boost/test/detail/unit_test_parameters.hpp>
Chris@16 25
Chris@16 26 #include <boost/test/utils/callback.hpp>
Chris@16 27 #include <boost/test/utils/wrap_stringstream.hpp>
Chris@16 28 #include <boost/test/utils/iterator/token_iterator.hpp>
Chris@16 29
Chris@16 30 #include <boost/test/interaction_based.hpp>
Chris@16 31 #include <boost/test/test_tools.hpp>
Chris@16 32 #include <boost/test/unit_test_log.hpp>
Chris@16 33 #include <boost/test/framework.hpp>
Chris@16 34 #include <boost/test/test_observer.hpp>
Chris@16 35 #include <boost/test/debug.hpp>
Chris@16 36
Chris@16 37 #include <boost/test/detail/suppress_warnings.hpp>
Chris@16 38
Chris@16 39 // Boost
Chris@16 40 #include <boost/lexical_cast.hpp>
Chris@16 41
Chris@16 42 // STL
Chris@16 43 #include <vector>
Chris@16 44 #include <cstdlib>
Chris@16 45 #include <map>
Chris@16 46 #include <iomanip>
Chris@16 47 #include <cctype>
Chris@16 48 #include <boost/limits.hpp>
Chris@16 49
Chris@16 50 //____________________________________________________________________________//
Chris@16 51
Chris@16 52 namespace boost {
Chris@16 53
Chris@16 54 using namespace ::boost::unit_test;
Chris@16 55
Chris@16 56 namespace itest {
Chris@16 57
Chris@16 58 // ************************************************************************** //
Chris@16 59 // ************** execution_path_point ************** //
Chris@16 60 // ************************************************************************** //
Chris@16 61
Chris@16 62 enum exec_path_point_type { EPP_SCOPE, EPP_EXCEPT, EPP_DECISION, EPP_ALLOC };
Chris@16 63
Chris@16 64 struct execution_path_point {
Chris@16 65 execution_path_point( exec_path_point_type t, const_string file, std::size_t line_num )
Chris@16 66 : m_type( t )
Chris@16 67 , m_file_name( file )
Chris@16 68 , m_line_num( line_num )
Chris@16 69 {}
Chris@16 70
Chris@16 71 exec_path_point_type m_type;
Chris@16 72 const_string m_file_name;
Chris@16 73 std::size_t m_line_num;
Chris@16 74
Chris@16 75 // Execution path point specific
Chris@16 76 struct decision_data {
Chris@16 77 bool value;
Chris@16 78 unsigned forced_exception_point;
Chris@16 79 };
Chris@16 80 struct scope_data {
Chris@16 81 unsigned size;
Chris@16 82 char const* name;
Chris@16 83 };
Chris@16 84 struct except_data {
Chris@16 85 char const* description;
Chris@16 86 };
Chris@16 87 struct alloc_data {
Chris@16 88 void* ptr;
Chris@16 89 std::size_t size;
Chris@16 90 };
Chris@16 91
Chris@16 92 union {
Chris@16 93 struct decision_data m_decision;
Chris@16 94 struct scope_data m_scope;
Chris@16 95 struct except_data m_except;
Chris@16 96 struct alloc_data m_alloc;
Chris@16 97 };
Chris@16 98 };
Chris@16 99
Chris@16 100 // ************************************************************************** //
Chris@16 101 // ************** exception safety test implementation ************** //
Chris@16 102 // ************************************************************************** //
Chris@16 103
Chris@16 104 struct exception_safety_tester : itest::manager, test_observer {
Chris@16 105 // helpers types
Chris@16 106 struct unique_exception {};
Chris@16 107
Chris@16 108 // Constructor
Chris@16 109 explicit exception_safety_tester( const_string test_name );
Chris@16 110 ~exception_safety_tester();
Chris@16 111
Chris@16 112 // check last run and prepare for next
Chris@16 113 bool next_execution_path();
Chris@16 114
Chris@16 115 // memory tracking
Chris@16 116
Chris@16 117 // manager interface implementation
Chris@16 118 virtual void exception_point( const_string file, std::size_t line_num, const_string description );
Chris@16 119 virtual bool decision_point( const_string file, std::size_t line_num );
Chris@16 120 virtual unsigned enter_scope( const_string file, std::size_t line_num, const_string scope_name );
Chris@16 121 virtual void leave_scope( unsigned enter_scope_point );
Chris@16 122 virtual void allocated( const_string file, std::size_t line_num, void* p, std::size_t s );
Chris@16 123 virtual void freed( void* p );
Chris@16 124
Chris@16 125 // test observer interface
Chris@16 126 virtual void assertion_result( bool passed );
Chris@16 127 virtual int priority() { return (std::numeric_limits<int>::max)(); } // we want this observer to run the last
Chris@16 128
Chris@16 129 private:
Chris@16 130 void failure_point();
Chris@16 131 void report_error();
Chris@16 132
Chris@16 133 typedef std::vector<execution_path_point> exec_path;
Chris@16 134 typedef std::map<void*,unsigned> registry;
Chris@16 135
Chris@16 136 // Data members
Chris@16 137 bool m_internal_activity;
Chris@16 138
Chris@16 139 unsigned m_exception_point_counter;
Chris@16 140 unsigned m_forced_exception_point;
Chris@16 141
Chris@16 142 unsigned m_exec_path_point;
Chris@16 143 exec_path m_execution_path;
Chris@16 144
Chris@16 145 unsigned m_exec_path_counter;
Chris@16 146 unsigned m_break_exec_path;
Chris@16 147
Chris@16 148 bool m_invairant_failed;
Chris@16 149 registry m_memory_in_use;
Chris@16 150 };
Chris@16 151
Chris@16 152 //____________________________________________________________________________//
Chris@16 153
Chris@16 154 struct activity_guard {
Chris@16 155 bool& m_v;
Chris@16 156
Chris@16 157 activity_guard( bool& v ) : m_v( v ) { m_v = true; }
Chris@16 158 ~activity_guard() { m_v = false; }
Chris@16 159 };
Chris@16 160
Chris@16 161 //____________________________________________________________________________//
Chris@16 162
Chris@16 163 exception_safety_tester::exception_safety_tester( const_string test_name )
Chris@16 164 : m_internal_activity( true )
Chris@16 165 , m_exception_point_counter( 0 )
Chris@16 166 , m_forced_exception_point( 1 )
Chris@16 167 , m_exec_path_point( 0 )
Chris@16 168 , m_exec_path_counter( 1 )
Chris@16 169 , m_break_exec_path( static_cast<unsigned>(-1) )
Chris@16 170 , m_invairant_failed( false )
Chris@16 171 {
Chris@16 172 framework::register_observer( *this );
Chris@16 173
Chris@16 174 if( !runtime_config::break_exec_path().is_empty() ) {
Chris@16 175 using namespace unit_test;
Chris@16 176
Chris@16 177 string_token_iterator tit( runtime_config::break_exec_path(),
Chris@16 178 (dropped_delimeters = ":",kept_delimeters = " ") );
Chris@16 179
Chris@16 180 const_string test_to_break = *tit;
Chris@16 181
Chris@16 182 if( test_to_break == test_name ) {
Chris@16 183 ++tit;
Chris@16 184
Chris@16 185 m_break_exec_path = lexical_cast<unsigned>( *tit );
Chris@16 186 }
Chris@16 187 }
Chris@16 188
Chris@16 189 m_internal_activity = false;
Chris@16 190 }
Chris@16 191
Chris@16 192 //____________________________________________________________________________//
Chris@16 193
Chris@16 194 exception_safety_tester::~exception_safety_tester()
Chris@16 195 {
Chris@16 196 m_internal_activity = true;
Chris@16 197
Chris@16 198 framework::deregister_observer( *this );
Chris@16 199 }
Chris@16 200
Chris@16 201 //____________________________________________________________________________//
Chris@16 202
Chris@16 203 bool
Chris@16 204 exception_safety_tester::next_execution_path()
Chris@16 205 {
Chris@16 206 activity_guard ag( m_internal_activity );
Chris@16 207
Chris@16 208 // check memory usage
Chris@16 209 if( m_execution_path.size() > 0 ) {
Chris@16 210 bool errors_detected = m_invairant_failed || (m_memory_in_use.size() != 0);
Chris@16 211 framework::assertion_result( !errors_detected );
Chris@16 212
Chris@16 213 if( errors_detected )
Chris@16 214 report_error();
Chris@16 215
Chris@16 216 m_memory_in_use.clear();
Chris@16 217 }
Chris@16 218
Chris@16 219 m_exec_path_point = 0;
Chris@16 220 m_exception_point_counter = 0;
Chris@16 221 m_invairant_failed = false;
Chris@16 222 ++m_exec_path_counter;
Chris@16 223
Chris@16 224 while( m_execution_path.size() > 0 ) {
Chris@16 225 switch( m_execution_path.back().m_type ) {
Chris@16 226 case EPP_SCOPE:
Chris@16 227 case EPP_ALLOC:
Chris@16 228 m_execution_path.pop_back();
Chris@16 229 break;
Chris@16 230
Chris@16 231 case EPP_DECISION:
Chris@16 232 if( !m_execution_path.back().m_decision.value ) {
Chris@16 233 m_execution_path.pop_back();
Chris@16 234 break;
Chris@16 235 }
Chris@16 236
Chris@16 237 m_execution_path.back().m_decision.value = false;
Chris@16 238 m_forced_exception_point = m_execution_path.back().m_decision.forced_exception_point;
Chris@16 239 return true;
Chris@16 240
Chris@16 241 case EPP_EXCEPT:
Chris@16 242 m_execution_path.pop_back();
Chris@16 243 ++m_forced_exception_point;
Chris@16 244 return true;
Chris@16 245 }
Chris@16 246 }
Chris@16 247
Chris@16 248 BOOST_TEST_MESSAGE( "Total tested " << --m_exec_path_counter << " execution path" );
Chris@16 249
Chris@16 250 return false;
Chris@16 251 }
Chris@16 252
Chris@16 253 //____________________________________________________________________________//
Chris@16 254
Chris@16 255 void
Chris@16 256 exception_safety_tester::exception_point( const_string file, std::size_t line_num, const_string description )
Chris@16 257 {
Chris@16 258 activity_guard ag( m_internal_activity );
Chris@16 259
Chris@16 260 if( ++m_exception_point_counter == m_forced_exception_point ) {
Chris@16 261 m_execution_path.push_back(
Chris@16 262 execution_path_point( EPP_EXCEPT, file, line_num ) );
Chris@16 263
Chris@16 264 m_execution_path.back().m_except.description = description.begin();
Chris@16 265
Chris@16 266 ++m_exec_path_point;
Chris@16 267
Chris@16 268 failure_point();
Chris@16 269 }
Chris@16 270 }
Chris@16 271
Chris@16 272 //____________________________________________________________________________//
Chris@16 273
Chris@16 274 bool
Chris@16 275 exception_safety_tester::decision_point( const_string file, std::size_t line_num )
Chris@16 276 {
Chris@16 277 activity_guard ag( m_internal_activity );
Chris@16 278
Chris@16 279 if( m_exec_path_point < m_execution_path.size() ) {
Chris@16 280 BOOST_REQUIRE_MESSAGE( m_execution_path[m_exec_path_point].m_type == EPP_DECISION &&
Chris@16 281 m_execution_path[m_exec_path_point].m_file_name == file &&
Chris@16 282 m_execution_path[m_exec_path_point].m_line_num == line_num,
Chris@16 283 "Function under test exibit non-deterministic behavior" );
Chris@16 284 }
Chris@16 285 else {
Chris@16 286 m_execution_path.push_back(
Chris@16 287 execution_path_point( EPP_DECISION, file, line_num ) );
Chris@16 288
Chris@16 289 m_execution_path.back().m_decision.value = true;
Chris@16 290 m_execution_path.back().m_decision.forced_exception_point = m_forced_exception_point;
Chris@16 291 }
Chris@16 292
Chris@16 293 return m_execution_path[m_exec_path_point++].m_decision.value;
Chris@16 294 }
Chris@16 295
Chris@16 296 //____________________________________________________________________________//
Chris@16 297
Chris@16 298 unsigned
Chris@16 299 exception_safety_tester::enter_scope( const_string file, std::size_t line_num, const_string scope_name )
Chris@16 300 {
Chris@16 301 activity_guard ag( m_internal_activity );
Chris@16 302
Chris@16 303 if( m_exec_path_point < m_execution_path.size() ) {
Chris@16 304 BOOST_REQUIRE_MESSAGE( m_execution_path[m_exec_path_point].m_type == EPP_SCOPE &&
Chris@16 305 m_execution_path[m_exec_path_point].m_file_name == file &&
Chris@16 306 m_execution_path[m_exec_path_point].m_line_num == line_num,
Chris@16 307 "Function under test exibit non-deterministic behavior" );
Chris@16 308 }
Chris@16 309 else {
Chris@16 310 m_execution_path.push_back(
Chris@16 311 execution_path_point( EPP_SCOPE, file, line_num ) );
Chris@16 312 }
Chris@16 313
Chris@16 314 m_execution_path[m_exec_path_point].m_scope.size = 0;
Chris@16 315 m_execution_path[m_exec_path_point].m_scope.name = scope_name.begin();
Chris@16 316
Chris@16 317 return m_exec_path_point++;
Chris@16 318 }
Chris@16 319
Chris@16 320 //____________________________________________________________________________//
Chris@16 321
Chris@16 322 void
Chris@16 323 exception_safety_tester::leave_scope( unsigned enter_scope_point )
Chris@16 324 {
Chris@16 325 activity_guard ag( m_internal_activity );
Chris@16 326
Chris@16 327 BOOST_REQUIRE_MESSAGE( m_execution_path[enter_scope_point].m_type == EPP_SCOPE,
Chris@16 328 "Function under test exibit non-deterministic behavior" );
Chris@16 329
Chris@16 330 m_execution_path[enter_scope_point].m_scope.size = m_exec_path_point - enter_scope_point;
Chris@16 331 }
Chris@16 332
Chris@16 333 //____________________________________________________________________________//
Chris@16 334
Chris@16 335 void
Chris@16 336 exception_safety_tester::allocated( const_string file, std::size_t line_num, void* p, std::size_t s )
Chris@16 337 {
Chris@16 338 if( m_internal_activity )
Chris@16 339 return;
Chris@16 340
Chris@16 341 activity_guard ag( m_internal_activity );
Chris@16 342
Chris@16 343 if( m_exec_path_point < m_execution_path.size() )
Chris@16 344 BOOST_REQUIRE_MESSAGE( m_execution_path[m_exec_path_point].m_type == EPP_ALLOC,
Chris@16 345 "Function under test exibit non-deterministic behavior" );
Chris@16 346 else
Chris@16 347 m_execution_path.push_back(
Chris@16 348 execution_path_point( EPP_ALLOC, file, line_num ) );
Chris@16 349
Chris@16 350 m_execution_path[m_exec_path_point].m_alloc.ptr = p;
Chris@16 351 m_execution_path[m_exec_path_point].m_alloc.size = s;
Chris@16 352
Chris@16 353 m_memory_in_use.insert( std::make_pair( p, m_exec_path_point++ ) );
Chris@16 354 }
Chris@16 355
Chris@16 356 //____________________________________________________________________________//
Chris@16 357
Chris@16 358 void
Chris@16 359 exception_safety_tester::freed( void* p )
Chris@16 360 {
Chris@16 361 if( m_internal_activity )
Chris@16 362 return;
Chris@16 363
Chris@16 364 activity_guard ag( m_internal_activity );
Chris@16 365
Chris@16 366 registry::iterator it = m_memory_in_use.find( p );
Chris@16 367 if( it != m_memory_in_use.end() ) {
Chris@16 368 m_execution_path[it->second].m_alloc.ptr = 0;
Chris@16 369 m_memory_in_use.erase( it );
Chris@16 370 }
Chris@16 371 }
Chris@16 372
Chris@16 373 //____________________________________________________________________________//
Chris@16 374
Chris@16 375 void
Chris@16 376 exception_safety_tester::assertion_result( bool passed )
Chris@16 377 {
Chris@16 378 if( !m_internal_activity && !passed ) {
Chris@16 379 m_invairant_failed = true;
Chris@16 380
Chris@16 381 failure_point();
Chris@16 382 }
Chris@16 383 }
Chris@16 384
Chris@16 385 //____________________________________________________________________________//
Chris@16 386
Chris@16 387 void
Chris@16 388 exception_safety_tester::failure_point()
Chris@16 389 {
Chris@16 390 if( m_exec_path_counter == m_break_exec_path )
Chris@16 391 debug::debugger_break();
Chris@16 392
Chris@16 393 throw unique_exception();
Chris@16 394 }
Chris@16 395
Chris@16 396 //____________________________________________________________________________//
Chris@16 397
Chris@16 398 namespace {
Chris@16 399
Chris@16 400 inline void
Chris@16 401 format_location( wrap_stringstream& formatter, execution_path_point const& /*p*/, unsigned indent )
Chris@16 402 {
Chris@16 403 if( indent )
Chris@16 404 formatter << std::left << std::setw( indent ) << "";
Chris@16 405
Chris@16 406 // !! ?? optional if( p.m_file_name )
Chris@16 407 // formatter << p.m_file_name << '(' << p.m_line_num << "): ";
Chris@16 408 }
Chris@16 409
Chris@16 410 //____________________________________________________________________________//
Chris@16 411
Chris@16 412 template<typename ExecPathIt>
Chris@16 413 inline void
Chris@16 414 format_execution_path( wrap_stringstream& formatter, ExecPathIt it, ExecPathIt end, unsigned indent = 0 )
Chris@16 415 {
Chris@16 416 while( it != end ) {
Chris@16 417 switch( it->m_type ) {
Chris@16 418 case EPP_SCOPE:
Chris@16 419 format_location( formatter, *it, indent );
Chris@16 420 formatter << "> \"" << it->m_scope.name << "\"\n";
Chris@16 421 format_execution_path( formatter, it+1, it + it->m_scope.size, indent + 2 );
Chris@16 422 format_location( formatter, *it, indent );
Chris@16 423 formatter << "< \"" << it->m_scope.name << "\"\n";
Chris@16 424 it += it->m_scope.size;
Chris@16 425 break;
Chris@16 426
Chris@16 427 case EPP_DECISION:
Chris@16 428 format_location( formatter, *it, indent );
Chris@16 429 formatter << "Decision made as " << std::boolalpha << it->m_decision.value << '\n';
Chris@16 430 ++it;
Chris@16 431 break;
Chris@16 432
Chris@16 433 case EPP_EXCEPT:
Chris@16 434 format_location( formatter, *it, indent );
Chris@16 435 formatter << "Forced failure";
Chris@16 436 if( it->m_except.description )
Chris@16 437 formatter << ": " << it->m_except.description;
Chris@16 438 formatter << "\n";
Chris@16 439 ++it;
Chris@16 440 break;
Chris@16 441
Chris@16 442 case EPP_ALLOC:
Chris@16 443 if( it->m_alloc.ptr ) {
Chris@16 444 format_location( formatter, *it, indent );
Chris@16 445 formatter << "Allocated memory block 0x" << std::uppercase << it->m_alloc.ptr
Chris@16 446 << ", " << it->m_alloc.size << " bytes long: <";
Chris@16 447
Chris@16 448 unsigned i;
Chris@16 449 for( i = 0; i < std::min<std::size_t>( it->m_alloc.size, 8 ); i++ ) {
Chris@16 450 unsigned char c = static_cast<unsigned char*>(it->m_alloc.ptr)[i];
Chris@16 451 if( (std::isprint)( c ) )
Chris@16 452 formatter << c;
Chris@16 453 else
Chris@16 454 formatter << '.';
Chris@16 455 }
Chris@16 456
Chris@16 457 formatter << "> ";
Chris@16 458
Chris@16 459 for( i = 0; i < std::min<std::size_t>( it->m_alloc.size, 8 ); i++ ) {
Chris@16 460 unsigned c = static_cast<unsigned char*>(it->m_alloc.ptr)[i];
Chris@16 461 formatter << std::hex << std::uppercase << c << ' ';
Chris@16 462 }
Chris@16 463
Chris@16 464 formatter << "\n";
Chris@16 465 }
Chris@16 466 ++it;
Chris@16 467 break;
Chris@16 468 }
Chris@16 469 }
Chris@16 470 }
Chris@16 471
Chris@16 472 //____________________________________________________________________________//
Chris@16 473
Chris@16 474 } // local namespace
Chris@16 475
Chris@16 476 void
Chris@16 477 exception_safety_tester::report_error()
Chris@16 478 {
Chris@16 479 activity_guard ag( m_internal_activity );
Chris@16 480
Chris@16 481 unit_test_log << unit_test::log::begin( m_execution_path.back().m_file_name,
Chris@16 482 m_execution_path.back().m_line_num )
Chris@16 483 << log_all_errors;
Chris@16 484
Chris@16 485 wrap_stringstream formatter;
Chris@16 486
Chris@16 487 if( m_invairant_failed )
Chris@16 488 formatter << "Failed invariant";
Chris@16 489
Chris@16 490 if( m_memory_in_use.size() != 0 ) {
Chris@16 491 if( m_invairant_failed )
Chris@16 492 formatter << " and ";
Chris@16 493
Chris@16 494 formatter << static_cast<unsigned int>(m_memory_in_use.size()) << " memory leak";
Chris@16 495 if( m_memory_in_use.size() > 1 )
Chris@16 496 formatter << 's';
Chris@16 497 }
Chris@16 498 formatter << " detected in the execution path " << m_exec_path_counter << ":\n";
Chris@16 499
Chris@16 500 format_execution_path( formatter, m_execution_path.begin(), m_execution_path.end() );
Chris@16 501
Chris@16 502 unit_test_log << const_string( formatter.str() ) << unit_test::log::end();
Chris@16 503 }
Chris@16 504
Chris@16 505 //____________________________________________________________________________//
Chris@16 506
Chris@16 507 // ************************************************************************** //
Chris@16 508 // ************** exception safety test ************** //
Chris@16 509 // ************************************************************************** //
Chris@16 510
Chris@16 511 void BOOST_TEST_DECL
Chris@16 512 exception_safety( callback0<> const& F, const_string test_name )
Chris@16 513 {
Chris@16 514 exception_safety_tester est( test_name );
Chris@16 515
Chris@16 516 do {
Chris@16 517 try {
Chris@16 518 F();
Chris@16 519 }
Chris@16 520 catch( exception_safety_tester::unique_exception const& ) {}
Chris@16 521
Chris@16 522 } while( est.next_execution_path() );
Chris@16 523 }
Chris@16 524
Chris@16 525 //____________________________________________________________________________//
Chris@16 526
Chris@16 527 } // namespace itest
Chris@16 528
Chris@16 529 } // namespace boost
Chris@16 530
Chris@16 531 //____________________________________________________________________________//
Chris@16 532
Chris@16 533 #include <boost/test/detail/enable_warnings.hpp>
Chris@16 534
Chris@16 535 #endif // non-ancient compiler
Chris@16 536
Chris@16 537 #endif // BOOST_TEST_EXECUTION_SAFETY_IPP_112005GER