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