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