| /*************************************************************************** |
| * |
| * 21.string.swap.cpp - string test exercising [lib.string::swap] |
| * |
| * $Id $ |
| * |
| *************************************************************************** |
| * |
| * Copyright 2006 The Apache Software Foundation or its licensors, |
| * as applicable. |
| * |
| * Copyright 2006 Rogue Wave Software. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| **************************************************************************/ |
| |
| #include <string> // for string |
| #include <cstddef> // size_t |
| #include <exception> // for exception |
| |
| #include <21.strings.h> // for StringIds |
| #include <driver.h> // for rw_assert() |
| #include <rw_allocator.h> // foir UserAlloc |
| #include <rw_char.h> // for rw_expand() |
| #include <rw_new.h> // for bad_alloc, replacement operator new |
| |
| /**************************************************************************/ |
| |
| // for convenience and brevity |
| #define Swap(sig) StringIds::swap_ ## sig |
| |
| static const char* const exceptions[] = { |
| "unknown exception", "out_of_range", "length_error", |
| "bad_alloc", "exception" |
| }; |
| |
| /**************************************************************************/ |
| |
| // exercises: |
| // swap (basic_string&) |
| static const StringTestCase |
| str_test_cases [] = { |
| |
| #undef TEST |
| #define TEST(str, arg) \ |
| { __LINE__, -1, -1, -1, -1, -1, \ |
| str, sizeof str - 1, arg, sizeof arg - 1, \ |
| 0, 0, 0 \ |
| } |
| |
| // +------------------------- controlled "destination" (str) sequence |
| // | +-------- controlled "source" (arg) sequence |
| // | | |
| // V V |
| TEST ("", ""), |
| TEST ("", "a"), |
| TEST ("a", ""), |
| TEST ("", "<U0>"), |
| TEST ("<U0>", ""), |
| |
| TEST ("a", "b"), |
| TEST ("a", "bc"), |
| TEST ("ab", "c"), |
| |
| TEST ("a<U0>b<U0>@2c", "<U0>b<U0>@2c"), |
| TEST ("<U0>b<U0>@2c", "a<U0>b<U0>@2c"), |
| |
| TEST ("a<U0>b<U0>@2c", "<U0>@2"), |
| TEST ("<U0>@2", "a<U0>b<U0>@2c"), |
| |
| TEST ("x@4096", ""), |
| TEST ("", "x@4096"), |
| TEST ("x@4096", "<U0>@3"), |
| TEST ("<U0>@3", "x@4096"), |
| TEST ("x@4096", "x@4096"), |
| |
| TEST ("", "x@128"), |
| TEST ("x@207", "x@128"), |
| TEST ("x@128", "x@334"), |
| TEST ("x@873", "x@334"), |
| TEST ("x@1412", "x@540"), |
| TEST ("x@540", "x@2284"), |
| TEST ("x@3695", "x@2284"), |
| TEST ("x@3695", "x@128"), |
| |
| TEST ("", 0), |
| TEST ("<U0>", 0), |
| TEST ("abc", 0), |
| TEST ("a<U0>b<U0>@2c", 0), |
| TEST ("x@4096", 0), |
| |
| TEST (0, ""), |
| TEST (0, "<U0>"), |
| TEST (0, "abc@1024"), |
| TEST (0, "a<U0>b<U0>@2c"), |
| TEST (0, "x@4096"), |
| |
| TEST ("last", "test") |
| }; |
| |
| |
| /**************************************************************************/ |
| |
| template <class charT, class Traits, class Allocator> |
| void test_swap (charT*, Traits*, |
| Allocator &a1, |
| Allocator &a2, |
| const StringTestCaseData<charT> &tdata) |
| { |
| typedef std::basic_string <charT, Traits, Allocator> String; |
| typedef UserTraits<UserChar>::MemFun UTMemFun; |
| |
| const StringTestCase &tcase = tdata.tcase_; |
| |
| // construct the string object to be modified |
| // and the argument string |
| String s_str (tdata.str_, tdata.strlen_, a1); |
| String s_arg (tdata.arg_, tdata.arglen_, a2); |
| |
| const charT* const p1 = s_str.data (); |
| const charT* const p2 = tcase.arg ? s_arg.data () : s_str.data (); |
| |
| const char* const src = tcase.arg ? tcase.arg : tcase.str; |
| const std::size_t src_len = tcase.arg ? tcase.arg_len : tcase.str_len; |
| const std::size_t srclen_ = tcase.arg ? tdata.arglen_ : tdata.strlen_; |
| String& arg_str = tcase.arg ? s_arg : s_str; |
| |
| // 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_str)); |
| const StringState arg_str_state (rw_get_string_state (arg_str)); |
| |
| std::size_t n_total_op_assign = 0; |
| std::size_t n_total_op_assign2 = 0; |
| std::size_t n_total_op_copy = 0; |
| std::size_t n_total_op_move = 0; |
| |
| std::size_t* const rg_calls = rw_get_call_counters ((Traits*)0, (charT*)0); |
| if (rg_calls) { |
| n_total_op_assign = rg_calls[UTMemFun::assign]; |
| n_total_op_assign2 = rg_calls[UTMemFun::assign2]; |
| n_total_op_copy = rg_calls[UTMemFun::copy]; |
| n_total_op_move = rg_calls[UTMemFun::move]; |
| } |
| |
| 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) { |
| // 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 // if defined (_RWSTD_NO_EXCEPTIONS) |
| |
| if (tcase.bthrow) |
| return; |
| |
| #endif // _RWSTD_NO_EXCEPTIONS |
| |
| try { |
| |
| // start checking for memory leaks |
| rw_check_leaks (s_str.get_allocator ()); |
| rw_check_leaks (arg_str.get_allocator ()); |
| |
| if (0 == tcase.str) |
| String ().swap (arg_str); |
| else |
| s_str.swap (arg_str); |
| |
| if (rg_calls) { |
| |
| std::size_t n_op_assign = |
| rg_calls[UTMemFun::assign] - n_total_op_assign; |
| std::size_t n_op_assign2 = |
| rg_calls[UTMemFun::assign2] - n_total_op_assign2; |
| std::size_t n_op_copy = |
| rg_calls[UTMemFun::copy] - n_total_op_copy; |
| std::size_t n_op_move = |
| rg_calls[UTMemFun::move] - n_total_op_move; |
| |
| bool success = |
| 0 == (n_op_assign | n_op_assign2 | n_op_copy | n_op_move); |
| |
| rw_assert (success, 0, tcase.line, |
| "line %d. %{$FUNCALL}: complexity: %zu assigns, " |
| "%zu assign2s, %zu copies, %zu moves", __LINE__, |
| n_op_assign, n_op_assign2, n_op_copy, n_op_move); |
| } |
| |
| if (0 == tcase.str) { |
| |
| rw_assert (0 == arg_str.capacity (), 0, tcase.line, |
| "line %d. %{$FUNCALL}: expected 0 capacity, " |
| "got %zu", __LINE__, arg_str.capacity ()); |
| } |
| else { |
| |
| const charT* const res_p1 = s_str.data (); |
| const charT* const res_p2 = |
| tcase.arg ? s_arg.data () : s_str.data (); |
| |
| const std::size_t res1_len = s_str.size (); |
| const std::size_t res2_len = |
| tcase.arg ? s_arg.size () : s_str.size (); |
| |
| rw_assert (res_p1 == p2 && res_p2 == p1, 0, tcase.line, |
| "line %d. %{$FUNCALL}: got offset %td from " |
| "expected value, arg.data (): got offset %td " |
| "from expected value", |
| __LINE__, p2 - res_p1, p1 - res_p2); |
| |
| std::size_t match = |
| rw_match (tcase.str, res_p2, res2_len); |
| |
| rw_assert (match == tdata.strlen_, 0, tcase.line, |
| "line %d. %{$FUNCALL}: this == %{#*s}, got this = " |
| "%{/*.*Gs}, differs at pos %zu", |
| __LINE__, int (src_len), src, int (sizeof (charT)), |
| int (res1_len), res_p1, match); |
| |
| match = rw_match (src, res_p1, res1_len); |
| |
| rw_assert (match == srclen_, 0, tcase.line, |
| "line %d. %{$FUNCALL}: str == %{#*s}, got str = " |
| "%{/*.*Gs}, differs at pos %zu", |
| __LINE__, int (tcase.str_len), tcase.str, |
| int (sizeof (charT)), int (res2_len), |
| res_p2, match); |
| } |
| } |
| |
| #ifndef _RWSTD_NO_EXCEPTIONS |
| |
| 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_str.get_allocator (), tcase.line, |
| std::size_t (-1), std::size_t (-1)); |
| |
| rw_check_leaks (arg_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 (s_str), |
| __LINE__, tcase.line, caught); |
| |
| arg_str_state.assert_equal (rw_get_string_state (arg_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; |
| } |
| |
| // 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 [3], throw_count); |
| |
| // disable bad_alloc exceptions |
| *pst->throw_at_calls_ [0] = 0; |
| pal->throw_at_calls_ [pal->m_allocate] = 0; |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| std::allocator<charT> |
| make_alloc (SharedAlloc&, std::allocator<charT>*) { |
| return std::allocator<charT>(); |
| } |
| |
| template <class charT, class Types> |
| UserAlloc<charT, Types> |
| make_alloc (SharedAlloc &shal, UserAlloc<charT, Types>*) { |
| return UserAlloc<charT, Types>(&shal); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT, class Traits, class Allocator> |
| void test_swap (charT*, Traits*, Allocator*, |
| const StringTestCaseData<charT> &tdata) |
| { |
| SharedAlloc sa1; |
| Allocator a1 = make_alloc(sa1, (Allocator*)0); |
| |
| // test swap using the same allocator objects |
| test_swap ((charT*)0, (Traits*)0, a1, a1, tdata); |
| |
| SharedAlloc sa2; |
| Allocator a2 = make_alloc(sa2, (Allocator*)0); |
| |
| if (a1 != a2) { |
| // test swap using different allocator objects |
| test_swap ((charT*)0, (Traits*)0, a1, a2, tdata); |
| } |
| } |
| |
| /**************************************************************************/ |
| |
| DEFINE_STRING_TEST_FUNCTIONS (test_swap); |
| |
| int main (int argc, char** argv) |
| { |
| static const StringTest |
| tests [] = { |
| |
| #undef TEST |
| #define TEST(sig) { \ |
| Swap (sig), sig ## _test_cases, \ |
| sizeof sig ## _test_cases / sizeof *sig ## _test_cases, \ |
| } |
| |
| TEST (str) |
| }; |
| |
| const std::size_t test_count = sizeof tests / sizeof *tests; |
| |
| const int status = |
| rw_run_string_test (argc, argv, __FILE__, |
| "lib.string.swap", |
| test_swap_func_array, tests, test_count); |
| |
| return status; |
| } |