posix-mock-test.cc revision 3133925ab2f38e6378bbbf702647fb59d222d315
1/* 2 Tests of the C++ interface to POSIX functions that require mocks 3 4 Copyright (c) 2012-2015, Victor Zverovich 5 All rights reserved. 6 7 Redistribution and use in source and binary forms, with or without 8 modification, are permitted provided that the following conditions are met: 9 10 1. Redistributions of source code must retain the above copyright notice, this 11 list of conditions and the following disclaimer. 12 2. Redistributions in binary form must reproduce the above copyright notice, 13 this list of conditions and the following disclaimer in the documentation 14 and/or other materials provided with the distribution. 15 16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28// Disable bogus MSVC warnings. 29#define _CRT_SECURE_NO_WARNINGS 30 31#include "posix-mock.h" 32#include <cppformat/posix.cc> 33 34#include <errno.h> 35#include <fcntl.h> 36#include <climits> 37 38#ifdef _WIN32 39# include <io.h> 40# undef max 41# undef ERROR 42#endif 43 44#include "gmock/gmock.h" 45#include "gtest-extra.h" 46#include "util.h" 47 48using fmt::BufferedFile; 49using fmt::ErrorCode; 50using fmt::File; 51 52using testing::internal::scoped_ptr; 53using testing::_; 54using testing::StrEq; 55using testing::Return; 56 57namespace { 58int open_count; 59int close_count; 60int dup_count; 61int dup2_count; 62int fdopen_count; 63int read_count; 64int write_count; 65int pipe_count; 66int fopen_count; 67int fclose_count; 68int fileno_count; 69std::size_t read_nbyte; 70std::size_t write_nbyte; 71bool sysconf_error; 72 73enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim; 74} 75 76#define EMULATE_EINTR(func, error_result) \ 77 if (func##_count != 0) { \ 78 if (func##_count++ != 3) { \ 79 errno = EINTR; \ 80 return error_result; \ 81 } \ 82 } 83 84#ifndef _MSC_VER 85int test::open(const char *path, int oflag, int mode) { 86 EMULATE_EINTR(open, -1); 87 return ::open(path, oflag, mode); 88} 89#else 90errno_t test::sopen_s( 91 int* pfh, const char *filename, int oflag, int shflag, int pmode) { 92 EMULATE_EINTR(open, EINTR); 93 return _sopen_s(pfh, filename, oflag, shflag, pmode); 94} 95#endif 96 97#ifndef _WIN32 98 99long test::sysconf(int name) { 100 long result = ::sysconf(name); 101 if (!sysconf_error) 102 return result; 103 // Simulate an error. 104 errno = EINVAL; 105 return -1; 106} 107 108static off_t max_file_size() { return std::numeric_limits<off_t>::max(); } 109 110int test::fstat(int fd, struct stat *buf) { 111 int result = ::fstat(fd, buf); 112 if (fstat_sim == MAX_SIZE) 113 buf->st_size = max_file_size(); 114 return result; 115} 116 117#else 118 119static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); } 120 121DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) { 122 if (fstat_sim == ERROR) { 123 SetLastError(ERROR_ACCESS_DENIED); 124 return INVALID_FILE_SIZE; 125 } 126 if (fstat_sim == MAX_SIZE) { 127 DWORD max = std::numeric_limits<DWORD>::max(); 128 *lpFileSizeHigh = max >> 1; 129 return max; 130 } 131 return ::GetFileSize(hFile, lpFileSizeHigh); 132} 133 134#endif 135 136int test::close(int fildes) { 137 // Close the file first because close shouldn't be retried. 138 int result = ::FMT_POSIX(close(fildes)); 139 EMULATE_EINTR(close, -1); 140 return result; 141} 142 143int test::dup(int fildes) { 144 EMULATE_EINTR(dup, -1); 145 return ::FMT_POSIX(dup(fildes)); 146} 147 148int test::dup2(int fildes, int fildes2) { 149 EMULATE_EINTR(dup2, -1); 150 return ::FMT_POSIX(dup2(fildes, fildes2)); 151} 152 153FILE *test::fdopen(int fildes, const char *mode) { 154 EMULATE_EINTR(fdopen, 0); 155 return ::FMT_POSIX(fdopen(fildes, mode)); 156} 157 158test::ssize_t test::read(int fildes, void *buf, test::size_t nbyte) { 159 read_nbyte = nbyte; 160 EMULATE_EINTR(read, -1); 161 return ::FMT_POSIX(read(fildes, buf, nbyte)); 162} 163 164test::ssize_t test::write(int fildes, const void *buf, test::size_t nbyte) { 165 write_nbyte = nbyte; 166 EMULATE_EINTR(write, -1); 167 return ::FMT_POSIX(write(fildes, buf, nbyte)); 168} 169 170#ifndef _WIN32 171int test::pipe(int fildes[2]) { 172 EMULATE_EINTR(pipe, -1); 173 return ::pipe(fildes); 174} 175#else 176int test::pipe(int *pfds, unsigned psize, int textmode) { 177 EMULATE_EINTR(pipe, -1); 178 return _pipe(pfds, psize, textmode); 179} 180#endif 181 182FILE *test::fopen(const char *filename, const char *mode) { 183 EMULATE_EINTR(fopen, 0); 184 return ::fopen(filename, mode); 185} 186 187int test::fclose(FILE *stream) { 188 EMULATE_EINTR(fclose, EOF); 189 return ::fclose(stream); 190} 191 192int (test::fileno)(FILE *stream) { 193 EMULATE_EINTR(fileno, -1); 194#ifdef fileno 195 return FMT_POSIX(fileno(stream)); 196#else 197 return ::FMT_POSIX(fileno(stream)); 198#endif 199} 200 201#ifndef _WIN32 202# define EXPECT_RETRY(statement, func, message) \ 203 func##_count = 1; \ 204 statement; \ 205 EXPECT_EQ(4, func##_count); \ 206 func##_count = 0; 207# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual) 208#else 209# define EXPECT_RETRY(statement, func, message) \ 210 func##_count = 1; \ 211 EXPECT_SYSTEM_ERROR(statement, EINTR, message); \ 212 func##_count = 0; 213# define EXPECT_EQ_POSIX(expected, actual) 214#endif 215 216void write_file(fmt::CStringRef filename, fmt::StringRef content) { 217 fmt::BufferedFile f(filename, "w"); 218 f.print("{}", content); 219} 220 221TEST(UtilTest, StaticAssert) { 222 FMT_STATIC_ASSERT(true, "success"); 223 // Static assertion failure is tested in compile-test because it causes 224 // a compile-time error. 225} 226 227TEST(UtilTest, GetPageSize) { 228#ifdef _WIN32 229 SYSTEM_INFO si = {}; 230 GetSystemInfo(&si); 231 EXPECT_EQ(si.dwPageSize, fmt::getpagesize()); 232#else 233 EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize()); 234 sysconf_error = true; 235 EXPECT_SYSTEM_ERROR( 236 fmt::getpagesize(), EINVAL, "cannot get memory page size"); 237 sysconf_error = false; 238#endif 239} 240 241TEST(FileTest, OpenRetry) { 242 write_file("test", "there must be something here"); 243 scoped_ptr<File> f; 244 EXPECT_RETRY(f.reset(new File("test", File::RDONLY)), 245 open, "cannot open file test"); 246#ifndef _WIN32 247 char c = 0; 248 f->read(&c, 1); 249#endif 250} 251 252TEST(FileTest, CloseNoRetryInDtor) { 253 File read_end, write_end; 254 File::pipe(read_end, write_end); 255 scoped_ptr<File> f(new File(std::move(read_end))); 256 int saved_close_count = 0; 257 EXPECT_WRITE(stderr, { 258 close_count = 1; 259 f.reset(); 260 saved_close_count = close_count; 261 close_count = 0; 262 }, format_system_error(EINTR, "cannot close file") + "\n"); 263 EXPECT_EQ(2, saved_close_count); 264} 265 266TEST(FileTest, CloseNoRetry) { 267 File read_end, write_end; 268 File::pipe(read_end, write_end); 269 close_count = 1; 270 EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file"); 271 EXPECT_EQ(2, close_count); 272 close_count = 0; 273} 274 275TEST(FileTest, Size) { 276 std::string content = "top secret, destroy before reading"; 277 write_file("test", content); 278 File f("test", File::RDONLY); 279 EXPECT_GE(f.size(), 0); 280 fmt::ULongLong file_size = f.size(); 281 EXPECT_EQ(content.size(), file_size); 282#ifdef _WIN32 283 fmt::MemoryWriter message; 284 fmt::internal::format_windows_error( 285 message, ERROR_ACCESS_DENIED, "cannot get file size"); 286 fstat_sim = ERROR; 287 EXPECT_THROW_MSG(f.size(), fmt::WindowsError, message.str()); 288 fstat_sim = NONE; 289#else 290 f.close(); 291 EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes"); 292#endif 293} 294 295TEST(FileTest, MaxSize) { 296 write_file("test", ""); 297 File f("test", File::RDONLY); 298 fstat_sim = MAX_SIZE; 299 EXPECT_GE(f.size(), 0); 300 EXPECT_EQ(max_file_size(), f.size()); 301 fstat_sim = NONE; 302} 303 304TEST(FileTest, ReadRetry) { 305 File read_end, write_end; 306 File::pipe(read_end, write_end); 307 enum { SIZE = 4 }; 308 write_end.write("test", SIZE); 309 write_end.close(); 310 char buffer[SIZE]; 311 std::streamsize count = 0; 312 EXPECT_RETRY(count = read_end.read(buffer, SIZE), 313 read, "cannot read from file"); 314 EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count); 315} 316 317TEST(FileTest, WriteRetry) { 318 File read_end, write_end; 319 File::pipe(read_end, write_end); 320 enum { SIZE = 4 }; 321 std::streamsize count = 0; 322 EXPECT_RETRY(count = write_end.write("test", SIZE), 323 write, "cannot write to file"); 324 write_end.close(); 325#ifndef _WIN32 326 EXPECT_EQ(static_cast<std::streamsize>(SIZE), count); 327 char buffer[SIZE + 1]; 328 read_end.read(buffer, SIZE); 329 buffer[SIZE] = '\0'; 330 EXPECT_STREQ("test", buffer); 331#endif 332} 333 334#ifdef _WIN32 335TEST(FileTest, ConvertReadCount) { 336 File read_end, write_end; 337 File::pipe(read_end, write_end); 338 char c; 339 std::size_t size = UINT_MAX; 340 if (sizeof(unsigned) != sizeof(std::size_t)) 341 ++size; 342 read_count = 1; 343 read_nbyte = 0; 344 EXPECT_THROW(read_end.read(&c, size), fmt::SystemError); 345 read_count = 0; 346 EXPECT_EQ(UINT_MAX, read_nbyte); 347} 348 349TEST(FileTest, ConvertWriteCount) { 350 File read_end, write_end; 351 File::pipe(read_end, write_end); 352 char c; 353 std::size_t size = UINT_MAX; 354 if (sizeof(unsigned) != sizeof(std::size_t)) 355 ++size; 356 write_count = 1; 357 write_nbyte = 0; 358 EXPECT_THROW(write_end.write(&c, size), fmt::SystemError); 359 write_count = 0; 360 EXPECT_EQ(UINT_MAX, write_nbyte); 361} 362#endif 363 364TEST(FileTest, DupNoRetry) { 365 int stdout_fd = FMT_POSIX(fileno(stdout)); 366 dup_count = 1; 367 EXPECT_SYSTEM_ERROR(File::dup(stdout_fd), EINTR, 368 fmt::format("cannot duplicate file descriptor {}", stdout_fd)); 369 dup_count = 0; 370} 371 372TEST(FileTest, Dup2Retry) { 373 int stdout_fd = FMT_POSIX(fileno(stdout)); 374 File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd); 375 EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2, 376 fmt::format("cannot duplicate file descriptor {} to {}", 377 f1.descriptor(), f2.descriptor())); 378} 379 380TEST(FileTest, Dup2NoExceptRetry) { 381 int stdout_fd = FMT_POSIX(fileno(stdout)); 382 File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd); 383 ErrorCode ec; 384 dup2_count = 1; 385 f1.dup2(f2.descriptor(), ec); 386#ifndef _WIN32 387 EXPECT_EQ(4, dup2_count); 388#else 389 EXPECT_EQ(EINTR, ec.get()); 390#endif 391 dup2_count = 0; 392} 393 394TEST(FileTest, PipeNoRetry) { 395 File read_end, write_end; 396 pipe_count = 1; 397 EXPECT_SYSTEM_ERROR( 398 File::pipe(read_end, write_end), EINTR, "cannot create pipe"); 399 pipe_count = 0; 400} 401 402TEST(FileTest, FdopenNoRetry) { 403 File read_end, write_end; 404 File::pipe(read_end, write_end); 405 fdopen_count = 1; 406 EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), 407 EINTR, "cannot associate stream with file descriptor"); 408 fdopen_count = 0; 409} 410 411TEST(BufferedFileTest, OpenRetry) { 412 write_file("test", "there must be something here"); 413 scoped_ptr<BufferedFile> f; 414 EXPECT_RETRY(f.reset(new BufferedFile("test", "r")), 415 fopen, "cannot open file test"); 416#ifndef _WIN32 417 char c = 0; 418 if (fread(&c, 1, 1, f->get()) < 1) 419 throw fmt::SystemError(errno, "fread failed"); 420#endif 421} 422 423TEST(BufferedFileTest, CloseNoRetryInDtor) { 424 File read_end, write_end; 425 File::pipe(read_end, write_end); 426 scoped_ptr<BufferedFile> f(new BufferedFile(read_end.fdopen("r"))); 427 int saved_fclose_count = 0; 428 EXPECT_WRITE(stderr, { 429 fclose_count = 1; 430 f.reset(); 431 saved_fclose_count = fclose_count; 432 fclose_count = 0; 433 }, format_system_error(EINTR, "cannot close file") + "\n"); 434 EXPECT_EQ(2, saved_fclose_count); 435} 436 437TEST(BufferedFileTest, CloseNoRetry) { 438 File read_end, write_end; 439 File::pipe(read_end, write_end); 440 BufferedFile f = read_end.fdopen("r"); 441 fclose_count = 1; 442 EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file"); 443 EXPECT_EQ(2, fclose_count); 444 fclose_count = 0; 445} 446 447TEST(BufferedFileTest, FilenoNoRetry) { 448 File read_end, write_end; 449 File::pipe(read_end, write_end); 450 BufferedFile f = read_end.fdopen("r"); 451 fileno_count = 1; 452 EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor"); 453 EXPECT_EQ(2, fileno_count); 454 fileno_count = 0; 455} 456 457template <typename Mock> 458struct ScopedMock : testing::StrictMock<Mock> { 459 private: 460 Mock *&global_mock_; 461 462 public: 463 explicit ScopedMock(Mock *&global_mock) : global_mock_(global_mock) { 464 global_mock = this; 465 } 466 ~ScopedMock() { global_mock_ = 0; } 467}; 468 469struct TestMock {}; 470 471TEST(ScopedMock, Scope) { 472 TestMock *global_mock = 0; 473 { 474 ScopedMock<TestMock> mock(global_mock); 475 EXPECT_EQ(&mock, global_mock); 476 TestMock © = mock; 477 } 478 EXPECT_EQ(0, global_mock); 479} 480 481#ifdef FMT_LOCALE 482 483typedef fmt::Locale::Type LocaleType; 484 485struct LocaleMock { 486 MOCK_METHOD3(newlocale, LocaleType (int category_mask, const char *locale, 487 LocaleType base)); 488 MOCK_METHOD1(freelocale, void (LocaleType locale)); 489 490 MOCK_METHOD3(strtod_l, double (const char *nptr, char **endptr, 491 LocaleType locale)); 492} *locale_mock; 493 494#ifdef _MSC_VER 495_locale_t _create_locale(int category, const char *locale) { 496 return locale_mock->newlocale(category, locale, 0); 497} 498 499void _free_locale(_locale_t locale) { 500 locale_mock->freelocale(locale); 501} 502 503double _strtod_l(const char *nptr, char **endptr, _locale_t locale) { 504 return locale_mock->strtod_l(nptr, endptr, locale); 505} 506#endif 507 508LocaleType newlocale(int category_mask, const char *locale, LocaleType base) { 509 return locale_mock->newlocale(category_mask, locale, base); 510} 511 512#ifdef __APPLE__ 513typedef int FreeLocaleResult; 514#else 515typedef void FreeLocaleResult; 516#endif 517 518FreeLocaleResult freelocale(LocaleType locale) { 519 locale_mock->freelocale(locale); 520} 521 522double strtod_l(const char *nptr, char **endptr, LocaleType locale) { 523 return locale_mock->strtod_l(nptr, endptr, locale); 524} 525 526TEST(LocaleTest, LocaleMock) { 527 ScopedMock<LocaleMock> mock(locale_mock); 528 LocaleType locale = reinterpret_cast<LocaleType>(11); 529 EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale)); 530 newlocale(222, "foo", locale); 531} 532 533TEST(LocaleTest, Locale) { 534#ifndef LC_NUMERIC_MASK 535 enum { LC_NUMERIC_MASK = LC_NUMERIC }; 536#endif 537 ScopedMock<LocaleMock> mock(locale_mock); 538 LocaleType impl = reinterpret_cast<LocaleType>(42); 539 EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), 0)) 540 .WillOnce(Return(impl)); 541 EXPECT_CALL(mock, freelocale(impl)); 542 fmt::Locale locale; 543 EXPECT_EQ(impl, locale.get()); 544} 545 546TEST(LocaleTest, Strtod) { 547 ScopedMock<LocaleMock> mock(locale_mock); 548 EXPECT_CALL(mock, newlocale(_, _, _)) 549 .WillOnce(Return(reinterpret_cast<LocaleType>(42))); 550 EXPECT_CALL(mock, freelocale(_)); 551 fmt::Locale locale; 552 const char *str = "4.2"; 553 char end = 'x'; 554 EXPECT_CALL(mock, strtod_l(str, _, locale.get())) 555 .WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777))); 556 EXPECT_EQ(777, locale.strtod(str)); 557 EXPECT_EQ(&end, str); 558} 559 560#endif // FMT_LOCALE 561