Home | History | Annotate | Download | only in test
      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 
     48 using fmt::BufferedFile;
     49 using fmt::ErrorCode;
     50 using fmt::File;
     51 
     52 using testing::internal::scoped_ptr;
     53 using testing::_;
     54 using testing::StrEq;
     55 using testing::Return;
     56 
     57 namespace {
     58 int open_count;
     59 int close_count;
     60 int dup_count;
     61 int dup2_count;
     62 int fdopen_count;
     63 int read_count;
     64 int write_count;
     65 int pipe_count;
     66 int fopen_count;
     67 int fclose_count;
     68 int fileno_count;
     69 std::size_t read_nbyte;
     70 std::size_t write_nbyte;
     71 bool sysconf_error;
     72 
     73 enum 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
     85 int test::open(const char *path, int oflag, int mode) {
     86   EMULATE_EINTR(open, -1);
     87   return ::open(path, oflag, mode);
     88 }
     89 #else
     90 errno_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 
     99 long 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 
    108 static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
    109 
    110 int 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 
    119 static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); }
    120 
    121 DWORD 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 
    136 int 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 
    143 int test::dup(int fildes) {
    144   EMULATE_EINTR(dup, -1);
    145   return ::FMT_POSIX(dup(fildes));
    146 }
    147 
    148 int test::dup2(int fildes, int fildes2) {
    149   EMULATE_EINTR(dup2, -1);
    150   return ::FMT_POSIX(dup2(fildes, fildes2));
    151 }
    152 
    153 FILE *test::fdopen(int fildes, const char *mode) {
    154   EMULATE_EINTR(fdopen, 0);
    155   return ::FMT_POSIX(fdopen(fildes, mode));
    156 }
    157 
    158 test::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 
    164 test::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
    171 int test::pipe(int fildes[2]) {
    172   EMULATE_EINTR(pipe, -1);
    173   return ::pipe(fildes);
    174 }
    175 #else
    176 int test::pipe(int *pfds, unsigned psize, int textmode) {
    177   EMULATE_EINTR(pipe, -1);
    178   return _pipe(pfds, psize, textmode);
    179 }
    180 #endif
    181 
    182 FILE *test::fopen(const char *filename, const char *mode) {
    183   EMULATE_EINTR(fopen, 0);
    184   return ::fopen(filename, mode);
    185 }
    186 
    187 int test::fclose(FILE *stream) {
    188   EMULATE_EINTR(fclose, EOF);
    189   return ::fclose(stream);
    190 }
    191 
    192 int (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 
    216 void write_file(fmt::CStringRef filename, fmt::StringRef content) {
    217   fmt::BufferedFile f(filename, "w");
    218   f.print("{}", content);
    219 }
    220 
    221 TEST(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 
    227 TEST(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 
    241 TEST(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 
    252 TEST(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 
    266 TEST(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 
    275 TEST(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 
    294 TEST(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 
    303 TEST(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 
    316 TEST(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
    334 TEST(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 
    348 TEST(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 
    363 TEST(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 
    371 TEST(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 
    379 TEST(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 
    393 TEST(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 
    401 TEST(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 
    410 TEST(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 
    422 TEST(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 
    436 TEST(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 
    446 TEST(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 
    456 struct TestMock {
    457   static TestMock *instance;
    458 } *TestMock::instance;
    459 
    460 TEST(ScopedMock, Scope) {
    461   {
    462     ScopedMock<TestMock> mock;
    463     EXPECT_EQ(&mock, TestMock::instance);
    464     TestMock &copy = mock;
    465   }
    466   EXPECT_EQ(0, TestMock::instance);
    467 }
    468 
    469 #ifdef FMT_LOCALE
    470 
    471 typedef fmt::Locale::Type LocaleType;
    472 
    473 struct 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 
    491 void _free_locale(_locale_t locale) {
    492   LocaleMock::instance->freelocale(locale);
    493 }
    494 
    495 double _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 
    501 LocaleType 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__)
    506 typedef int FreeLocaleResult;
    507 #else
    508 typedef void FreeLocaleResult;
    509 #endif
    510 
    511 FreeLocaleResult freelocale(LocaleType locale) {
    512   LocaleMock::instance->freelocale(locale);
    513   return FreeLocaleResult();
    514 }
    515 
    516 double strtod_l(const char *nptr, char **endptr, LocaleType locale) {
    517   return LocaleMock::instance->strtod_l(nptr, endptr, locale);
    518 }
    519 
    520 TEST(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 
    527 TEST(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 
    540 TEST(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