Index: 21.string.assign.cpp =================================================================== --- 21.string.assign.cpp (revision 396881) +++ 21.string.assign.cpp (working copy) @@ -43,6 +43,10 @@ // disabled for compilers such as IBM VAC++ or MSVC // that can't reliably replace the operators # include + +#else +# include + #endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE @@ -62,8 +66,10 @@ static const std::size_t long_string_len = 4096; static char long_string [long_string_len]; -static const char* const exp_exceptions [] = - { "unknown exception", "out_of_range", "length_error" }; +static const char* const exceptions[] = { + "unknown exception", "out_of_range", "length_error", + "bad_alloc", "exception" +}; /**************************************************************************/ @@ -400,152 +406,6 @@ /**************************************************************************/ -template -void test_exceptions (charT, Traits*, - AssignOverload which, - const TestCase &tcase) -{ - typedef std::allocator Allocator; - typedef std::basic_string TestString; - typedef typename TestString::iterator StringIter; - typedef typename TestString::const_iterator ConstStringIter; - - static charT wstr [LLEN]; - static charT wsrc [LLEN]; - - rw_widen (wstr, tcase.str, tcase.str_len); - rw_widen (wsrc, tcase.arg, tcase.arg_len); - - /* const */ TestString s_str (wstr, tcase.str_len); - const TestString s_arg (wsrc, tcase.arg_len); - - std::size_t throw_after = 0; - - const std::size_t size = s_str.size (); - const std::size_t capacity = s_str.capacity (); - const ConstStringIter begin = s_str.begin (); - -#ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE - - rwt_free_store* const pst = rwt_get_free_store (0); - -#endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE - - const charT* const arg_ptr = tcase.arg ? wsrc : s_str.c_str (); - const TestString& arg_str = tcase.arg ? s_arg : s_str; - const charT arg_val = make_char (char (tcase.val), (charT*)0); - - // iterate for`n=throw_after' starting at the next call to operator - // new, forcing each call to throw an exception, until the assignion - // finally succeeds (i.e, no exception is thrown) - for ( ; ; ) { - -#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 { - switch (which) { - case Assign (ptr): - s_str.assign (arg_ptr); - break; - - case Assign (str): - s_str.assign (arg_str); - break; - - case Assign (ptr_size): - s_str.assign (arg_ptr, tcase.size); - break; - - case Assign (str_size_size): - s_str.assign (arg_str, tcase.off, tcase.size); - break; - - case Assign (size_val): - s_str.assign (tcase.size, arg_val); - break; - - case Assign (range): { - const ConstStringIter first = s_arg.begin (); - const ConstStringIter last = s_arg.end (); - s_str.assign (first, last); - break; - } - - default: - RW_ASSERT (!"test logic error: unknown assign overload"); - } - - break; // out of for loop - } - _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 (s_str.size () == size, 0, tcase.line, - "line %d: %{$FUNCALL}: size unexpectedly changed " - "from %zu to %zu after an exception", - __LINE__, size, s_str.size ()); - - rw_assert (s_str.capacity () == capacity, 0, tcase.line, - "line %d: %{$FUNCALL}: capacity unexpectedly " - "changed from %zu to %zu after an exception", - __LINE__, capacity, s_str.capacity ()); - - - rw_assert (s_str.begin () == begin, 0, tcase.line, - "line %d: %{$FUNCALL}: begin() unexpectedly " - "changed from after an exception by %d", - __LINE__, s_str.begin () - begin); - - - // increment to allow this call to operator new to succeed - // and force the next one to fail, and try to assign again - ++throw_after; - -#endif // _RWSTD_NO_EXCEPTIONS - - } // catch - } // for - -#ifndef _RWSTD_NO_EXCEPTIONS -# ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE - - // verify that if exceptions are enabled and when capacity changes - // at least one exception is thrown - rw_assert ( *pst->throw_at_calls_ [0] == std::size_t (-1) - || throw_after, - 0, tcase.line, - "line %d: %{$FUNCALL}: failed to throw an expected exception", - __LINE__); - -# endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE -#else // if defined (_RWSTD_NO_EXCEPTIONS) - - _RWSTD_UNUSED (size); - _RWSTD_UNUSED (capacity); - _RWSTD_UNUSED (throw_after); - -#endif // _RWSTD_NO_EXCEPTIONS - -#ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE - - *pst->throw_at_calls_ [0] = std::size_t (-1); - -#endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE - -} - -/**************************************************************************/ - template void test_assign_range (charT *wstr, charT *wsrc, @@ -555,6 +415,7 @@ { typedef std::allocator Allocator; typedef std::basic_string String; + typedef typename String::iterator StringIter; const char* const itname = tcase.arg ? type_name (it, (charT*)0) : "basic_string::iterator"; @@ -565,7 +426,8 @@ std::size_t off_last = tcase.off + tcase.size; if (tcase.arg) { - off_last = off_last > s_arg.size () ? s_arg.size () : off_last; + if (off_last > s_arg.size ()) + off_last = s_arg.size (); const Iterator first (wsrc + tcase.off, wsrc + tcase.off, wsrc + off_last); @@ -575,7 +437,6 @@ s_str.assign (first, last); } else { - typedef typename String::iterator StringIter; const StringIter first (s_str.begin () + tcase.off); const StringIter last (off_last > s_str.size () ? @@ -635,12 +496,8 @@ typedef std::allocator Allocator; typedef std::basic_string TestString; typedef typename TestString::iterator StringIter; + typedef typename TestString::const_iterator ConstStringIter; - if (-1 == tcase.bthrow) { - test_exceptions (charT (), (Traits*)0, which, tcase); - return; - } - static charT wstr [LLEN]; static charT wsrc [LLEN]; @@ -657,108 +514,228 @@ const TestString s_arg (wsrc, tcase.arg_len); std::size_t res_off = 0; + std::size_t throw_after = 0; std::size_t size = tcase.size >= 0 ? tcase.size : s_str.max_size () + 1; + const std::size_t ssize = s_str.size (); + const std::size_t capacity = s_str.capacity (); + const ConstStringIter begin = s_str.begin (); + // first function argument const charT* const arg_ptr = tcase.arg ? wsrc : s_str.c_str (); const TestString& arg_str = tcase.arg ? s_arg : s_str; - // address of returned reference - const TestString* res_ptr = 0; +#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 appendion + // finally succeeds (i.e, no exception is thrown) + for ( ; ; ) { + #ifndef _RWSTD_NO_EXCEPTIONS +# ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE - // is some exception expected ? - const char* expected = 0; - if (1 == tcase.bthrow && Assign (str_size_size) == which) - expected = exp_exceptions [1]; - if (2 == tcase.bthrow) - expected = exp_exceptions [2]; + if (-1 == tcase.bthrow) + *pst->throw_at_calls_ [0] = pst->new_calls_ [0] + throw_after + 1; - const char* caught = 0; +# endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE +#endif // _RWSTD_NO_EXCEPTIONS - try { +#ifndef _RWSTD_NO_EXCEPTIONS + // is some exception expected ? + const char* expected = 0; + if (1 == tcase.bthrow && Assign (str_size_size) == which) + expected = exceptions [1]; // out_of_range + if (2 == tcase.bthrow) + expected = exceptions [2]; // length_error + if (-1 == tcase.bthrow) + expected = exceptions [3]; // bad_alloc + + const char* caught = 0; + #else // if defined (_RWSTD_NO_EXCEPTIONS) - if (tcase.bthrow) - return; + if (tcase.bthrow) + return; #endif // _RWSTD_NO_EXCEPTIONS - switch (which) { - case Assign (ptr): - res_ptr = &s_str.assign (arg_ptr); - break; + try { + switch (which) { + case Assign (ptr): { + const TestString& s_res = s_str.assign (arg_ptr); + res_off = &s_res - &s_str; + break; + } - case Assign (str): - res_ptr = &s_str.assign (arg_str); - break; + case Assign (str): { + const TestString& s_res = s_str.assign (arg_str); + res_off = &s_res - &s_str; + break; + } - case Assign (ptr_size): - res_ptr = &s_str.assign (arg_ptr, size); - break; + case Assign (ptr_size): { + const TestString& s_res = s_str.assign (arg_ptr, size); + res_off = &s_res - &s_str; + break; + } - case Assign (str_size_size): - res_ptr = &s_str.assign (arg_str, tcase.off, size); - break; + case Assign (str_size_size): { + const TestString& s_res = + s_str.assign (arg_str, tcase.off, size); + res_off = &s_res - &s_str; + break; + } - case Assign (size_val): { - const charT val = make_char (char (tcase.val), (charT*)0); - res_ptr = &s_str.assign (size, val); - break; - } + case Assign (size_val): { + const charT val = make_char (char (tcase.val), (charT*)0); + const TestString& s_res = s_str.assign (size, val); + res_off = &s_res - &s_str; + break; + } - default: - RW_ASSERT (!"test logic error: unknown assign overload"); - } + default: + RW_ASSERT (!"test logic error: unknown assign overload"); + } - res_off = res_ptr - &s_str; - // verify the returned value - rw_assert (0 == res_off, 0, tcase.line, - "line %d. %{$FUNCALL} returned invalid reference, offset is %zu", - __LINE__, res_off); + // verify the returned value + rw_assert (0 == res_off, 0, tcase.line, + "line %d. %{$FUNCALL} returned invalid reference, " + "offset is %zu", __LINE__, res_off); - // verfiy that strings length are equal - rw_assert (tcase.res_len == s_str.size (), 0, tcase.line, - "line %d. %{$FUNCALL}: expected %{#*s} with length %zu, " - "got %{/*.*Gs} with length %zu", __LINE__, int (tcase.res_len), - tcase.res, tcase.res_len, int (sizeof (charT)), - int (s_str.size ()), s_str.c_str (), s_str.size ()); + // verfiy that strings length are equal + rw_assert (tcase.res_len == s_str.size (), 0, tcase.line, + "line %d. %{$FUNCALL}: expected %{#*s} with length" + "%zu, got %{/*.*Gs} with length %zu", __LINE__, + int (tcase.res_len), tcase.res, tcase.res_len, + int (sizeof (charT)), int (s_str.size ()), + s_str.c_str (), s_str.size ()); - // verfiy that assign results match expected result - const std::size_t match = - rw_match (tcase.res, s_str.c_str(), tcase.res_len); + if (tcase.res_len == s_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, s_str.c_str(), tcase.res_len); - rw_assert (match == tcase.res_len, 0, tcase.line, - "line %d. %{$FUNCALL}: expected %{#*s}, got %{/*.*Gs}, " - "difference at off %zu", - __LINE__, int (tcase.res_len), tcase.res, - int (sizeof (charT)), int (s_str.size ()), s_str.c_str (), - match); + rw_assert (match == tcase.res_len, 0, tcase.line, + "line %d. %{$FUNCALL}: expected %{#*s}, " + "got %{/*.*Gs}, difference at off %zu", + __LINE__, int (tcase.res_len), tcase.res, + int (sizeof (charT)), int (s_str.size ()), + s_str.c_str (), match); + } + } #ifndef _RWSTD_NO_EXCEPTIONS - } - catch (std::out_of_range) { - caught = exp_exceptions [1]; - } - catch (std::length_error) { - caught = exp_exceptions [2]; - } - catch (...) { - caught = exp_exceptions [0]; - } + 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 (-1 == 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); + } #else // if defined (_RWSTD_NO_EXCEPTIONS) + _RWSTD_UNUSED (should_throw); + #endif // _RWSTD_NO_EXCEPTIONS - rw_assert (caught == expected, 0, tcase.line, - "line %d. %{$FUNCALL}: %{?}expected %s, caught %s" - "%{:}unexpectedly caught %s%{;}", - __LINE__, 0 != expected, expected, caught, caught); + if (caught) { + // verify that an exception thrown during allocation + // didn't cause a change in the state of the object + + rw_assert (s_str.size () == ssize, 0, tcase.line, + "line %d: %{$FUNCALL}: size unexpectedly changed " + "from %zu to %zu after an exception", + __LINE__, ssize, s_str.size ()); + + rw_assert (s_str.capacity () == capacity, 0, tcase.line, + "line %d: %{$FUNCALL}: capacity unexpectedly " + "changed from %zu to %zu after an exception", + __LINE__, capacity, s_str.capacity ()); + + rw_assert (s_str.begin () == begin, 0, tcase.line, + "line %d: %{$FUNCALL}: begin() unexpectedly " + "changed from after an exception by %d", + __LINE__, s_str.begin () - begin); + + if (-1 == tcase.bthrow) { + // increment to allow this call to operator new to succeed + // and force the next one to fail, and try calling the same + // function again + ++throw_after; + continue; + } + } + else if (-1 != 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_EXCEPTIONS +# ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE + + // verify that if exceptions are enabled and when capacity changes + // at least one exception is thrown + rw_assert ( *pst->throw_at_calls_ [0] == std::size_t (-1) + || throw_after, + 0, tcase.line, + "line %d: %{$FUNCALL}: failed to throw an expected exception", + __LINE__); + + *pst->throw_at_calls_ [0] = std::size_t (-1); + +# endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE +#else // if defined (_RWSTD_NO_EXCEPTIONS) + + _RWSTD_UNUSED (size); + _RWSTD_UNUSED (capacity); + _RWSTD_UNUSED (throw_after); + +#endif // _RWSTD_NO_EXCEPTIONS + } /**************************************************************************/