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; }
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; }
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
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; }
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; }