posix-mock-test.cc revision fac5546321031b9a32e9ea90c001bc4cef174589
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 "fmt/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 EXPECT_EQ(content.size(), static_cast<fmt::ULongLong>(f.size())); 281#ifdef _WIN32 282 fmt::MemoryWriter message; 283 fmt::internal::format_windows_error( 284 message, ERROR_ACCESS_DENIED, "cannot get file size"); 285 fstat_sim = ERROR; 286 EXPECT_THROW_MSG(f.size(), fmt::WindowsError, message.str()); 287 fstat_sim = NONE; 288#else 289 f.close(); 290 EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes"); 291#endif 292} 293 294TEST(FileTest, MaxSize) { 295 write_file("test", ""); 296 File f("test", File::RDONLY); 297 fstat_sim = MAX_SIZE; 298 EXPECT_GE(f.size(), 0); 299 EXPECT_EQ(max_file_size(), f.size()); 300 fstat_sim = NONE; 301} 302 303TEST(FileTest, ReadRetry) { 304 File read_end, write_end; 305 File::pipe(read_end, write_end); 306 enum { SIZE = 4 }; 307 write_end.write("test", SIZE); 308 write_end.close(); 309 char buffer[SIZE]; 310 std::size_t count = 0; 311 EXPECT_RETRY(count = read_end.read(buffer, SIZE), 312 read, "cannot read from file"); 313 EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count); 314} 315 316TEST(FileTest, WriteRetry) { 317 File read_end, write_end; 318 File::pipe(read_end, write_end); 319 enum { SIZE = 4 }; 320 std::size_t count = 0; 321 EXPECT_RETRY(count = write_end.write("test", SIZE), 322 write, "cannot write to file"); 323 write_end.close(); 324#ifndef _WIN32 325 EXPECT_EQ(static_cast<std::streamsize>(SIZE), count); 326 char buffer[SIZE + 1]; 327 read_end.read(buffer, SIZE); 328 buffer[SIZE] = '\0'; 329 EXPECT_STREQ("test", buffer); 330#endif 331} 332 333#ifdef _WIN32 334TEST(FileTest, ConvertReadCount) { 335 File read_end, write_end; 336 File::pipe(read_end, write_end); 337 char c; 338 std::size_t size = UINT_MAX; 339 if (sizeof(unsigned) != sizeof(std::size_t)) 340 ++size; 341 read_count = 1; 342 read_nbyte = 0; 343 EXPECT_THROW(read_end.read(&c, size), fmt::SystemError); 344 read_count = 0; 345 EXPECT_EQ(UINT_MAX, read_nbyte); 346} 347 348TEST(FileTest, ConvertWriteCount) { 349 File read_end, write_end; 350 File::pipe(read_end, write_end); 351 char c; 352 std::size_t size = UINT_MAX; 353 if (sizeof(unsigned) != sizeof(std::size_t)) 354 ++size; 355 write_count = 1; 356 write_nbyte = 0; 357 EXPECT_THROW(write_end.write(&c, size), fmt::SystemError); 358 write_count = 0; 359 EXPECT_EQ(UINT_MAX, write_nbyte); 360} 361#endif 362 363TEST(FileTest, DupNoRetry) { 364 int stdout_fd = FMT_POSIX(fileno(stdout)); 365 dup_count = 1; 366 EXPECT_SYSTEM_ERROR(File::dup(stdout_fd), EINTR, 367 fmt::format("cannot duplicate file descriptor {}", stdout_fd)); 368 dup_count = 0; 369} 370 371TEST(FileTest, Dup2Retry) { 372 int stdout_fd = FMT_POSIX(fileno(stdout)); 373 File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd); 374 EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2, 375 fmt::format("cannot duplicate file descriptor {} to {}", 376 f1.descriptor(), f2.descriptor())); 377} 378 379TEST(FileTest, Dup2NoExceptRetry) { 380 int stdout_fd = FMT_POSIX(fileno(stdout)); 381 File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd); 382 ErrorCode ec; 383 dup2_count = 1; 384 f1.dup2(f2.descriptor(), ec); 385#ifndef _WIN32 386 EXPECT_EQ(4, dup2_count); 387#else 388 EXPECT_EQ(EINTR, ec.get()); 389#endif 390 dup2_count = 0; 391} 392 393TEST(FileTest, PipeNoRetry) { 394 File read_end, write_end; 395 pipe_count = 1; 396 EXPECT_SYSTEM_ERROR( 397 File::pipe(read_end, write_end), EINTR, "cannot create pipe"); 398 pipe_count = 0; 399} 400 401TEST(FileTest, FdopenNoRetry) { 402 File read_end, write_end; 403 File::pipe(read_end, write_end); 404 fdopen_count = 1; 405 EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), 406 EINTR, "cannot associate stream with file descriptor"); 407 fdopen_count = 0; 408} 409 410TEST(BufferedFileTest, OpenRetry) { 411 write_file("test", "there must be something here"); 412 scoped_ptr<BufferedFile> f; 413 EXPECT_RETRY(f.reset(new BufferedFile("test", "r")), 414 fopen, "cannot open file test"); 415#ifndef _WIN32 416 char c = 0; 417 if (fread(&c, 1, 1, f->get()) < 1) 418 throw fmt::SystemError(errno, "fread failed"); 419#endif 420} 421 422TEST(BufferedFileTest, CloseNoRetryInDtor) { 423 File read_end, write_end; 424 File::pipe(read_end, write_end); 425 scoped_ptr<BufferedFile> f(new BufferedFile(read_end.fdopen("r"))); 426 int saved_fclose_count = 0; 427 EXPECT_WRITE(stderr, { 428 fclose_count = 1; 429 f.reset(); 430 saved_fclose_count = fclose_count; 431 fclose_count = 0; 432 }, format_system_error(EINTR, "cannot close file") + "\n"); 433 EXPECT_EQ(2, saved_fclose_count); 434} 435 436TEST(BufferedFileTest, CloseNoRetry) { 437 File read_end, write_end; 438 File::pipe(read_end, write_end); 439 BufferedFile f = read_end.fdopen("r"); 440 fclose_count = 1; 441 EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file"); 442 EXPECT_EQ(2, fclose_count); 443 fclose_count = 0; 444} 445 446TEST(BufferedFileTest, FilenoNoRetry) { 447 File read_end, write_end; 448 File::pipe(read_end, write_end); 449 BufferedFile f = read_end.fdopen("r"); 450 fileno_count = 1; 451 EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor"); 452 EXPECT_EQ(2, fileno_count); 453 fileno_count = 0; 454} 455 456struct TestMock { 457 static TestMock *instance; 458} *TestMock::instance; 459 460TEST(ScopedMock, Scope) { 461 { 462 ScopedMock<TestMock> mock; 463 EXPECT_EQ(&mock, TestMock::instance); 464 TestMock © = mock; 465 } 466 EXPECT_EQ(0, TestMock::instance); 467} 468 469#ifdef FMT_LOCALE 470 471typedef fmt::Locale::Type LocaleType; 472 473struct LocaleMock { 474 static LocaleMock *instance; 475 MOCK_METHOD3(newlocale, LocaleType (int category_mask, const char *locale, 476 LocaleType base)); 477 MOCK_METHOD1(freelocale, void (LocaleType locale)); 478 479 MOCK_METHOD3(strtod_l, double (const char *nptr, char **endptr, 480 LocaleType locale)); 481} *LocaleMock::instance; 482 483#ifdef _MSC_VER 484# pragma warning(push) 485# pragma warning(disable: 4273) 486 487_locale_t _create_locale(int category, const char *locale) { 488 return LocaleMock::instance->newlocale(category, locale, 0); 489} 490 491void _free_locale(_locale_t locale) { 492 LocaleMock::instance->freelocale(locale); 493} 494 495double _strtod_l(const char *nptr, char **endptr, _locale_t locale) { 496 return LocaleMock::instance->strtod_l(nptr, endptr, locale); 497} 498# pragma warning(pop) 499#endif 500 501LocaleType newlocale(int category_mask, const char *locale, LocaleType base) { 502 return LocaleMock::instance->newlocale(category_mask, locale, base); 503} 504 505#if defined(__APPLE__) || defined(__FreeBSD__) 506typedef int FreeLocaleResult; 507#else 508typedef void FreeLocaleResult; 509#endif 510 511FreeLocaleResult freelocale(LocaleType locale) { 512 LocaleMock::instance->freelocale(locale); 513 return FreeLocaleResult(); 514} 515 516double strtod_l(const char *nptr, char **endptr, LocaleType locale) { 517 return LocaleMock::instance->strtod_l(nptr, endptr, locale); 518} 519 520TEST(LocaleTest, LocaleMock) { 521 ScopedMock<LocaleMock> mock; 522 LocaleType locale = reinterpret_cast<LocaleType>(11); 523 EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale)); 524 newlocale(222, "foo", locale); 525} 526 527TEST(LocaleTest, Locale) { 528#ifndef LC_NUMERIC_MASK 529 enum { LC_NUMERIC_MASK = LC_NUMERIC }; 530#endif 531 ScopedMock<LocaleMock> mock; 532 LocaleType impl = reinterpret_cast<LocaleType>(42); 533 EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), 0)) 534 .WillOnce(Return(impl)); 535 EXPECT_CALL(mock, freelocale(impl)); 536 fmt::Locale locale; 537 EXPECT_EQ(impl, locale.get()); 538} 539 540TEST(LocaleTest, Strtod) { 541 ScopedMock<LocaleMock> mock; 542 EXPECT_CALL(mock, newlocale(_, _, _)) 543 .WillOnce(Return(reinterpret_cast<LocaleType>(42))); 544 EXPECT_CALL(mock, freelocale(_)); 545 fmt::Locale locale; 546 const char *str = "4.2"; 547 char end = 'x'; 548 EXPECT_CALL(mock, strtod_l(str, _, locale.get())) 549 .WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777))); 550 EXPECT_EQ(777, locale.strtod(str)); 551 EXPECT_EQ(&end, str); 552} 553 554#endif // FMT_LOCALE 555