• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef _CRT_SECURE_NO_WARNINGS
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 #  undef ERROR
27 #endif
28 
29 #include "gmock.h"
30 #include "gtest-extra.h"
31 #include "util.h"
32 
33 using fmt::buffered_file;
34 using fmt::error_code;
35 
36 using testing::_;
37 using testing::Return;
38 using testing::StrEq;
39 
40 namespace {
41 int open_count;
42 int close_count;
43 int dup_count;
44 int dup2_count;
45 int fdopen_count;
46 int read_count;
47 int write_count;
48 int pipe_count;
49 int fopen_count;
50 int fclose_count;
51 int fileno_count;
52 size_t read_nbyte;
53 size_t write_nbyte;
54 bool sysconf_error;
55 
56 enum { NONE, MAX_SIZE, ERROR } fstat_sim;
57 }  // namespace
58 
59 #define EMULATE_EINTR(func, error_result) \
60   if (func##_count != 0) {                \
61     if (func##_count++ != 3) {            \
62       errno = EINTR;                      \
63       return error_result;                \
64     }                                     \
65   }
66 
67 #ifndef _MSC_VER
open(const char * path,int oflag,int mode)68 int test::open(const char* path, int oflag, int mode) {
69   EMULATE_EINTR(open, -1);
70   return ::open(path, oflag, mode);
71 }
72 #else
sopen_s(int * pfh,const char * filename,int oflag,int shflag,int pmode)73 errno_t test::sopen_s(int* pfh, const char* filename, int oflag, int shflag,
74                       int pmode) {
75   EMULATE_EINTR(open, EINTR);
76   return _sopen_s(pfh, filename, oflag, shflag, pmode);
77 }
78 #endif
79 
80 #ifndef _WIN32
81 
sysconf(int name)82 long test::sysconf(int name) {
83   long result = ::sysconf(name);
84   if (!sysconf_error) return result;
85   // Simulate an error.
86   errno = EINVAL;
87   return -1;
88 }
89 
max_file_size()90 static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
91 
fstat(int fd,struct stat * buf)92 int test::fstat(int fd, struct stat* buf) {
93   int result = ::fstat(fd, buf);
94   if (fstat_sim == MAX_SIZE) buf->st_size = max_file_size();
95   return result;
96 }
97 
98 #else
99 
max_file_size()100 static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); }
101 
GetFileSize(HANDLE hFile,LPDWORD lpFileSizeHigh)102 DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
103   if (fstat_sim == ERROR) {
104     SetLastError(ERROR_ACCESS_DENIED);
105     return INVALID_FILE_SIZE;
106   }
107   if (fstat_sim == MAX_SIZE) {
108     DWORD max = std::numeric_limits<DWORD>::max();
109     *lpFileSizeHigh = max >> 1;
110     return max;
111   }
112   return ::GetFileSize(hFile, lpFileSizeHigh);
113 }
114 
115 #endif
116 
close(int fildes)117 int test::close(int fildes) {
118   // Close the file first because close shouldn't be retried.
119   int result = ::FMT_POSIX(close(fildes));
120   EMULATE_EINTR(close, -1);
121   return result;
122 }
123 
dup(int fildes)124 int test::dup(int fildes) {
125   EMULATE_EINTR(dup, -1);
126   return ::FMT_POSIX(dup(fildes));
127 }
128 
dup2(int fildes,int fildes2)129 int test::dup2(int fildes, int fildes2) {
130   EMULATE_EINTR(dup2, -1);
131   return ::FMT_POSIX(dup2(fildes, fildes2));
132 }
133 
fdopen(int fildes,const char * mode)134 FILE* test::fdopen(int fildes, const char* mode) {
135   EMULATE_EINTR(fdopen, nullptr);
136   return ::FMT_POSIX(fdopen(fildes, mode));
137 }
138 
read(int fildes,void * buf,test::size_t nbyte)139 test::ssize_t test::read(int fildes, void* buf, test::size_t nbyte) {
140   read_nbyte = nbyte;
141   EMULATE_EINTR(read, -1);
142   return ::FMT_POSIX(read(fildes, buf, nbyte));
143 }
144 
write(int fildes,const void * buf,test::size_t nbyte)145 test::ssize_t test::write(int fildes, const void* buf, test::size_t nbyte) {
146   write_nbyte = nbyte;
147   EMULATE_EINTR(write, -1);
148   return ::FMT_POSIX(write(fildes, buf, nbyte));
149 }
150 
151 #ifndef _WIN32
pipe(int fildes[2])152 int test::pipe(int fildes[2]) {
153   EMULATE_EINTR(pipe, -1);
154   return ::pipe(fildes);
155 }
156 #else
pipe(int * pfds,unsigned psize,int textmode)157 int test::pipe(int* pfds, unsigned psize, int textmode) {
158   EMULATE_EINTR(pipe, -1);
159   return _pipe(pfds, psize, textmode);
160 }
161 #endif
162 
fopen(const char * filename,const char * mode)163 FILE* test::fopen(const char* filename, const char* mode) {
164   EMULATE_EINTR(fopen, nullptr);
165   return ::fopen(filename, mode);
166 }
167 
fclose(FILE * stream)168 int test::fclose(FILE* stream) {
169   EMULATE_EINTR(fclose, EOF);
170   return ::fclose(stream);
171 }
172 
173 int(test::fileno)(FILE* stream) {
174   EMULATE_EINTR(fileno, -1);
175 #ifdef fileno
176   return FMT_POSIX(fileno(stream));
177 #else
178   return ::FMT_POSIX(fileno(stream));
179 #endif
180 }
181 
182 #ifndef _WIN32
183 #  define EXPECT_RETRY(statement, func, message) \
184     func##_count = 1;                            \
185     statement;                                   \
186     EXPECT_EQ(4, func##_count);                  \
187     func##_count = 0;
188 #  define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
189 #else
190 #  define EXPECT_RETRY(statement, func, message)    \
191     func##_count = 1;                               \
192     EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
193     func##_count = 0;
194 #  define EXPECT_EQ_POSIX(expected, actual)
195 #endif
196 
write_file(fmt::cstring_view filename,fmt::string_view content)197 static void write_file(fmt::cstring_view filename, fmt::string_view content) {
198   fmt::buffered_file f(filename, "w");
199   f.print("{}", content);
200 }
201 
202 #if FMT_USE_FCNTL
203 using fmt::file;
204 
TEST(UtilTest,GetPageSize)205 TEST(UtilTest, GetPageSize) {
206 #  ifdef _WIN32
207   SYSTEM_INFO si = {};
208   GetSystemInfo(&si);
209   EXPECT_EQ(si.dwPageSize, fmt::getpagesize());
210 #  else
211   EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
212   sysconf_error = true;
213   EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL,
214                       "cannot get memory page size");
215   sysconf_error = false;
216 #  endif
217 }
218 
TEST(FileTest,OpenRetry)219 TEST(FileTest, OpenRetry) {
220   write_file("temp", "there must be something here");
221   std::unique_ptr<file> f{nullptr};
222   EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
223                "cannot open file temp");
224 #  ifndef _WIN32
225   char c = 0;
226   f->read(&c, 1);
227 #  endif
228 }
229 
TEST(FileTest,CloseNoRetryInDtor)230 TEST(FileTest, CloseNoRetryInDtor) {
231   file read_end, write_end;
232   file::pipe(read_end, write_end);
233   std::unique_ptr<file> f(new file(std::move(read_end)));
234   int saved_close_count = 0;
235   EXPECT_WRITE(
236       stderr,
237       {
238         close_count = 1;
239         f.reset(nullptr);
240         saved_close_count = close_count;
241         close_count = 0;
242       },
243       format_system_error(EINTR, "cannot close file") + "\n");
244   EXPECT_EQ(2, saved_close_count);
245 }
246 
TEST(FileTest,CloseNoRetry)247 TEST(FileTest, CloseNoRetry) {
248   file read_end, write_end;
249   file::pipe(read_end, write_end);
250   close_count = 1;
251   EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file");
252   EXPECT_EQ(2, close_count);
253   close_count = 0;
254 }
255 
TEST(FileTest,Size)256 TEST(FileTest, Size) {
257   std::string content = "top secret, destroy before reading";
258   write_file("temp", content);
259   file f("temp", file::RDONLY);
260   EXPECT_GE(f.size(), 0);
261   EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
262 #  ifdef _WIN32
263   fmt::memory_buffer message;
264   fmt::detail::format_windows_error(message, ERROR_ACCESS_DENIED,
265                                     "cannot get file size");
266   fstat_sim = ERROR;
267   EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
268   fstat_sim = NONE;
269 #  else
270   f.close();
271   EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
272 #  endif
273 }
274 
TEST(FileTest,MaxSize)275 TEST(FileTest, MaxSize) {
276   write_file("temp", "");
277   file f("temp", file::RDONLY);
278   fstat_sim = MAX_SIZE;
279   EXPECT_GE(f.size(), 0);
280   EXPECT_EQ(max_file_size(), f.size());
281   fstat_sim = NONE;
282 }
283 
TEST(FileTest,ReadRetry)284 TEST(FileTest, ReadRetry) {
285   file read_end, write_end;
286   file::pipe(read_end, write_end);
287   enum { SIZE = 4 };
288   write_end.write("test", SIZE);
289   write_end.close();
290   char buffer[SIZE];
291   size_t count = 0;
292   EXPECT_RETRY(count = read_end.read(buffer, SIZE), read,
293                "cannot read from file");
294   EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
295 }
296 
TEST(FileTest,WriteRetry)297 TEST(FileTest, WriteRetry) {
298   file read_end, write_end;
299   file::pipe(read_end, write_end);
300   enum { SIZE = 4 };
301   size_t count = 0;
302   EXPECT_RETRY(count = write_end.write("test", SIZE), write,
303                "cannot write to file");
304   write_end.close();
305 #  ifndef _WIN32
306   EXPECT_EQ(static_cast<std::streamsize>(SIZE), count);
307   char buffer[SIZE + 1];
308   read_end.read(buffer, SIZE);
309   buffer[SIZE] = '\0';
310   EXPECT_STREQ("test", buffer);
311 #  endif
312 }
313 
314 #  ifdef _WIN32
TEST(FileTest,ConvertReadCount)315 TEST(FileTest, ConvertReadCount) {
316   file read_end, write_end;
317   file::pipe(read_end, write_end);
318   char c;
319   size_t size = UINT_MAX;
320   if (sizeof(unsigned) != sizeof(size_t)) ++size;
321   read_count = 1;
322   read_nbyte = 0;
323   EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
324   read_count = 0;
325   EXPECT_EQ(UINT_MAX, read_nbyte);
326 }
327 
TEST(FileTest,ConvertWriteCount)328 TEST(FileTest, ConvertWriteCount) {
329   file read_end, write_end;
330   file::pipe(read_end, write_end);
331   char c;
332   size_t size = UINT_MAX;
333   if (sizeof(unsigned) != sizeof(size_t)) ++size;
334   write_count = 1;
335   write_nbyte = 0;
336   EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
337   write_count = 0;
338   EXPECT_EQ(UINT_MAX, write_nbyte);
339 }
340 #  endif
341 
TEST(FileTest,DupNoRetry)342 TEST(FileTest, DupNoRetry) {
343   int stdout_fd = FMT_POSIX(fileno(stdout));
344   dup_count = 1;
345   EXPECT_SYSTEM_ERROR(
346       file::dup(stdout_fd), EINTR,
347       fmt::format("cannot duplicate file descriptor {}", stdout_fd));
348   dup_count = 0;
349 }
350 
TEST(FileTest,Dup2Retry)351 TEST(FileTest, Dup2Retry) {
352   int stdout_fd = FMT_POSIX(fileno(stdout));
353   file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
354   EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
355                fmt::format("cannot duplicate file descriptor {} to {}",
356                            f1.descriptor(), f2.descriptor()));
357 }
358 
TEST(FileTest,Dup2NoExceptRetry)359 TEST(FileTest, Dup2NoExceptRetry) {
360   int stdout_fd = FMT_POSIX(fileno(stdout));
361   file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
362   error_code ec;
363   dup2_count = 1;
364   f1.dup2(f2.descriptor(), ec);
365 #  ifndef _WIN32
366   EXPECT_EQ(4, dup2_count);
367 #  else
368   EXPECT_EQ(EINTR, ec.get());
369 #  endif
370   dup2_count = 0;
371 }
372 
TEST(FileTest,PipeNoRetry)373 TEST(FileTest, PipeNoRetry) {
374   file read_end, write_end;
375   pipe_count = 1;
376   EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR,
377                       "cannot create pipe");
378   pipe_count = 0;
379 }
380 
TEST(FileTest,FdopenNoRetry)381 TEST(FileTest, FdopenNoRetry) {
382   file read_end, write_end;
383   file::pipe(read_end, write_end);
384   fdopen_count = 1;
385   EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EINTR,
386                       "cannot associate stream with file descriptor");
387   fdopen_count = 0;
388 }
389 
TEST(BufferedFileTest,OpenRetry)390 TEST(BufferedFileTest, OpenRetry) {
391   write_file("temp", "there must be something here");
392   std::unique_ptr<buffered_file> f{nullptr};
393   EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
394                "cannot open file temp");
395 #  ifndef _WIN32
396   char c = 0;
397   if (fread(&c, 1, 1, f->get()) < 1)
398     throw fmt::system_error(errno, "fread failed");
399 #  endif
400 }
401 
TEST(BufferedFileTest,CloseNoRetryInDtor)402 TEST(BufferedFileTest, CloseNoRetryInDtor) {
403   file read_end, write_end;
404   file::pipe(read_end, write_end);
405   std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
406   int saved_fclose_count = 0;
407   EXPECT_WRITE(
408       stderr,
409       {
410         fclose_count = 1;
411         f.reset(nullptr);
412         saved_fclose_count = fclose_count;
413         fclose_count = 0;
414       },
415       format_system_error(EINTR, "cannot close file") + "\n");
416   EXPECT_EQ(2, saved_fclose_count);
417 }
418 
TEST(BufferedFileTest,CloseNoRetry)419 TEST(BufferedFileTest, CloseNoRetry) {
420   file read_end, write_end;
421   file::pipe(read_end, write_end);
422   buffered_file f = read_end.fdopen("r");
423   fclose_count = 1;
424   EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
425   EXPECT_EQ(2, fclose_count);
426   fclose_count = 0;
427 }
428 
TEST(BufferedFileTest,FilenoNoRetry)429 TEST(BufferedFileTest, FilenoNoRetry) {
430   file read_end, write_end;
431   file::pipe(read_end, write_end);
432   buffered_file f = read_end.fdopen("r");
433   fileno_count = 1;
434   EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
435   EXPECT_EQ(2, fileno_count);
436   fileno_count = 0;
437 }
438 #endif  // FMT_USE_FCNTL
439 
440 struct test_mock {
441   static test_mock* instance;
442 } * test_mock::instance;
443 
TEST(ScopedMock,Scope)444 TEST(ScopedMock, Scope) {
445   {
446     ScopedMock<test_mock> mock;
447     EXPECT_EQ(&mock, test_mock::instance);
448     test_mock& copy = mock;
449     static_cast<void>(copy);
450   }
451   EXPECT_EQ(nullptr, test_mock::instance);
452 }
453 
454 #ifdef FMT_LOCALE
455 
456 typedef fmt::locale::type locale_type;
457 
458 struct locale_mock {
459   static locale_mock* instance;
460   MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
461                                       locale_type base));
462   MOCK_METHOD1(freelocale, void(locale_type locale));
463 
464   MOCK_METHOD3(strtod_l,
465                double(const char* nptr, char** endptr, locale_type locale));
466 } * locale_mock::instance;
467 
468 #  ifdef _MSC_VER
469 #    pragma warning(push)
470 #    pragma warning(disable : 4273)
471 #    ifdef __clang__
472 #      pragma clang diagnostic push
473 #      pragma clang diagnostic ignored "-Winconsistent-dllimport"
474 #    endif
475 
_create_locale(int category,const char * locale)476 _locale_t _create_locale(int category, const char* locale) {
477   return locale_mock::instance->newlocale(category, locale, 0);
478 }
479 
_free_locale(_locale_t locale)480 void _free_locale(_locale_t locale) {
481   locale_mock::instance->freelocale(locale);
482 }
483 
_strtod_l(const char * nptr,char ** endptr,_locale_t locale)484 double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
485   return locale_mock::instance->strtod_l(nptr, endptr, locale);
486 }
487 #    ifdef __clang__
488 #      pragma clang diagnostic pop
489 #    endif
490 #    pragma warning(pop)
491 #  endif
492 
493 #  if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
494 #    define FMT_LOCALE_THROW __THROW
495 #  else
496 #    define FMT_LOCALE_THROW
497 #  endif
498 
499 #  if defined(__APPLE__) || \
500       (defined(__FreeBSD__) && __FreeBSD_version < 1200002)
501 typedef int FreeLocaleResult;
502 #  else
503 typedef void FreeLocaleResult;
504 #  endif
505 
freelocale(locale_type locale)506 FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW {
507   locale_mock::instance->freelocale(locale);
508   return FreeLocaleResult();
509 }
510 
strtod_l(const char * nptr,char ** endptr,locale_type locale)511 double strtod_l(const char* nptr, char** endptr,
512                 locale_type locale) FMT_LOCALE_THROW {
513   return locale_mock::instance->strtod_l(nptr, endptr, locale);
514 }
515 
516 #  undef FMT_LOCALE_THROW
517 
518 #  ifndef _WIN32
newlocale(int category_mask,const char * locale,locale_t base)519 locale_t test::newlocale(int category_mask, const char* locale, locale_t base) {
520   return locale_mock::instance->newlocale(category_mask, locale, base);
521 }
522 
TEST(LocaleTest,LocaleMock)523 TEST(LocaleTest, LocaleMock) {
524   ScopedMock<locale_mock> mock;
525   locale_type locale = reinterpret_cast<locale_type>(11);
526   EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
527   FMT_SYSTEM(newlocale(222, "foo", locale));
528 }
529 #  endif
530 
TEST(LocaleTest,Locale)531 TEST(LocaleTest, Locale) {
532 #  ifndef LC_NUMERIC_MASK
533   enum { LC_NUMERIC_MASK = LC_NUMERIC };
534 #  endif
535   ScopedMock<locale_mock> mock;
536   locale_type impl = reinterpret_cast<locale_type>(42);
537   EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
538       .WillOnce(Return(impl));
539   EXPECT_CALL(mock, freelocale(impl));
540   fmt::locale loc;
541   EXPECT_EQ(impl, loc.get());
542 }
543 
TEST(LocaleTest,Strtod)544 TEST(LocaleTest, Strtod) {
545   ScopedMock<locale_mock> mock;
546   EXPECT_CALL(mock, newlocale(_, _, _))
547       .WillOnce(Return(reinterpret_cast<locale_type>(42)));
548   EXPECT_CALL(mock, freelocale(_));
549   fmt::locale loc;
550   const char* str = "4.2";
551   char end = 'x';
552   EXPECT_CALL(mock, strtod_l(str, _, loc.get()))
553       .WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
554   EXPECT_EQ(777, loc.strtod(str));
555   EXPECT_EQ(&end, str);
556 }
557 
558 #endif  // FMT_LOCALE
559