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