/***************************************************************************
 *
 * 27.ostream.cpp - test exercising class template basic_ostream
 *
 * $Id: 27.ostream.cpp 580483 2007-09-28 20:55:52Z sebor $
 *
 ***************************************************************************
 *
 * 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 2002-2006 Rogue Wave Software.
 * 
 **************************************************************************/


#include <exception>   // for uncaught_exception()
#include <ostream>     // for basic_ostream
#include <climits>     // for XXX_MAX and XXX_MIN
#include <cstddef>     // for ptrdiff_t
#include <cstring>     // for memset()

#include <any.h>       // for rw_any_t()
#include <driver.h>    // for rw_test()

/***************************************************************************/

int rw_opt_sentry;          // for --enable/disable-sentry
int rw_opt_formatted;       // for --enable/disable-formatted
int rw_opt_unformatted;     // for --enable/disable-unformatted
int rw_opt_flush;           // for --enable/disable-flush
int rw_opt_exceptions;      // for --enable/disable-exceptions

/***************************************************************************/

enum streambuf_failure {
    Setbuf = 1, Seekoff, Seekpos, Shwomanyc, Xsgetn,
    Underflow, Uflow, Overflow, Pbackfail, Xsputn, Sync
};


template <class charT>
struct test_streambuf: std::basic_streambuf<charT, std::char_traits<charT> >
{
    typedef charT                                        char_type;
    typedef std::char_traits<charT>                      traits_type;
    typedef std::basic_streambuf<char_type, traits_type> Streambuf;
    typedef typename Streambuf::int_type                 int_type;
    typedef typename Streambuf::off_type                 off_type;
    typedef typename Streambuf::pos_type                 pos_type;

    test_streambuf (std::basic_ios<char_type, traits_type> *pstrm)
        : Streambuf (), buf_ (0), bufsize_ (0),
          throws_ (streambuf_failure ()), fails_ (streambuf_failure ()),
          pstrm_ (pstrm) {
        std::memset (ncalls_, 0, sizeof ncalls_);
        this->setp (buf_, buf_);
    }

    test_streambuf (charT *buf, std::streamsize bufsize)
        : Streambuf (), buf_ (buf), bufsize_ (bufsize),
          throws_ (streambuf_failure ()), fails_ (streambuf_failure ()) {
        std::memset (ncalls_, 0, sizeof ncalls_);
        this->setp (buf_, buf_);
    }

    bool test (streambuf_failure) const;

    virtual Streambuf* setbuf (char_type*, std::streamsize) {
        return test (Setbuf) ? this : 0;
    }

    virtual pos_type
    seekoff (off_type, std::ios_base::seekdir, std::ios_base::openmode) {
        test (Seekoff);
        return pos_type (off_type (-1));
    }

    virtual pos_type
    seekpos (pos_type, std::ios_base::openmode) {
        test (Seekpos);
        return pos_type (off_type (-1));
    }

    virtual std::streamsize showmanyc () {
        test (Shwomanyc);
        return 0;
    }

    virtual std::streamsize xsgetn (char_type*, std::streamsize) {
        test (Xsgetn);
        return 0;
    }

    virtual int_type underflow () {
        test (Underflow);
        return traits_type::eof ();
    }

    virtual int_type uflow () {
        test (Uflow);
        return traits_type::eof ();
    }

    virtual int_type
    overflow (int_type = traits_type::eof ());

    virtual int_type
    pbackfail (int_type = traits_type::eof ()) {
        test (Pbackfail);
        return traits_type::eof ();
    }

    virtual std::streamsize
    xsputn (const char_type *buf, std::streamsize bufsize) {
        if (!test (Xsputn))
            return 0;
        return Streambuf::xsputn (buf, bufsize);
    }

    virtual int sync () {
        if (!test (Sync))
            return -1;

        if (pstrm_)
            pstrm_->setstate (std::ios_base::failbit);

        return 0;
    }

    char_type       *buf_;
    std::streamsize  bufsize_;

    int ncalls_ [Sync + 1];     // number of calls made to each function

    streambuf_failure throws_;  // exception value to throw
    streambuf_failure fails_;   // which function to fail

    std::basic_ios<char_type, traits_type> *pstrm_;

    static int fail_when_;      // call number on which to fail
};


template <class charT>
int test_streambuf<charT>::fail_when_ = 1;


template <class charT>
bool
test_streambuf<charT>::test (streambuf_failure which) const
{
    ++_RWSTD_CONST_CAST (test_streambuf*, this)->ncalls_ [which];

    if (ncalls_ [which] == fail_when_ && throws_ == which)
        throw which;

    return fails_ != which || ncalls_ [which] != fail_when_;
}


template <class charT>
typename test_streambuf<charT>::int_type
test_streambuf<charT>::overflow (int_type c /* = traits_type::eof () */)
{
    if (!test (Overflow))
        return traits_type::eof ();

    if (traits_type::eq_int_type (c, traits_type::eof ()))
        return traits_type::not_eof (c);

    const std::ptrdiff_t pptr_off = this->pptr () - this->pbase ();

    if (   this->pptr () == this->epptr ()
        && this->epptr () < buf_ + bufsize_) {
        this->setp (buf_, this->epptr () + 1);
        this->pbump (pptr_off + 1);
        *(this->pptr () - 1) = traits_type::to_char_type (c);
        return c;
    }

    return traits_type::eof ();
}



enum num_put_overload {
    Bool = Sync + 1, Long, ULong, Dbl, LDbl, PVoid, LLong, ULLong
};


#ifndef _RWSTD_NO_NATIVE_BOOL

num_put_overload select_overload (bool)           { return Bool; }

#endif   // _RWSTD_NO_NATIVE_BOOL

template <class T>
num_put_overload select_overload (T) { return num_put_overload (); }

num_put_overload select_overload (short)          { return Long; }
num_put_overload select_overload (unsigned short) { return ULong; }
num_put_overload select_overload (int)            { return Long; }
num_put_overload select_overload (unsigned int)   { return ULong; }
num_put_overload select_overload (long)           { return Long; }
num_put_overload select_overload (unsigned long)  { return ULong; }

#ifdef _RWSTD_LONG_LONG

num_put_overload select_overload (_RWSTD_LONG_LONG)          { return Long; }
num_put_overload select_overload (unsigned _RWSTD_LONG_LONG) { return ULong; }

#endif

num_put_overload select_overload (float)          { return Dbl; }
num_put_overload select_overload (double)         { return Dbl; }

#ifndef _RWSTD_NO_LONG_DOUBLE

num_put_overload select_overload (long double)    { return LDbl; }

#endif   // _RWSTD_NO_LONG_DOUBLE

num_put_overload select_overload (const void*)    { return PVoid; }


template <class charT>
struct test_num_put: std::num_put<charT, std::ostreambuf_iterator<charT> >
{
    typedef charT                           char_type;
    typedef std::ostreambuf_iterator<charT> iter_type;
    typedef std::num_put<charT, iter_type>  NumPut;
    typedef std::ios_base                   Ios;

    explicit test_num_put (std::size_t refs = 0)
        : NumPut (refs),
          throws_ (num_put_overload ()),
          fails_ (num_put_overload ()) {
        std::memset (ncalls_, 0, sizeof ncalls_);
    }

    iter_type test (iter_type, num_put_overload) const;

#define TEST(it, val)   test (it, select_overload (val))

#ifndef _RWSTD_NO_NATIVE_BOOL

    virtual iter_type
    do_put (iter_type it, Ios &fl, char_type fill, bool val) const {
        return NumPut::do_put (TEST (it, val), fl, fill, val);
    }

#endif   // _RWSTD_NO_NATIVE_BOOL

    virtual iter_type
    do_put (iter_type it, Ios &fl, char_type fill, long val) const {
        return NumPut::do_put (TEST (it, val), fl, fill, val);
    }

    virtual iter_type
    do_put (iter_type it, Ios &fl, char_type fill, unsigned long val) const {
        return NumPut::do_put (TEST (it, val), fl, fill, val);
    }

    virtual iter_type
    do_put (iter_type it, Ios &fl, char_type fill, double val) const {
        return NumPut::do_put (TEST (it, val), fl, fill, val);
    }

    virtual iter_type
    do_put (iter_type it, Ios &fl, char_type fill, long double val) const {
        return NumPut::do_put (TEST (it, val), fl, fill, val);
    }

    virtual iter_type
    do_put (iter_type it, Ios &fl, char_type fill, const void *val) const {
        return NumPut::do_put (TEST (it, val), fl, fill, val);
    }

#ifdef _RWSTD_LONG_LONG

    // extensions
    virtual iter_type
    do_put (iter_type it, Ios &fl, char_type fill,
            _RWSTD_LONG_LONG val) const {
        return NumPut::do_put (TEST (it, val), fl, fill, val);
    }

    virtual iter_type
    do_put (iter_type it, Ios &fl, char_type fill,
            unsigned _RWSTD_LONG_LONG val) const {
        return NumPut::do_put (TEST (it, val), fl, fill, val);
    }

#endif   // _RWSTD_LONG_LONG

#undef TEST

    int ncalls_ [ULLong + 1];
    num_put_overload throws_;
    num_put_overload fails_;
};


template <class charT>
typename test_num_put<charT>::iter_type
test_num_put<charT>::test (iter_type it, num_put_overload which) const
{
    ++_RWSTD_CONST_CAST (test_num_put*, this)->ncalls_ [which];

    if (throws_ == which)
        throw which;

    if (fails_ == which) {

        // construct an iter_type object in a failed state
        test_streambuf<charT> tsb (0, 0);
        iter_type failed (&tsb);
        failed = char_type ();

        _RWSTD_ASSERT (failed.failed ());

        return failed;
    }

    return it;
}

/***************************************************************************/

bool uncaught = false;


template <class charT>
struct DtorChek
{
    typedef std::basic_ostream<charT, std::char_traits<charT> > Ostream;
    typedef typename Ostream::sentry                            Sentry;

    Ostream *pstrm_;

    ~DtorChek () {

#ifndef _RWSTD_UNCAUGHT_EXCEPTION

        uncaught = std::uncaught_exception ();

#endif   // _RWSTD_UNCAUGHT_EXCEPTION

        const Sentry guard (*pstrm_);
    }
};


template <class charT>
void test_sentry (charT)
{
    static const char* const cname = rw_any_t (charT ()).type_name ();

    typedef std::basic_ostream<charT, std::char_traits<charT> > Ostream;
    typedef typename Ostream::sentry                            Sentry;

    const Sentry *pguard = 0;

    {
        rw_info (0, 0, __LINE__,
                 "std::basic_ostream<%s>::sentry::sentry "
                 "(basic_ostream&)", cname);

        // verify that sentry ctor handles streams with rdbuf() == 0
        static Ostream strm (0);

        strm.setf (std::ios_base::unitbuf);

        // pguard will be deleted in sentry dtor block
        pguard = new Sentry (strm);
    }

    {
        // verify 27.6.2.3, p2 and 3:
        //
        // explicit sentry(basic_ostream<charT,traits>& os);
        //
        // -2-  If os.good() is nonzero, prepares for formatted or
        //      unformatted output. If os.tie() is not a null pointer,
        //      calls os.tie()->flush().
        // -3-  If, after any preparation is completed, os.good() is
        //      true, ok_ == true otherwise, ok_ == false.

        // verify that sentry ctor calls stream.tie()->flush() once
        // and that the object returns true when converted to bool
        test_streambuf<charT> tsb_tied (0);
        test_streambuf<charT> tsb_strm (0);

        Ostream tied (&tsb_tied);
        Ostream strm (&tsb_strm);

        strm.tie (&tied);

        int nsyncs = tsb_tied.ncalls_ [Sync];

        const Sentry guard (strm);

        rw_assert (1 + nsyncs == tsb_tied.ncalls_ [Sync], 0, __LINE__,
                   "std::basic_ostream<%s>::sentry::sentry "
                   "(basic_ostream &s) called s.tie ()->flush() once; "
                   "got %d times",
                   cname, tsb_tied.ncalls_ [Sync] - nsyncs);

        rw_info (0, 0, __LINE__, "std::basic_ostream<%s>::sentry::operator "
                 "bool () const", cname);

        rw_assert (bool (guard), 0, __LINE__,
                   "std::basic_ostream<%s>::sentry::operator bool() == true "
                   "after a successful construction", cname);
    }

    {
        // verify 27.6.2.3, p3: i.e., same as above, but check that
        // the ctor sets ok_ to false after the preparation fails
        // (which is done by having stream.tie()->flush() call 
        // stream.setf (failbit))

        test_streambuf<charT> tsb_strm (0);
        Ostream strm (&tsb_strm);

        // create a use-defined streambuf object and associate it
        // with the stream object, strm, constructed above so that
        // when the latter calls strm.tie()->flush() the former
        // can set failbit in strm
        test_streambuf<charT> tsb_tied (&strm);
        Ostream tied (&tsb_tied);

        // tie the two streams together
        strm.tie (&tied);

        // sentry ctor calls strm.tie()->flush() which sets failbit
        // in strm's; the sentry ctor should detect this condition
        const Sentry guard (strm);

        rw_assert (!guard, 0, __LINE__,
                   "std::basic_ostream<%s>::sentry::operator bool() == "
                   "false after a failed construction (stream.rdstate() "
                   "== %{Is})", cname, strm.rdstate ());
    }

    {
        // verify 27.6.2.3, p3: i.e., same as above, but check that
        // stream.tie()->flush() is not called when stream.good() is
        // false

        test_streambuf<charT> tsb_strm (0);
        Ostream strm (&tsb_strm);

        // create a use-defined streambuf object and associate it
        // with the stream object, strm, constructed above to keep
        // track of the number of any calls to sync() from flush()
        test_streambuf<charT> tsb_tied (&strm);
        Ostream tied (&tsb_tied);

        // tie the two streams together
        strm.tie (&tied);

        strm.setstate (std::ios_base::failbit);

        // sentry ctor should not call strm.tie()->flush() if
        // strm.good() returns false
        const Sentry guard (strm);

        rw_assert (!tsb_tied.ncalls_ [Sync], 0, __LINE__,
                   "std::basic_ostream<%s>::sentry::sentry(basic_ostream&) "
                   "unexpectedly called stream.tie()->flush() when "
                   "stream.good() == false", cname);

        rw_assert (!guard, 0, __LINE__,
                   "std::basic_ostream<%s>::sentry::operator bool() == "
                   "false for a stream whose stream.rdstate() = %{Is}",
                   cname, strm.rdstate ());
    }

#ifndef _RWSTD_NO_EXCEPTIONS

    if (0 <= rw_opt_exceptions) {

        test_streambuf<charT> tsb_strm (0);
        Ostream strm (&tsb_strm);

        test_streambuf<charT> tsb_tied (&strm);
        Ostream tied (&tsb_tied);

        strm.tie (&tied);

        tsb_tied.throws_ = Sync;

        // verify that an exception thrown during the call to
        // strm.tie()->flush() is caught and not propagated, and in
        // response to it badbit is set in the tied stream, but that
        // strm is not affected in any way
        try {

            // sentry ctor calls strm.tie()->flush() which should call
            // strm.tie()->flush() (or its equivalent), which in turn
            // calls rdbuf()->pubsync() which throws an exception (from
            // the virtual overridden above)
            // the sentry ctor must not prevent badbit from being set
            // in the tied stream but it must not allow the exception
            // to propagate to its caller
            const Sentry guard (strm);

            rw_assert (!!guard, 0, __LINE__,
                       "std::basic_ostream<%s>::sentry::operator bool() == "
                       "true after it failed to flush a tied stream due to "
                       "an exception (stream.rdstate() == %{Is})",
                       cname, strm.rdstate ());
        }
        catch (...) {
            rw_assert (false, 0, __LINE__,
                       "std::basic_ostream<%s>::sentry::sentry(basic_ostream&)"
                       " unexpectedly allowed an exception to propagate",
                       cname);
        }

        // verify that the sentry ctor did not affect the state of the stream
        // as a result of the exception thrown during the flushing of the tied
        // stream
        rw_assert (strm.good (), 0, __LINE__,
                   "std::basic_ostream<%s>::sentry::sentry(basic_ostream&) "
                   "unexpectedly affected the state of the stream as a result "
                   "pf an exception thrown during the flushing of a tied "
                   "stream; stream state = %{Is}", cname, strm.rdstate ());

        // verify that the the tied stream object's state has set badbit
        // in response to the exception thrown from its stream buffer
        rw_assert (tied.bad (), 0, __LINE__,
                   "std::basic_ostream<%s>::sentry::sentry(basic_ostream&) "
                   "failed to cause badbit to be set in stream's tied object "
                   "after an exception; tied stream's state = %{Is}",
                   cname, tied.rdstate ());
    }

#endif   // _RWSTD_NO_EXCEPTIONS

    {
        rw_info (0, 0, __LINE__,
                 "std::basic_ostream<%s>::sentry::~sentry()", cname);

        // verify that sentry dtor handles streams with rdbuf() == 0

        // pguard dynamically constructed in sentry ctor block
        delete pguard;
    }

    {
        // verify 27.6.2.3, p4:
        //
        // ~sentry();
        //
        // -4-  If ((os.flags() & iose::unitbuf) && !uncaught_exception())
        //      is true, calls os.flush().

        test_streambuf<charT> tsb (0);
        Ostream strm (&tsb);

        int nsyncs;

        // verify that os.flush() is not called if ios::unitbuf is clear
        {
            const Sentry guard (strm);

            nsyncs = tsb.ncalls_ [Sync];
        }

        rw_assert (nsyncs == tsb.ncalls_ [Sync], 0, __LINE__,
                   "std::basic_ostream<%s>::sentry::~sentry() unexpectedly "
                   "called stream.flush () when ios::unitbuf is clear",
                   cname);

        // verify that os.flush() is called if ios::unitbuf is set
        // (assuming uncaugt_exception() returns false
        {
            strm.setf (std::ios_base::unitbuf);

            const Sentry guard (strm);

            nsyncs = tsb.ncalls_ [Sync];
        }

        rw_assert (1 + nsyncs == tsb.ncalls_ [Sync], 0, __LINE__,
                   "std::basic_ostream<%s>::sentry::~sentry() failed to call "
                   "stream.flush () when ios::unitbuf is set",
                   cname);

#ifndef _RWSTD_NO_EXCEPTIONS
#  ifndef _RWSTD_NO_UNCAUGHT_EXCEPTION

        if (0 <= rw_opt_exceptions) {

            // verify that os.flush() is not called if ios::unitbuf is set
            // but uncaught_exception() returns true

            nsyncs = tsb.ncalls_ [Sync];

            try {
                strm.setf (std::ios_base::unitbuf);

                DtorChek<charT> to_be_destroyed;

                to_be_destroyed.pstrm_ = &strm;

                throw 0;
            }
            catch (...) {
            }

            rw_assert (nsyncs == tsb.ncalls_ [Sync], 0, __LINE__,
                       "std::basic_ostream<%s>::sentry::~sentry() "
                       "unexpectedly called stream.flush() when "
                       "ios::unitbuf is set but uncaught_exception() "
                       "returns true", cname);

            rw_assert (uncaught, 0, __LINE__,
                       "std::uncaught_exception () == true when an exception "
                       "is pending (language runtime library error)");
        }

#  endif   // _RWSTD_NO_UNCAUGHT_EXCEPTION
#endif   // _RWSTD_NO_EXCEPTIONS

    }
}


/***************************************************************************/


// for convenience
#define Boolalpha   std::ios_base::boolalpha
#define Dec         std::ios_base::dec
#define Fixed       std::ios_base::fixed
#define Hex         std::ios_base::hex
#define Internal    std::ios_base::internal
#define Left        std::ios_base::left
#define Oct         std::ios_base::oct
#define Right       std::ios_base::right
#define Scientific  std::ios_base::scientific
#define Showbase    std::ios_base::showbase
#define Showpoint   std::ios_base::showpoint
#define Showpos     std::ios_base::showpos
#define Skipws      std::ios_base::skipws
#define Unitbuf     std::ios_base::unitbuf
#define Uppercase   std::ios_base::uppercase
#define Bin         std::ios_base::bin
#define Adjustfield std::ios_base::adjustfield
#define Basefield   std::ios_base::basefield
#define Floatfield  std::ios_base::floatfield
#define Nolock      std::ios_base::nolock
#define Nolockbuf   std::ios_base::nolockbuf

#define Bad         std::ios_base::badbit
#define Eof         std::ios_base::eofbit
#define Fail        std::ios_base::failbit
#define Good        std::ios_base::goodbit


const int Throw = 1 << 16;


template <class charT, class T>
void test_formatted (charT, int line1, int line2,
                     T           val,
                     const char *str,
                     int         fmtflags,
                     int         width,
                     int         fill,
                     int         rdstate,
                     int         exceptions,
                     int         failure = 0,
                     int         fail_when = 1)
{
    static const char* const cname = rw_any_t (charT ()).type_name ();
    static const char* const tname = rw_any_t (T ()).type_name ();

    typedef std::basic_ostream<charT, std::char_traits<charT> > Ostream;

    charT charbuf [256] = { 0 };

    // define prior to the definition of `tsb' below
    // to guarantee the proper order of destruction
    test_num_put<charT> tnp (1);

    test_streambuf<charT> tsb (charbuf, sizeof charbuf / sizeof *charbuf);

    if (failure & Throw) {
        failure &= ~Throw;

        if (failure < Bool)
            tsb.throws_ = streambuf_failure (failure);
        else
            tnp.throws_ = num_put_overload (failure);
    }
    else {
        if (failure < Bool)
            tsb.fails_ = streambuf_failure (failure);
        else
            tnp.fails_ = num_put_overload (failure);
    }

    tsb.fail_when_ = fail_when;

    Ostream os (&tsb);

    if (select_overload (T ())) {

        // for arithmetic types and void*, imbue a custom num_put facet
        os.imbue (std::locale (std::locale::classic (), &tnp));
    }

    os.flags (std::ios_base::fmtflags (fmtflags));
    os.width (std::streamsize (width));
    os.exceptions (std::ios_base::iostate (exceptions));

    if (-1 != fill)
        os.fill (charT (fill));

    enum { E_unknown = 1, E_failure };
    int caught = 0;

    const char *caught_what = "none (none thrown)";

    try {
        os << val;
    }
    catch (streambuf_failure e) {
        caught = e;
        caught_what = "streambuf_failure";
    }
    catch (num_put_overload e) {
        caught = e;
        caught_what = "num_put_overload";
    }
    catch (const std::ios_base::failure&) {
        caught = E_failure;
        caught_what = "std::ios_base::failure";
    }
    catch (...) {
        caught = E_unknown;
        caught_what = "unknown exception";
    }

    if (str) {
        char cbuf [256] = "";
        for (std::size_t i = 0; i != sizeof cbuf && str [i]; ++i)
            cbuf [i] = char (charbuf [i]);

        rw_assert (0 == std::strcmp (cbuf, str), __FILE__, line1,
                   "%d. std::basic_ostream<%s>::operator<<(%s = %{#lc}) "
                   "inserted \"%s\", expected \"%s\"",
                   line2, cname, tname, cbuf, str);
    }

    // verify that stream is in the expected state
    rw_assert (rdstate == os.rdstate (), __FILE__, line1,
               "%d. std::basic_ostream<%s>::operator<<(%s = %{#lc})"
               ".rdstate() == %{Is}, got %{Is}",
               line2, cname, tname, val, rdstate, os.rdstate ());

    if (!tsb.throws_ && !tnp.throws_) {

        bool pass;

#ifdef _RWSTD_NO_EXT_KEEP_WIDTH_ON_FAILURE
        // verify that width(0) has been called (unless there are exceptions
        // involved, in which case it's unspecified whether width(0) has or
        // has not been called
        pass = !((!exceptions || !tsb.fails_ && !tnp.fails_) && os.width ());

        rw_assert (pass, __FILE__, line1,
                   "%d. std::basic_ostream<%s>::operator<<(%s = %{#lc})"
                   ".width () == 0, got %d",
                   line2, cname, tname, val, os.width  ());
#endif   // _RWSTD_NO_EXT_KEEP_WIDTH_ON_FAILURE

        // verify that ios_base::failure has been thrown (and caught)
        // if badbit is set in exceptions
        pass = !(exceptions & os.rdstate () && caught != E_failure);

        rw_assert (pass, __FILE__, line1,
                   "%d. std::basic_ostream<%s>::operator<<(%s = %{#lc}) "
                   "set %{Is} but failed to throw ios_base::failure "
                   "when the same bit is set in exceptions",
                   line2, cname, tname, val, rdstate);
    }

    if (tsb.throws_) {

        // verify that the same exception (and not ios_base::failure)
        // as the one thrown from basic_filebuf has been propagated
        // and caught when badbit is set in exceptions, and that no
        // exception has been thrown if exceptions is clear
        if (exceptions & Bad && caught != tsb.throws_) {

            rw_assert (false, __FILE__, line1,
                       "%d. std::basic_ostream<%s>::operator<<(%s = %{#lc}) "
                       "failed to propagate an exception thrown by "
                       "basic_filebuf; caught %s instead",
                       line2, cname, tname, val, caught_what);
        }
        else {
            rw_assert ((exceptions & Bad) || !caught, 0, line1,
                       "%d. std::basic_ostream<%s>::operator<<(%s = %{#lc}) "
                       "propagated an exception thrown by basic_filebuf "
                       "when ios_base::badbit is clear in exceptions",
                       line2, cname, tname, val);
        }
    }

    if (tnp.throws_) {

        // verify that the same exception (and not ios_base::failure)
        // as the one thrown from num_put has been propagated and caught
        // when badbit is set in exceptions, and that no exception has
        // been thrown if exceptions is clear
        if (exceptions & Bad)
            rw_assert (caught == tnp.throws_, 0, line1,
                       "%d. std::basic_ostream<%s>::operator<<(%s = %{#lc}) "
                       "failed to propagate exception thrown by "
                       "basic_filebuf; caught %s instead",
                       line2, cname, tname, val, caught_what);
        else
            rw_assert (!caught, __FILE__, line1,
                       "%d. std::basic_ostream<%s>::operator<<(%s = %{#lc}) "
                       "propagated an exception thrown by basic_filebuf"
                       "when ios_base::badbit is clear in exceptions",
                       line2, cname, tname, val);
    }
}

/***************************************************************************/

template <class charT>
bool is_char (charT) { return false; }

bool is_char (char) { return true; }
bool is_char (signed char) { return true; }
bool is_char (unsigned char) { return true; }

bool is_char (const char*) { return true; }
bool is_char (const signed char*) { return true; }
bool is_char (const unsigned char*) { return true; }

template <class charT>
bool is_wchar_t (charT) { return false; }

#ifndef _RWSTD_NO_WCHAR_T

bool is_wchar_t (wchar_t) { return true; }
bool is_wchar_t (const wchar_t*) { return true; }

#endif   // _RWSTD_NO_WCHAR_T


template <class charT, class T>
void test_formatted (charT, int line, T val, 
                     int flags, int width, const char *str, int fill = -1)
{
    static const char* const cname = rw_any_t (charT ()).type_name ();
    static const char* const tname = rw_any_t (T ()).type_name ();

    static int done = 0;

    if (!done++) {
        // show message only the first time for each specialization

        if (is_char (T ()) || is_wchar_t (T ()))
            rw_info (0, 0, __LINE__,
                     "std::operator<<(basic_ostream<%s>&, %s)", cname, tname);
        else
            rw_info (0, 0, __LINE__,
                     "std::basic_ostream<%s>::operator<<(%s)", cname, tname);
    }

    // determine which overload of num_put::put() is expected
    // to be called for a value of type T; 0 for non-numeric
    const int npo = select_overload (val);

    typedef std::basic_ostream<charT, std::char_traits<charT> > Ostream;

#define T      charT (), line, __LINE__
#define TEST   test_formatted

    TEST (T, val, str, flags, width, fill, Good, 0, 0);

    if (npo) {
        // exercise the behavior of basic_ostream with exception bits
        // clear when num_put fails by returning an ostreambuf_iterator
        // in a failed state  (expect ios::badbit to be set w/o throwing
        // an exception)
        TEST (T, val, "", flags, width, fill, Bad, 0, npo);

        // exercise the behavior of basic_ostream with ios::badbit
        // set in exceptions when num_put fails by returning an
        // ostreambuf_iterator in a failed state  (expect ios::badbit
        // to be set and ios::failure to be thrown)
        TEST (T, val, "", flags, width, fill, Bad, Bad, npo);

        // exercise the behavior of basic_ostream with exceptions bits
        // clear when num_put fails by throwing an exception (expect
        // ios::badbit to be set and no exception to propagate)
        TEST (T, val, "", flags, width, fill, Bad, 0, Throw | npo);

        // exercise the behavior of basic_ostream with ios::badbit
        // set in exceptions when num_put fails by throwing an exception
        // (expect ios::badbit to be set and the original exception to
        // propagate)
        TEST (T, val, "", flags, width, fill, Bad, Bad, Throw | npo);
    }

    // exercise the behavior of basic_ostream with exception bits clear
    // when basic_streambuf::overflow() fails by returning eof() (expect
    // ios::badbit to be set w/o throwing an exception)
    TEST (T, val, "", flags, width, fill, Bad, 0, Overflow);

    // exercise the behavior of basic_ostream with ios::badbit set in
    // exceptions when basic_streambuf::overlow() fails by returning
    // eof() (expect ios::badbit to be set and ios::failure to be thrown)
    TEST (T, val, "", flags, width, fill, Bad, Bad, Overflow);

    // exercise the behavior of basic_ostream with exceptions bits clear
    // when basic_streambuf::overlow() fails by throwing an exception
    // (expect ios::badbit to be set and no exception to propagate)
    TEST (T, val, "", flags, width, fill, Bad, 0, Throw | Overflow);

    // exercise the behavior of basic_ostream with ios::badbit set in
    // exceptions when basic_streambuf::overflow() fails by throwing
    // an exception (expect ios::badbit to be set and the original
    // exception to propagate)
    TEST (T, val, "", flags, width, fill, Bad, Bad, Throw | Overflow);
}

/***************************************************************************/


template <class charT>
void test_formatted (charT)
{
#undef T
#define T(val)   charT (), __LINE__, val

#undef TEST
#define TEST test_formatted

    //////////////////////////////////////////////////////////////////
    // exercise operator<< (basic_ostream, char)

    //        +------------ character to insert
    //        |    +------- fmtflags
    //        |    |  +---- field width
    //        |    |  |  +- expected output
    //        |    |  |  |
    //        v    v  v  v
    TEST (T ('a'), 0, 0, "a");
    TEST (T ('A'), 0, 0, "A");
    TEST (T ('@'), 0, 1, "@");
    TEST (T ('#'), 0, 2, " #");

    if (is_wchar_t (charT ())) {

#ifndef _RWSTD_NO_NATIVE_WCHAR_T

        //////////////////////////////////////////////////////////////////
        // exercise operator<< (basic_ostream, wchar_t)
        TEST (T (L'b'), 0, 0, "b");
        TEST (T (L'B'), 0, 0, "B");

#endif   // _RWSTD_NO_NATIVE_WCHAR_T

    }
    else {

        // no operator<< (basic_ostream<wchar_t>, {signed,unsigned} char)

        //////////////////////////////////////////////////////////////////
        // exercise operator<< (basic_ostream, signed char)
        typedef signed char SChar;

        TEST (T (SChar ('b')), 0, 0, "b");
        TEST (T (SChar ('B')), 0, 0, "B");

        //////////////////////////////////////////////////////////////////
        // exercise operator<< (basic_ostream, signed char)
        typedef unsigned char UChar;

        TEST (T (UChar ('c')), 0, 0, "c");
        TEST (T (UChar ('C')), 0, 0, "C");
    }


#ifndef _RWSTD_NO_NATIVE_BOOL

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (bool)
    TEST (T (false), 0, 0, "0");
    TEST (T (true),  0, 0, "1");

    TEST (T (false), Hex | Showbase, 0, "0");
    TEST (T (true),  Hex | Showbase, 0, "0x1");

    TEST (T (false), Boolalpha, 0, "false");
    TEST (T (true),  Boolalpha, 0, "true");

#endif   // _RWSTD_NO_NATIVE_BOOL


    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (short)
    TEST (T (short ( 0)), 0, 0,  "0");
    TEST (T (short ( 1)), 0, 0,  "1");
    TEST (T (short (-1)), 0, 0, "-1");

#if SHRT_MAX == 32767

    TEST (T (short (SHRT_MAX)), 0, 0,  "32767");
    TEST (T (short (SHRT_MIN)), 0, 0, "-32768");

    TEST (T (short (SHRT_MAX)), Hex, 0, "7fff");
    TEST (T (short (SHRT_MIN)), Hex, 0, "8000");

#elif SHRT_MAX == 2147483647

    TEST (T (short (SHRT_MAX)), 0, 0,  "2147483647");
    TEST (T (short (SHRT_MIN)), 0, 0, "-2147483648");

    TEST (T (short (SHRT_MAX)), Hex, 0, "ffffffff");
    TEST (T (short (SHRT_MIN)), Hex, 0, "80000000");

#endif   // SHRT_MAX

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (unsigned short)
    typedef unsigned short UShort;

    TEST (T (UShort (  0)), 0, 0,   "0");
    TEST (T (UShort (  1)), 0, 0,   "1");
    TEST (T (UShort (255)), 0, 0, "255");

#if USHRT_MAX == 0xffffU

    TEST (T (UShort (USHRT_MAX)), 0, 0,  "65535");
    TEST (T (UShort (USHRT_MAX)), Hex, 0, "ffff");

#elif USHRT_MAX == 0xffffffffU

    TEST (T (UShort (SHRT_MAX)), 0, 0,   "4294967295");
    TEST (T (UShort (SHRT_MAX)), Hex, 0,   "ffffffff");

#endif   // USHRT_MAX

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (int)
    TEST (T ( 0), 0, 0,  "0");
    TEST (T ( 1), 0, 0,  "1");
    TEST (T (-1), 0, 0, "-1");

#if INT_MAX == 32767

    TEST (T (INT_MAX), 0, 0,  "32767");
    TEST (T (INT_MIN), 0, 0, "-32768");

    TEST (T (INT_MAX), Hex, 0, "7fff");
    TEST (T (INT_MIN), Hex, 0, "8000");

#elif INT_MAX == 2147483647

    TEST (T (INT_MAX), 0, 0,  "2147483647");
    TEST (T (INT_MIN), 0, 0, "-2147483648");

    TEST (T (INT_MAX), Hex, 0, "7fffffff");
    TEST (T (INT_MIN), Hex, 0, "80000000");

#elif INT_MAX == 9223372036854775807

    TEST (T (INT_MAX), 0, 0,  "9223372036854775807");
    TEST (T (INT_MIN), 0, 0, "-9223372036854775808");

    TEST (T (INT_MAX), Hex, 0, "7fffffffffff");
    TEST (T (INT_MIN), Hex, 0, "800000000000");

#endif   // INT_MAX

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (unsigned int)
    TEST (T (  0), 0, 0,   "0");
    TEST (T (  1), 0, 0,   "1");
    TEST (T (255), 0, 0, "255");

#if UINT_MAX == 0xffffU

    TEST (T (UINT_MAX), 0, 0,  "65535");
    TEST (T (UINT_MAX), Hex, 0, "ffff");

#elif UINT_MAX == 0xffffffffU

    TEST (T (UINT_MAX), 0, 0,   "4294967295");
    TEST (T (UINT_MAX), Hex, 0,   "ffffffff");

#elif UINT_MAX == 0xffffffffffffffffU

    TEST (T (UINT_MAX), 0, 0,   "18446744073709551615");
    TEST (T (UINT_MAX), Hex, 0,     "ffffffffffffffff");

#endif   // UINT_MAX

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (long)
    TEST (T ( 0L), 0, 0,  "0");
    TEST (T ( 1L), 0, 0,  "1");
    TEST (T (-1L), 0, 0, "-1");

#if LONG_MAX == 32767L

    TEST (T (LONG_MAX), 0, 0,  "32767");
    TEST (T (LONG_MIN), 0, 0, "-32768");

    TEST (T (LONG_MAX), Hex, 0, "7fff");
    TEST (T (LONG_MIN), Hex, 0, "8000");

#elif LONG_MAX == 2147483647L

    TEST (T (LONG_MAX), 0, 0,  "2147483647");
    TEST (T (LONG_MIN), 0, 0, "-2147483648");

    TEST (T (LONG_MAX), Hex, 0, "7fffffff");
    TEST (T (LONG_MIN), Hex, 0, "80000000");

#elif LONG_MAX == 9223372036854775807L

    TEST (T (LONG_MAX), 0, 0,  "9223372036854775807");
    TEST (T (LONG_MIN), 0, 0, "-9223372036854775808");

    TEST (T (LONG_MAX), Hex, 0, "7fffffffffff");
    TEST (T (LONG_MIN), Hex, 0, "800000000000");

#endif   // LONG_MAX

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (unsigned int)

    TEST (T (  0UL), 0, 0,   "0");
    TEST (T (  1UL), 0, 0,   "1");
    TEST (T (257UL), 0, 0, "257");

#if ULONG_MAX == 0xffffUL

    TEST (T (ULONG_MAX), 0, 0,  "65535");
    TEST (T (ULONG_MAX), Hex, 0, "ffff");

#elif ULONG_MAX == 0xffffffffUL

    TEST (T (ULONG_MAX), 0, 0,   "4294967295");
    TEST (T (ULONG_MAX), Hex, 0,   "ffffffff");

#elif ULONG_MAX == 0xffffffffffffffffUL

    TEST (T (ULONG_MAX), 0, 0,   "18446744073709551615");
    TEST (T (ULONG_MAX), Hex, 0,     "ffffffffffffffff");

#endif   // ULONG_MAX

#ifdef _RWSTD_LONG_LONG

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (long long)
    typedef _RWSTD_LONG_LONG LongLong;

    TEST (T (LongLong ( 0)), 0, 0,  "0");
    TEST (T (LongLong ( 1)), 0, 0,  "1");
    TEST (T (LongLong (-1)), 0, 0, "-1");

    // avoid using the preprocessor here to prevent warnings
    // or errors about invalid preprocessor constants (e.g.,
    // PR #28595)
    if (_RWSTD_LLONG_MAX == 32767L) {

        TEST (T (_RWSTD_LLONG_MAX), 0, 0,  "32767");
        TEST (T (_RWSTD_LLONG_MIN), 0, 0, "-32768");

        TEST (T (_RWSTD_LLONG_MAX), Hex, 0, "7fff");
        TEST (T (_RWSTD_LLONG_MIN), Hex, 0, "8000");
    }
    else if (_RWSTD_LLONG_MAX == 2147483647L) {

        TEST (T (_RWSTD_LLONG_MAX), 0, 0,  "2147483647");
        TEST (T (_RWSTD_LLONG_MIN), 0, 0, "-2147483648");

        TEST (T (_RWSTD_LLONG_MAX), Hex, 0, "7fffffff");
        TEST (T (_RWSTD_LLONG_MIN), Hex, 0, "80000000");

    }
    else if (_RWSTD_LLONG_MAX > 2147483647L) {

        TEST (T (_RWSTD_LLONG_MAX), 0, 0,  "9223372036854775807");
        TEST (T (_RWSTD_LLONG_MIN), 0, 0, "-9223372036854775808");

        TEST (T (_RWSTD_LLONG_MAX), Hex, 0, "7fffffffffff");
        TEST (T (_RWSTD_LLONG_MIN), Hex, 0, "800000000000");

    }

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (unsigned long long)

    typedef unsigned _RWSTD_LONG_LONG ULongLong;

    TEST (T (ULongLong (  0)), 0, 0,   "0");
    TEST (T (ULongLong (  1)), 0, 0,   "1");
    TEST (T (ULongLong (257)), 0, 0, "257");

    if (_RWSTD_ULLONG_MAX == 0xffffUL) {
        TEST (T (_RWSTD_ULLONG_MAX), 0, 0,  "65535");
        TEST (T (_RWSTD_ULLONG_MAX), Hex, 0, "ffff");
    }
    else if (_RWSTD_ULLONG_MAX == 0xffffffffUL) {
        TEST (T (_RWSTD_ULLONG_MAX), 0, 0,   "4294967295");
        TEST (T (_RWSTD_ULLONG_MAX), Hex, 0,   "ffffffff");
    }
    else if (_RWSTD_ULLONG_MAX > 0xffffffffUL) {
        TEST (T (_RWSTD_ULLONG_MAX), 0, 0,   "18446744073709551615");
        TEST (T (_RWSTD_ULLONG_MAX), Hex, 0,     "ffffffffffffffff");
    }

#endif   // _RWSTD_LONG_LONG

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (float)
    TEST (T ( 0.0f), 0, 0, "0");
    TEST (T ( 1.0f), 0, 0, "1");
    TEST (T (-2.1f), 0, 0, "-2.1");

    // exercise the default precision of 6, which gives the maximum number
    // of significant digits for the "%g" or "%G" conversion, which is
    // the default for the default flags
    TEST (T (3.1415926f), 0, 0,          "3.14159");
    TEST (T (3.1415926f), Scientific, 0, "3.14159");

    // exercise the default precision of 6, which gives the number of
    // digits after the decimal point for the "%f" or "%F" conversion,
    // which is used for the fixed flag
    TEST (T (12.3456789f), Fixed, 0, "12.345679");

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (double)
    TEST (T ( 0.0), 0, 0, "0");
    TEST (T ( 2.0), 0, 0, "2");
    TEST (T (-4.2), 0, 0, "-4.2");

    // exercise the default precision of 6, which gives the maximum number
    // of significant digits for the "%g" or "%G" conversion, which is
    // the default for the default flags
    TEST (T (1.23456789), 0, 0,          "1.23457");
    TEST (T (9.87654321), Scientific, 0, "9.87654");

    // exercise the default precision of 6, which gives the number of
    // digits after the decimal point for the "%f" or "%F" conversion,
    // which is used for the fixed flag
    TEST (T (12.3456789), Fixed, 0, "12.345679");

#ifndef _RWSTD_NO_LONG_DOUBLE

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (long double)
    TEST (T ( 0.0L), 0, 0, "0");
    TEST (T ( 3.0L), 0, 0, "3");
    TEST (T (-5.3L), 0, 0, "-5.3");

    // exercise the default precision of 6, which gives the maximum number
    // of significant digits for the "%g" or "%G" conversion, which is
    // the default for the default flags
    TEST (T (1.23456789L), 0, 0,          "1.23457");
    TEST (T (9.87654321L), Scientific, 0, "9.87654");

    // exercise the default precision of 6, which gives the number of
    // digits after the decimal point for the "%f" or "%F" conversion,
    // which is used for the fixed flag
    TEST (T (12.3456789L), Fixed, 0, "12.345679");

#endif   // _RWSTD_NO_LONG_DOUBLE

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (const void*)
    TEST (T ((void*)0),      0, 0, 0 /* format implementation-defined */);
    TEST (T ((void*)1),      0, 0, 0);
    TEST (T ((void*)0x2345), 0, 0, 0);

    //////////////////////////////////////////////////////////////////
    // exercise basic_ostream::operator<< (const char*)
    TEST (T ("a"),     0,                        0, "a");
    TEST (T ("b"),     0,                       -1, "b");
    TEST (T ("ab"),    0,                        0, "ab");
    TEST (T ("abc"),   0,                        4, " abc");
    TEST (T ("def"),   Left,                     5, "def<<", '<');
    TEST (T ("ghi"),   Right,                    6, ">>>ghi", '>');
    TEST (T ("jklm"),  Internal,                 7, "   jklm");
    TEST (T ("nopqr"), Left | Right,             8, "   nopqr");
    TEST (T ("stuv"),  Left | Internal,          9, "     stuv");
    TEST (T ("wxy"),   Right | Internal,        10, "       wxy");
    TEST (T ("z"),     Left | Right | Internal, 11, "__________z", '_');

    if (is_char (charT ())) {

        // inserters overloaded on signed and unsigned char*
        // defined only for basic_ostream<char>, not wchar_t

        //////////////////////////////////////////////////////////////////
        // exercise basic_ostream::operator<< (const signed char*)

#undef SCH
#define SCH(val)      charT (), __LINE__, (const signed char*)val

        TEST (SCH ("A"),     0,                        0, "A");
        TEST (SCH ("B"),     0,                       -1, "B");
        TEST (SCH ("AB"),    0,                        0, "AB");
        TEST (SCH ("ABC"),   0,                        4, " ABC");
        TEST (SCH ("DEF"),   Left,                     5, "DEF++", '+');
        TEST (SCH ("GHI"),   Right,                    6, "---GHI", '-');
        TEST (SCH ("JKLM"),  Internal,                 7, "   JKLM");
        TEST (SCH ("NOPQR"), Left | Right,             8, "   NOPQR");
        TEST (SCH ("STUV"),  Left | Internal,          9, "     STUV");
        TEST (SCH ("WXY"),   Right | Internal,        10, "       WXY");
        TEST (SCH ("Z"),     Left | Right | Internal, 11, "##########Z", '#');

        //////////////////////////////////////////////////////////////////
        // exercise basic_ostream::operator<< (const unsigned char*)

#undef UCH
#define UCH(val)      charT (), __LINE__, (const unsigned char*)val

        TEST (UCH ("Z"),     0,                        0, "Z");
        TEST (UCH ("Y"),     0,                       -1, "Y");
        TEST (UCH ("YZ"),    0,                        0, "YZ");
        TEST (UCH ("XYZ"),   0,                        4, " XYZ");
        TEST (UCH ("UVW"),   Left,                     5, "UVW\\\\", '\\');
        TEST (UCH ("RST"),   Right,                    6, "///RST", '/');
        TEST (UCH ("OPQR"),  Internal,                 7, "   OPQR");
        TEST (UCH ("JKLMN"), Left | Right,             8, "   JKLMN");
        TEST (UCH ("FGHI"),  Left | Internal,          9, "     FGHI");
        TEST (UCH ("CDE"),   Right | Internal,        10, "       CDE");
        TEST (UCH ("AB"),    Left | Right | Internal, 11, "^^^^^^^^^AB", '^');

    }
}

/***************************************************************************/

template <class charT>
void test_flush (charT)
{
    static const char* const cname = rw_any_t (charT ()).type_name ();

    rw_info (0, 0, __LINE__,
             "std::basic_ostream<%s>::flush() ", cname);

    typedef std::basic_ostream<charT, std::char_traits<charT> >   Ostream;
    typedef std::basic_streambuf<charT, std::char_traits<charT> > Streambuf;

    {
        test_streambuf<charT> tsb (0);
        Ostream strm (&tsb);

        const int nsyncs = tsb.ncalls_ [Sync];

        strm.flush ();

        rw_assert (1 + nsyncs == tsb.ncalls_ [Sync], 0, __LINE__,
                   "std::basic_ostream<%s>::flush() called rdbuf()->sync() "
                   "once; got %d times",
                   cname, tsb.ncalls_ [Sync] - nsyncs);
    }

    {
        test_streambuf<charT> tsb (0);
        Ostream strm (&tsb);

        tsb.fails_     = Sync;
        tsb.fail_when_ = 1;

        const int nsyncs = tsb.ncalls_ [Sync];

        strm.flush ();

        rw_assert (1 + nsyncs == tsb.ncalls_ [Sync], 0, __LINE__,
                   "std::basic_ostream<%s>::flush() called rdbuf()->sync() "
                   "once; got %zu times",
                   cname, tsb.ncalls_ [Sync] - nsyncs);

        rw_assert (std::ios_base::badbit == strm.rdstate (), 0, __LINE__,
                   "std::basic_ostream<%s>::flush() failed to set badbit "
                   "after rdbuf()->pubsync() failed; state = %{Is}",
                   cname, strm.rdstate ());
    }

    {
        // exercise LWG issue 581: flush() not unformatted function

        test_streambuf<charT> tsb (0);
        Ostream strm (&tsb);

        strm.setstate (std::ios_base::failbit);

        const int nsyncs = tsb.ncalls_ [Sync];

        strm.flush ();

        rw_assert (nsyncs == tsb.ncalls_ [Sync], 0, __LINE__,
                   "std::basic_ostream<%s>::flush() unexpectedly made "
                   "%zu calls to rdbuf()->sync() when rdstate () == "
                   "failbit",
                   cname, tsb.ncalls_ [Sync] - nsyncs);

        rw_assert (std::ios_base::failbit == strm.rdstate (), 0, __LINE__,
                   "std::basic_ostream<%s>::flush() unexpectedly changed "
                   "state from std::ios_base::failbit to %{Is}",
                   cname, strm.rdstate ());
    }
}

/***************************************************************************/

template <class charT>
void run_test (charT)
{
    // exercise ostream::sentry
    if (rw_opt_sentry < 0)
        rw_note (0, 0, __LINE__, "sentry tests disabled");
    else
        test_sentry (charT ());

    // exercise formatted output functions
    if (rw_opt_formatted < 0)
        rw_note (0, 0, __LINE__, "tests of formatted functions disabled");
    else
        test_formatted (charT ());

    // exercise formatted output functions
    if (rw_opt_unformatted < 0)
        rw_note (0, 0, __LINE__, "tests of unformatted functions disabled");
    else {
        // FIXME: implement this
        // test_unformatted (charT ());
    }

    // exercise flush()
    if (rw_opt_flush < 0)
        rw_note (0, 0, __LINE__, "flush tests disabled");
    else
        test_flush (charT ());
}

/***************************************************************************/

static int
run_test (int, char**)
{
    run_test (char ());

#ifndef _RWSTD_NO_WCHAR_T

    run_test (wchar_t ());

#endif   // _RWSTD_NO_WCHAR_T

    return 0;
}

/***************************************************************************/

int main (int argc, char *argv[])
{
    return rw_test (argc, argv, __FILE__,
                    "lib.ostream",
                    0 /* no comment */,
                    run_test,
                    "|-sentry~ "
                    "|-formatted~ "
                    "|-unformatted~ "
                    "|-flush~ "
                    "|-no-exceptions~ ",
                    &rw_opt_sentry,
                    &rw_opt_formatted,
                    &rw_opt_unformatted,
                    &rw_opt_flush,
                    &rw_opt_exceptions);
}
