1 // (C) Copyright Frank Birbacher 2007
2 // Distributed under the Boost Software License, Version 1.0. (See accompanying
3 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
4 
5 // See http://www.boost.org/libs/iostreams for documentation.
6 
7 #include <boost/iostreams/categories.hpp>  // tags.
8 #include <boost/iostreams/detail/ios.hpp>  // openmode, seekdir, int types.
9 #include <boost/iostreams/detail/error.hpp>
10 #include <boost/iostreams/positioning.hpp>
11 #include <boost/iostreams/stream.hpp>
12 #include <boost/test/test_tools.hpp>
13 #include <boost/test/unit_test.hpp>
14 
15 using namespace boost::iostreams;
16 using boost::unit_test::test_suite;
17 
18 /*
19  * This test unit uses a custom device to trigger errors. The device supports
20  * input, output, and seek according to the SeekableDevice concept. And each
21  * of the required functions throw a special detail::bad_xxx exception. This
22  * should trigger the iostreams::stream to set the badbit status flag.
23  * Additionally the exception can be propagated to the caller if the exception
24  * mask of the stream allows exceptions.
25  *
26  * The stream offers four different functions: read, write, seekg, and seekp.
27  * Each of them is tested with three different error reporting concepts:
28  * test by reading status flags, test by propagated exception, and test by
29  * calling std::ios_base::exceptions when badbit is already set.
30  *
31  * In each case all of the status checking functions of a stream are checked.
32  */
33 
34 //------------------Definition of error_device--------------------------------//
35 
36 // Device whose member functions throw
37 struct error_device {
38     typedef char                 char_type;
39     typedef seekable_device_tag  category;
error_deviceerror_device40     error_device(char const*) {}
readerror_device41     std::streamsize read(char_type*, std::streamsize)
42     {
43         throw detail::bad_read();
44     }
writeerror_device45     std::streamsize write(const char_type*, std::streamsize)
46     {
47         throw detail::bad_write();
48     }
seekerror_device49     std::streampos seek(stream_offset, BOOST_IOS::seekdir)
50     {
51         throw detail::bad_seek();
52     }
53 };
54 
55 typedef stream<error_device> test_stream;
56 
57 //------------------Stream state tester---------------------------------------//
58 
check_stream_for_badbit(std::iostream & str)59 void check_stream_for_badbit(std::iostream& str)
60 {
61     BOOST_CHECK_MESSAGE(!str.good(), "stream still good");
62     BOOST_CHECK_MESSAGE(!str.eof(), "eofbit set but not expected");
63     BOOST_CHECK_MESSAGE(str.bad(), "stream did not set badbit");
64     BOOST_CHECK_MESSAGE(str.fail(), "stream did not fail");
65     BOOST_CHECK_MESSAGE(str.operator ! (),
66             "stream does not report failure by operator !");
67     BOOST_CHECK_MESSAGE(0 == str.operator void* (),
68             "stream does not report failure by operator void*");
69 }
70 
71 //------------------Test case generators--------------------------------------//
72 
73 template<void (*const function)(std::iostream&)>
74 struct wrap_nothrow {
executewrap_nothrow75     static void execute()
76     {
77         test_stream stream("foo");
78         BOOST_CHECK_NO_THROW( function(stream) );
79         check_stream_for_badbit(stream);
80     }
81 };
82 
83 template<void (*const function)(std::iostream&)>
84 struct wrap_throw {
executewrap_throw85     static void execute()
86     {
87         typedef std::ios_base ios;
88         test_stream stream("foo");
89 
90         stream.exceptions(ios::failbit | ios::badbit);
91         BOOST_CHECK_THROW( function(stream), std::exception );
92 
93         check_stream_for_badbit(stream);
94     }
95 };
96 
97 template<void (*const function)(std::iostream&)>
98 struct wrap_throw_delayed {
executewrap_throw_delayed99     static void execute()
100     {
101         typedef std::ios_base ios;
102         test_stream stream("foo");
103 
104         function(stream);
105         BOOST_CHECK_THROW(
106                 stream.exceptions(ios::failbit | ios::badbit),
107                 ios::failure
108             );
109 
110         check_stream_for_badbit(stream);
111     }
112 };
113 
114 //------------------Stream operations that throw------------------------------//
115 
test_read(std::iostream & str)116 void test_read(std::iostream& str)
117 {
118     char data[10];
119     str.read(data, 10);
120 }
121 
test_write(std::iostream & str)122 void test_write(std::iostream& str)
123 {
124     char data[10] = {0};
125     str.write(data, 10);
126         //force use of streambuf
127     str.flush();
128 }
129 
test_seekg(std::iostream & str)130 void test_seekg(std::iostream& str)
131 {
132     str.seekg(10);
133 }
134 
test_seekp(std::iostream & str)135 void test_seekp(std::iostream& str)
136 {
137     str.seekp(10);
138 }
139 
init_unit_test_suite(int,char * [])140 test_suite* init_unit_test_suite(int, char* [])
141 {
142     test_suite* test = BOOST_TEST_SUITE("stream state test");
143 
144     test->add(BOOST_TEST_CASE(&wrap_nothrow      <&test_read>::execute));
145     test->add(BOOST_TEST_CASE(&wrap_throw        <&test_read>::execute));
146     test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_read>::execute));
147 
148     test->add(BOOST_TEST_CASE(&wrap_nothrow      <&test_write>::execute));
149     test->add(BOOST_TEST_CASE(&wrap_throw        <&test_write>::execute));
150     test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_write>::execute));
151 
152     test->add(BOOST_TEST_CASE(&wrap_nothrow      <&test_seekg>::execute));
153     test->add(BOOST_TEST_CASE(&wrap_throw        <&test_seekg>::execute));
154     test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_seekg>::execute));
155 
156     test->add(BOOST_TEST_CASE(&wrap_nothrow      <&test_seekp>::execute));
157     test->add(BOOST_TEST_CASE(&wrap_throw        <&test_seekp>::execute));
158     test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_seekp>::execute));
159 
160     return test;
161 }
162