blob: 1c363851aef9cefdbf77886b0354d57002e555ea [file] [log] [blame]
Jeff Thompsonef2d5a42013-08-22 19:09:24 -07001// (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
Jeff Thompson3d613fd2013-10-15 15:39:04 -070015#ifndef NDNBOOST_TEST_EXECUTION_SAFETY_IPP_112005GER
16#define NDNBOOST_TEST_EXECUTION_SAFETY_IPP_112005GER
Jeff Thompsonef2d5a42013-08-22 19:09:24 -070017
18// Boost.Test
19#include <ndnboost/test/detail/config.hpp>
20
Jeff Thompson3d613fd2013-10-15 15:39:04 -070021#if NDNBOOST_TEST_SUPPORT_INTERACTION_TESTING
Jeff Thompsonef2d5a42013-08-22 19:09:24 -070022
23#include <ndnboost/test/detail/global_typedef.hpp>
24#include <ndnboost/test/detail/unit_test_parameters.hpp>
25
26#include <ndnboost/test/utils/callback.hpp>
27#include <ndnboost/test/utils/wrap_stringstream.hpp>
28#include <ndnboost/test/utils/iterator/token_iterator.hpp>
29
30#include <ndnboost/test/interaction_based.hpp>
31#include <ndnboost/test/test_tools.hpp>
32#include <ndnboost/test/unit_test_log.hpp>
33#include <ndnboost/test/framework.hpp>
34#include <ndnboost/test/test_observer.hpp>
35#include <ndnboost/test/debug.hpp>
36
37#include <ndnboost/test/detail/suppress_warnings.hpp>
38
39// Boost
40#include <ndnboost/lexical_cast.hpp>
41
42// STL
43#include <vector>
44#include <cstdlib>
45#include <map>
46#include <iomanip>
47#include <cctype>
48#include <ndnboost/limits.hpp>
49
50//____________________________________________________________________________//
51
52namespace ndnboost {
53
54using namespace ::ndnboost::unit_test;
55
56namespace itest {
57
58// ************************************************************************** //
59// ************** execution_path_point ************** //
60// ************************************************************************** //
61
62enum exec_path_point_type { EPP_SCOPE, EPP_EXCEPT, EPP_DECISION, EPP_ALLOC };
63
64struct 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
104struct 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
129private:
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
154struct 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
163exception_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
194exception_safety_tester::~exception_safety_tester()
195{
196 m_internal_activity = true;
197
198 framework::deregister_observer( *this );
199}
200
201//____________________________________________________________________________//
202
203bool
204exception_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
Jeff Thompson3d613fd2013-10-15 15:39:04 -0700248 NDNBOOST_TEST_MESSAGE( "Total tested " << --m_exec_path_counter << " execution path" );
Jeff Thompsonef2d5a42013-08-22 19:09:24 -0700249
250 return false;
251}
252
253//____________________________________________________________________________//
254
255void
256exception_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
274bool
275exception_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() ) {
Jeff Thompson3d613fd2013-10-15 15:39:04 -0700280 NDNBOOST_REQUIRE_MESSAGE( m_execution_path[m_exec_path_point].m_type == EPP_DECISION &&
Jeff Thompsonef2d5a42013-08-22 19:09:24 -0700281 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
298unsigned
299exception_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() ) {
Jeff Thompson3d613fd2013-10-15 15:39:04 -0700304 NDNBOOST_REQUIRE_MESSAGE( m_execution_path[m_exec_path_point].m_type == EPP_SCOPE &&
Jeff Thompsonef2d5a42013-08-22 19:09:24 -0700305 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
322void
323exception_safety_tester::leave_scope( unsigned enter_scope_point )
324{
325 activity_guard ag( m_internal_activity );
326
Jeff Thompson3d613fd2013-10-15 15:39:04 -0700327 NDNBOOST_REQUIRE_MESSAGE( m_execution_path[enter_scope_point].m_type == EPP_SCOPE,
Jeff Thompsonef2d5a42013-08-22 19:09:24 -0700328 "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
335void
336exception_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() )
Jeff Thompson3d613fd2013-10-15 15:39:04 -0700344 NDNBOOST_REQUIRE_MESSAGE( m_execution_path[m_exec_path_point].m_type == EPP_ALLOC,
Jeff Thompsonef2d5a42013-08-22 19:09:24 -0700345 "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
358void
359exception_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
375void
376exception_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
387void
388exception_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
398namespace {
399
400inline void
401format_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
412template<typename ExecPathIt>
413inline void
414format_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
476void
477exception_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
Jeff Thompson3d613fd2013-10-15 15:39:04 -0700511void NDNBOOST_TEST_DECL
Jeff Thompsonef2d5a42013-08-22 19:09:24 -0700512exception_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 ndnboost
530
531//____________________________________________________________________________//
532
533#include <ndnboost/test/detail/enable_warnings.hpp>
534
535#endif // non-ancient compiler
536
Jeff Thompson3d613fd2013-10-15 15:39:04 -0700537#endif // NDNBOOST_TEST_EXECUTION_SAFETY_IPP_112005GER