blob: 17b511ff3840a8ce7b56b9214735b547520cf2a2 [file] [log] [blame]
/***************************************************************************
*
* 23.list.special.cpp - test exercising [lib.list.special]
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you 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 <list> // for list
#include <cstddef> // size_t
#include <rw_lists.h> // for ListMembers
#include <rw_driver.h> // for rw_assert()
#include <rw_allocator.h> // for UserAlloc
#include <rw_new.h> // for bad_alloc, replacement operator new
/**************************************************************************/
// for convenience and brevity
#define Swap(sig) ListIds::swap_ ## sig
static const char* const exceptions[] = {
"unknown exception", "bad_alloc", "exception"
};
/**************************************************************************/
// exercises:
// swap (list&)
static const ContainerTestCase
cont_cont_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" sequence
// | +-------- controlled "source" sequence
// | |
// V V
TEST ("", ""),
};
// exercises:
// swap (list&)
static const ContainerTestCase
cont_test_cases [] = {
// +------------------------- controlled "destination" sequence
// | +-------- controlled "source" 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")
};
/**************************************************************************/
struct ListValueType { };
typedef std::allocator<ListValueType> ListAllocator;
typedef std::list<ListValueType, ListAllocator> ListType;
static int list_swap_called;
_RWSTD_NAMESPACE (std) {
// define an explicit specialization of the list::swap() member
// to verify tha the non-member swap function calls the member
_RWSTD_SPECIALIZED_FUNCTION
void ListType::swap (ListType&)
{
++list_swap_called;
}
} // namespace std
/**************************************************************************/
void test_std_swap ()
{
static bool tested = false;
if (tested)
return;
tested = true;
rw_info (0, 0, 0,
"Testing std::swap (std::list&, std::list&) "
"calls std::list::swap");
// verify the signature of the function specialization
void (*pswap)(ListType&, ListType&) =
&std::swap<ListValueType, ListAllocator>;
_RWSTD_UNUSED (pswap);
// verify that std::swap() calls std::list::swap()
ListType lst;
std::swap (lst, lst);
rw_assert (1 == list_swap_called, 0, __LINE__,
"std::swap (std::list<T, A>&, std::list<T, A>&) called "
"std::list<T, A>::swap (std::list<T, A>&) exactly once; "
"got %d times", list_swap_called);
}
/**************************************************************************/
template <class T, class Allocator>
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;
}
/**************************************************************************/
template <class T>
std::allocator<T>
make_alloc (SharedAlloc&, std::allocator<T>*) {
return std::allocator<T>();
}
template <class T, class Types>
UserAlloc<T, Types>
make_alloc (SharedAlloc &shal, UserAlloc<T, Types>*) {
return UserAlloc<T, Types>(&shal);
}
/**************************************************************************/
template <class T, class Allocator>
void test_swap (T*, Allocator*,
const ContainerTestCaseData<T> &tdata)
{
if (Swap (cont_cont) == tdata.func_.which_) {
test_std_swap ();
return;
}
SharedAlloc sa1;
Allocator a1 = make_alloc(sa1, (Allocator*)0);
// test swap using the same allocator objects
test_swap ((T*)0, a1, a1, tdata);
SharedAlloc sa2;
Allocator a2 = make_alloc(sa2, (Allocator*)0);
if (a1 != a2) {
// test swap using different allocator objects
test_swap ((T*)0, a1, a2, tdata);
}
}
/**************************************************************************/
DEFINE_CONTAINER_TEST_FUNCTIONS (test_swap);
int main (int argc, char** argv)
{
static const ContainerTest
tests [] = {
#undef TEST
#define TEST(sig) { \
Swap (sig), sig ## _test_cases, \
sizeof sig ## _test_cases / sizeof *sig ## _test_cases, \
}
TEST (cont),
TEST (cont_cont)
};
const std::size_t test_count = sizeof tests / sizeof *tests;
const int status =
rw_run_cont_test (argc, argv, __FILE__,
"lib.list.special",
ContainerIds::List,
test_swap_func_array, tests, test_count);
return status;
}