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