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