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
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  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
294TEST(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
303TEST(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
316TEST(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
334TEST(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
348TEST(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
363TEST(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
371TEST(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
379TEST(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
393TEST(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
401TEST(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
410TEST(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
422TEST(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
436TEST(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
446TEST(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
456struct TestMock {
457  static TestMock *instance;
458} *TestMock::instance;
459
460TEST(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
471typedef fmt::Locale::Type LocaleType;
472
473struct 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
491void _free_locale(_locale_t locale) {
492  LocaleMock::instance->freelocale(locale);
493}
494
495double _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
501LocaleType 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__)
506typedef int FreeLocaleResult;
507#else
508typedef void FreeLocaleResult;
509#endif
510
511FreeLocaleResult freelocale(LocaleType locale) {
512  LocaleMock::instance->freelocale(locale);
513  return FreeLocaleResult();
514}
515
516double strtod_l(const char *nptr, char **endptr, LocaleType locale) {
517  return LocaleMock::instance->strtod_l(nptr, endptr, locale);
518}
519
520TEST(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
527TEST(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
540TEST(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