/*************************************************************************** * * 21.string.exceptions.cpp - test string members that throw exceptions * * $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. * * Copyright 1994-2008 Rogue Wave Software. * **************************************************************************/ #include #include #include #include #include // uses setjmp() and longjmp() to verify correct function if exception // exception support is disabled #ifdef _RWSTD_NO_EXCEPTIONS # include int id; // "thrown" from user_throw () defined below jmp_buf env; // used to implement fake exception handling # undef try # define try if (0 == (id = setjmp (env))) # undef catch # define catch(ignore) else if (0 != id) #endif // _RWSTD_NO_EXCEPTIONS // use a type that string isn't instantiated/specialized on by the library // also use a native type to avoid having to specialize char_traits typedef int Char; typedef std::char_traits CharTraits; typedef std::allocator CharAllocator; typedef std::basic_string String; _RWSTD_NAMESPACE (std) { // specialize in order to easily induce exceptional conditions _RWSTD_SPECIALIZED_CLASS String::size_type String::max_size () const { return _RWSTD_NEW_CAPACITY (String, this, 0); } } // namespace std #ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE # include #else // can't check for memory leaks using the replacement // operators new and delete # define rwt_check_leaks(psize, ignore) \ ((psize) ? *(std::size_t*)(psize) = 0 : 0) #endif /* static */ void user_throw (int ex_id, char *what) { delete[] what; #ifndef _RWSTD_NO_EXCEPTIONS throw ex_id; #else // if defined (_RWSTD_NO_EXCEPTIONS) longjmp (env, ex_id); #endif // _RWSTD_NO_EXCEPTIONS } /**************************************************************************/ static void test_simple_throw () { rw_info (0, __FILE__, __LINE__, "exception handling test setup"); // establish a chekpoint for memory leaks rwt_check_leaks (0, 0); #ifdef _RWSTD_NO_EXCEPTIONS // prevent library from aborting if exception support is disabled _RW::__rw_throw_proc = user_throw; #endif // _RWSTD_NO_EXCEPTIONS int thrown = 0; try { // throw and catch a bogus exception in order to initialize // data structures internal to the library to prevent any // memory allocation from throwing off memory leak detection _RWSTD_REQUIRES (0, (_RWSTD_ERROR_OUT_OF_RANGE, _RWSTD_FUNC ("test_simple_throw ()"), 0, 0)); } #ifndef _RWSTD_NO_EXCEPTIONS catch (std::out_of_range&) { thrown = 1; } #else // if defined (_RWSTD_NO_EXCEPTIONS) catch (int id) { thrown = id == _RWSTD_ERROR_OUT_OF_RANGE; } #endif // _RWSTD_NO_EXCEPTIONS catch (...) { thrown = -1; } rw_assert (1 == thrown, __FILE__, __LINE__, "_RWSTD_REQUIRES (_RWSTD_ERROR_OUT_OF_RANGE) failed to " "throw std::out_of_range"); } /**************************************************************************/ // const objects used throughout remaining test cases const Char s[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', '\0' }; const String s0 (s); /**************************************************************************/ // constructor tests static void test_size_ctor () { rw_info (0, __FILE__, __LINE__, "21.3.1, p4 (size)"); // establish a chekpoint for memory leaks rwt_check_leaks (0, 0); int thrown = 0; try { // throws std::out_of_range if pos > str.size () String s1 (s0, s0.size () + 1); } #ifndef _RWSTD_NO_EXCEPTIONS catch (std::out_of_range&) { thrown = 1; } #else // if defined (_RWSTD_NO_EXCEPTIONS) catch (int id) { thrown = id == _RWSTD_ERROR_OUT_OF_RANGE; } #endif // _RWSTD_NO_EXCEPTIONS catch (...) { thrown = -1; } std::size_t nbytes; /* uninitialized */ std::size_t nblocks = rwt_check_leaks (&nbytes, 0); _RWSTD_UNUSED (nblocks); rw_assert (1 == thrown, __FILE__, __LINE__, ("string::string (const string&, size_type, size_type, " "const allocator_type&) failed to throw std::out_of_range")); rw_assert (s == s0, __FILE__, __LINE__, "original const string modified"); rw_assert (0 == nbytes, __FILE__, __LINE__, "string::string (const string&, size_type, size_type, " "const allocator_type&) leaked %u bytes", nbytes); } static void test_npos_ctor () { rw_info (0, __FILE__, __LINE__, "21.3.1, p4 (npos)"); _RW::__rw_throw_proc = user_throw; // establish a chekpoint for memory leaks rwt_check_leaks (0, 0); int thrown = 0; try { // throws std::out_of_range if pos > str.size () String s1 (s0, String::npos); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } std::size_t nbytes; /* uninitialized */ std::size_t nblocks = rwt_check_leaks (&nbytes, 0); _RWSTD_UNUSED (nblocks); rw_assert (1 == thrown, __FILE__, __LINE__, "string::string (const string&, size_type, size_type, " "const allocator_type&) failed to use __rw::__rw_throw()"); rw_assert (0 == nbytes, __FILE__, __LINE__, "string::string (const string&, size_type, size_type, " "const allocator_type&) leaked %u bytes", nbytes); rw_assert (s == s0, __FILE__, __LINE__, "original const string modified"); } static void test_max_size_ctor1 () { rw_info (0, __FILE__, __LINE__, "21.3.1, p7"); // establish a chekpoint for memory leaks rwt_check_leaks (0, 0); int thrown = 0; try { // throws std::out_of_range if n > max_size () (*) // (*) see also lwg issue 83 String s1 (s0.data (), s0.max_size () + 1); } catch (int id) { thrown = _RWSTD_ERROR_LENGTH_ERROR == id; } catch (...) { /* empty */ } std::size_t nbytes; /* uninitialized */ std::size_t nblocks = rwt_check_leaks (&nbytes, 0); _RWSTD_UNUSED (nblocks); rw_assert (1 == thrown, __FILE__, __LINE__, "string::string (const char_type*, size_type) " "failed to use __rw::__rw_throw()"); rw_assert (0 == nbytes, __FILE__, __LINE__, "string::string (const char_type*, size_type)" "leaked %u bytes", nbytes); rw_assert (s == s0, __FILE__, __LINE__, "original const string modified"); } static void test_max_size_ctor2 () { rw_info (0, __FILE__, __LINE__, "21.3.1, p13"); int thrown = 0; try { // throws std::length_error if n > max_size () (*) // (*) see also lwg issue 83 String s1 (s0.max_size () + 1, Char ()); } catch (int id) { thrown = _RWSTD_ERROR_LENGTH_ERROR == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::string (size_type, char_type) " "failed to use __rw::__rw_throw()"); } static void test_len_ctor () { #ifndef _RWSTD_NO_MEMBER_TEMPLATES rw_info (0, __FILE__, __LINE__, "21.3.1, p15"); int thrown = 0; try { // same as 21.3.1, p13 for integral arguments String s1 = String (long (String::npos), long (' ')); } catch (int id) { thrown = _RWSTD_ERROR_LENGTH_ERROR == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::string (InputIterator, InputIterator)" "failed to use __rw::__rw_throw()"); #endif // _RWSTD_NO_MEMBER_TEMPLATES } /**************************************************************************/ // member functions static void test_resize () { rw_info (0, __FILE__, __LINE__, "21.3.3, p6"); String s1 (s0); // save data, size and capacity of a constructed string const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); rw_assert (s1.size () == s0.size () && s1 == s0, __FILE__, __LINE__, "std::string::string (const string&)"); int thrown = 0; try { // throws std::length_error if n > max_size () s1.resize (s1.max_size () + 1U /* , char () */); } catch (int id) { thrown = _RWSTD_ERROR_LENGTH_ERROR == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::resize (size_type, char_type)" "failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::resize (size_type, char_type) modified *this"); } static void test_reserve () { rw_info (0, __FILE__, __LINE__, "21.3.3, p12"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); try { // throws std::length_error if n > max_size () s1.reserve (s1.max_size () + 1U); } catch (int id) { thrown = _RWSTD_ERROR_LENGTH_ERROR == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::reserve (size_type)" "failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::reserve (size_type) modified *this"); } static void test_at () { rw_info (0, __FILE__, __LINE__, "21.3.4, p3"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); try { // throws std::out_of_range if pos > size () s1.at (s1.size ()); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::at (size_type) failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::at (size_type) modified *this"); } static void test_append () { rw_info (0, __FILE__, __LINE__, "21.3.5.2, p3"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); try { // throws std::out_of_range if pos > size () s1.append (s1, s1.size () + 1, 0); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::append (const string&, size_type, size_type) " "failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::append (const string&, size_type, size_type) " "modified *this"); } static void test_assign () { rw_info (0, __FILE__, __LINE__, "21.3.5.3, p3"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); try { // throws std::out_of_range if pos > size () s1.assign (s1, s1.size () + 1, 0); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::assign (const string&, size_type, size_type) " "failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::assign (const string&, size_type, size_type) " "modified *this"); } static void test_insert1 () { rw_info (0, __FILE__, __LINE__, "21.3.5.4, p3 (pos1 > size ())"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); try { // throws std::out_of_range if: // 1) pos1 > size () <-- testing // 2) or pos2 > str.size () s1.insert (s1.size () + 1, s1, 0, 0); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::insert (size_type, const string&, size_type, " "size_type) failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::insert (size_type, const string&, size_type, " "modified *this"); } static void test_insert2 () { rw_info (0, __FILE__, __LINE__, "21.3.5.4, p3 (pos2 > str.size ())"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); try { // throws std::out_of_range if: // 1) pos1 > size () // 2) or pos2 > str.size () <-- testing s1.insert (s1.size (), s1, s1.size () + 1, 0); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::insert (size_type, const string&, size_type, " "size_type) failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::insert (size_type, const string&, size_type, " "modified *this"); } static void test_erase () { rw_info (0, __FILE__, __LINE__, "21.3.5.5, p2"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); try { // throws std::out_of_range if pos > size () s1.erase (s1.size () + 1 /* , String::npos */); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::erase (size_type, size_type) " "failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::erase (size_type, size_type) modified *this"); } static void test_replace1 () { rw_info (0, __FILE__, __LINE__, "21.3.5.6, p3 (pos1 > size ())"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); try { // throws std::out_of_range if: // 1) pos1 > size () <-- testing // 2) or pos2 > str.size () s1.replace (s1.size () + 1, 0, s0, s0.size (), 0); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::replace (size_type, size_type, const string&, " "size_type, size_type) failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::replace (size_type, size_type, const string&, " "size_type, size_type) modified *this"); } static void test_replace2 () { rw_info (0, __FILE__, __LINE__, "21.3.5.6, p3 (pos2 > str.size ())"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); try { // throws std::out_of_range if: // 1) pos1 > size () // 2) or pos2 > str.size () <-- testing s1.replace (s1.size (), 0, s1, s1.size () + 1, 0); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::replace (size_type, size_type, const string&, " "size_type, size_type) failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::replace (size_type, size_type, const string&, " "size_type, size_type) modified *this"); } static void test_replace3 () { rw_info (0, __FILE__, __LINE__, "21.3.5.6, p5"); int thrown = 0; String s1 (s0); const String::const_pointer s1_data = s1.data (); const String::size_type s1_size = s1.size (); const String::size_type s1_cap = s1.capacity (); // establish a chekpoint for memory leaks rwt_check_leaks (0, 0); try { // make sure max_size() isn't too big assert (s1.max_size () == _RWSTD_NEW_CAPACITY (String, &s1, 0)); thrown = -1; // must not throw String s2 (s1.max_size () - s1.size () + 2, Char ()); thrown = 0; // throws std::length_error if: // size () - xlen >= max_size () - rlen (*) // where xlen = min (n1, this->size () - pos1) // and rlen = min (n2, str.size () - pos2) // (*) see also lwg issue 86 s1.replace (0, 1, s2, 0, s2.size ()); } catch (int id) { thrown = 0 == thrown && _RWSTD_ERROR_LENGTH_ERROR == id; } catch (...) { /* empty */ } std::size_t nbytes; /* uninitialized */ std::size_t nblocks = rwt_check_leaks (&nbytes, 0); _RWSTD_UNUSED (nblocks); rw_assert (1 == thrown, __FILE__, __LINE__, "string::replace (size_type, size_type, const string&, " "size_type, size_type) failed to use __rw::__rw_throw()"); // verify that string wasn't modified rw_assert (s1_data == s1.data () && s1_size == s1.size () && s1_cap == s1.capacity (), __FILE__, __LINE__, "string::replace (size_type, size_type, const string&, " "size_type, size_type) modified *this"); // tests not only replace() but also string ctor (s2 above) rw_assert (1 == thrown, __FILE__, __LINE__, "string::replace (size_type, size_type, const string&, " "size_type, size_type) leaked %u bytes", nbytes); } static void test_copy () { rw_info (0, __FILE__, __LINE__, "21.3.5.7, p2"); int thrown = 0; Char c = '\1'; String s1 (s0); try { // throws std::out_of_range if pos > size () s1.copy (&c, 1, s1.size () + 1); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::copy (pointer, size_type, size_type) " "failed to use __rw::__rw_throw()"); // verify that destination buffer wasn't modified rw_assert ('\1' == c, __FILE__, __LINE__, "string::copy (pointer, size_type, size_type) " "modified buffer"); } static void test_substr () { rw_info (0, __FILE__, __LINE__, "21.3.6.7, p2"); int thrown = 0; String s1 (s0); try { // throws std::out_of_range if pos > size () s1.substr (s1.size () + 1 /* , String::npos */); } catch (int id) { thrown = _RWSTD_ERROR_OUT_OF_RANGE == id; } catch (...) { /* empty */ } rw_assert (1 == thrown, __FILE__, __LINE__, "string::substr (size_type, size_type) " "failed to use __rw::__rw_throw()"); } /**************************************************************************/ static int run_test (int /*unused*/, char* /*unused*/ []) { test_simple_throw (); // constructors test_size_ctor (); test_npos_ctor (); test_max_size_ctor1 (); test_max_size_ctor2 (); test_len_ctor (); // member functions test_resize (); test_reserve (); test_at (); test_append (); test_assign (); test_insert1 (); test_insert2 (); test_erase (); test_replace1 (); test_replace2 (); test_replace3 (); test_copy (); test_substr (); return 0; } /*extern*/ int main (int argc, char* argv []) { return rw_test (argc, argv, __FILE__, "lib.basic.string", "exception saftety", run_test, "", 0); }