• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
open(const char * path,int oflag,int mode)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
sopen_s(int * pfh,const char * filename,int oflag,int shflag,int pmode)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 
sysconf(int name)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 
max_file_size()108 static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
109 
fstat(int fd,struct stat * buf)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 
max_file_size()119 static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); }
120 
GetFileSize(HANDLE hFile,LPDWORD lpFileSizeHigh)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 
close(int fildes)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 
dup(int fildes)143 int test::dup(int fildes) {
144   EMULATE_EINTR(dup, -1);
145   return ::FMT_POSIX(dup(fildes));
146 }
147 
dup2(int fildes,int fildes2)148 int test::dup2(int fildes, int fildes2) {
149   EMULATE_EINTR(dup2, -1);
150   return ::FMT_POSIX(dup2(fildes, fildes2));
151 }
152 
fdopen(int fildes,const char * mode)153 FILE *test::fdopen(int fildes, const char *mode) {
154   EMULATE_EINTR(fdopen, 0);
155   return ::FMT_POSIX(fdopen(fildes, mode));
156 }
157 
read(int fildes,void * buf,test::size_t nbyte)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 
write(int fildes,const void * buf,test::size_t nbyte)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
pipe(int fildes[2])171 int test::pipe(int fildes[2]) {
172   EMULATE_EINTR(pipe, -1);
173   return ::pipe(fildes);
174 }
175 #else
pipe(int * pfds,unsigned psize,int textmode)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 
fopen(const char * filename,const char * mode)182 FILE *test::fopen(const char *filename, const char *mode) {
183   EMULATE_EINTR(fopen, 0);
184   return ::fopen(filename, mode);
185 }
186 
fclose(FILE * stream)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 
write_file(fmt::CStringRef filename,fmt::StringRef content)216 void write_file(fmt::CStringRef filename, fmt::StringRef content) {
217   fmt::BufferedFile f(filename, "w");
218   f.print("{}", content);
219 }
220 
TEST(UtilTest,StaticAssert)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 
TEST(UtilTest,GetPageSize)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 
TEST(FileTest,OpenRetry)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 
TEST(FileTest,CloseNoRetryInDtor)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 
TEST(FileTest,CloseNoRetry)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 
TEST(FileTest,Size)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 
TEST(FileTest,MaxSize)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 
TEST(FileTest,ReadRetry)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 
TEST(FileTest,WriteRetry)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
TEST(FileTest,ConvertReadCount)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 
TEST(FileTest,ConvertWriteCount)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 
TEST(FileTest,DupNoRetry)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 
TEST(FileTest,Dup2Retry)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 
TEST(FileTest,Dup2NoExceptRetry)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 
TEST(FileTest,PipeNoRetry)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 
TEST(FileTest,FdopenNoRetry)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 
TEST(BufferedFileTest,OpenRetry)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 
TEST(BufferedFileTest,CloseNoRetryInDtor)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 
TEST(BufferedFileTest,CloseNoRetry)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 
TEST(BufferedFileTest,FilenoNoRetry)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 
TEST(ScopedMock,Scope)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 
_create_locale(int category,const char * locale)487 _locale_t _create_locale(int category, const char *locale) {
488   return LocaleMock::instance->newlocale(category, locale, 0);
489 }
490 
_free_locale(_locale_t locale)491 void _free_locale(_locale_t locale) {
492   LocaleMock::instance->freelocale(locale);
493 }
494 
_strtod_l(const char * nptr,char ** endptr,_locale_t locale)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 
newlocale(int category_mask,const char * locale,LocaleType base)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 
freelocale(LocaleType locale)511 FreeLocaleResult freelocale(LocaleType locale) {
512   LocaleMock::instance->freelocale(locale);
513   return FreeLocaleResult();
514 }
515 
strtod_l(const char * nptr,char ** endptr,LocaleType locale)516 double strtod_l(const char *nptr, char **endptr, LocaleType locale) {
517   return LocaleMock::instance->strtod_l(nptr, endptr, locale);
518 }
519 
TEST(LocaleTest,LocaleMock)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 
TEST(LocaleTest,Locale)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 
TEST(LocaleTest,Strtod)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