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