Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/boost/test/impl/exception_safety.ipp @ 29

Last change on this file since 29 was 29, checked in by landauf, 16 years ago

updated boost from 1_33_1 to 1_34_1

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