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