]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/nowide/test/test_filebuf.cpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / libs / nowide / test / test_filebuf.cpp
1 // Copyright (c) 2015 Artyom Beilis (Tonkikh)
2 // Copyright (c) 2019-2021 Alexander Grund
3 //
4 // Distributed under the Boost Software License, Version 1.0.
5 // (See accompanying file LICENSE or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7
8 #include <boost/nowide/filebuf.hpp>
9
10 #include "file_test_helpers.hpp"
11 #include "test.hpp"
12 #include <cstdint>
13 #include <random>
14 #include <string>
15 #include <type_traits>
16
17 namespace nw = boost::nowide;
18 using namespace boost::nowide::test;
19
20 // Check member types
21 static_assert(std::is_same<nw::filebuf::char_type, char>::value, "!");
22 static_assert(std::is_same<nw::filebuf::traits_type::char_type, char>::value, "!");
23 static_assert(std::is_same<nw::filebuf::int_type, nw::filebuf::traits_type::int_type>::value, "!");
24 static_assert(std::is_same<nw::filebuf::pos_type, nw::filebuf::traits_type::pos_type>::value, "!");
25 static_assert(std::is_same<nw::filebuf::off_type, nw::filebuf::traits_type::off_type>::value, "!");
26
27 void test_open_close(const std::string& filepath)
28 {
29 const std::string filepath2 = filepath + ".2";
30 ensure_not_exists(filepath2);
31 remove_file_at_exit _(filepath);
32 remove_file_at_exit _2(filepath2);
33
34 nw::filebuf buf;
35 TEST(buf.open(filepath, std::ios_base::out) == &buf);
36 TEST(buf.is_open());
37
38 // Opening when already open fails
39 TEST(buf.open(filepath2, std::ios_base::out) == nullptr);
40 // Still open
41 TEST(buf.is_open());
42 TEST(buf.close() == &buf);
43 // Failed opening did not create file
44 TEST(!file_exists(filepath2));
45
46 // But it should work now:
47 TEST(buf.open(filepath2, std::ios_base::out) == &buf);
48 TEST(buf.close() == &buf);
49 TEST(file_exists(filepath2));
50 }
51
52 void test_pubseekpos(const std::string& filepath)
53 {
54 const std::string data = create_random_data(BUFSIZ * 4, data_type::binary);
55 create_file(filepath, data, data_type::binary);
56 nw::filebuf buf;
57 TEST(buf.open(filepath, std::ios_base::in | std::ios_base::binary) == &buf);
58
59 // Fuzzy test: Seek to a couple random positions
60 std::minstd_rand rng(std::random_device{}());
61 using pos_type = nw::filebuf::pos_type;
62 const auto eofPos = pos_type(data.size());
63 std::uniform_int_distribution<size_t> distr(0, static_cast<size_t>(eofPos) - 1);
64 using traits = nw::filebuf::traits_type;
65
66 const auto getData = [&](pos_type pos) { return traits::to_int_type(data[static_cast<size_t>(pos)]); };
67
68 for(int i = 0; i < 100; i++)
69 {
70 const pos_type pos = distr(rng);
71 TEST_EQ(buf.pubseekpos(pos), pos);
72 TEST_EQ(buf.sgetc(), getData(pos));
73 }
74 // Seek to first and last as corner case tests
75 TEST_EQ(buf.pubseekpos(0), pos_type(0));
76 TEST_EQ(buf.sgetc(), traits::to_int_type(data[0]));
77 TEST_EQ(buf.pubseekpos(eofPos), eofPos);
78 TEST_EQ(buf.sgetc(), traits::eof());
79 }
80
81 void test_pubseekoff(const std::string& filepath)
82 {
83 const std::string data = create_random_data(BUFSIZ * 4, data_type::binary);
84 create_file(filepath, data, data_type::binary);
85 nw::filebuf buf;
86 TEST(buf.open(filepath, std::ios_base::in | std::ios_base::binary) == &buf);
87
88 // Fuzzy test: Seek to a couple random positions
89 std::minstd_rand rng(std::random_device{}());
90 using pos_type = nw::filebuf::pos_type;
91 using off_type = nw::filebuf::off_type;
92 const auto eofPos = pos_type(data.size());
93 std::uniform_int_distribution<size_t> distr(0, static_cast<size_t>(eofPos) - 1);
94 using traits = nw::filebuf::traits_type;
95
96 const auto getData = [&](pos_type pos) { return traits::to_int_type(data[static_cast<size_t>(pos)]); };
97 // tellg/tellp function as called by basic_[io]fstream
98 const auto tellg = [&]() { return buf.pubseekoff(0, std::ios_base::cur); };
99
100 for(int i = 0; i < 100; i++)
101 {
102 // beg
103 pos_type pos = distr(rng);
104 TEST_EQ(buf.pubseekoff(pos, std::ios_base::beg), pos);
105 TEST_EQ(tellg(), pos);
106 TEST_EQ(buf.sgetc(), getData(pos));
107 // cur
108 off_type diff = static_cast<pos_type>(distr(rng)) - pos;
109 pos += diff;
110 TEST_EQ(buf.pubseekoff(diff, std::ios_base::cur), pos);
111 TEST_EQ(tellg(), pos);
112 TEST_EQ(buf.sgetc(), getData(pos));
113 // end
114 diff = static_cast<pos_type>(distr(rng)) - eofPos;
115 pos = eofPos + diff;
116 TEST_EQ(buf.pubseekoff(diff, std::ios_base::end), pos);
117 TEST_EQ(tellg(), pos);
118 TEST_EQ(buf.sgetc(), getData(pos));
119 }
120 // Seek to first and last as corner case tests
121 TEST_EQ(buf.pubseekoff(0, std::ios_base::beg), pos_type(0));
122 TEST_EQ(tellg(), pos_type(0));
123 TEST_EQ(buf.sgetc(), traits::to_int_type(data[0]));
124 TEST_EQ(buf.pubseekoff(0, std::ios_base::end), eofPos);
125 TEST_EQ(tellg(), eofPos);
126 TEST_EQ(buf.sgetc(), traits::eof());
127 }
128
129 void test_64_bit_seek(const std::string& filepath)
130 {
131 // Create a value which does not fit into a 32 bit value.
132 // Use an unsigned intermediate to have the truncation defined to wrap to 0
133 using unsigned_off_type = std::make_unsigned<nw::filebuf::off_type>::type;
134 nw::filebuf::off_type offset = static_cast<unsigned_off_type>(std::uint64_t(1) << 33u);
135
136 #ifdef BOOST_MSVC
137 #pragma warning(push)
138 #pragma warning(disable : 4127)
139 #endif
140 // if we can't use 64 bit offsets through the API, don't test anything
141 // coverity[result_independent_of_operands]
142 if(offset == nw::filebuf::off_type(0))
143 {
144 // coverity[dead_error_line]
145 return; // LCOV_EXCL_LINE
146 }
147 #ifdef BOOST_MSVC
148 #pragma warning(pop)
149 #endif
150
151 create_file(filepath, "test");
152 remove_file_at_exit _(filepath);
153
154 nw::filebuf buf;
155 TEST(buf.open(filepath, std::ios_base::in | std::ios_base::binary) == &buf);
156 const std::streampos knownPos = 2;
157 TEST_EQ(buf.pubseekpos(knownPos), knownPos); // Just to make sure we know where we are
158 const std::streampos newPos = buf.pubseekoff(offset, std::ios_base::cur);
159 // On 32 bit mode or when seek beyond EOF is not allowed, the current position should be unchanged
160 if(newPos == std::streampos(-1))
161 TEST_EQ(buf.pubseekoff(0, std::ios_base::cur), knownPos);
162 else
163 {
164 #if !BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
165 // libc++ may truncate the 64 bit value when calling fseek which yields an offset of 0
166 if(newPos == knownPos)
167 offset = 0; // LCOV_EXCL_LINE
168 #endif
169 TEST_EQ(newPos, offset + knownPos);
170 TEST_EQ(buf.pubseekoff(0, std::ios_base::cur), newPos);
171 }
172 }
173
174 void test_swap(const std::string& filepath)
175 {
176 const std::string filepath2 = filepath + ".2";
177 remove_file_at_exit _(filepath);
178 remove_file_at_exit _2(filepath2);
179
180 const auto eof = nw::filebuf::traits_type::eof();
181 // Note: Make sure to have en uneven number of swaps so the destructor runs on the others data
182
183 // Check: FILE*, buffer, buffer_size
184 {
185 nw::filebuf buf1, buf2;
186 char buffer1[3]{}, buffer2[5]{};
187 buf1.pubsetbuf(buffer1, sizeof(buffer1));
188 buf2.pubsetbuf(buffer2, sizeof(buffer2));
189 TEST(buf1.open(filepath, std::ios_base::out) == &buf1);
190 buf1.swap(buf2);
191 TEST(!buf1.is_open());
192 TEST(buf2.is_open());
193 TEST(buf1.open(filepath2, std::ios_base::out | std::ios_base::binary) == &buf1);
194
195 // Write "FooBar" to filepath and "HelloWorld" to filepath2
196 buf1.sputc('H');
197 buf1.sputn("ello", 4);
198 buf2.sputc('F');
199 buf2.sputn("oo", 2);
200 buf2.swap(buf1);
201 buf1.sputc('B');
202 buf1.sputn("ar", 2);
203 buf2.sputc('W');
204 buf2.sputn("orld", 4);
205
206 buf1.close();
207 TEST(!buf1.is_open());
208 TEST(buf2.is_open());
209 buf1.swap(buf2);
210 TEST(buf1.is_open());
211 TEST(!buf2.is_open());
212 buf1.close();
213 TEST(!buf1.is_open());
214 TEST(!buf2.is_open());
215 TEST_EQ(read_file(filepath), "FooBar");
216 TEST_EQ(read_file(filepath2), "HelloWorld");
217 }
218 // Check: mode, owns_buffer
219 {
220 nw::filebuf buf1, buf2;
221 char buffer[3]{};
222 buf1.pubsetbuf(buffer, sizeof(buffer));
223 TEST(buf1.open(filepath, std::ios_base::out) == &buf1);
224 TEST(buf2.open(filepath2, std::ios_base::in) == &buf2);
225 TEST_EQ(buf1.sputc('B'), 'B');
226 TEST_EQ(buf2.sbumpc(), 'H');
227 buf1.swap(buf2);
228 // Trying to read in write mode or other way round should fail
229 TEST_EQ(buf1.sputc('x'), eof);
230 TEST_EQ(buf2.sbumpc(), eof);
231 TEST_EQ(buf1.sbumpc(), 'e');
232 TEST_EQ(buf2.sputc('a'), 'a');
233 buf2.swap(buf1);
234 TEST_EQ(buf2.sputc('x'), eof);
235 TEST_EQ(buf1.sbumpc(), eof);
236 TEST_EQ(buf2.sbumpc(), 'l');
237 TEST_EQ(buf1.sputn("zXYZ", 4), 4);
238 swap(buf2, buf1);
239 buf1.close();
240 buf2.close();
241 TEST_EQ(read_file(filepath), "BazXYZ");
242 TEST_EQ(read_file(filepath2), "HelloWorld");
243 }
244 // Check: last_char, gptr, eback
245 {
246 nw::filebuf buf1, buf2;
247 // Need to disable buffering to use last_char, but only for 1 to detect wrong conditions
248 buf1.pubsetbuf(0, 0);
249 TEST(buf1.open(filepath, std::ios_base::in) == &buf1);
250 TEST(buf2.open(filepath2, std::ios_base::in) == &buf2);
251 // Peek
252 TEST_EQ(buf1.sgetc(), 'B');
253 TEST_EQ(buf2.sgetc(), 'H');
254 swap(buf1, buf2);
255 TEST_EQ(buf2.sgetc(), 'B');
256 TEST_EQ(buf1.sgetc(), 'H');
257 // Advance
258 TEST_EQ(buf2.sbumpc(), 'B');
259 TEST_EQ(buf1.sbumpc(), 'H');
260 TEST_EQ(buf2.sbumpc(), 'a');
261 TEST_EQ(buf1.sbumpc(), 'e');
262 swap(buf1, buf2);
263 TEST_EQ(buf1.sbumpc(), 'z');
264 TEST_EQ(buf2.sbumpc(), 'l');
265 swap(buf1, buf2);
266 TEST_EQ(buf2.sgetc(), 'X');
267 TEST_EQ(buf1.sgetc(), 'l');
268 }
269 // Check: pptr, epptr
270 {
271 nw::filebuf buf1, buf2;
272 // Need to disable buffering to use last_char, but only for 1 to detect wrong conditions
273 buf1.pubsetbuf(0, 0);
274 TEST(buf1.open(filepath, std::ios_base::out) == &buf1);
275 TEST(buf2.open(filepath2, std::ios_base::out) == &buf2);
276 TEST_EQ(buf1.sputc('1'), '1');
277 TEST_EQ(buf2.sputc('a'), 'a');
278 swap(buf1, buf2);
279 // buf1: filepath2, buf2: filepath
280 TEST_EQ(buf1.sputc('b'), 'b');
281 TEST_EQ(buf2.sputc('2'), '2');
282 // Sync and check if file was written
283 TEST_EQ(buf1.pubsync(), 0);
284 TEST_EQ(read_file(filepath2), "ab");
285 TEST_EQ(buf2.pubsync(), 0);
286 TEST_EQ(read_file(filepath), "12");
287 swap(buf1, buf2);
288 // buf1: filepath, buf2: filepath2
289 TEST_EQ(buf1.pubsync(), 0);
290 TEST_EQ(read_file(filepath), "12");
291 TEST_EQ(buf2.pubsync(), 0);
292 TEST_EQ(read_file(filepath2), "ab");
293 TEST_EQ(buf1.sputc('3'), '3');
294 TEST_EQ(buf2.sputc('c'), 'c');
295 swap(buf1, buf2);
296 // buf1: filepath2, buf2: filepath
297 TEST_EQ(buf1.pubsync(), 0);
298 TEST_EQ(read_file(filepath2), "abc");
299 TEST_EQ(buf2.pubsync(), 0);
300 TEST_EQ(read_file(filepath), "123");
301 }
302 }
303
304 // coverity [root_function]
305 void test_main(int, char** argv, char**)
306 {
307 const std::string exampleFilename = std::string(argv[0]) + "-\xd7\xa9-\xd0\xbc-\xce\xbd.txt";
308
309 test_open_close(exampleFilename);
310 test_pubseekpos(exampleFilename);
311 test_pubseekoff(exampleFilename);
312 test_64_bit_seek(exampleFilename);
313 // These tests are only useful for the nowide filebuf and are known to fail for
314 // std::filebuf due to bugs in libc++
315 #if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
316 test_swap(exampleFilename);
317 #endif
318 }