]> git.proxmox.com Git - ceph.git/blob - ceph/src/fmt/test/os-test.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / fmt / test / os-test.cc
1 // Formatting library for C++ - tests of the OS-specific functionality
2 //
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7
8 #include "fmt/os.h"
9
10 #include <cstdlib> // std::exit
11 #include <cstring>
12 #include <memory>
13
14 #include "gtest-extra.h"
15 #include "util.h"
16
17 using fmt::buffered_file;
18 using testing::HasSubstr;
19 using wstring_view = fmt::basic_string_view<wchar_t>;
20
21 #ifdef _WIN32
22
23 # include <windows.h>
24
25 TEST(util_test, utf16_to_utf8) {
26 auto s = std::string("ёжик");
27 fmt::detail::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
28 EXPECT_EQ(s, u.str());
29 EXPECT_EQ(s.size(), u.size());
30 }
31
32 TEST(util_test, utf16_to_utf8_empty_string) {
33 std::string s = "";
34 fmt::detail::utf16_to_utf8 u(L"");
35 EXPECT_EQ(s, u.str());
36 EXPECT_EQ(s.size(), u.size());
37 }
38
39 template <typename Converter, typename Char>
40 void check_utf_conversion_error(const char* message,
41 fmt::basic_string_view<Char> str =
42 fmt::basic_string_view<Char>(nullptr, 1)) {
43 fmt::memory_buffer out;
44 fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
45 auto error = std::system_error(std::error_code());
46 try {
47 (Converter)(str);
48 } catch (const std::system_error& e) {
49 error = e;
50 }
51 EXPECT_EQ(ERROR_INVALID_PARAMETER, error.code().value());
52 EXPECT_THAT(error.what(), HasSubstr(fmt::to_string(out)));
53 }
54
55 TEST(util_test, utf16_to_utf8_error) {
56 check_utf_conversion_error<fmt::detail::utf16_to_utf8, wchar_t>(
57 "cannot convert string from UTF-16 to UTF-8");
58 }
59
60 TEST(util_test, utf16_to_utf8_convert) {
61 fmt::detail::utf16_to_utf8 u;
62 EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(wstring_view(nullptr, 1)));
63 EXPECT_EQ(ERROR_INVALID_PARAMETER,
64 u.convert(wstring_view(L"foo", INT_MAX + 1u)));
65 }
66
67 TEST(os_test, format_std_error_code) {
68 EXPECT_EQ("generic:42",
69 fmt::format(FMT_STRING("{0}"),
70 std::error_code(42, std::generic_category())));
71 EXPECT_EQ("system:42",
72 fmt::format(FMT_STRING("{0}"),
73 std::error_code(42, fmt::system_category())));
74 EXPECT_EQ("system:-42",
75 fmt::format(FMT_STRING("{0}"),
76 std::error_code(-42, fmt::system_category())));
77 }
78
79 TEST(os_test, format_windows_error) {
80 LPWSTR message = nullptr;
81 auto result = FormatMessageW(
82 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
83 FORMAT_MESSAGE_IGNORE_INSERTS,
84 nullptr, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
85 reinterpret_cast<LPWSTR>(&message), 0, nullptr);
86 fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2));
87 LocalFree(message);
88 fmt::memory_buffer actual_message;
89 fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
90 EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
91 fmt::to_string(actual_message));
92 actual_message.resize(0);
93 }
94
95 TEST(os_test, format_long_windows_error) {
96 LPWSTR message = nullptr;
97 // this error code is not available on all Windows platforms and
98 // Windows SDKs, so do not fail the test if the error string cannot
99 // be retrieved.
100 int provisioning_not_allowed = 0x80284013L; // TBS_E_PROVISIONING_NOT_ALLOWED
101 auto result = FormatMessageW(
102 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
103 FORMAT_MESSAGE_IGNORE_INSERTS,
104 nullptr, static_cast<DWORD>(provisioning_not_allowed),
105 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
106 reinterpret_cast<LPWSTR>(&message), 0, nullptr);
107 if (result == 0) {
108 LocalFree(message);
109 return;
110 }
111 fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2));
112 LocalFree(message);
113 fmt::memory_buffer actual_message;
114 fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,
115 "test");
116 EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
117 fmt::to_string(actual_message));
118 }
119
120 TEST(os_test, windows_error) {
121 auto error = std::system_error(std::error_code());
122 try {
123 throw fmt::windows_error(ERROR_FILE_EXISTS, "test {}", "error");
124 } catch (const std::system_error& e) {
125 error = e;
126 }
127 fmt::memory_buffer message;
128 fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
129 EXPECT_THAT(error.what(), HasSubstr(to_string(message)));
130 EXPECT_EQ(ERROR_FILE_EXISTS, error.code().value());
131 }
132
133 TEST(os_test, report_windows_error) {
134 fmt::memory_buffer out;
135 fmt::detail::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
136 out.push_back('\n');
137 EXPECT_WRITE(stderr,
138 fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"),
139 fmt::to_string(out));
140 }
141
142 #endif // _WIN32
143
144 #if FMT_USE_FCNTL
145
146 using fmt::file;
147
148 bool isclosed(int fd) {
149 char buffer;
150 auto result = std::streamsize();
151 SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1)));
152 return result == -1 && errno == EBADF;
153 }
154
155 // Opens a file for reading.
156 file open_file() {
157 file read_end, write_end;
158 file::pipe(read_end, write_end);
159 write_end.write(file_content, std::strlen(file_content));
160 write_end.close();
161 return read_end;
162 }
163
164 // Attempts to write a string to a file.
165 void write(file& f, fmt::string_view s) {
166 size_t num_chars_left = s.size();
167 const char* ptr = s.data();
168 do {
169 size_t count = f.write(ptr, num_chars_left);
170 ptr += count;
171 // We can't write more than size_t bytes since num_chars_left
172 // has type size_t.
173 num_chars_left -= count;
174 } while (num_chars_left != 0);
175 }
176
177 TEST(buffered_file_test, default_ctor) {
178 auto f = buffered_file();
179 EXPECT_TRUE(f.get() == nullptr);
180 }
181
182 TEST(buffered_file_test, move_ctor) {
183 buffered_file bf = open_buffered_file();
184 FILE* fp = bf.get();
185 EXPECT_TRUE(fp != nullptr);
186 buffered_file bf2(std::move(bf));
187 EXPECT_EQ(fp, bf2.get());
188 EXPECT_TRUE(bf.get() == nullptr);
189 }
190
191 TEST(buffered_file_test, move_assignment) {
192 buffered_file bf = open_buffered_file();
193 FILE* fp = bf.get();
194 EXPECT_TRUE(fp != nullptr);
195 buffered_file bf2;
196 bf2 = std::move(bf);
197 EXPECT_EQ(fp, bf2.get());
198 EXPECT_TRUE(bf.get() == nullptr);
199 }
200
201 TEST(buffered_file_test, move_assignment_closes_file) {
202 buffered_file bf = open_buffered_file();
203 buffered_file bf2 = open_buffered_file();
204 int old_fd = bf2.descriptor();
205 bf2 = std::move(bf);
206 EXPECT_TRUE(isclosed(old_fd));
207 }
208
209 TEST(buffered_file_test, move_from_temporary_in_ctor) {
210 FILE* fp = nullptr;
211 buffered_file f = open_buffered_file(&fp);
212 EXPECT_EQ(fp, f.get());
213 }
214
215 TEST(buffered_file_test, move_from_temporary_in_assignment) {
216 FILE* fp = nullptr;
217 auto f = buffered_file();
218 f = open_buffered_file(&fp);
219 EXPECT_EQ(fp, f.get());
220 }
221
222 TEST(buffered_file_test, move_from_temporary_in_assignment_closes_file) {
223 buffered_file f = open_buffered_file();
224 int old_fd = f.descriptor();
225 f = open_buffered_file();
226 EXPECT_TRUE(isclosed(old_fd));
227 }
228
229 TEST(buffered_file_test, close_file_in_dtor) {
230 int fd = 0;
231 {
232 buffered_file f = open_buffered_file();
233 fd = f.descriptor();
234 }
235 EXPECT_TRUE(isclosed(fd));
236 }
237
238 TEST(buffered_file_test, close_error_in_dtor) {
239 auto f =
240 std::unique_ptr<buffered_file>(new buffered_file(open_buffered_file()));
241 EXPECT_WRITE(
242 stderr,
243 {
244 // The close function must be called inside EXPECT_WRITE,
245 // otherwise the system may recycle closed file descriptor when
246 // redirecting the output in EXPECT_STDERR and the second close
247 // will break output redirection.
248 FMT_POSIX(close(f->descriptor()));
249 SUPPRESS_ASSERT(f.reset(nullptr));
250 },
251 system_error_message(EBADF, "cannot close file") + "\n");
252 }
253
254 TEST(buffered_file_test, close) {
255 buffered_file f = open_buffered_file();
256 int fd = f.descriptor();
257 f.close();
258 EXPECT_TRUE(f.get() == nullptr);
259 EXPECT_TRUE(isclosed(fd));
260 }
261
262 TEST(buffered_file_test, close_error) {
263 buffered_file f = open_buffered_file();
264 FMT_POSIX(close(f.descriptor()));
265 EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
266 EXPECT_TRUE(f.get() == nullptr);
267 }
268
269 TEST(buffered_file_test, descriptor) {
270 auto f = open_buffered_file();
271 EXPECT_TRUE(f.descriptor() != -1);
272 file copy = file::dup(f.descriptor());
273 EXPECT_READ(copy, file_content);
274 }
275
276 TEST(ostream_test, move) {
277 fmt::ostream out = fmt::output_file("test-file");
278 fmt::ostream moved(std::move(out));
279 moved.print("hello");
280 }
281
282 TEST(ostream_test, move_while_holding_data) {
283 {
284 fmt::ostream out = fmt::output_file("test-file");
285 out.print("Hello, ");
286 fmt::ostream moved(std::move(out));
287 moved.print("world!\n");
288 }
289 {
290 file in("test-file", file::RDONLY);
291 EXPECT_READ(in, "Hello, world!\n");
292 }
293 }
294
295 TEST(ostream_test, print) {
296 fmt::ostream out = fmt::output_file("test-file");
297 out.print("The answer is {}.\n",
298 fmt::join(std::initializer_list<int>{42}, ", "));
299 out.close();
300 file in("test-file", file::RDONLY);
301 EXPECT_READ(in, "The answer is 42.\n");
302 }
303
304 TEST(ostream_test, buffer_boundary) {
305 auto str = std::string(4096, 'x');
306 fmt::ostream out = fmt::output_file("test-file");
307 out.print("{}", str);
308 out.print("{}", str);
309 out.close();
310 file in("test-file", file::RDONLY);
311 EXPECT_READ(in, str + str);
312 }
313
314 TEST(ostream_test, buffer_size) {
315 fmt::ostream out = fmt::output_file("test-file", fmt::buffer_size = 1);
316 out.print("{}", "foo");
317 out.close();
318 file in("test-file", file::RDONLY);
319 EXPECT_READ(in, "foo");
320 }
321
322 TEST(ostream_test, truncate) {
323 {
324 fmt::ostream out = fmt::output_file("test-file");
325 out.print("0123456789");
326 }
327 {
328 fmt::ostream out = fmt::output_file("test-file");
329 out.print("foo");
330 }
331 file in("test-file", file::RDONLY);
332 EXPECT_EQ("foo", read(in, 4));
333 }
334
335 TEST(ostream_test, flush) {
336 auto out = fmt::output_file("test-file");
337 out.print("x");
338 out.flush();
339 auto in = fmt::file("test-file", file::RDONLY);
340 EXPECT_READ(in, "x");
341 }
342
343 TEST(file_test, default_ctor) {
344 file f;
345 EXPECT_EQ(-1, f.descriptor());
346 }
347
348 TEST(file_test, open_buffered_file_in_ctor) {
349 FILE* fp = safe_fopen("test-file", "w");
350 std::fputs(file_content, fp);
351 std::fclose(fp);
352 file f("test-file", file::RDONLY);
353 // Check if the file is open by reading one character from it.
354 char buffer;
355 bool isopen = FMT_POSIX(read(f.descriptor(), &buffer, 1)) == 1;
356 ASSERT_TRUE(isopen);
357 }
358
359 TEST(file_test, open_buffered_file_error) {
360 EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT,
361 "cannot open file nonexistent");
362 }
363
364 TEST(file_test, move_ctor) {
365 file f = open_file();
366 int fd = f.descriptor();
367 EXPECT_NE(-1, fd);
368 file f2(std::move(f));
369 EXPECT_EQ(fd, f2.descriptor());
370 EXPECT_EQ(-1, f.descriptor());
371 }
372
373 TEST(file_test, move_assignment) {
374 file f = open_file();
375 int fd = f.descriptor();
376 EXPECT_NE(-1, fd);
377 file f2;
378 f2 = std::move(f);
379 EXPECT_EQ(fd, f2.descriptor());
380 EXPECT_EQ(-1, f.descriptor());
381 }
382
383 TEST(file_test, move_assignment_closes_file) {
384 file f = open_file();
385 file f2 = open_file();
386 int old_fd = f2.descriptor();
387 f2 = std::move(f);
388 EXPECT_TRUE(isclosed(old_fd));
389 }
390
391 file open_buffered_file(int& fd) {
392 file f = open_file();
393 fd = f.descriptor();
394 return f;
395 }
396
397 TEST(file_test, move_from_temporary_in_ctor) {
398 int fd = 0xdead;
399 file f(open_buffered_file(fd));
400 EXPECT_EQ(fd, f.descriptor());
401 }
402
403 TEST(file_test, move_from_temporary_in_assignment) {
404 int fd = 0xdead;
405 file f;
406 f = open_buffered_file(fd);
407 EXPECT_EQ(fd, f.descriptor());
408 }
409
410 TEST(file_test, move_from_temporary_in_assignment_closes_file) {
411 int fd = 0xdead;
412 file f = open_file();
413 int old_fd = f.descriptor();
414 f = open_buffered_file(fd);
415 EXPECT_TRUE(isclosed(old_fd));
416 }
417
418 TEST(file_test, close_file_in_dtor) {
419 int fd = 0;
420 {
421 file f = open_file();
422 fd = f.descriptor();
423 }
424 EXPECT_TRUE(isclosed(fd));
425 }
426
427 TEST(file_test, close_error_in_dtor) {
428 std::unique_ptr<file> f(new file(open_file()));
429 EXPECT_WRITE(
430 stderr,
431 {
432 // The close function must be called inside EXPECT_WRITE,
433 // otherwise the system may recycle closed file descriptor when
434 // redirecting the output in EXPECT_STDERR and the second close
435 // will break output redirection.
436 FMT_POSIX(close(f->descriptor()));
437 SUPPRESS_ASSERT(f.reset(nullptr));
438 },
439 system_error_message(EBADF, "cannot close file") + "\n");
440 }
441
442 TEST(file_test, close) {
443 file f = open_file();
444 int fd = f.descriptor();
445 f.close();
446 EXPECT_EQ(-1, f.descriptor());
447 EXPECT_TRUE(isclosed(fd));
448 }
449
450 TEST(file_test, close_error) {
451 file f = open_file();
452 FMT_POSIX(close(f.descriptor()));
453 EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
454 EXPECT_EQ(-1, f.descriptor());
455 }
456
457 TEST(file_test, read) {
458 file f = open_file();
459 EXPECT_READ(f, file_content);
460 }
461
462 TEST(file_test, read_error) {
463 file f("test-file", file::WRONLY);
464 char buf;
465 // We intentionally read from a file opened in the write-only mode to
466 // cause error.
467 EXPECT_SYSTEM_ERROR(f.read(&buf, 1), EBADF, "cannot read from file");
468 }
469
470 TEST(file_test, write) {
471 file read_end, write_end;
472 file::pipe(read_end, write_end);
473 write(write_end, "test");
474 write_end.close();
475 EXPECT_READ(read_end, "test");
476 }
477
478 TEST(file_test, write_error) {
479 file f("test-file", file::RDONLY);
480 // We intentionally write to a file opened in the read-only mode to
481 // cause error.
482 EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
483 }
484
485 TEST(file_test, dup) {
486 file f = open_file();
487 file copy = file::dup(f.descriptor());
488 EXPECT_NE(f.descriptor(), copy.descriptor());
489 EXPECT_EQ(file_content, read(copy, std::strlen(file_content)));
490 }
491
492 # ifndef __COVERITY__
493 TEST(file_test, dup_error) {
494 int value = -1;
495 EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF,
496 "cannot duplicate file descriptor -1");
497 }
498 # endif
499
500 TEST(file_test, dup2) {
501 file f = open_file();
502 file copy = open_file();
503 f.dup2(copy.descriptor());
504 EXPECT_NE(f.descriptor(), copy.descriptor());
505 EXPECT_READ(copy, file_content);
506 }
507
508 TEST(file_test, dup2_error) {
509 file f = open_file();
510 EXPECT_SYSTEM_ERROR_NOASSERT(
511 f.dup2(-1), EBADF,
512 fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
513 }
514
515 TEST(file_test, dup2_noexcept) {
516 file f = open_file();
517 file copy = open_file();
518 std::error_code ec;
519 f.dup2(copy.descriptor(), ec);
520 EXPECT_EQ(ec.value(), 0);
521 EXPECT_NE(f.descriptor(), copy.descriptor());
522 EXPECT_READ(copy, file_content);
523 }
524
525 TEST(file_test, dup2_noexcept_error) {
526 file f = open_file();
527 std::error_code ec;
528 SUPPRESS_ASSERT(f.dup2(-1, ec));
529 EXPECT_EQ(EBADF, ec.value());
530 }
531
532 TEST(file_test, pipe) {
533 file read_end, write_end;
534 file::pipe(read_end, write_end);
535 EXPECT_NE(-1, read_end.descriptor());
536 EXPECT_NE(-1, write_end.descriptor());
537 write(write_end, "test");
538 EXPECT_READ(read_end, "test");
539 }
540
541 TEST(file_test, fdopen) {
542 file read_end, write_end;
543 file::pipe(read_end, write_end);
544 int read_fd = read_end.descriptor();
545 EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
546 }
547 #endif // FMT_USE_FCNTL