posix-mock-test.cc revision d02a5031f0b550605d49fc9b65f70afe57d3a782
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 "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 "gtest-extra.h"
45#include "util.h"
46
47using fmt::BufferedFile;
48using fmt::ErrorCode;
49using fmt::File;
50
51namespace {
52int open_count;
53int close_count;
54int dup_count;
55int dup2_count;
56int fdopen_count;
57int read_count;
58int write_count;
59int pipe_count;
60int fopen_count;
61int fclose_count;
62int fileno_count;
63std::size_t read_nbyte;
64std::size_t write_nbyte;
65bool sysconf_error;
66
67enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim;
68}
69
70#define EMULATE_EINTR(func, error_result) \
71  if (func##_count != 0) { \
72    if (func##_count++ != 3) { \
73      errno = EINTR; \
74      return error_result; \
75    } \
76  }
77
78#ifndef _MSC_VER
79int test::open(const char *path, int oflag, int mode) {
80  EMULATE_EINTR(open, -1);
81  return ::open(path, oflag, mode);
82}
83#else
84errno_t test::sopen_s(
85    int* pfh, const char *filename, int oflag, int shflag, int pmode) {
86  EMULATE_EINTR(open, EINTR);
87  return _sopen_s(pfh, filename, oflag, shflag, pmode);
88}
89#endif
90
91#ifndef _WIN32
92
93long test::sysconf(int name) {
94  long result = ::sysconf(name);
95  if (!sysconf_error)
96    return result;
97  // Simulate an error.
98  errno = EINVAL;
99  return -1;
100}
101
102static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
103
104int test::fstat(int fd, struct stat *buf) {
105  int result = ::fstat(fd, buf);
106  if (fstat_sim == MAX_SIZE)
107    buf->st_size = max_file_size();
108  return result;
109}
110
111#else
112
113static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); }
114
115DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
116  if (fstat_sim == ERROR) {
117    SetLastError(ERROR_ACCESS_DENIED);
118    return INVALID_FILE_SIZE;
119  }
120  if (fstat_sim == MAX_SIZE) {
121    DWORD max = std::numeric_limits<DWORD>::max();
122    *lpFileSizeHigh = max >> 1;
123    return max;
124  }
125  return ::GetFileSize(hFile, lpFileSizeHigh);
126}
127
128#endif
129
130int test::close(int fildes) {
131  // Close the file first because close shouldn't be retried.
132  int result = ::FMT_POSIX(close(fildes));
133  EMULATE_EINTR(close, -1);
134  return result;
135}
136
137int test::dup(int fildes) {
138  EMULATE_EINTR(dup, -1);
139  return ::FMT_POSIX(dup(fildes));
140}
141
142int test::dup2(int fildes, int fildes2) {
143  EMULATE_EINTR(dup2, -1);
144  return ::FMT_POSIX(dup2(fildes, fildes2));
145}
146
147FILE *test::fdopen(int fildes, const char *mode) {
148  EMULATE_EINTR(fdopen, 0);
149  return ::FMT_POSIX(fdopen(fildes, mode));
150}
151
152test::ssize_t test::read(int fildes, void *buf, test::size_t nbyte) {
153  read_nbyte = nbyte;
154  EMULATE_EINTR(read, -1);
155  return ::FMT_POSIX(read(fildes, buf, nbyte));
156}
157
158test::ssize_t test::write(int fildes, const void *buf, test::size_t nbyte) {
159  write_nbyte = nbyte;
160  EMULATE_EINTR(write, -1);
161  return ::FMT_POSIX(write(fildes, buf, nbyte));
162}
163
164#ifndef _WIN32
165int test::pipe(int fildes[2]) {
166  EMULATE_EINTR(pipe, -1);
167  return ::pipe(fildes);
168}
169#else
170int test::pipe(int *pfds, unsigned psize, int textmode) {
171  EMULATE_EINTR(pipe, -1);
172  return _pipe(pfds, psize, textmode);
173}
174#endif
175
176FILE *test::fopen(const char *filename, const char *mode) {
177  EMULATE_EINTR(fopen, 0);
178  return ::fopen(filename, mode);
179}
180
181int test::fclose(FILE *stream) {
182  EMULATE_EINTR(fclose, EOF);
183  return ::fclose(stream);
184}
185
186int (test::fileno)(FILE *stream) {
187  EMULATE_EINTR(fileno, -1);
188#ifdef fileno
189  return FMT_POSIX(fileno(stream));
190#else
191  return ::FMT_POSIX(fileno(stream));
192#endif
193}
194
195#ifndef _WIN32
196# define EXPECT_RETRY(statement, func, message) \
197    func##_count = 1; \
198    statement; \
199    EXPECT_EQ(4, func##_count); \
200    func##_count = 0;
201# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
202#else
203# define EXPECT_RETRY(statement, func, message) \
204    func##_count = 1; \
205    EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
206    func##_count = 0;
207# define EXPECT_EQ_POSIX(expected, actual)
208#endif
209
210void write_file(fmt::CStringRef filename, fmt::StringRef content) {
211  fmt::BufferedFile f(filename, "w");
212  f.print("{}", content);
213}
214
215TEST(UtilTest, StaticAssert) {
216  FMT_STATIC_ASSERT(true, "success");
217  // Static assertion failure is tested in compile-test because it causes
218  // a compile-time error.
219}
220
221TEST(UtilTest, GetPageSize) {
222#ifdef _WIN32
223  SYSTEM_INFO si = {};
224  GetSystemInfo(&si);
225  EXPECT_EQ(si.dwPageSize, fmt::getpagesize());
226#else
227  EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
228  sysconf_error = true;
229  EXPECT_SYSTEM_ERROR(
230      fmt::getpagesize(), EINVAL, "cannot get memory page size");
231  sysconf_error = false;
232#endif
233}
234
235TEST(FileTest, OpenRetry) {
236  write_file("test", "there must be something here");
237  File *f = 0;
238  EXPECT_RETRY(f = new File("test", File::RDONLY),
239               open, "cannot open file test");
240#ifndef _WIN32
241  char c = 0;
242  f->read(&c, 1);
243#endif
244  delete f;
245}
246
247TEST(FileTest, CloseNoRetryInDtor) {
248  File read_end, write_end;
249  File::pipe(read_end, write_end);
250  testing::internal::scoped_ptr<File> f(new File(std::move(read_end)));
251  int saved_close_count = 0;
252  EXPECT_WRITE(stderr, {
253    close_count = 1;
254    f.reset();
255    saved_close_count = close_count;
256    close_count = 0;
257  }, format_system_error(EINTR, "cannot close file") + "\n");
258  EXPECT_EQ(2, saved_close_count);
259}
260
261TEST(FileTest, CloseNoRetry) {
262  File read_end, write_end;
263  File::pipe(read_end, write_end);
264  close_count = 1;
265  EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file");
266  EXPECT_EQ(2, close_count);
267  close_count = 0;
268}
269
270TEST(FileTest, Size) {
271  std::string content = "top secret, destroy before reading";
272  write_file("test", content);
273  File f("test", File::RDONLY);
274  EXPECT_GE(f.size(), 0);
275  fmt::ULongLong file_size = f.size();
276  EXPECT_EQ(content.size(), file_size);
277#ifdef _WIN32
278  fmt::MemoryWriter message;
279  fmt::internal::format_windows_error(
280      message, ERROR_ACCESS_DENIED, "cannot get file size");
281  fstat_sim = ERROR;
282  EXPECT_THROW_MSG(f.size(), fmt::WindowsError, message.str());
283  fstat_sim = NONE;
284#else
285  f.close();
286  EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
287#endif
288}
289
290TEST(FileTest, MaxSize) {
291  write_file("test", "");
292  File f("test", File::RDONLY);
293  fstat_sim = MAX_SIZE;
294  EXPECT_GE(f.size(), 0);
295  EXPECT_EQ(max_file_size(), f.size());
296  fstat_sim = NONE;
297}
298
299TEST(FileTest, ReadRetry) {
300  File read_end, write_end;
301  File::pipe(read_end, write_end);
302  enum { SIZE = 4 };
303  write_end.write("test", SIZE);
304  write_end.close();
305  char buffer[SIZE];
306  std::streamsize count = 0;
307  EXPECT_RETRY(count = read_end.read(buffer, SIZE),
308      read, "cannot read from file");
309  EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
310}
311
312TEST(FileTest, WriteRetry) {
313  File read_end, write_end;
314  File::pipe(read_end, write_end);
315  enum { SIZE = 4 };
316  std::streamsize count = 0;
317  EXPECT_RETRY(count = write_end.write("test", SIZE),
318      write, "cannot write to file");
319  write_end.close();
320#ifndef _WIN32
321  EXPECT_EQ(static_cast<std::streamsize>(SIZE), count);
322  char buffer[SIZE + 1];
323  read_end.read(buffer, SIZE);
324  buffer[SIZE] = '\0';
325  EXPECT_STREQ("test", buffer);
326#endif
327}
328
329#ifdef _WIN32
330TEST(FileTest, ConvertReadCount) {
331  File read_end, write_end;
332  File::pipe(read_end, write_end);
333  char c;
334  std::size_t size = UINT_MAX;
335  if (sizeof(unsigned) != sizeof(std::size_t))
336    ++size;
337  read_count = 1;
338  read_nbyte = 0;
339  EXPECT_THROW(read_end.read(&c, size), fmt::SystemError);
340  read_count = 0;
341  EXPECT_EQ(UINT_MAX, read_nbyte);
342}
343
344TEST(FileTest, ConvertWriteCount) {
345  File read_end, write_end;
346  File::pipe(read_end, write_end);
347  char c;
348  std::size_t size = UINT_MAX;
349  if (sizeof(unsigned) != sizeof(std::size_t))
350    ++size;
351  write_count = 1;
352  write_nbyte = 0;
353  EXPECT_THROW(write_end.write(&c, size), fmt::SystemError);
354  write_count = 0;
355  EXPECT_EQ(UINT_MAX, write_nbyte);
356}
357#endif
358
359TEST(FileTest, DupNoRetry) {
360  int stdout_fd = FMT_POSIX(fileno(stdout));
361  dup_count = 1;
362  EXPECT_SYSTEM_ERROR(File::dup(stdout_fd), EINTR,
363      fmt::format("cannot duplicate file descriptor {}", stdout_fd));
364  dup_count = 0;
365}
366
367TEST(FileTest, Dup2Retry) {
368  int stdout_fd = FMT_POSIX(fileno(stdout));
369  File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd);
370  EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
371      fmt::format("cannot duplicate file descriptor {} to {}",
372      f1.descriptor(), f2.descriptor()));
373}
374
375TEST(FileTest, Dup2NoExceptRetry) {
376  int stdout_fd = FMT_POSIX(fileno(stdout));
377  File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd);
378  ErrorCode ec;
379  dup2_count = 1;
380  f1.dup2(f2.descriptor(), ec);
381#ifndef _WIN32
382  EXPECT_EQ(4, dup2_count);
383#else
384  EXPECT_EQ(EINTR, ec.get());
385#endif
386  dup2_count = 0;
387}
388
389TEST(FileTest, PipeNoRetry) {
390  File read_end, write_end;
391  pipe_count = 1;
392  EXPECT_SYSTEM_ERROR(
393      File::pipe(read_end, write_end), EINTR, "cannot create pipe");
394  pipe_count = 0;
395}
396
397TEST(FileTest, FdopenNoRetry) {
398  File read_end, write_end;
399  File::pipe(read_end, write_end);
400  fdopen_count = 1;
401  EXPECT_SYSTEM_ERROR(read_end.fdopen("r"),
402      EINTR, "cannot associate stream with file descriptor");
403  fdopen_count = 0;
404}
405
406TEST(BufferedFileTest, OpenRetry) {
407  write_file("test", "there must be something here");
408  BufferedFile *f = 0;
409  EXPECT_RETRY(f = new BufferedFile("test", "r"),
410               fopen, "cannot open file test");
411#ifndef _WIN32
412  char c = 0;
413  if (fread(&c, 1, 1, f->get()) < 1)
414    throw fmt::SystemError(errno, "fread failed");
415#endif
416  delete f;
417}
418
419TEST(BufferedFileTest, CloseNoRetryInDtor) {
420  File read_end, write_end;
421  File::pipe(read_end, write_end);
422  BufferedFile *f = new BufferedFile(read_end.fdopen("r"));
423  int saved_fclose_count = 0;
424  EXPECT_WRITE(stderr, {
425    fclose_count = 1;
426    delete f;
427    saved_fclose_count = fclose_count;
428    fclose_count = 0;
429  }, format_system_error(EINTR, "cannot close file") + "\n");
430  EXPECT_EQ(2, saved_fclose_count);
431}
432
433TEST(BufferedFileTest, CloseNoRetry) {
434  File read_end, write_end;
435  File::pipe(read_end, write_end);
436  BufferedFile f = read_end.fdopen("r");
437  fclose_count = 1;
438  EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
439  EXPECT_EQ(2, fclose_count);
440  fclose_count = 0;
441}
442
443TEST(BufferedFileTest, FilenoNoRetry) {
444  File read_end, write_end;
445  File::pipe(read_end, write_end);
446  BufferedFile f = read_end.fdopen("r");
447  fileno_count = 1;
448  EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
449  EXPECT_EQ(2, fileno_count);
450  fileno_count = 0;
451}
452