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/config.hpp>
8 #include <boost/iostreams/categories.hpp>  // tags.
9 #include <boost/iostreams/detail/ios.hpp>  // openmode, seekdir, int types.
10 #include <boost/iostreams/detail/error.hpp>
11 #include <boost/iostreams/positioning.hpp>
12 #include <boost/iostreams/stream.hpp>
13 #include <boost/test/test_tools.hpp>
14 #include <boost/test/unit_test.hpp>
15 
16 using boost::iostreams::detail::bad_read;
17 using boost::iostreams::detail::bad_seek;
18 using boost::iostreams::detail::bad_write;
19 using boost::iostreams::seekable_device_tag;
20 using boost::iostreams::stream;
21 using boost::iostreams::stream_offset;
22 using boost::unit_test::test_suite;
23 
24 /*
25  * This test unit uses a custom device to trigger errors. The device supports
26  * input, output, and seek according to the SeekableDevice concept. And each
27  * of the required functions throw a special detail::bad_xxx exception. This
28  * should trigger the iostreams::stream to set the badbit status flag.
29  * Additionally the exception can be propagated to the caller if the exception
30  * mask of the stream allows exceptions.
31  *
32  * The stream offers four different functions: read, write, seekg, and seekp.
33  * Each of them is tested with three different error reporting concepts:
34  * test by reading status flags, test by propagated exception, and test by
35  * calling std::ios_base::exceptions when badbit is already set.
36  *
37  * In each case all of the status checking functions of a stream are checked.
38  *
39  * MSVCPRT (Visual Studio 2017, at least) does not perform exception
40  * handling in the seek methods (confirmed by inspecting sources).
41  *
42  * CYGWIN (with gcc-7.3.0) does not behave properly on the throw_delayed cases.
43  */
44 
45 //------------------Definition of error_device--------------------------------//
46 
47 // Device whose member functions throw
48 struct error_device {
49     typedef char                 char_type;
50     typedef seekable_device_tag  category;
error_deviceerror_device51     error_device(char const*) {}
readerror_device52     std::streamsize read(char_type*, std::streamsize)
53     {
54         throw bad_read();
55     }
writeerror_device56     std::streamsize write(const char_type*, std::streamsize)
57     {
58         throw bad_write();
59     }
seekerror_device60     std::streampos seek(stream_offset, BOOST_IOS::seekdir)
61     {
62         throw bad_seek();
63     }
64 };
65 
66 typedef stream<error_device> test_stream;
67 
68 //------------------Stream state tester---------------------------------------//
69 
check_stream_for_badbit(const std::iostream & str)70 void check_stream_for_badbit(const std::iostream& str)
71 {
72     BOOST_CHECK_MESSAGE(!str.good(), "stream still good");
73     BOOST_CHECK_MESSAGE(!str.eof(), "eofbit set but not expected");
74     BOOST_CHECK_MESSAGE(str.bad(), "stream did not set badbit");
75     BOOST_CHECK_MESSAGE(str.fail(), "stream did not fail");
76     BOOST_CHECK_MESSAGE(str.operator ! (),
77             "stream does not report failure by operator !");
78     BOOST_CHECK_MESSAGE(false == static_cast<bool>(str),
79             "stream does not report failure by operator void* or bool");
80 }
81 
82 //------------------Test case generators--------------------------------------//
83 
84 template<void (*const function)(std::iostream&)>
85 struct wrap_nothrow {
executewrap_nothrow86     static void execute()
87     {
88         test_stream stream("foo");
89         BOOST_CHECK_NO_THROW( function(stream) );
90         check_stream_for_badbit(stream);
91     }
92 };
93 
94 template<void (*const function)(std::iostream&)>
95 struct wrap_throw {
executewrap_throw96     static void execute()
97     {
98         typedef std::ios_base ios;
99         test_stream stream("foo");
100 
101         stream.exceptions(ios::failbit | ios::badbit);
102         BOOST_CHECK_THROW( function(stream), std::exception );
103 
104         check_stream_for_badbit(stream);
105     }
106 };
107 
108 template<void (*const function)(std::iostream&)>
109 struct wrap_throw_delayed {
executewrap_throw_delayed110     static void execute()
111     {
112         typedef std::ios_base ios;
113         test_stream stream("foo");
114 
115         function(stream);
116         BOOST_CHECK_THROW(
117                 stream.exceptions(ios::failbit | ios::badbit),
118                 ios::failure
119             );
120 
121         check_stream_for_badbit(stream);
122     }
123 };
124 
125 //------------------Stream operations that throw------------------------------//
126 
test_read(std::iostream & str)127 void test_read(std::iostream& str)
128 {
129     char data[10];
130     str.read(data, 10);
131 }
132 
test_write(std::iostream & str)133 void test_write(std::iostream& str)
134 {
135     char data[10] = {0};
136     str.write(data, 10);
137         //force use of streambuf
138     str.flush();
139 }
140 
test_seekg(std::iostream & str)141 void test_seekg(std::iostream& str)
142 {
143     str.seekg(10);
144 }
145 
test_seekp(std::iostream & str)146 void test_seekp(std::iostream& str)
147 {
148     str.seekp(10);
149 }
150 
init_unit_test_suite(int,char * [])151 test_suite* init_unit_test_suite(int, char* [])
152 {
153     test_suite* test = BOOST_TEST_SUITE("stream state test");
154 
155     test->add(BOOST_TEST_CASE(&wrap_nothrow      <&test_read>::execute));
156     test->add(BOOST_TEST_CASE(&wrap_throw        <&test_read>::execute));
157 #ifndef __CYGWIN__
158     test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_read>::execute));
159 #endif
160 
161     test->add(BOOST_TEST_CASE(&wrap_nothrow      <&test_write>::execute));
162     test->add(BOOST_TEST_CASE(&wrap_throw        <&test_write>::execute));
163 #ifndef __CYGWIN__
164     test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_write>::execute));
165 #endif
166 
167 #ifndef BOOST_MSVC
168     test->add(BOOST_TEST_CASE(&wrap_nothrow      <&test_seekg>::execute));
169     test->add(BOOST_TEST_CASE(&wrap_throw        <&test_seekg>::execute));
170 #ifndef __CYGWIN__
171     test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_seekg>::execute));
172 #endif
173 
174     test->add(BOOST_TEST_CASE(&wrap_nothrow      <&test_seekp>::execute));
175     test->add(BOOST_TEST_CASE(&wrap_throw        <&test_seekp>::execute));
176 #ifndef __CYGWIN__
177     test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_seekp>::execute));
178 #endif
179 #endif // BOOST_MSVC
180 
181     return test;
182 }
183