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