예제 #1
0
void exception_loop (int              line /* line number in caller*/,
                     MemberFunction   mfun /* deque member function */,
                     const char      *fcall /* function call string */,
                     int              exceptions /* enabled exceptions */,
                     Deque           &deq /* container to call function on */,
                     const Deque::iterator &it /* iterator into container */,
                     int              n /* number of elements or offset */,
                     const UserClass *x /* pointer to an element or 0 */,
                     const Iterator  &first /* beginning of range */,
                     const Iterator  &last /* end of range to insert */,
                     int             *n_copy /* number of copy ctors */,
                     int             *n_asgn /* number of assignments */)
{
    std::size_t throw_after = 0;

    // get the initial size of the container and its begin() iterator
    // to detect illegal changes after an exception (i.e., violations
    // if the strong exception guarantee)
    const std::size_t           size  = deq.size ();
    const Deque::const_iterator begin = deq.begin ();
    const Deque::const_iterator end   = deq.end ();

#ifdef DEFINE_REPLACEMENT_NEW_AND_DELETE

    rwt_free_store* const pst = rwt_get_free_store (0);

#endif   // DEFINE_REPLACEMENT_NEW_AND_DELETE

    // repeatedly call the specified member function until it returns
    // without throwing an exception
    for ( ; ; ) {

        // detect objects constructed but not destroyed after an exception
        std::size_t x_count = UserClass::count_;

        _RWSTD_ASSERT (n_copy);
        _RWSTD_ASSERT (n_asgn);

        *n_copy = UserClass::n_total_copy_ctor_;
        *n_asgn = UserClass::n_total_op_assign_;

#ifndef _RWSTD_NO_EXCEPTIONS

        // iterate for `n=throw_after' starting at the next call to operator
        // new, forcing each call to throw an exception, until the insertion
        // finally succeeds (i.e, no exception is thrown)

#  ifdef DEFINE_REPLACEMENT_NEW_AND_DELETE

        if (exceptions & NewThrows) {
            *pst->throw_at_calls_ [0] = pst->new_calls_ [0] + throw_after + 1;
        }

#  endif   // DEFINE_REPLACEMENT_NEW_AND_DELETE

        if (exceptions & CopyCtorThrows) {
            UserClass::copy_ctor_throw_count_ =
                UserClass::n_total_copy_ctor_ + throw_after;
        }

        if (exceptions & AssignmentThrows) {
            UserClass::op_assign_throw_count_ =
                UserClass::n_total_op_assign_ + throw_after;
        }

#endif   // _RWSTD_NO_EXCEPTIONS

        _TRY {

            switch (mfun) {
            case Assign_n:
                _RWSTD_ASSERT (x);
                deq.assign (n, *x);
                break;
            case AssignRange:
                deq.assign (first, last);
                break;

            case Erase_1:
                deq.erase (it);
                break;
            case EraseRange: {
                const Deque::iterator erase_end (it + n);
                deq.erase (it, erase_end);
                break;
            }

            case Insert_1:
                _RWSTD_ASSERT (x);
                deq.insert (it, *x);
                break;
            case Insert_n:
                _RWSTD_ASSERT (x);
                deq.insert (it, n, *x);
                break;
            case InsertRange:
                deq.insert (it, first, last);
                break;
            }
        }
        _CATCH (...) {

            // verify that an exception thrown from the member function
            // didn't cause a change in the state of the container

            rw_assert (deq.size () == size, 0, line,
                       "line %d: %s: size unexpectedly changed "
                       "from %zu to %zu after an exception",
                       __LINE__, fcall, size, deq.size ());
            
            rw_assert (deq.begin () == begin, 0, line,
                       "line %d: %s: begin() unexpectedly "
                       "changed after an exception by %td",
                       __LINE__, fcall, deq.begin () - begin);

            rw_assert (deq.end () == end, 0, line,
                       "line %d: %s: end() unexpectedly "
                       "changed after an exception by %td",
                       __LINE__, fcall, deq.end () - end);
            

            // count the number of objects to detect leaks
            x_count = UserClass::count_ - x_count;
            rw_assert (x_count == deq.size () - size, 0, line,
                       "line %d: %s: leaked %zu objects after an exception",
                       __LINE__, fcall, x_count - (deq.size () - size));
            
            if (exceptions) {

                // increment to allow this call to operator new to succeed
                // and force the next one to fail, and try to insert again
                ++throw_after;
            }
            else
                break;

            continue;
        }

        // count the number of objects to detect leaks
        x_count = UserClass::count_ - x_count;
        rw_assert (x_count == deq.size () - size, 0, line,
                   "line %d: %s: leaked %zu objects "
                   "after a successful insertion",
                   __LINE__, fcall, x_count - (deq.size () - size));

        break;
    }
예제 #2
0
void test_swap (T*,
                Allocator                      &a1,
                Allocator                      &a2,
                const ContainerTestCaseData<T> &tdata)
{
    typedef std::list <T, Allocator> List;
    typedef ListState<List>          ListState;

    const ContainerTestCase &tcase = tdata.tcase_;
    const ContainerFunc     &func  = tdata.func_;

    // construct the list object to be modified
    // and the argument list
    List src_list (tdata.str_, tdata.str_ + tdata.strlen_, a1);
    List dst_list (tdata.arg_, tdata.arg_ + tdata.arglen_, a2);

    List& arg_list            = tcase.arg ? dst_list : src_list;

    // save the state of the list object before the call
    // to detect exception safety violations (changes to
    // the state of the object after an exception)
    const ListState src_state (src_list);
    const ListState arg_state (arg_list);

    rwt_free_store* const pst = rwt_get_free_store (0);
    SharedAlloc*    const pal = SharedAlloc::instance ();

    // iterate for`throw_count' starting at the next call to operator new,
    // forcing each call to throw an exception, until the function finally
    // succeeds (i.e, no exception is thrown)
    std::size_t throw_count;
    for (throw_count = 0; ; ++throw_count) {

        const char* expected = 0;
        const char* caught = 0;

#ifndef _RWSTD_NO_EXCEPTIONS

        // no exceptions expected
        if (0 == tcase.bthrow && a1 == a2) {
            // by default exercise the exception safety of the function
            // by iteratively inducing an exception at each call to operator
            // new or Allocator::allocate() until the call succeeds
            expected = exceptions [1];      // bad_alloc
            *pst->throw_at_calls_ [0] = pst->new_calls_ [0] + throw_count + 1;
            pal->throw_at_calls_ [pal->m_allocate] =
                pal->throw_at_calls_ [pal->m_allocate] + throw_count + 1;
        }

#else   // if defined (_RWSTD_NO_EXCEPTIONS)

        if (tcase.bthrow)
            return;

#endif   // _RWSTD_NO_EXCEPTIONS

        // start checking for memory leaks
        rw_check_leaks (src_list.get_allocator ());
        rw_check_leaks (arg_list.get_allocator ());

        try {
            const bool is_class = ListIds::UserClass == func.elem_id_;

            // reset function call counters
            if (is_class)
                UserClass::reset_totals ();

            src_list.swap (arg_list);

            if (is_class && a1 == a2) {

                bool success =  0 == (UserClass::n_total_def_ctor_
                                    | UserClass::n_total_copy_ctor_
                                    | UserClass::n_total_op_assign_);

                rw_assert (success, 0, tcase.line, 
                           "line %d. %{$FUNCALL}: complexity: %zu def ctors, "
                           "%zu copy ctors, %zu assigns", __LINE__,
                           UserClass::n_total_def_ctor_,
                           UserClass::n_total_copy_ctor_,
                           UserClass::n_total_op_assign_);
            }

            if (0 == tcase.str) {

                rw_assert (0 == arg_list.size (), 0, tcase.line, 
                           "line %d. %{$FUNCALL}: expected 0 size, "
                           "got %zu",  __LINE__, arg_list.size ());
            }

            if (a1 == a2) {

                src_state.assert_equal (ListState (arg_list),
                    __LINE__, tcase.line, "swap");

                arg_state.assert_equal (ListState (src_list),
                    __LINE__, tcase.line, "swap");
            }
        }

#ifndef _RWSTD_NO_EXCEPTIONS

        catch (const std::bad_alloc &ex) {
            caught = exceptions [1];
            rw_assert (0 == tcase.bthrow, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught std::%s(%#s)",
                       __LINE__, 0 != expected, expected, caught, ex.what ());
        }
        catch (const std::exception &ex) {
            caught = exceptions [2];
            rw_assert (0, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught std::%s(%#s)",
                       __LINE__, 0 != expected, expected, caught, ex.what ());
        }
        catch (...) {
            caught = exceptions [0];
            rw_assert (0, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught %s",
                       __LINE__, 0 != expected, expected, caught);
        }

#endif   // _RWSTD_NO_EXCEPTIONS

        // FIXME: verify the number of blocks the function call
        // is expected to allocate and detect any memory leaks
        rw_check_leaks (src_list.get_allocator (), tcase.line,
                        std::size_t (-1), std::size_t (-1));

        rw_check_leaks (arg_list.get_allocator (), tcase.line,
                        std::size_t (-1), std::size_t (-1));

        if (caught) {
            // verify that an exception thrown during allocation
            // didn't cause a change in the state of the object
            src_state.assert_equal (ListState (src_list),
                                    __LINE__, tcase.line, caught);

            arg_state.assert_equal (ListState (arg_list),
                                    __LINE__, tcase.line, caught);

            if (0 == tcase.bthrow) {
                // allow this call to operator new to succeed and try
                // to make the next one to fail during the next call
                // to the same function again
                continue;
            }
        }
        else if (0 < tcase.bthrow) {
            rw_assert (caught == expected, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s, caught %s"
                       "%{:}unexpectedly caught %s%{;}",
                       __LINE__, 0 != expected, expected, caught, caught);
        }

        break;
    }

    // no exception expected
    const std::size_t expect_throws = 0;

    rw_assert (expect_throws == throw_count, 0, tcase.line,
               "line %d: %{$FUNCALL}: expected exactly 0 %s exception "
               "while the swap, got %zu",
               __LINE__, exceptions [1], throw_count);

    // disable bad_alloc exceptions
    *pst->throw_at_calls_ [0] = 0;
    pal->throw_at_calls_ [pal->m_allocate] = 0;
}
예제 #3
0
void exception_loop (int line, const char *fcall, bool capchg,
                     Vector &vec, const Vector::iterator &it,
                     int n, const UserClass *x,
                     const Iterator &first, const Iterator &last,
                     std::size_t *n_copy, std::size_t *n_asgn)
{
    std::size_t throw_after = 0;

    const std::size_t            size     = vec.size ();
    const std::size_t            capacity = vec.capacity ();
    const Vector::const_iterator begin    = vec.begin ();

#ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE

    rwt_free_store* const pst = rwt_get_free_store (0);

#endif   // _RWSTD_NO_REPLACEABLE_NEW_DELETE

    // iterate for`n=throw_after' starting at the next call to operator
    // new, forcing each call to throw an exception, until the insertion
    // finally succeeds (i.e, no exception is thrown)
    for ( ; ; ) {

        *n_copy = UserClass::n_total_copy_ctor_;
        *n_asgn = UserClass::n_total_op_assign_;

#ifndef _RWSTD_NO_EXCEPTIONS

        // detect objects constructed but not destroyed after an exception
        const std::size_t count = UserClass::count_;

#  ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE

        // disable out of memory exceptions before copying iterators
        // (InputIter ctors may dynamically allocate memory)
        *pst->throw_at_calls_ [0] = std::size_t (-1);
#  endif   // _RWSTD_NO_REPLACEABLE_NEW_DELETE
#endif   // _RWSTD_NO_EXCEPTIONS

        // create "deep" copies of the iterators to thwart
        // InpuIter's multi-pass detection
        const Iterator beg = copy_iter (first, (UserClass*)0);
        const Iterator end = copy_iter (last, (UserClass*)0);

#ifndef _RWSTD_NO_EXCEPTIONS
#  ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE

        *pst->throw_at_calls_ [0] = pst->new_calls_ [0] + throw_after + 1;

#  endif   // _RWSTD_NO_REPLACEABLE_NEW_DELETE
#endif   // _RWSTD_NO_EXCEPTIONS

        _TRY {
            if (-2 == n) {
                vec.insert (it, *x);
            }
            else if (-1 == n) {

#ifndef _RWSTD_NO_EXT_VECTOR_INSERT_IN_PLACE
#  ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE
                // disable exceptions if Iterator is not at least
                // a ForwardIterator since the extension doesn't
                // provide the strong exception safety guarantee
                // required in 23.2.4.3, p1
                if (!is_forward (beg)) {
                    *pst->throw_at_calls_ [0] = std::size_t (-1);
                }
#  endif   //_RWSTD_NO_REPLACEABLE_NEW_DELETE
#endif   // _RWSTD_NO_EXT_VECTOR_INSERT_IN_PLACE

                vec.insert (it, beg, end);
            }
            else {
                vec.insert (it, n, *x);
            }
            break;
        }
        _CATCH (...) {

#ifndef _RWSTD_NO_EXCEPTIONS

            // verify that an exception thrown during allocation
            // doesn't cause a change in the state of the vector

            rw_assert (vec.size () == size, 0, line,
                       "line %d: %s: size unexpectedly changed "
                       "from %zu to %zu after an exception",
                       __LINE__, fcall, size, vec.size ());

            rw_assert (vec.capacity () == capacity, 0, line,
                       "line %d: %s: capacity unexpectedly "
                       "changed from %zu to %zu after an exception",
                       __LINE__, fcall, capacity, vec.capacity ());

            
            rw_assert (vec.begin () == begin, 0, line,
                       "line %d: %s: begin() unexpectedly "
                       "changed from after an exception by %d",
                       __LINE__, fcall, vec.begin () - begin);


            rw_assert (count == UserClass::count_, 0, line,
                       "line %d: %s: leaked %d objects "
                       "after an exception",
                       __LINE__, fcall, UserClass::count_ - count);

            // increment to allow this call to operator new to succeed
            // and force the next one to fail, and try to insert again
            ++throw_after;

#endif   // _RWSTD_NO_EXCEPTIONS

        }   // catch
    }   // for
예제 #4
0
void test_op_plus (charT, Traits*, Allocator*,
                   const StringFunc     &func,
                   const StringTestCase &tcase)
{
    typedef std::basic_string <charT, Traits, Allocator> String;
    typedef typename UserTraits<charT>::MemFun           UTMemFun;

    static const std::size_t BUFSIZE = 256;

    static charT wstr_buf [BUFSIZE];
    static charT warg_buf [BUFSIZE];

    std::size_t str_len = sizeof wstr_buf / sizeof *wstr_buf;
    std::size_t arg_len = sizeof warg_buf / sizeof *warg_buf;

    charT* wstr = rw_expand (wstr_buf, tcase.str, tcase.str_len, &str_len);
    charT* warg = rw_expand (warg_buf, tcase.arg, tcase.arg_len, &arg_len);

    static charT wres_buf [BUFSIZE];
    std::size_t res_len = sizeof wres_buf / sizeof *wres_buf;
    charT* wres = rw_expand (wres_buf, tcase.res, tcase.nres, &res_len);

    // construct the string object to be modified
    // and the (possibly unused) argument string
    /* const */ String  s_str (wstr, str_len);
    const       String  s_arg (warg, arg_len);

    if (wstr != wstr_buf)
        delete[] wstr;

    if (warg != warg_buf)
        delete[] warg;

    wstr = 0;
    warg = 0;

    String s_res;

    // save the state of the string object before the call
    // to detect wxception safety violations (changes to
    // the state of the object after an exception)
    const StringState str_state (rw_get_string_state (s_res));

    const charT* const arg1_ptr = tcase.str ? s_str.c_str () : s_arg.c_str ();
    const String&      arg1_str = tcase.str ? s_str : s_arg;
    const charT* const arg2_ptr = tcase.arg ? s_arg.c_str () : s_str.c_str ();
    const String&      arg2_str = tcase.arg ? s_arg : s_str;
    const charT        arg_val = make_char (char (tcase.val), (charT*)0);

    std::size_t total_length_calls = 0;
    std::size_t n_length_calls = 0;
    std::size_t* const rg_calls = rw_get_call_counters ((Traits*)0, (charT*)0);

    if (rg_calls)
        total_length_calls = rg_calls [UTMemFun::length];

    rwt_free_store* const pst = rwt_get_free_store (0);
    SharedAlloc*    const pal = SharedAlloc::instance ();

    // iterate for`throw_after' starting at the next call to operator new,
    // forcing each call to throw an exception, until the function finally
    // succeeds (i.e, no exception is thrown)
    std::size_t throw_count;
    for (throw_count = 0; ; ++throw_count) {

        // (name of) expected and caught exception
        const char* expected = 0;
        const char* caught   = 0;

#ifndef _RWSTD_NO_EXCEPTIONS

        if (0 == tcase.bthrow) {
            // by default excercise the exception safety of the function
            // by iteratively inducing an exception at each call to operator
            // new or Allocator::allocate() until the call succeeds
            expected = exceptions [3];      // bad_alloc
            *pst->throw_at_calls_ [0] = pst->new_calls_ [0] + throw_count + 1;
            pal->throw_at_calls_ [pal->m_allocate] =
                pal->throw_at_calls_ [pal->m_allocate] + throw_count + 1;
        }
        else {
            // exceptions disabled for this test case
        }

#else   // if defined (_RWSTD_NO_EXCEPTIONS)

    if (tcase.bthrow) {
        if (wres != wres_buf)
            delete[] wres;

        return;
    }

#endif   // _RWSTD_NO_EXCEPTIONS

        // start checking for memory leaks
        rw_check_leaks (s_res.get_allocator ());

        try {

            switch (func.which_) {
            case OpPlus (cptr_cstr): 
                s_res = arg1_ptr + arg2_str;
                if (rg_calls)
                    n_length_calls = rg_calls [UTMemFun::length];
                break;
            
            case OpPlus (cstr_cstr): 
                s_res = arg1_str + arg2_str;
                break;
            
            case OpPlus (cstr_cptr): 
                s_res = arg1_str + arg2_ptr;
                if (rg_calls)
                    n_length_calls = rg_calls [UTMemFun::length];
                break;
            
            case OpPlus (cstr_val): 
                s_res = arg1_str + arg_val;
                break;
            
            case OpPlus (val_cstr): 
                s_res = arg_val + arg2_str;
                break;

            default:
                RW_ASSERT ("test logic error: unknown operator+ overload");
                return;
            }

            // verfiy that strings length are equal
            rw_assert (res_len == s_res.size (), 0, tcase.line,
                       "line %d. %{$FUNCALL} expected %{#*s} "
                       "with length %zu, got %{/*.*Gs} with length %zu",
                       __LINE__, int (tcase.nres), tcase.res, res_len,
                       int (sizeof (charT)), int (s_res.size ()),
                       s_res.c_str (), s_res.size ());

            if (res_len == s_res.size ()) {
                // if the result length matches the expected length
                // (and only then), also verify that the modified
                // string matches the expected result
                const std::size_t match =
                    rw_match (tcase.res, s_res.c_str(), s_res.size ());

                rw_assert (match == res_len, 0, tcase.line,
                           "line %d. %{$FUNCALL} expected %{#*s}, "
                           "got %{/*.*Gs}, difference at offset %zu",
                           __LINE__, int (tcase.nres), tcase.res,
                           int (sizeof (charT)), int (s_res.size ()),
                           s_res.c_str (), match);
            }

            // verify that Traits::length was used
            if ((OpPlus (cptr_cstr) == func.which_ 
              || OpPlus (cstr_cptr) == func.which_) && rg_calls) {
                    rw_assert (n_length_calls - total_length_calls > 0, 
                               0, tcase.line, "line %d. %{$FUNCALL} doesn't "
                               "use traits::length()", __LINE__);
            }
        }

#ifndef _RWSTD_NO_EXCEPTIONS

        catch (const std::bad_alloc &ex) {
            caught = exceptions [3];
            rw_assert (0 == tcase.bthrow, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught std::%s(%#s)",
                       __LINE__, 0 != expected, expected, caught, ex.what ());
        }
        catch (const std::exception &ex) {
            caught = exceptions [4];
            rw_assert (0, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught std::%s(%#s)",
                       __LINE__, 0 != expected, expected, caught, ex.what ());
        }
        catch (...) {
            caught = exceptions [0];
            rw_assert (0, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught %s",
                       __LINE__, 0 != expected, expected, caught);
        }

#endif   // _RWSTD_NO_EXCEPTIONS

        // FIXME: verify the number of blocks the function call
        // is expected to allocate and detect any memory leaks
        rw_check_leaks (s_res.get_allocator (), tcase.line,
                        std::size_t (-1), std::size_t (-1));

        if (caught) {
            // verify that an exception thrown during allocation
            // didn't cause a change in the state of the object
            str_state.assert_equal (rw_get_string_state (s_res),
                                    __LINE__, tcase.line, caught);

            if (0 == tcase.bthrow) {
                // allow this call to operator new to succeed and try
                // to make the next one to fail during the next call
                // to the same function again
                continue;
            }
        }
        else if (0 < tcase.bthrow) {
            rw_assert (caught == expected, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s, caught %s"
                       "%{:}unexpectedly caught %s%{;}",
                       __LINE__, 0 != expected, expected, caught, caught);
        }

        break;
    }

#ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE

    // verify that if exceptions are enabled and when capacity changes
    // at least one exception is thrown
    const std::size_t expect_throws = str_state.capacity_ < s_res.capacity ();

#else   // if defined (_RWSTD_NO_REPLACEABLE_NEW_DELETE)

    const std::size_t expect_throws = 
        (StringIds::UserAlloc == func.alloc_id_) 
      ? str_state.capacity_ < s_res.capacity () : 0;

#endif   // _RWSTD_NO_REPLACEABLE_NEW_DELETE

    // we may got no more 2 bad_allocs
    rw_assert (expect_throws <= throw_count && throw_count <= 2, 0, tcase.line,
               "line %d: %{$FUNCALL}: expected at least 1 %s exception "
               "while changing capacity from %zu to %zu, got %zu",
               __LINE__, exceptions [3],
               str_state.capacity_, s_res.capacity (), throw_count);

    // disable bad_alloc exceptions
    *pst->throw_at_calls_ [0] = 0;
    pal->throw_at_calls_ [pal->m_allocate] = 0;

    if (wres != wres_buf)
        delete[] wres;
}
예제 #5
0
void test_insert (charT*, Traits*, Allocator*, const RangeBase<
                      std::basic_string <charT, Traits, Allocator> > &rng,
                  const StringTestCaseData<charT>                    &tdata)
{
    typedef std::basic_string <charT, Traits, Allocator> String;
    typedef typename String::iterator                    StringIter;
    typedef typename UserTraits<charT>::MemFun           TraitsFunc;

    const StringFunc     &func  = tdata.func_;
    const StringTestCase &tcase = tdata.tcase_;

    // construct the string object to be modified
    // and the (possibly unused) argument string
    /* const */ String str (tdata.str_, tdata.strlen_);
    const       String arg (tdata.arg_, tdata.arglen_);

    // save the state of the string object before the call
    // to detect wxception safety violations (changes to
    // the state of the object after an exception)
    const StringState str_state (rw_get_string_state (str));

    std::ptrdiff_t exp_off = Insert (iter_val) == func.which_ ? tcase.off : 0;

    // compute the offset and the extent (the number of elements)
    // of the first range into the string object being modified
    const std::size_t size1 = tdata.strlen_;
    const std::size_t off1 =
        std::size_t (tcase.off) < size1 ? std::size_t (tcase.off) : size1;

    const charT* const arg_ptr = tcase.arg ? arg.c_str () : str.c_str ();
    const String&      arg_str = tcase.arg ? arg : str;
    const charT        arg_val = make_char (char (tcase.val), (charT*)0);

    std::size_t total_length_calls = 0;
    std::size_t n_length_calls = 0;
    std::size_t* const rg_calls = rw_get_call_counters ((Traits*)0, (charT*)0);

    if (rg_calls)
        total_length_calls = rg_calls [TraitsFunc::length];

    rwt_free_store* const pst = rwt_get_free_store (0);
    SharedAlloc*    const pal = SharedAlloc::instance ();

    // iterate for`throw_after' starting at the next call to operator new,
    // forcing each call to throw an exception, until the function finally
    // succeeds (i.e, no exception is thrown)
    std::size_t throw_count;
    for (throw_count = 0; ; ++throw_count) {

        // (name of) expected and caught exception
        const char* expected = 0;
        const char* caught   = 0;

#ifndef _RWSTD_NO_EXCEPTIONS

        const bool use_iters =
            StringIds::arg_iter == StringIds::arg_type (func.which_, 1);

        if (1 == tcase.bthrow && !use_iters)
            expected = exceptions [1];      // out_of_range
        else if (   2 == tcase.bthrow
                 && Insert (size_cstr_size_size) == func.which_)
            expected = exceptions [1];      // out_of_range
        else if (3 == tcase.bthrow && !use_iters)
            expected = exceptions [2];      // length_error
        else if (0 == tcase.bthrow) {
            // by default excercise the exception safety of the function
            // by iteratively inducing an exception at each call to operator
            // new or Allocator::allocate() until the call succeeds
            expected = exceptions [3];      // bad_alloc
            *pst->throw_at_calls_ [0] = pst->new_calls_ [0] + throw_count + 1;
            pal->throw_at_calls_ [pal->m_allocate] =
                pal->throw_at_calls_ [pal->m_allocate] + throw_count + 1;
        }
        else {
            // exceptions disabled for this test case
        }

#else   // if defined (_RWSTD_NO_EXCEPTIONS)

        if (tcase.bthrow) 
            return;

#endif   // _RWSTD_NO_EXCEPTIONS

        // pointer to the returned reference
        const String* ret_ptr = 0;

        // start checking for memory leaks
        rw_check_leaks (str.get_allocator ());

        try {
            switch (func.which_) {

            case Insert (size_cptr):
                ret_ptr = &str.insert (tcase.off, arg_ptr);
                if (rg_calls)
                    n_length_calls = rg_calls [TraitsFunc::length];
                break;

            case Insert (size_cstr):
                ret_ptr = &str.insert (tcase.off, arg_str);
                break;

            case Insert (size_cptr_size):
                ret_ptr = &str.insert (tcase.off, arg_ptr, tcase.size2);
                break;

            case Insert (size_cstr_size_size):
                ret_ptr =
                    &str.insert (tcase.off, arg_str, tcase.off2, tcase.size2);
                break;

            case Insert (size_size_val):
                ret_ptr = &str.insert (tcase.off, tcase.size2, arg_val);
                break;

            case Insert (iter_size_val): {
                const StringIter it (str.begin () + off1);
                str.insert (it, tcase.size2, arg_val);
                ret_ptr = &str;   // function returns void
                break;
            }

            case Insert (iter_val): {
                StringIter it (str.begin () + off1);
                it = str.insert (it, arg_val);
                ret_ptr = &str + (it - str.begin ());
                break;
            }

            case Insert (iter_range): {
                ret_ptr = &rng (str, tdata);  // function returns void
                break;
            }

            default:
                RW_ASSERT (!"logic error: unknown insert overload");
                return;
            }

            // verify that the reference returned from the function
            // refers to the modified string object (i.e., *this
            // within the function)
            const std::ptrdiff_t ret_off = ret_ptr - &str;

            // verify the returned value
            rw_assert (exp_off == ret_off, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}== begin() + %td, got %td%{;}"
                       "%{?}returned invalid reference, offset is %td%{;}",
                       __LINE__, Insert (iter_val) == func.which_,
                       exp_off, ret_off,
                       Insert (iter_val) != func.which_, ret_off);

            // for convenience
            static const int cwidth = sizeof (charT);

            // verfiy that strings length are equal
            rw_assert (tdata.reslen_ == str.size (), 0, tcase.line,
                       "line %d. %{$FUNCALL} expected %{/*.*Gs} with length "
                       "%zu, got %{/*.*Gs} with length %zu", __LINE__, 
                       cwidth, int (tdata.reslen_), tdata.res_, tdata.reslen_, 
                       cwidth, int (str.size ()), str.c_str (), str.size ());

            if (tdata.reslen_ == str.size ()) {
                // if the result length matches the expected length
                // (and only then), also verify that the modified
                // string matches the expected result
                const std::size_t match =
                    rw_match (tcase.res, str.c_str(), str.size ());

                rw_assert (match == tdata.reslen_, 0, tcase.line,
                           "line %d. %{$FUNCALL} expected %{/*.*Gs}, "
                           "got %{/*.*Gs}, difference at offset %zu",
                           __LINE__, cwidth, int (tdata.reslen_), tdata.res_,
                           cwidth, int (str.size ()), str.c_str (), match);
            }

            // verify that Traits::length was used
            if (Insert (size_cptr) == func.which_ && rg_calls) {
                rw_assert (n_length_calls - total_length_calls > 0, 
                           0, tcase.line, "line %d. %{$FUNCALL} doesn't "
                           "use traits::length()", __LINE__);
            }
        }

#ifndef _RWSTD_NO_EXCEPTIONS

        catch (const std::out_of_range &ex) {
            caught = exceptions [1];
            rw_assert (caught == expected, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught std::%s(%#s)",
                       __LINE__, 0 != expected, expected, caught, ex.what ());
        }
        catch (const std::length_error &ex) {
            caught = exceptions [2];
            rw_assert (caught == expected, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught std::%s(%#s)",
                       __LINE__, 0 != expected, expected, caught, ex.what ());
        }
        catch (const std::bad_alloc &ex) {
            caught = exceptions [3];
            rw_assert (0 == tcase.bthrow, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught std::%s(%#s)",
                       __LINE__, 0 != expected, expected, caught, ex.what ());
        }
        catch (const std::exception &ex) {
            caught = exceptions [4];
            rw_assert (0, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught std::%s(%#s)",
                       __LINE__, 0 != expected, expected, caught, ex.what ());
        }
        catch (...) {
            caught = exceptions [0];
            rw_assert (0, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s,%{:}"
                       "unexpectedly%{;} caught %s",
                       __LINE__, 0 != expected, expected, caught);
        }

#endif   // _RWSTD_NO_EXCEPTIONS

        // FIXME: verify the number of blocks the function call
        // is expected to allocate and detect any memory leaks
        rw_check_leaks (str.get_allocator (), tcase.line,
                        std::size_t (-1), std::size_t (-1));

        if (caught) {
            // verify that an exception thrown during allocation
            // didn't cause a change in the state of the object
            str_state.assert_equal (rw_get_string_state (str),
                                    __LINE__, tcase.line, caught);

            if (0 == tcase.bthrow) {
                // allow this call to operator new to succeed and try
                // to make the next one to fail during the next call
                // to the same function again
                continue;
            }
        }
        else if (0 < tcase.bthrow) {
            rw_assert (caught == expected, 0, tcase.line,
                       "line %d. %{$FUNCALL} %{?}expected %s, caught %s"
                       "%{:}unexpectedly caught %s%{;}",
                       __LINE__, 0 != expected, expected, caught, caught);
        }

        break;
    }

#ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE

    // verify that if exceptions are enabled and when capacity changes
    // at least one exception is thrown
    const std::size_t expect_throws = str_state.capacity_ < str.capacity ();

#else   // if defined (_RWSTD_NO_REPLACEABLE_NEW_DELETE)

    const std::size_t expect_throws = 
        (StringIds::UserAlloc == func.alloc_id_) 
      ? str_state.capacity_ < str.capacity (): 0;

#endif   // _RWSTD_NO_REPLACEABLE_NEW_DELETE

    // verify number of exceptions thrown
    // for range version the allocation may take place several times
    if (Insert (iter_range) != func.which_)
        rw_assert (expect_throws == throw_count, 0, tcase.line,
                   "line %d: %{$FUNCALL}: expected exactly 1 %s exception "
                   "while changing capacity from %zu to %zu, got %zu",
                   __LINE__, exceptions [3],
                   str_state.capacity_, str.capacity (), throw_count);
    else
        rw_assert (expect_throws <= throw_count, 0, tcase.line,
                   "line %d: %{$FUNCALL}: expected at least 1 %s exception "
                   "while changing capacity from %zu to %zu, got %zu",
                   __LINE__, exceptions [3],
                   str_state.capacity_, str.capacity (), throw_count);

    // disable bad_alloc exceptions
    *pst->throw_at_calls_ [0] = 0;
    pal->throw_at_calls_ [pal->m_allocate] = 0;
}