> -----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;
     }

Reply via email to