]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/nowide/test/test_filebuf.cpp
1 // Copyright (c) 2015 Artyom Beilis (Tonkikh)
2 // Copyright (c) 2019-2021 Alexander Grund
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)
8 #include <boost/nowide/filebuf.hpp>
10 #include "file_test_helpers.hpp"
15 #include <type_traits>
17 namespace nw
= boost::nowide
;
18 using namespace boost::nowide::test
;
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
, "!");
27 void test_open_close(const std::string
& filepath
)
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
);
35 TEST(buf
.open(filepath
, std::ios_base::out
) == &buf
);
38 // Opening when already open fails
39 TEST(buf
.open(filepath2
, std::ios_base::out
) == nullptr);
42 TEST(buf
.close() == &buf
);
43 // Failed opening did not create file
44 TEST(!file_exists(filepath2
));
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
));
52 void test_pubseekpos(const std::string
& filepath
)
54 const std::string data
= create_random_data(BUFSIZ
* 4, data_type::binary
);
55 create_file(filepath
, data
, data_type::binary
);
57 TEST(buf
.open(filepath
, std::ios_base::in
| std::ios_base::binary
) == &buf
);
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
;
66 const auto getData
= [&](pos_type pos
) { return traits::to_int_type(data
[static_cast<size_t>(pos
)]); };
68 for(int i
= 0; i
< 100; i
++)
70 const pos_type pos
= distr(rng
);
71 TEST_EQ(buf
.pubseekpos(pos
), pos
);
72 TEST_EQ(buf
.sgetc(), getData(pos
));
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());
81 void test_pubseekoff(const std::string
& filepath
)
83 const std::string data
= create_random_data(BUFSIZ
* 4, data_type::binary
);
84 create_file(filepath
, data
, data_type::binary
);
86 TEST(buf
.open(filepath
, std::ios_base::in
| std::ios_base::binary
) == &buf
);
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
;
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
); };
100 for(int i
= 0; i
< 100; i
++)
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
));
108 off_type diff
= static_cast<pos_type
>(distr(rng
)) - pos
;
110 TEST_EQ(buf
.pubseekoff(diff
, std::ios_base::cur
), pos
);
111 TEST_EQ(tellg(), pos
);
112 TEST_EQ(buf
.sgetc(), getData(pos
));
114 diff
= static_cast<pos_type
>(distr(rng
)) - eofPos
;
116 TEST_EQ(buf
.pubseekoff(diff
, std::ios_base::end
), pos
);
117 TEST_EQ(tellg(), pos
);
118 TEST_EQ(buf
.sgetc(), getData(pos
));
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());
129 void test_64_bit_seek(const std::string
& filepath
)
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);
137 #pragma warning(push)
138 #pragma warning(disable : 4127)
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))
144 // coverity[dead_error_line]
145 return; // LCOV_EXCL_LINE
151 create_file(filepath
, "test");
152 remove_file_at_exit
_(filepath
);
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
);
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
169 TEST_EQ(newPos
, offset
+ knownPos
);
170 TEST_EQ(buf
.pubseekoff(0, std::ios_base::cur
), newPos
);
174 void test_swap(const std::string
& filepath
)
176 const std::string filepath2
= filepath
+ ".2";
177 remove_file_at_exit
_(filepath
);
178 remove_file_at_exit
_2(filepath2
);
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
183 // Check: FILE*, buffer, buffer_size
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
);
191 TEST(!buf1
.is_open());
192 TEST(buf2
.is_open());
193 TEST(buf1
.open(filepath2
, std::ios_base::out
| std::ios_base::binary
) == &buf1
);
195 // Write "FooBar" to filepath and "HelloWorld" to filepath2
197 buf1
.sputn("ello", 4);
204 buf2
.sputn("orld", 4);
207 TEST(!buf1
.is_open());
208 TEST(buf2
.is_open());
210 TEST(buf1
.is_open());
211 TEST(!buf2
.is_open());
213 TEST(!buf1
.is_open());
214 TEST(!buf2
.is_open());
215 TEST_EQ(read_file(filepath
), "FooBar");
216 TEST_EQ(read_file(filepath2
), "HelloWorld");
218 // Check: mode, owns_buffer
220 nw::filebuf buf1
, buf2
;
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');
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');
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);
241 TEST_EQ(read_file(filepath
), "BazXYZ");
242 TEST_EQ(read_file(filepath2
), "HelloWorld");
244 // Check: last_char, gptr, eback
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
);
252 TEST_EQ(buf1
.sgetc(), 'B');
253 TEST_EQ(buf2
.sgetc(), 'H');
255 TEST_EQ(buf2
.sgetc(), 'B');
256 TEST_EQ(buf1
.sgetc(), 'H');
258 TEST_EQ(buf2
.sbumpc(), 'B');
259 TEST_EQ(buf1
.sbumpc(), 'H');
260 TEST_EQ(buf2
.sbumpc(), 'a');
261 TEST_EQ(buf1
.sbumpc(), 'e');
263 TEST_EQ(buf1
.sbumpc(), 'z');
264 TEST_EQ(buf2
.sbumpc(), 'l');
266 TEST_EQ(buf2
.sgetc(), 'X');
267 TEST_EQ(buf1
.sgetc(), 'l');
269 // Check: pptr, epptr
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');
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");
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');
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");
304 // coverity [root_function]
305 void test_main(int, char** argv
, char**)
307 const std::string exampleFilename
= std::string(argv
[0]) + "-\xd7\xa9-\xd0\xbc-\xce\xbd.txt";
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
);