> -----Original Message-----
> From: Martin Sebor [mailto:[EMAIL PROTECTED]
> Sent: Friday, July 07, 2006 10:40 PM
> To: [email protected]
> Subject: design of testuite exceptions (was: Re: svn commit:
> r418319 - /incubator/stdcxx/trunk/tests/strings/21.string.io.cpp)
>
[...]
> Great! A few (rather lengthy) comments:
Implemented your notes about testsuite exception (rw_exception.h,
exception.cpp).
Added the testcases which forces the streambuf virtual functions to
throw to exercise the exception safety (21.string.io.cpp.diff).
Farid.
/************************************************************************
*
* exception.cpp - exceptions testsuite helpers
*
* $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.
*
**************************************************************************/
// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC
#include <rw_exception.h>
#include <driver.h>
#include <rw_printf.h>
#include <string.h>
#include <stddef.h> // for size_t
#include <stdarg.h> // for va_arg(), va_list
#include <new> // for std::bad_alloc
/**************************************************************************/
ExceptionBase::~ExceptionBase ()
{
}
const char* ExceptionBase::what () const
{
return what_;
}
/**************************************************************************/
struct BadAlloc: _RWSTD_BAD_ALLOC {
char what_ [256];
/* virtual */ const char* what () const _THROWS (());
};
const char* BadAlloc::what () const _THROWS (())
{
return what_;
}
/**************************************************************************/
_TEST_EXPORT int
rw_vasnprintf (char**, size_t*, const char*, va_list);
/**************************************************************************/
template <class Exception>
static void
_rw_prepare_exception (Exception& ex, bool logtostderr,
const char *file, int line,
const char *function,
const char *fmt, va_list va)
{
char *buf = ex.what_;
size_t bufsize = sizeof (ex.what_) / sizeof (ex.what_[0]);
rw_snprintfa (buf, bufsize, "\"%s\", %d, \"%s\" ", file, line, function);
size_t off = strlen (buf);
buf += off;
bufsize -= off;
rw_vasnprintf (&buf, &bufsize, fmt, va);
if (logtostderr)
rw_fprintf (rw_stderr, "%s\n", ex.what ());
}
/**************************************************************************/
template <class Exception>
void _rw_throw (Exception*, bool logtostderr,
const char *file, int line, const char *function,
const char *fmt, va_list va)
{
Exception ex;
_rw_prepare_exception (ex, logtostderr, file, line, function, fmt, va);
throw ex;
}
/**************************************************************************/
_TEST_EXPORT void
rw_throw (ExceptionId exid, bool logtostderr,
const char *file, int line, const char *function,
const char *fmt, ...)
{
va_list va;
va_start (va, fmt);
try {
if (ex_stream == exid)
_rw_throw ((StreamException*)0, logtostderr,
file, line, function, fmt, va);
if (ex_bad_alloc == exid)
_rw_throw ((BadAlloc*)0, logtostderr,
file, line, function, fmt, va);
if (logtostderr)
rw_fprintf (rw_stderr, "%s\n", "unknown exception thrown");
throw ex_unknown;
}
catch (...) {
// clean up arguments list
va_end (va);
throw;
}
}
/************************************************************************
*
* rw_exception.h - defines a test driver exception
*
* $Id: $
*
***************************************************************************
*
* Copyright 2006 The Apache Software Foundation or its licensors,
* as applicable.
*
* Copyright 2004-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.
*
**************************************************************************/
#ifndef RW_EXCEPTION_H_INCLUDED
#define RW_EXCEPTION_H_INCLUDED
#include <testdefs.h>
enum ExceptionId
{
ex_unknown = 0,
// custom exceptions, i.e. not derived for std::exception
ex_stream = 1,
ex_custom = 7,
// exceptions dervide from std::exception
ex_bad_alloc = 8,
ex_std = (7 << 3)
};
struct _TEST_EXPORT ExceptionBase {
char what_ [256];
ExceptionBase () {
}
virtual ~ExceptionBase ();
virtual const char* what () const;
};
struct _TEST_EXPORT StreamException : ExceptionBase
{
int dummy_;
StreamException () {
}
};
_TEST_EXPORT void
rw_throw (ExceptionId exid, bool logtostderr,
const char *file, int line, const char *function,
const char *fmt, ...);
#endif // RW_EXCEPTION_H_INCLUDED
Index: 21.string.io.cpp
===================================================================
--- 21.string.io.cpp (revision 420901)
+++ 21.string.io.cpp (working copy)
@@ -59,6 +59,8 @@
#define WHITESPACE " \f\n\r\t\v"
+const char SYMB_THROW = '!';
+
// off - width
// size - fmtflags
// off2 - iostate or exceptions mask
@@ -278,36 +280,35 @@
// | | | |
// | | | |
// V V V V
- TEST3 ("abc", 0, 0, "abc"),
- TEST3 ("abc", 1, 0, "a"),
- TEST3 ("abc", 2, 0, "ab" ),
- TEST3 ("abc", 10, 0, "abc"),
- TEST3 ("abc ", 10, 0, "abc"),
- TEST3 ("abc\f", 10, 0, "abc"),
- TEST3 ("abc\n", 10, 0, "abc"),
- TEST3 ("abc\r", 10, 0, "abc"),
- TEST3 ("abc\t", 10, 0, "abc"),
- TEST3 ("abc\v", 10, 0, "abc"),
+ TEST3 ("a!bc", 1, 0, "a" ),
+ TEST3 ("a!bc", 2, 0, "a!" ),
+ TEST3 ("a!bc", 10, 0, "a!bc"),
+ TEST3 ("ab!c ", 10, 0, "ab!c"),
+ TEST3 ("ab!c\f", 10, 0, "ab!c"),
+ TEST3 ("ab!c\n", 10, 0, "ab!c"),
+ TEST3 ("ab!c\r", 10, 0, "ab!c"),
+ TEST3 ("ab!c\t", 10, 0, "ab!c"),
+ TEST3 ("ab!c\v", 10, 0, "ab!c"),
- TEST3 ("abc" WHITESPACE, 0, 0, "abc"),
- TEST3 ("abc" WHITESPACE, 2, 0, "ab" ),
- TEST3 ("abc" WHITESPACE, 10, 0, "abc"),
+ TEST3 ("a!bc" WHITESPACE, 0, 0, "a!bc"),
+ TEST3 ("ab!c" WHITESPACE, 2, 0, "ab" ),
+ TEST3 ("abc!" WHITESPACE, 10, 0, "abc!"),
- TEST3 ("abc", 0, Skipws, "abc"),
- TEST3 ("abc", 2, Skipws, "ab" ),
- TEST3 ("abc", 10, Skipws, "abc"),
+ TEST3 ("abc!", 0, Skipws, "abc!"),
+ TEST3 ("ab!c", 2, Skipws, "ab" ),
+ TEST3 ("a!bc", 10, Skipws, "a!bc"),
- TEST3 ("abc" WHITESPACE, 0, Skipws, "abc"),
- TEST3 ("abc" WHITESPACE, 2, Skipws, "ab" ),
- TEST3 ("abc" WHITESPACE, 10, Skipws, "abc"),
+ TEST3 ("abc" WHITESPACE, 0, Skipws, "abc" ),
+ TEST3 ("abc" WHITESPACE, 2, Skipws, "ab" ),
+ TEST3 ("abc" WHITESPACE, 10, Skipws, "abc" ),
- TEST3 (WHITESPACE "abc", 0, Skipws, "abc"),
- TEST3 (WHITESPACE "abc", 2, Skipws, "ab" ),
- TEST3 (WHITESPACE "abc", 30, Skipws, "abc"),
+ TEST3 (WHITESPACE "abc", 0, Skipws, "abc" ),
+ TEST3 (WHITESPACE "abc", 2, Skipws, "ab" ),
+ TEST3 (WHITESPACE "abc", 30, Skipws, "abc" ),
- TEST3 (WHITESPACE "abc" WHITESPACE, 0, Skipws, "abc"),
- TEST3 (WHITESPACE "abc" WHITESPACE, 2, Skipws, "ab" ),
- TEST3 (WHITESPACE "abc" WHITESPACE, 30, Skipws, "abc")
+ TEST3 (WHITESPACE "abc" WHITESPACE, 0, Skipws, "abc" ),
+ TEST3 (WHITESPACE "abc" WHITESPACE, 2, Skipws, "ab" ),
+ TEST3 (WHITESPACE "abc" WHITESPACE, 30, Skipws, "abc" )
};
/**************************************************************************/
@@ -516,10 +517,10 @@
// | |
// | |
// V V
- TEST3 ("abc", "abc" ),
- TEST3 ("ab\nc", "ab" ),
- TEST3 ("a\nbc", "a" ),
- TEST3 (" \t\n", " \t" ),
+ TEST3 ("ab!c", "ab!c" ),
+ TEST3 ("a!b\nc", "a!b" ),
+ TEST3 ("a\n!bc", "a" ),
+ TEST3 (" !\t\n", " !\t" ),
TEST3 (" \f\r\t\v\n", " \f\r\t\v" ),
TEST3 (" \f\r\n\t\v\n", " \f\r" ),
TEST3 ("<U0>a<U0>b<U0>\n<U0>@2", "<U0>a<U0>b<U0>"),
@@ -735,9 +736,9 @@
// | | |
// | | |
// V V V
- TEST3 ("abc", '\t', "abc" ),
- TEST3 ("ab<U0>c", '\0', "ab" ),
- TEST3 ("a\nbc", 'b', "a\n" ),
+ TEST3 ("ab!c", '\t', "ab!c" ),
+ TEST3 ("a!b<U0>c", '\0', "a!b" ),
+ TEST3 ("a\n!bc", 'b', "a\n!" ),
TEST3 (" \t\n", '\n', " \t" ),
TEST3 (" \f\r\t\v\n", '\v', " \f\r\t" ),
TEST3 (" \f\r\n\t\v\n", '\r', " \f" ),
@@ -976,7 +977,7 @@
return low;
}
- virtual char do_narrow (UserChar ch, char /*dfault*/) const
+ virtual char do_narrow (UserChar ch, char/* dfault*/) const
{
char c[2];
rw_narrow (c, &ch, 1);
@@ -1011,6 +1012,8 @@
const StringFunc &func = tdata.func_;
const StringTestCase &tcase = tdata.tcase_;
+ bool test_inserter = func.which_ == StringIds::inserter_ostream_cstr;
+
// construct the string object
String str (tdata.str_, tdata.strlen_);
// construct a const reference to the string object
@@ -1024,122 +1027,267 @@
size_t arg_len = 0;
const char* arg = rw_expand (arg_buf, tcase.arg, tcase.arg_len, &arg_len);
+ // prepare arrays of indexes on which force the exception throwing
+ const charT* arg_throw = test_inserter ? tdata.str_ : tdata.arg_;
+ const size_t arg_throw_len =
+ test_inserter ? tdata.strlen_ : tdata.arglen_;
+
+ size_t* throw_on = new size_t [arg_throw_len + 1];
+ size_t pthrow = 0;
+ for (size_t k = 0; k < arg_throw_len; ++k) {
+ if (1 == rw_match (&SYMB_THROW, arg_throw + k, 1))
+ throw_on [pthrow++] = k + 1;
+ }
+ throw_on [pthrow] = NPOS;
+
// construct ctype facet to install in each stream's locale
// (the facet's lifetime must exceed that of the locale in
// which it is installed, which might include both the stream
// and the stream's streambuf)
const Ctype ctyp (1);
- // construct streambuf objects to associate with each stream
- Streambuf inbuf (arg, arg_len, 0, -1);
- Streambuf outbuf (std::streamsize (tcase.arg_len), 0, -1);
+ // set the initial formatting flags in both streams
+ const Fmtflags flags = Fmtflags (tcase.size);
- if (arg != arg_buf)
- delete[] arg;
+ // save the state of the const string object before the call
+ // to any (changes to the state of the object after a call)
+ const StringState cstr_state (rw_get_string_state (cstr));
- arg = 0;
+ // Xsgetn, Sync, Xsputn
+ size_t throw_count [] = { 1, 1, 1 };
+ size_t throw_inx = 0;
- // construct both stream objects even though only one will be used
- Istream is (&inbuf);
- Ostream os (&outbuf);
+ while (1) {
- // base referes to the basic_ios subobject of the stream used in
- // each test case (to avoid unnecessarily manipulating both streams)
- BasicIos &strm = StringIds::inserter_ostream_cstr == func.which_ ?
- (BasicIos&)os : (BasicIos&)is;
+ Streambuf inbuf (arg, arg_len, Throw | Underflow | Sync | Xsgetn, -1);
- // install std::ctype<UserChar> facet
- strm.imbue (std::locale (strm.getloc (), &ctyp));
+ Streambuf outbuf (std::streamsize (tcase.arg_len),
+ Throw | Overflow | Sync | Xsputn, -1);
- // set the inital width of both streams
- strm.width (tcase.off);
+ Streambuf& sbuf = test_inserter ? outbuf : inbuf;
- // set the initial formatting flags in both streams
- const Fmtflags flags = Fmtflags (tcase.size);
+#ifndef _RWSTD_NO_EXCEPTIONS
- strm.flags (flags);
+ // (name of) expected and caught exception
+ const char* expected = 0;
+ const char* caught = 0;
- // save the state of the const string object before the call
- // to any (changes to the state of the object after a call)
- const StringState cstr_state (rw_get_string_state (cstr));
+ if (1 == tcase.bthrow) {
+ expected = exceptions [1]; // ios_base::failure
+ }
+ else if (0 == tcase.bthrow) {
+ // set on which call of which method to throw
+ sbuf.throw_when_ [sbuf.memfun_inx (
+ test_inserter ? Overflow : Underflow) ] = throw_on [throw_inx];
-#ifndef _RWSTD_NO_EXCEPTIONS
+ sbuf.throw_when_ [sbuf.memfun_inx (Xsgetn) ] =
+ throw_count [0];
+ sbuf.throw_when_ [sbuf.memfun_inx (Sync) ] =
+ throw_count [1];
+ sbuf.throw_when_ [sbuf.memfun_inx (Xsputn) ] =
+ throw_count [2];
+ }
+ else {
+ // exceptions disabled for this test case
+ }
- // (name of) expected and caught exception
- const char* expected = 0;
- const char* caught = 0;
-
- if (1 == tcase.bthrow) {
- expected = exceptions [1]; // ios_base::failure
- }
- else {
- // exceptions disabled for this test case
- }
-
#else // if defined (_RWSTD_NO_EXCEPTIONS)
- if (tcase.bthrow)
- return;
+ if (tcase.bthrow)
+ return;
#endif // _RWSTD_NO_EXCEPTIONS
- const Iostate state = Iostate (tcase.off2);
+ // construct both stream objects even though only one will be used
+ Istream is (&inbuf);
+ Ostream os (&outbuf);
- if (tcase.bthrow) {
- // set exception bits leaving the initial stream state good
- strm.exceptions (state);
- }
- else {
- // set the initial stream state leaving exceptions clear
- strm.clear (state);
- }
+ // base referes to the basic_ios subobject of the stream used in
+ // each test case (to avoid unnecessarily manipulating both streams)
+ BasicIos &strm = test_inserter ? (BasicIos&)os : (BasicIos&)is;
- // start checking for memory leaks
- rw_check_leaks (str.get_allocator ());
+ // install std::ctype<UserChar> facet
+ strm.imbue (std::locale (strm.getloc (), &ctyp));
- try {
+ // set the initial width of both streams
+ strm.width (tcase.off);
- // the offset of the address of the returned reference
- // from the address of the stream object argument (the
- // two must be the same)
- std::ptrdiff_t ret_off = 0;
+ strm.flags (flags);
+ const Iostate state = Iostate (tcase.off2);
+
+ if (tcase.bthrow) {
+ // set exception bits leaving the initial stream state good
+ strm.exceptions (state);
+ }
+ else {
+ // set the initial stream state leaving exceptions clear
+ strm.clear (state);
+ }
+
+ // start checking for memory leaks
+ rw_check_leaks (str.get_allocator ());
+
+ bool streambuf_threw = false;
+
try {
- switch (func.which_) {
- case StringIds::extractor_istream_str:
- ret_off = &is - &(is >> str);
- break;
+ // the offset of the address of the returned reference
+ // from the address of the stream object argument (the
+ // two must be the same)
+ std::ptrdiff_t ret_off = 0;
- case StringIds::getline_istream_str:
- ret_off = &is - &std::getline (is, str);
- break;
+ try {
+ switch (func.which_) {
- case StringIds::getline_istream_str_val: {
- const charT delim = make_char (tcase.val, (charT*)0);
- ret_off = &is - &std::getline (is, str, delim);
- break;
+ case StringIds::extractor_istream_str:
+ ret_off = &is - &(is >> str);
+ break;
+
+ case StringIds::getline_istream_str:
+ ret_off = &is - &std::getline (is, str);
+ break;
+
+ case StringIds::getline_istream_str_val: {
+ const charT delim = make_char (tcase.val, (charT*)0);
+ ret_off = &is - &std::getline (is, str, delim);
+ break;
+ }
+
+ case StringIds::inserter_ostream_cstr:
+ ret_off = &os - &(os << cstr);
+ break;
+
+ default:
+ RW_ASSERT (!"test logic error: unknown io overload");
+ return;
+ }
}
+#ifndef _RWSTD_NO_EXCEPTIONS
- case StringIds::inserter_ostream_cstr:
- ret_off = &os - &(os << cstr);
- break;
+ catch (std::ios_base::failure& 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 ());
- default:
- RW_ASSERT (!"test logic error: unknown io overload");
- return;
}
+ 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
+
+ const std::streamsize ret_width = strm.width ();
+ const Iostate ret_state = strm.rdstate ();
+ const charT* ret_str = str.data ();
+ size_t ret_sz = str.size ();
+
+ if (test_inserter) {
+ ret_str = outbuf.pubpbase ();
+ ret_sz = outbuf.pubepptr () - ret_str;
+ }
+
+ streambuf_threw = None != sbuf.threw_;
+
+ if (streambuf_threw) {
+ if (Overflow == sbuf.threw_ || Underflow == sbuf.threw_)
+ throw_inx++;
+ if (Xsgetn == sbuf.threw_)
+ throw_count [0] ++;
+ if (Sync == sbuf.threw_)
+ throw_count [1] ++;
+ if (Xsputn == sbuf.threw_)
+ throw_count [2] ++;
+ }
+
+ // character width (for convenience)
+ static const int cwidth = sizeof (charT);
+
+ // verify that the reference returned from the function
+ // refers to the passed stream object
+ bool success = 0 == ret_off;
+ rw_assert (success, 0, tcase.line,
+ "line %d. %{$FUNCALL} returned invalid reference, "
+ "offset is %td", __LINE__, ret_off);
+
+ // verify the width
+ const std::streamsize width =
+ ( func.which_ == StringIds::getline_istream_str
+ || func.which_ == StringIds::getline_istream_str_val) ?
+ tcase.off : tcase.val;
+
+ success = width == ret_width;
+ rw_assert (success, 0, tcase.line,
+ "line %d. %{$FUNCALL}: width() == %td, got %td",
+ __LINE__, width, ret_width);
+
+ // tcase.size2 is the expected iostate
+ if (0 <= tcase.size2) {
+
+ // verify the iostate
+ const Iostate res_state =
+ streambuf_threw ? Bad : Iostate (tcase.size2);
+
+ success = res_state ?
+ (res_state == (ret_state & res_state)) : (0 == ret_state);
+ rw_assert (success, 0, tcase.line,
+ "line %d. %{$FUNCALL}: rdstate() == %{Is}, got %{Is}",
+ __LINE__, res_state, ret_state);
+ }
+
+ if (!streambuf_threw ||
+ (Overflow == sbuf.threw_ || Underflow == sbuf.threw_)) {
+
+ size_t res_sz = streambuf_threw ?
+ sbuf.throw_when_ [sbuf.memfun_inx (sbuf.threw_)] - 1 :
+ tdata.reslen_;
+
+ // verify that strings length are equal
+ success = res_sz == ret_sz;
+ rw_assert (success, 0, tcase.line,
+ "line %d. %{$FUNCALL}: expected %{/*.*Gs} with length "
+ "%zu, got %{/*.*Gs} with length %zu", __LINE__,
+ cwidth, int (res_sz), tdata.res_, res_sz,
+ cwidth, int (ret_sz), ret_str, ret_sz);
+
+ if (res_sz == ret_sz) {
+ // if the result length matches the expected length
+ // (and only then), also verify that the modified
+ // string matches the expected result
+ const size_t match = rw_match (tcase.res, ret_str, ret_sz);
+
+ success = match == res_sz;
+ rw_assert (success, 0, tcase.line,
+ "line %d. %{$FUNCALL}: expected %{/*.*Gs}, "
+ "got %{/*.*Gs}, difference at off %zu",
+ __LINE__, cwidth, int (res_sz), tdata.res_,
+ cwidth, int (ret_sz), ret_str, match);
+ }
+ }
}
+
#ifndef _RWSTD_NO_EXCEPTIONS
- catch (std::ios_base::failure& ex) {
- caught = exceptions [1];
- rw_assert (caught == expected, 0, tcase.line,
+ catch (const std::bad_alloc &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 (const std::exception &ex) {
+ caught = exceptions [3];
+ 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,
@@ -1150,97 +1298,26 @@
#endif // _RWSTD_NO_EXCEPTIONS
- const std::streamsize ret_width = strm.width ();
- const Iostate ret_state = strm.rdstate ();
- const charT* ret_str = str.data ();
- size_t ret_sz = str.size ();
+ // 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,
+ size_t (-1), size_t (-1));
- if (StringIds::inserter_ostream_cstr == func.which_) {
- ret_str = outbuf.pubpbase ();
- ret_sz = outbuf.pubepptr () - ret_str;
+ if (streambuf_threw && 0 == tcase.bthrow) {
+ // call the function again until streambuf methods
+ // not throws the exception
+ continue;
}
-
- // character width (for convenience)
- static const int cwidth = sizeof (charT);
-
- // verify that the reference returned from the function
- // refers to the passed stream object
- bool success = 0 == ret_off;
- rw_assert (success, 0, tcase.line,
- "line %d. %{$FUNCALL} returned invalid reference, "
- "offset is %td", __LINE__, ret_off);
-
- // verify the width
- const std::streamsize width =
- ( func.which_ == StringIds::getline_istream_str
- || func.which_ == StringIds::getline_istream_str_val) ?
- tcase.off : tcase.val;
-
- success = width == ret_width;
- rw_assert (success, 0, tcase.line,
- "line %d. %{$FUNCALL}: width() == %td, got %td",
- __LINE__, width, ret_width);
-
- // tcase.size2 is the expected iostate
- if (0 <= tcase.size2) {
- // verify the iostate
- const Iostate res_state = Iostate (tcase.size2);
- success = res_state ?
- (res_state == (ret_state & res_state)) : (0 == ret_state);
- rw_assert (success, 0, tcase.line,
- "line %d. %{$FUNCALL}: rdstate() == %{Is}, got %{Is}",
- __LINE__, res_state, ret_state);
+ 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);
}
- // verify that strings length are equal
- success = tdata.reslen_ == ret_sz;
- rw_assert (success, 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 (ret_sz), ret_str, ret_sz);
-
- if (tdata.reslen_ == ret_sz) {
- // 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, ret_str, ret_sz);
-
- success = match == tdata.reslen_;
- rw_assert (success, 0, tcase.line,
- "line %d. %{$FUNCALL}: expected %{/*.*Gs}, "
- "got %{/*.*Gs}, difference at off %zu",
- __LINE__, cwidth, int (tdata.reslen_), tdata.res_,
- cwidth, int (ret_sz), ret_str, match);
- }
+ break;
}
-#ifndef _RWSTD_NO_EXCEPTIONS
-
- catch (const std::bad_alloc &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 (const std::exception &ex) {
- caught = exceptions [3];
- 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
-
if (StringIds::fid_inserter == (func.which_ & StringIds::fid_mask)) {
// verify that const string object was not modified during
// the call to the inserter (input functions may, obviously,
@@ -1249,10 +1326,13 @@
__LINE__, tcase.line, "call");
}
- // 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 (arg != arg_buf)
+ delete[] arg;
+
+ arg = 0;
+
+ if (throw_on)
+ delete[] throw_on;
}
/**************************************************************************/
@@ -1276,9 +1356,10 @@
TEST (StringIds::getline_istream_str_val, getline_val)
};
- const std::size_t test_count = sizeof tests / sizeof *tests;
+ const size_t test_count = sizeof tests / sizeof *tests;
return rw_run_string_test (argc, argv, __FILE__,
"lib.string.io",
test_io_func_array, tests, test_count);
+
}
Index: allocator.cpp
===================================================================
--- allocator.cpp (revision 420901)
+++ allocator.cpp (working copy)
@@ -34,6 +34,7 @@
#include <new> // for bad_alloc, placement new
#include <stdarg.h> // for va_arg(), va_list
#include <string.h> // for memset()
+#include <rw_exception.h> // for _rw_throw_exception
/**************************************************************************/
@@ -55,37 +56,7 @@
static int _rw_id_gen; // generates unique ids
-/**************************************************************************/
-_TEST_EXPORT int
-rw_vasnprintf (char**, size_t*, const char*, va_list);
-
-
-static void
-_rw_throw_exception (const char *file, int line, const char *fmt, ...)
-{
- struct BadSharedAlloc: _RWSTD_BAD_ALLOC {
- char what_ [4096];
-
- /* virtual */ const char* what () const _THROWS (()) {
- return what_;
- }
- };
-
- BadSharedAlloc ex;
-
- va_list va;
- va_start (va, fmt);
-
- char *buf = ex.what_;
- size_t bufsize = sizeof ex.what_;
- rw_vasnprintf (&buf, &bufsize, fmt, va);
-
- va_end (va);
-
- throw ex;
-}
-
/**************************************************************************/
SharedAlloc::
@@ -136,14 +107,16 @@
const size_t nbytes = nelems * size;
if (max_blocks_ < n_blocks_ + nelems)
- _rw_throw_exception (__FILE__, __LINE__,
- "allocate (%zu): reached block limit of %zu",
- nbytes, max_blocks_);
+ rw_throw (ex_bad_alloc, false, __FILE__, __LINE__,
+ "SharedAlloc::allocate",
+ "allocate (%zu): reached block limit of %zu",
+ nbytes, max_blocks_);
if (max_bytes_ < n_bytes_ + nbytes)
- _rw_throw_exception (__FILE__, __LINE__,
- "allocate (%zu): reached size limit of %zu",
- nbytes, max_bytes_);
+ rw_throw (ex_bad_alloc, false, __FILE__, __LINE__,
+ "SharedAlloc::allocate",
+ "allocate (%zu): reached size limit of %zu",
+ nbytes, max_bytes_);
return operator_new (nbytes, false);
}
@@ -229,9 +202,10 @@
// increment the exception counter for this function
++n_throws_ [mf];
- _rw_throw_exception (__FILE__, __LINE__,
- "UserAlloc::%s: reached call limit of %zu",
- _rw_func_names [mf], throw_at_calls_);
+ rw_throw (ex_bad_alloc, false, __FILE__, __LINE__,
+ "SharedAlloc::funcall",
+ "UserAlloc::%s: reached call limit of %zu",
+ _rw_func_names [mf], throw_at_calls_);
RW_ASSERT (!"logic error: should not reach");
}
Index: new.cpp
===================================================================
--- new.cpp (revision 420901)
+++ new.cpp (working copy)
@@ -37,6 +37,8 @@
#include <rw_printf.h>
#include <rw_new.h>
+#include <rw_exception.h>
+
/************************************************************************/
#ifndef _RWSTD_BAD_ALLOC
@@ -272,14 +274,16 @@
}
+/*
struct BadAlloc: _RWSTD_BAD_ALLOC
{
char what_ [4096];
- /* virtual */ const char* what () const _THROWS (()) {
+ const char* what () const _THROWS (()) {
return what_;
}
};
+*/
static size_t seq_gen; // sequence number generator
@@ -365,40 +369,31 @@
|| reached_block_limit
|| reached_size_limit) {
- BadAlloc ex;
-
#ifndef _RWSTD_NO_EXCEPTIONS
- rw_snprintfa (ex.what_, 4096U, "%s:%d: %s (%zu) ",
- __FILE__, __LINE__, name [array], nbytes);
+ bool logtostderr =
+ trace_sequence [0] <= seq_gen && seq_gen < trace_sequence [1];
- strcat (ex.what_, "threw bad_alloc: ");
-
if (reached_call_limit)
+ rw_throw (ex_bad_alloc, logtostderr,
+ __FILE__, __LINE__, name [array],
+ "(%zu), reached call limit of %zu",
+ nbytes, pst->new_calls_ [array]);
- rw_snprintfa (
- ex.what_ + strlen (ex.what_), 4096U - strlen (ex.what_),
- "reached call limit of %zu", pst->new_calls_ [array]);
-
else if (reached_block_limit)
+ rw_throw (ex_bad_alloc, logtostderr,
+ __FILE__, __LINE__, name [array],
+ "(%zu), reached block limit of %zu: %zu",
+ nbytes, *pst->throw_at_blocks_ [array],
+ pst->blocks_ [array]);
- rw_snprintfa (
- ex.what_ + strlen (ex.what_), 4096U - strlen (ex.what_),
- "reached block limit of %zu: %zu",
- *pst->throw_at_blocks_ [array], pst->blocks_ [array]);
-
else if (reached_size_limit)
+ rw_throw (ex_bad_alloc, logtostderr,
+ __FILE__, __LINE__, name [array],
+ "(%zu), reached size limit of %zu: %zu",
+ nbytes, *pst->throw_at_bytes_ [array],
+ pst->bytes_ [array]);
- rw_snprintfa (
- ex.what_ + strlen (ex.what_), 4096U - strlen (ex.what_),
- "reached size limit of %zu: %zu",
- *pst->throw_at_bytes_ [array], pst->bytes_ [array]);
-
- if (trace_sequence [0] <= seq_gen && seq_gen < trace_sequence [1])
- rw_fprintf (rw_stderr, "%s\n", ex.what ());
-
- throw ex;
-
#else // if defined (_RWSTD_NO_EXCEPTIONS)
if (reached_breakpoint) {
@@ -426,16 +421,16 @@
ptr = malloc (block_size);
if (!ptr) {
- BadAlloc ex;
- rw_snprintfa (ex.what_, 4096U,
- "%s:%d: %s (%zu) threw bad_alloc: malloc() returned 0",
- __FILE__, __LINE__, name [array], nbytes);
-
- if (trace_sequence [0] <= seq_gen && seq_gen < trace_sequence [1])
- rw_fprintf (rw_stderr, "%s\n", ex.what ());
#ifndef _RWSTD_NO_EXCEPTIONS
- throw ex;
+
+ bool logerr =
+ trace_sequence [0] <= seq_gen && seq_gen < trace_sequence [1];
+
+ rw_throw (ex_bad_alloc, logerr,
+ __FILE__, __LINE__, name [array],
+ "(%zu): malloc() returned 0", nbytes);
+
#else // if defined (_RWSTD_NO_EXCEPTIONS)
return ptr;
#endif // _RWSTD_NO_EXCEPTIONS
Index: rw_streambuf.h
===================================================================
--- rw_streambuf.h (revision 420901)
+++ rw_streambuf.h (working copy)
@@ -29,11 +29,12 @@
#define RW_STREAMBUF_H_INCLUDED
-#include <cstring> // for memset()
-#include <streambuf> // for basic_streambuf
+#include <cstring> // for memset()
+#include <streambuf> // for basic_streambuf
#include <testdefs.h>
-#include <rw_char.h> // for make_char
+#include <rw_char.h> // for make_char
+#include <rw_exception.h> // for _rw_throw_exception
enum MemFun {
@@ -56,6 +57,10 @@
Unknown = 0x4000
};
+static const char* const streambuf_func_names[] = {
+ "setbuf", "seekoff", "seekpos", "showmanyc", "xsgetn", "underflow",
+ "uflow", "overflow", "pbackfail", "xsputn", "sync"
+};
template <class charT, class Traits>
struct MyStreambuf: std::basic_streambuf<charT, Traits>
@@ -163,17 +168,21 @@
public:
int ncalls (MemFun) const;
+ int memfun_inx (MemFun) const;
char_type *buf_;
std::streamsize bufsize_;
- int throw_set_; // functions that should throw
- int fail_set_; // functions that should fail
- MemFun threw_; // which function threw
- MemFun failed_; // which function failed
+ int throw_set_; // functions that should throw
+ int fail_set_; // functions that should fail
+ MemFun threw_; // which function threw
+ MemFun failed_; // which function failed
- int fail_when_; // call number on which to fail
+ int fail_when_; // call number on which to fail
+ int throw_when_ [11]; // call number on which to throw for each func
+ int allthrows_; // total number of thrown exceptions
+
// max size of the pending input sequence
static std::streamsize in_pending_;
@@ -183,7 +192,10 @@
private:
bool test (MemFun) const;
+
int ncalls_ [11]; // number of calls made to each function
+
+ int allcalls_; // total number of calls
};
@@ -202,11 +214,14 @@
MyStreambuf (std::streamsize bufsize, int fail_set, int when)
: Base (), buf_ (0), bufsize_ (bufsize),
throw_set_ (0), fail_set_ (0), threw_ (None), failed_ (None),
- fail_when_ (when)
+ fail_when_ (when), allthrows_ (0), allcalls_ (0)
{
// reset the member function call counters
std::memset (ncalls_, 0, sizeof ncalls_);
+ // reset the member function throw counters
+ std::memset (throw_when_, 0, sizeof throw_when_);
+
// allocate a (possibly wide) character buffer for output
buf_ = new charT [bufsize_];
@@ -232,11 +247,14 @@
MyStreambuf (const char *buf, std::streamsize bufsize, int fail_set, int when)
: Base (), buf_ (0), bufsize_ (bufsize),
throw_set_ (0), fail_set_ (0), threw_ (None), failed_ (None),
- fail_when_ (when)
+ fail_when_ (when), allthrows_ (0), allcalls_ (0)
{
// reset the member function call counters
std::memset (ncalls_, 0, sizeof ncalls_);
+ // reset the member function throw counters
+ std::memset (throw_when_, 0, sizeof throw_when_);
+
// as a convenience, if `bufsize == -1' compute the size
// from the length of `buf'
if (std::streamsize (-1) == bufsize_)
@@ -396,11 +414,10 @@
return traits_type::eof ();
}
-
template <class charT, class Traits>
int
MyStreambuf<charT, Traits>::
-ncalls (MemFun which) const
+memfun_inx (MemFun which) const
{
int inx = -1;
@@ -413,10 +430,22 @@
}
}
- return ncalls_ [inx];
+ return inx;
}
+template <class charT, class Traits>
+int
+MyStreambuf<charT, Traits>::
+ncalls (MemFun which) const
+{
+ int inx = memfun_inx (which);
+ if (0 <= inx)
+ return ncalls_ [inx];
+
+ return -1;
+}
+
template <class charT, class Traits>
bool
MyStreambuf<charT, Traits>::
@@ -424,35 +453,34 @@
{
MyStreambuf* const self = _RWSTD_CONST_CAST (MyStreambuf*, this);
- int inx = -1;
+ int inx = memfun_inx (which);
+ if (-1 == inx)
+ return true;
- for (unsigned i = 0; i < sizeof (which) * _RWSTD_CHAR_BIT; ++i) {
- if (which & (1U << i)) {
- if (inx < 0)
- inx = i;
- else
- return true;
- }
- }
-
// increment the counter tracking the number of calls made
// to each member function; do so regardless of whether
// an exception will be thrown below
- const int callno = self->ncalls_ [inx]++;
+ self->ncalls_ [inx] ++;
+ self->allcalls_ ++;
+ const int callno = self->ncalls_ [inx];
#ifndef _RWSTD_NO_EXCEPTIONS
// if the call counter is equal to the `fail_when_' watermark
// and `shich' is set in the `throw_set_' bitmask, throw an
// exception with the value of the member id
- if (callno == fail_when_ && throw_set_ & which) {
+ if (callno == throw_when_ [inx] && throw_set_ & which) {
self->threw_ = which;
- throw which;
+ self->allthrows_++;
+
+ rw_throw (ex_stream, false, __FILE__, __LINE__,
+ streambuf_func_names [inx],
+ "%s", "test exception");
}
#else // if defined (_RWSTD_NO_EXCEPTIONS)
- if (callno == fail_when_ && throw_set_ & which) {
+ if (callno == throw_when_ [inx] && throw_set_ & which) {
self->threw_ = which;
return false;
}