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