Mercurial > minori
comparison dep/fmt/test/posix-mock-test.cc @ 343:1faa72660932
*: transfer back to cmake from autotools
autotools just made lots of things more complicated than
they should have and many things broke (i.e. translations)
| author | Paper <paper@paper.us.eu.org> | 
|---|---|
| date | Thu, 20 Jun 2024 05:56:06 -0400 | 
| parents | |
| children | 
   comparison
  equal
  deleted
  inserted
  replaced
| 342:adb79bdde329 | 343:1faa72660932 | 
|---|---|
| 1 // Tests of the C++ interface to POSIX functions that require mocks | |
| 2 // | |
| 3 // Copyright (c) 2012 - present, Victor Zverovich | |
| 4 // All rights reserved. | |
| 5 // | |
| 6 // For the license information refer to format.h. | |
| 7 | |
| 8 // Disable bogus MSVC warnings. | |
| 9 #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) | |
| 10 # define _CRT_SECURE_NO_WARNINGS | |
| 11 #endif | |
| 12 | |
| 13 #include "posix-mock.h" | |
| 14 | |
| 15 #include <errno.h> | |
| 16 #include <fcntl.h> | |
| 17 | |
| 18 #include <climits> | |
| 19 #include <memory> | |
| 20 | |
| 21 #include "../src/os.cc" | |
| 22 | |
| 23 #ifdef _WIN32 | |
| 24 # include <io.h> | |
| 25 # undef max | |
| 26 #endif | |
| 27 | |
| 28 #include "gmock/gmock.h" | |
| 29 #include "gtest-extra.h" | |
| 30 #include "util.h" | |
| 31 | |
| 32 using fmt::buffered_file; | |
| 33 | |
| 34 using testing::_; | |
| 35 using testing::Return; | |
| 36 using testing::StrEq; | |
| 37 | |
| 38 template <typename Mock> struct scoped_mock : testing::StrictMock<Mock> { | |
| 39 scoped_mock() { Mock::instance = this; } | |
| 40 ~scoped_mock() { Mock::instance = nullptr; } | |
| 41 }; | |
| 42 | |
| 43 namespace { | |
| 44 int open_count; | |
| 45 int close_count; | |
| 46 int dup_count; | |
| 47 int dup2_count; | |
| 48 int fdopen_count; | |
| 49 int read_count; | |
| 50 int write_count; | |
| 51 int pipe_count; | |
| 52 int fopen_count; | |
| 53 int fclose_count; | |
| 54 int fileno_count; | |
| 55 size_t read_nbyte; | |
| 56 size_t write_nbyte; | |
| 57 bool sysconf_error; | |
| 58 | |
| 59 enum { none, max_size, error } fstat_sim; | |
| 60 } // namespace | |
| 61 | |
| 62 #define EMULATE_EINTR(func, error_result) \ | |
| 63 if (func##_count != 0) { \ | |
| 64 if (func##_count++ != 3) { \ | |
| 65 errno = EINTR; \ | |
| 66 return error_result; \ | |
| 67 } \ | |
| 68 } | |
| 69 | |
| 70 #ifndef _MSC_VER | |
| 71 int test::open(const char* path, int oflag, int mode) { | |
| 72 EMULATE_EINTR(open, -1); | |
| 73 return ::open(path, oflag, mode); | |
| 74 } | |
| 75 #endif | |
| 76 | |
| 77 #ifndef _WIN32 | |
| 78 | |
| 79 long test::sysconf(int name) { | |
| 80 long result = ::sysconf(name); | |
| 81 if (!sysconf_error) return result; | |
| 82 // Simulate an error. | |
| 83 errno = EINVAL; | |
| 84 return -1; | |
| 85 } | |
| 86 | |
| 87 static off_t max_file_size() { return std::numeric_limits<off_t>::max(); } | |
| 88 | |
| 89 int test::fstat(int fd, struct stat* buf) { | |
| 90 int result = ::fstat(fd, buf); | |
| 91 if (fstat_sim == max_size) buf->st_size = max_file_size(); | |
| 92 return result; | |
| 93 } | |
| 94 | |
| 95 #else | |
| 96 | |
| 97 static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); } | |
| 98 | |
| 99 DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) { | |
| 100 if (fstat_sim == error) { | |
| 101 SetLastError(ERROR_ACCESS_DENIED); | |
| 102 return INVALID_FILE_SIZE; | |
| 103 } | |
| 104 if (fstat_sim == max_size) { | |
| 105 DWORD max = std::numeric_limits<DWORD>::max(); | |
| 106 *lpFileSizeHigh = max >> 1; | |
| 107 return max; | |
| 108 } | |
| 109 return ::GetFileSize(hFile, lpFileSizeHigh); | |
| 110 } | |
| 111 | |
| 112 #endif | |
| 113 | |
| 114 int test::close(int fildes) { | |
| 115 // Close the file first because close shouldn't be retried. | |
| 116 int result = ::FMT_POSIX(close(fildes)); | |
| 117 EMULATE_EINTR(close, -1); | |
| 118 return result; | |
| 119 } | |
| 120 | |
| 121 int test::dup(int fildes) { | |
| 122 EMULATE_EINTR(dup, -1); | |
| 123 return ::FMT_POSIX(dup(fildes)); | |
| 124 } | |
| 125 | |
| 126 int test::dup2(int fildes, int fildes2) { | |
| 127 EMULATE_EINTR(dup2, -1); | |
| 128 return ::FMT_POSIX(dup2(fildes, fildes2)); | |
| 129 } | |
| 130 | |
| 131 FILE* test::fdopen(int fildes, const char* mode) { | |
| 132 EMULATE_EINTR(fdopen, nullptr); | |
| 133 return ::FMT_POSIX(fdopen(fildes, mode)); | |
| 134 } | |
| 135 | |
| 136 test::ssize_t test::read(int fildes, void* buf, test::size_t nbyte) { | |
| 137 read_nbyte = nbyte; | |
| 138 EMULATE_EINTR(read, -1); | |
| 139 return ::FMT_POSIX(read(fildes, buf, nbyte)); | |
| 140 } | |
| 141 | |
| 142 test::ssize_t test::write(int fildes, const void* buf, test::size_t nbyte) { | |
| 143 write_nbyte = nbyte; | |
| 144 EMULATE_EINTR(write, -1); | |
| 145 return ::FMT_POSIX(write(fildes, buf, nbyte)); | |
| 146 } | |
| 147 | |
| 148 #ifndef _WIN32 | |
| 149 int test::pipe(int fildes[2]) { | |
| 150 EMULATE_EINTR(pipe, -1); | |
| 151 return ::pipe(fildes); | |
| 152 } | |
| 153 #else | |
| 154 int test::pipe(int* pfds, unsigned psize, int textmode) { | |
| 155 EMULATE_EINTR(pipe, -1); | |
| 156 return _pipe(pfds, psize, textmode); | |
| 157 } | |
| 158 #endif | |
| 159 | |
| 160 FILE* test::fopen(const char* filename, const char* mode) { | |
| 161 EMULATE_EINTR(fopen, nullptr); | |
| 162 return ::fopen(filename, mode); | |
| 163 } | |
| 164 | |
| 165 int test::fclose(FILE* stream) { | |
| 166 EMULATE_EINTR(fclose, EOF); | |
| 167 return ::fclose(stream); | |
| 168 } | |
| 169 | |
| 170 int(test::fileno)(FILE* stream) { | |
| 171 EMULATE_EINTR(fileno, -1); | |
| 172 #ifdef fileno | |
| 173 return FMT_POSIX(fileno(stream)); | |
| 174 #else | |
| 175 return ::FMT_POSIX(fileno(stream)); | |
| 176 #endif | |
| 177 } | |
| 178 | |
| 179 #ifndef _WIN32 | |
| 180 # define EXPECT_RETRY(statement, func, message) \ | |
| 181 func##_count = 1; \ | |
| 182 statement; \ | |
| 183 EXPECT_EQ(4, func##_count); \ | |
| 184 func##_count = 0; | |
| 185 # define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual) | |
| 186 #else | |
| 187 # define EXPECT_RETRY(statement, func, message) \ | |
| 188 func##_count = 1; \ | |
| 189 EXPECT_SYSTEM_ERROR(statement, EINTR, message); \ | |
| 190 func##_count = 0; | |
| 191 # define EXPECT_EQ_POSIX(expected, actual) | |
| 192 #endif | |
| 193 | |
| 194 #if FMT_USE_FCNTL | |
| 195 void write_file(fmt::cstring_view filename, fmt::string_view content) { | |
| 196 fmt::buffered_file f(filename, "w"); | |
| 197 f.print("{}", content); | |
| 198 } | |
| 199 | |
| 200 using fmt::file; | |
| 201 | |
| 202 TEST(os_test, getpagesize) { | |
| 203 # ifdef _WIN32 | |
| 204 SYSTEM_INFO si = {}; | |
| 205 GetSystemInfo(&si); | |
| 206 EXPECT_EQ(si.dwPageSize, fmt::getpagesize()); | |
| 207 # else | |
| 208 EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize()); | |
| 209 sysconf_error = true; | |
| 210 EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL, | |
| 211 "cannot get memory page size"); | |
| 212 sysconf_error = false; | |
| 213 # endif | |
| 214 } | |
| 215 | |
| 216 TEST(file_test, open_retry) { | |
| 217 # ifndef _WIN32 | |
| 218 write_file("temp", "there must be something here"); | |
| 219 std::unique_ptr<file> f{nullptr}; | |
| 220 EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open, | |
| 221 "cannot open file temp"); | |
| 222 char c = 0; | |
| 223 f->read(&c, 1); | |
| 224 # endif | |
| 225 } | |
| 226 | |
| 227 TEST(file_test, close_no_retry_in_dtor) { | |
| 228 file read_end, write_end; | |
| 229 file::pipe(read_end, write_end); | |
| 230 std::unique_ptr<file> f(new file(std::move(read_end))); | |
| 231 int saved_close_count = 0; | |
| 232 EXPECT_WRITE( | |
| 233 stderr, | |
| 234 { | |
| 235 close_count = 1; | |
| 236 f.reset(nullptr); | |
| 237 saved_close_count = close_count; | |
| 238 close_count = 0; | |
| 239 }, | |
| 240 system_error_message(EINTR, "cannot close file") + "\n"); | |
| 241 EXPECT_EQ(2, saved_close_count); | |
| 242 } | |
| 243 | |
| 244 TEST(file_test, close_no_retry) { | |
| 245 file read_end, write_end; | |
| 246 file::pipe(read_end, write_end); | |
| 247 close_count = 1; | |
| 248 EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file"); | |
| 249 EXPECT_EQ(2, close_count); | |
| 250 close_count = 0; | |
| 251 } | |
| 252 | |
| 253 TEST(file_test, size) { | |
| 254 std::string content = "top secret, destroy before reading"; | |
| 255 write_file("temp", content); | |
| 256 file f("temp", file::RDONLY); | |
| 257 EXPECT_GE(f.size(), 0); | |
| 258 EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size())); | |
| 259 # ifdef _WIN32 | |
| 260 auto error_code = std::error_code(); | |
| 261 fstat_sim = error; | |
| 262 try { | |
| 263 f.size(); | |
| 264 } catch (const std::system_error& e) { | |
| 265 error_code = e.code(); | |
| 266 } | |
| 267 fstat_sim = none; | |
| 268 EXPECT_EQ(error_code, | |
| 269 std::error_code(ERROR_ACCESS_DENIED, fmt::system_category())); | |
| 270 # else | |
| 271 f.close(); | |
| 272 EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes"); | |
| 273 # endif | |
| 274 } | |
| 275 | |
| 276 TEST(file_test, max_size) { | |
| 277 write_file("temp", ""); | |
| 278 file f("temp", file::RDONLY); | |
| 279 fstat_sim = max_size; | |
| 280 EXPECT_GE(f.size(), 0); | |
| 281 EXPECT_EQ(max_file_size(), f.size()); | |
| 282 fstat_sim = none; | |
| 283 } | |
| 284 | |
| 285 TEST(file_test, read_retry) { | |
| 286 file read_end, write_end; | |
| 287 file::pipe(read_end, write_end); | |
| 288 enum { SIZE = 4 }; | |
| 289 write_end.write("test", SIZE); | |
| 290 write_end.close(); | |
| 291 char buffer[SIZE]; | |
| 292 size_t count = 0; | |
| 293 EXPECT_RETRY(count = read_end.read(buffer, SIZE), read, | |
| 294 "cannot read from file"); | |
| 295 EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count); | |
| 296 } | |
| 297 | |
| 298 TEST(file_test, write_retry) { | |
| 299 file read_end, write_end; | |
| 300 file::pipe(read_end, write_end); | |
| 301 enum { SIZE = 4 }; | |
| 302 size_t count = 0; | |
| 303 EXPECT_RETRY(count = write_end.write("test", SIZE), write, | |
| 304 "cannot write to file"); | |
| 305 write_end.close(); | |
| 306 # ifndef _WIN32 | |
| 307 EXPECT_EQ(static_cast<std::streamsize>(SIZE), count); | |
| 308 char buffer[SIZE + 1]; | |
| 309 read_end.read(buffer, SIZE); | |
| 310 buffer[SIZE] = '\0'; | |
| 311 EXPECT_STREQ("test", buffer); | |
| 312 # endif | |
| 313 } | |
| 314 | |
| 315 # ifdef _WIN32 | |
| 316 TEST(file_test, convert_read_count) { | |
| 317 file read_end, write_end; | |
| 318 file::pipe(read_end, write_end); | |
| 319 char c; | |
| 320 size_t size = UINT_MAX; | |
| 321 if (sizeof(unsigned) != sizeof(size_t)) ++size; | |
| 322 read_count = 1; | |
| 323 read_nbyte = 0; | |
| 324 EXPECT_THROW(read_end.read(&c, size), std::system_error); | |
| 325 read_count = 0; | |
| 326 EXPECT_EQ(UINT_MAX, read_nbyte); | |
| 327 } | |
| 328 | |
| 329 TEST(file_test, convert_write_count) { | |
| 330 file read_end, write_end; | |
| 331 file::pipe(read_end, write_end); | |
| 332 char c; | |
| 333 size_t size = UINT_MAX; | |
| 334 if (sizeof(unsigned) != sizeof(size_t)) ++size; | |
| 335 write_count = 1; | |
| 336 write_nbyte = 0; | |
| 337 EXPECT_THROW(write_end.write(&c, size), std::system_error); | |
| 338 write_count = 0; | |
| 339 EXPECT_EQ(UINT_MAX, write_nbyte); | |
| 340 } | |
| 341 # endif | |
| 342 | |
| 343 TEST(file_test, dup_no_retry) { | |
| 344 int stdout_fd = FMT_POSIX(fileno(stdout)); | |
| 345 dup_count = 1; | |
| 346 EXPECT_SYSTEM_ERROR( | |
| 347 file::dup(stdout_fd), EINTR, | |
| 348 fmt::format("cannot duplicate file descriptor {}", stdout_fd)); | |
| 349 dup_count = 0; | |
| 350 } | |
| 351 | |
| 352 TEST(file_test, dup2_retry) { | |
| 353 int stdout_fd = FMT_POSIX(fileno(stdout)); | |
| 354 file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd); | |
| 355 EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2, | |
| 356 fmt::format("cannot duplicate file descriptor {} to {}", | |
| 357 f1.descriptor(), f2.descriptor())); | |
| 358 } | |
| 359 | |
| 360 TEST(file_test, dup2_no_except_retry) { | |
| 361 int stdout_fd = FMT_POSIX(fileno(stdout)); | |
| 362 file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd); | |
| 363 std::error_code ec; | |
| 364 dup2_count = 1; | |
| 365 f1.dup2(f2.descriptor(), ec); | |
| 366 # ifndef _WIN32 | |
| 367 EXPECT_EQ(4, dup2_count); | |
| 368 # else | |
| 369 EXPECT_EQ(EINTR, ec.value()); | |
| 370 # endif | |
| 371 dup2_count = 0; | |
| 372 } | |
| 373 | |
| 374 TEST(file_test, pipe_no_retry) { | |
| 375 file read_end, write_end; | |
| 376 pipe_count = 1; | |
| 377 EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR, | |
| 378 "cannot create pipe"); | |
| 379 pipe_count = 0; | |
| 380 } | |
| 381 | |
| 382 TEST(file_test, fdopen_no_retry) { | |
| 383 file read_end, write_end; | |
| 384 file::pipe(read_end, write_end); | |
| 385 fdopen_count = 1; | |
| 386 EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EINTR, | |
| 387 "cannot associate stream with file descriptor"); | |
| 388 fdopen_count = 0; | |
| 389 } | |
| 390 | |
| 391 TEST(buffered_file_test, open_retry) { | |
| 392 write_file("temp", "there must be something here"); | |
| 393 std::unique_ptr<buffered_file> f{nullptr}; | |
| 394 EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen, | |
| 395 "cannot open file temp"); | |
| 396 # ifndef _WIN32 | |
| 397 char c = 0; | |
| 398 if (fread(&c, 1, 1, f->get()) < 1) | |
| 399 throw fmt::system_error(errno, "fread failed"); | |
| 400 # endif | |
| 401 } | |
| 402 | |
| 403 TEST(buffered_file_test, close_no_retry_in_dtor) { | |
| 404 file read_end, write_end; | |
| 405 file::pipe(read_end, write_end); | |
| 406 std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r"))); | |
| 407 int saved_fclose_count = 0; | |
| 408 EXPECT_WRITE( | |
| 409 stderr, | |
| 410 { | |
| 411 fclose_count = 1; | |
| 412 f.reset(nullptr); | |
| 413 saved_fclose_count = fclose_count; | |
| 414 fclose_count = 0; | |
| 415 }, | |
| 416 system_error_message(EINTR, "cannot close file") + "\n"); | |
| 417 EXPECT_EQ(2, saved_fclose_count); | |
| 418 } | |
| 419 | |
| 420 TEST(buffered_file_test, close_no_retry) { | |
| 421 file read_end, write_end; | |
| 422 file::pipe(read_end, write_end); | |
| 423 buffered_file f = read_end.fdopen("r"); | |
| 424 fclose_count = 1; | |
| 425 EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file"); | |
| 426 EXPECT_EQ(2, fclose_count); | |
| 427 fclose_count = 0; | |
| 428 } | |
| 429 | |
| 430 TEST(buffered_file_test, fileno_no_retry) { | |
| 431 file read_end, write_end; | |
| 432 file::pipe(read_end, write_end); | |
| 433 buffered_file f = read_end.fdopen("r"); | |
| 434 fileno_count = 1; | |
| 435 EXPECT_SYSTEM_ERROR((f.descriptor)(), EINTR, "cannot get file descriptor"); | |
| 436 EXPECT_EQ(2, fileno_count); | |
| 437 fileno_count = 0; | |
| 438 } | |
| 439 #endif // FMT_USE_FCNTL | |
| 440 | |
| 441 struct test_mock { | |
| 442 static test_mock* instance; | |
| 443 } * test_mock::instance; | |
| 444 | |
| 445 TEST(scoped_mock, scope) { | |
| 446 { | |
| 447 scoped_mock<test_mock> mock; | |
| 448 EXPECT_EQ(&mock, test_mock::instance); | |
| 449 test_mock& copy = mock; | |
| 450 static_cast<void>(copy); | |
| 451 } | |
| 452 EXPECT_EQ(nullptr, test_mock::instance); | |
| 453 } | 
