exception_safety.ipp 17 KB

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