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; |
51 | error_device(char const*) {} |
52 | std::streamsize read(char_type*, std::streamsize) |
53 | { |
54 | throw bad_read(); |
55 | } |
56 | std::streamsize write(const char_type*, std::streamsize) |
57 | { |
58 | throw bad_write(); |
59 | } |
60 | 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 | |
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 { |
86 | static void execute() |
87 | { |
88 | test_stream stream("foo" ); |
89 | BOOST_CHECK_NO_THROW( function(stream) ); |
90 | check_stream_for_badbit(str: stream); |
91 | } |
92 | }; |
93 | |
94 | template<void (*const function)(std::iostream&)> |
95 | struct wrap_throw { |
96 | static void execute() |
97 | { |
98 | typedef std::ios_base ios; |
99 | test_stream stream("foo" ); |
100 | |
101 | stream.exceptions(except: ios::failbit | ios::badbit); |
102 | BOOST_CHECK_THROW( function(stream), std::exception ); |
103 | |
104 | check_stream_for_badbit(str: stream); |
105 | } |
106 | }; |
107 | |
108 | template<void (*const function)(std::iostream&)> |
109 | struct wrap_throw_delayed { |
110 | 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(str: stream); |
122 | } |
123 | }; |
124 | |
125 | //------------------Stream operations that throw------------------------------// |
126 | |
127 | void test_read(std::iostream& str) |
128 | { |
129 | char data[10]; |
130 | str.read(s: data, n: 10); |
131 | } |
132 | |
133 | void test_write(std::iostream& str) |
134 | { |
135 | char data[10] = {0}; |
136 | str.write(s: data, n: 10); |
137 | //force use of streambuf |
138 | str.flush(); |
139 | } |
140 | |
141 | void test_seekg(std::iostream& str) |
142 | { |
143 | str.seekg(10); |
144 | } |
145 | |
146 | void test_seekp(std::iostream& str) |
147 | { |
148 | str.seekp(10); |
149 | } |
150 | |
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 | // MSSTL and libc++ don't handle exceptions from seekg correctly |
168 | #if !defined(_CPPLIB_VER) && !defined(_LIBCPP_VERSION) |
169 | |
170 | test->add(BOOST_TEST_CASE(&wrap_nothrow <&test_seekg>::execute)); |
171 | test->add(BOOST_TEST_CASE(&wrap_throw <&test_seekg>::execute)); |
172 | #ifndef __CYGWIN__ |
173 | test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_seekg>::execute)); |
174 | #endif |
175 | |
176 | #endif // !defined(_CPPLIB_VER) && !defined(_LIBCPP_VERSION) |
177 | |
178 | // Since C++11, seekp does not catch exceptions |
179 | #if 0 |
180 | |
181 | test->add(BOOST_TEST_CASE(&wrap_nothrow <&test_seekp>::execute)); |
182 | test->add(BOOST_TEST_CASE(&wrap_throw <&test_seekp>::execute)); |
183 | #ifndef __CYGWIN__ |
184 | test->add(BOOST_TEST_CASE(&wrap_throw_delayed<&test_seekp>::execute)); |
185 | #endif |
186 | |
187 | #endif |
188 | |
189 | return test; |
190 | } |
191 | |