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 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 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 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 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 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__) 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 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. 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. 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 136 TEST(buffered_file_test, default_ctor) { 137 auto f = buffered_file(); 138 EXPECT_TRUE(f.get() == nullptr); 139 } 140 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 302 TEST(file_test, default_ctor) { 303 file f; 304 EXPECT_EQ(-1, f.descriptor()); 305 } 306 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 318 TEST(file_test, open_buffered_file_error) { 319 EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT, 320 "cannot open file nonexistent"); 321 } 322 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 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 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 350 file open_buffered_file(int& fd) { 351 file f = open_file(); 352 fd = f.descriptor(); 353 return f; 354 } 355 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 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 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 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 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 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 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 416 TEST(file_test, read) { 417 file f = open_file(); 418 EXPECT_READ(f, file_content); 419 } 420 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 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 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 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__ 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 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 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 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 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 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 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