xref: /aosp_15_r20/external/fmtlib/test/posix-mock-test.cc (revision 5c90c05cd622c0a81b57953a4d343e0e489f2e08)
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 #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
10 #  define _CRT_SECURE_NO_WARNINGS
11 #endif
12 
13 #include "posix-mock.h"
14 
15 #include <errno.h>
16 #include <fcntl.h>
17 
18 #include <climits>
19 #include <memory>
20 
21 #include "../src/os.cc"
22 
23 #ifdef _WIN32
24 #  include <io.h>
25 #  undef max
26 #endif
27 
28 #include "gmock/gmock.h"
29 #include "gtest-extra.h"
30 #include "util.h"
31 
32 using fmt::buffered_file;
33 
34 using testing::_;
35 using testing::Return;
36 using testing::StrEq;
37 
38 template <typename Mock> struct scoped_mock : testing::StrictMock<Mock> {
scoped_mockscoped_mock39   scoped_mock() { Mock::instance = this; }
~scoped_mockscoped_mock40   ~scoped_mock() { Mock::instance = nullptr; }
41 };
42 
43 namespace {
44 int open_count;
45 int close_count;
46 int dup_count;
47 int dup2_count;
48 int fdopen_count;
49 int read_count;
50 int write_count;
51 int pipe_count;
52 int fopen_count;
53 int fclose_count;
54 int fileno_count;
55 size_t read_nbyte;
56 size_t write_nbyte;
57 bool sysconf_error;
58 
59 enum { none, max_size, error } fstat_sim;
60 }  // namespace
61 
62 #define EMULATE_EINTR(func, error_result) \
63   if (func##_count != 0) {                \
64     if (func##_count++ != 3) {            \
65       errno = EINTR;                      \
66       return error_result;                \
67     }                                     \
68   }
69 
70 #ifndef _MSC_VER
open(const char * path,int oflag,int mode)71 int test::open(const char* path, int oflag, int mode) {
72   EMULATE_EINTR(open, -1);
73   return ::open(path, oflag, mode);
74 }
75 #endif
76 
77 #ifndef _WIN32
78 
sysconf(int name)79 long test::sysconf(int name) {
80   long result = ::sysconf(name);
81   if (!sysconf_error) return result;
82   // Simulate an error.
83   errno = EINVAL;
84   return -1;
85 }
86 
max_file_size()87 static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
88 
fstat(int fd,struct stat * buf)89 int test::fstat(int fd, struct stat* buf) {
90   int result = ::fstat(fd, buf);
91   if (fstat_sim == max_size) buf->st_size = max_file_size();
92   return result;
93 }
94 
95 #else
96 
max_file_size()97 static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); }
98 
GetFileSize(HANDLE hFile,LPDWORD lpFileSizeHigh)99 DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
100   if (fstat_sim == error) {
101     SetLastError(ERROR_ACCESS_DENIED);
102     return INVALID_FILE_SIZE;
103   }
104   if (fstat_sim == max_size) {
105     DWORD max = std::numeric_limits<DWORD>::max();
106     *lpFileSizeHigh = max >> 1;
107     return max;
108   }
109   return ::GetFileSize(hFile, lpFileSizeHigh);
110 }
111 
112 #endif
113 
close(int fildes)114 int test::close(int fildes) {
115   // Close the file first because close shouldn't be retried.
116   int result = ::FMT_POSIX(close(fildes));
117   EMULATE_EINTR(close, -1);
118   return result;
119 }
120 
dup(int fildes)121 int test::dup(int fildes) {
122   EMULATE_EINTR(dup, -1);
123   return ::FMT_POSIX(dup(fildes));
124 }
125 
dup2(int fildes,int fildes2)126 int test::dup2(int fildes, int fildes2) {
127   EMULATE_EINTR(dup2, -1);
128   return ::FMT_POSIX(dup2(fildes, fildes2));
129 }
130 
fdopen(int fildes,const char * mode)131 FILE* test::fdopen(int fildes, const char* mode) {
132   EMULATE_EINTR(fdopen, nullptr);
133   return ::FMT_POSIX(fdopen(fildes, mode));
134 }
135 
read(int fildes,void * buf,test::size_t nbyte)136 test::ssize_t test::read(int fildes, void* buf, test::size_t nbyte) {
137   read_nbyte = nbyte;
138   EMULATE_EINTR(read, -1);
139   return ::FMT_POSIX(read(fildes, buf, nbyte));
140 }
141 
write(int fildes,const void * buf,test::size_t nbyte)142 test::ssize_t test::write(int fildes, const void* buf, test::size_t nbyte) {
143   write_nbyte = nbyte;
144   EMULATE_EINTR(write, -1);
145   return ::FMT_POSIX(write(fildes, buf, nbyte));
146 }
147 
148 #ifndef _WIN32
pipe(int fildes[2])149 int test::pipe(int fildes[2]) {
150   EMULATE_EINTR(pipe, -1);
151   return ::pipe(fildes);
152 }
153 #else
pipe(int * pfds,unsigned psize,int textmode)154 int test::pipe(int* pfds, unsigned psize, int textmode) {
155   EMULATE_EINTR(pipe, -1);
156   return _pipe(pfds, psize, textmode);
157 }
158 #endif
159 
fopen(const char * filename,const char * mode)160 FILE* test::fopen(const char* filename, const char* mode) {
161   EMULATE_EINTR(fopen, nullptr);
162   return ::fopen(filename, mode);
163 }
164 
fclose(FILE * stream)165 int test::fclose(FILE* stream) {
166   EMULATE_EINTR(fclose, EOF);
167   return ::fclose(stream);
168 }
169 
170 int(test::fileno)(FILE* stream) {
171   EMULATE_EINTR(fileno, -1);
172 #ifdef fileno
173   return FMT_POSIX(fileno(stream));
174 #else
175   return ::FMT_POSIX(fileno(stream));
176 #endif
177 }
178 
179 #ifndef _WIN32
180 #  define EXPECT_RETRY(statement, func, message) \
181     func##_count = 1;                            \
182     statement;                                   \
183     EXPECT_EQ(4, func##_count);                  \
184     func##_count = 0;
185 #  define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
186 #else
187 #  define EXPECT_RETRY(statement, func, message)    \
188     func##_count = 1;                               \
189     EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
190     func##_count = 0;
191 #  define EXPECT_EQ_POSIX(expected, actual)
192 #endif
193 
194 #if FMT_USE_FCNTL
write_file(fmt::cstring_view filename,fmt::string_view content)195 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 using fmt::file;
201 
TEST(os_test,getpagesize)202 TEST(os_test, getpagesize) {
203 #  ifdef _WIN32
204   SYSTEM_INFO si = {};
205   GetSystemInfo(&si);
206   EXPECT_EQ(si.dwPageSize, fmt::getpagesize());
207 #  else
208   EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
209   sysconf_error = true;
210   EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL,
211                       "cannot get memory page size");
212   sysconf_error = false;
213 #  endif
214 }
215 
TEST(file_test,open_retry)216 TEST(file_test, open_retry) {
217 #  ifndef _WIN32
218   write_file("temp", "there must be something here");
219   std::unique_ptr<file> f{nullptr};
220   EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
221                "cannot open file temp");
222   char c = 0;
223   f->read(&c, 1);
224 #  endif
225 }
226 
TEST(file_test,close_no_retry_in_dtor)227 TEST(file_test, close_no_retry_in_dtor) {
228   auto pipe = fmt::pipe();
229   std::unique_ptr<file> f(new file(std::move(pipe.read_end)));
230   int saved_close_count = 0;
231   EXPECT_WRITE(
232       stderr,
233       {
234         close_count = 1;
235         f.reset(nullptr);
236         saved_close_count = close_count;
237         close_count = 0;
238       },
239       system_error_message(EINTR, "cannot close file") + "\n");
240   EXPECT_EQ(2, saved_close_count);
241 }
242 
TEST(file_test,close_no_retry)243 TEST(file_test, close_no_retry) {
244   auto pipe = fmt::pipe();
245   close_count = 1;
246   EXPECT_SYSTEM_ERROR(pipe.read_end.close(), EINTR, "cannot close file");
247   EXPECT_EQ(2, close_count);
248   close_count = 0;
249 }
250 
TEST(file_test,size)251 TEST(file_test, size) {
252   std::string content = "top secret, destroy before reading";
253   write_file("temp", content);
254   file f("temp", file::RDONLY);
255   EXPECT_GE(f.size(), 0);
256   EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
257 #  ifdef _WIN32
258   auto error_code = std::error_code();
259   fstat_sim = error;
260   try {
261     f.size();
262   } catch (const std::system_error& e) {
263     error_code = e.code();
264   }
265   fstat_sim = none;
266   EXPECT_EQ(error_code,
267             std::error_code(ERROR_ACCESS_DENIED, fmt::system_category()));
268 #  else
269   f.close();
270   EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
271 #  endif
272 }
273 
TEST(file_test,max_size)274 TEST(file_test, max_size) {
275   write_file("temp", "");
276   file f("temp", file::RDONLY);
277   fstat_sim = max_size;
278   EXPECT_GE(f.size(), 0);
279   EXPECT_EQ(max_file_size(), f.size());
280   fstat_sim = none;
281 }
282 
TEST(file_test,read_retry)283 TEST(file_test, read_retry) {
284   auto pipe = fmt::pipe();
285   enum { SIZE = 4 };
286   pipe.write_end.write("test", SIZE);
287   pipe.write_end.close();
288   char buffer[SIZE];
289   size_t count = 0;
290   EXPECT_RETRY(count = pipe.read_end.read(buffer, SIZE), read,
291                "cannot read from file");
292   EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
293 }
294 
TEST(file_test,write_retry)295 TEST(file_test, write_retry) {
296   auto pipe = fmt::pipe();
297   enum { SIZE = 4 };
298   size_t count = 0;
299   EXPECT_RETRY(count = pipe.write_end.write("test", SIZE), write,
300                "cannot write to file");
301   pipe.write_end.close();
302 #  ifndef _WIN32
303   EXPECT_EQ(static_cast<std::streamsize>(SIZE), count);
304   char buffer[SIZE + 1];
305   pipe.read_end.read(buffer, SIZE);
306   buffer[SIZE] = '\0';
307   EXPECT_STREQ("test", buffer);
308 #  endif
309 }
310 
311 #  ifdef _WIN32
TEST(file_test,convert_read_count)312 TEST(file_test, convert_read_count) {
313   auto pipe = fmt::pipe();
314   char c;
315   size_t size = UINT_MAX;
316   if (sizeof(unsigned) != sizeof(size_t)) ++size;
317   read_count = 1;
318   read_nbyte = 0;
319   EXPECT_THROW(pipe.read_end.read(&c, size), std::system_error);
320   read_count = 0;
321   EXPECT_EQ(UINT_MAX, read_nbyte);
322 }
323 
TEST(file_test,convert_write_count)324 TEST(file_test, convert_write_count) {
325   auto pipe = fmt::pipe();
326   char c;
327   size_t size = UINT_MAX;
328   if (sizeof(unsigned) != sizeof(size_t)) ++size;
329   write_count = 1;
330   write_nbyte = 0;
331   EXPECT_THROW(pipe.write_end.write(&c, size), std::system_error);
332   write_count = 0;
333   EXPECT_EQ(UINT_MAX, write_nbyte);
334 }
335 #  endif
336 
TEST(file_test,dup_no_retry)337 TEST(file_test, dup_no_retry) {
338   int stdout_fd = FMT_POSIX(fileno(stdout));
339   dup_count = 1;
340   EXPECT_SYSTEM_ERROR(
341       file::dup(stdout_fd), EINTR,
342       fmt::format("cannot duplicate file descriptor {}", stdout_fd));
343   dup_count = 0;
344 }
345 
TEST(file_test,dup2_retry)346 TEST(file_test, dup2_retry) {
347   int stdout_fd = FMT_POSIX(fileno(stdout));
348   file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
349   EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
350                fmt::format("cannot duplicate file descriptor {} to {}",
351                            f1.descriptor(), f2.descriptor()));
352 }
353 
TEST(file_test,dup2_no_except_retry)354 TEST(file_test, dup2_no_except_retry) {
355   int stdout_fd = FMT_POSIX(fileno(stdout));
356   file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
357   std::error_code ec;
358   dup2_count = 1;
359   f1.dup2(f2.descriptor(), ec);
360 #  ifndef _WIN32
361   EXPECT_EQ(4, dup2_count);
362 #  else
363   EXPECT_EQ(EINTR, ec.value());
364 #  endif
365   dup2_count = 0;
366 }
367 
TEST(file_test,pipe_no_retry)368 TEST(file_test, pipe_no_retry) {
369   pipe_count = 1;
370   EXPECT_SYSTEM_ERROR(fmt::pipe(), EINTR, "cannot create pipe");
371   pipe_count = 0;
372 }
373 
TEST(file_test,fdopen_no_retry)374 TEST(file_test, fdopen_no_retry) {
375   auto pipe = fmt::pipe();
376   fdopen_count = 1;
377   EXPECT_SYSTEM_ERROR(pipe.read_end.fdopen("r"), EINTR,
378                       "cannot associate stream with file descriptor");
379   fdopen_count = 0;
380 }
381 
TEST(buffered_file_test,open_retry)382 TEST(buffered_file_test, open_retry) {
383   write_file("temp", "there must be something here");
384   std::unique_ptr<buffered_file> f{nullptr};
385   EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
386                "cannot open file temp");
387 #  ifndef _WIN32
388   char c = 0;
389   if (fread(&c, 1, 1, f->get()) < 1)
390     throw fmt::system_error(errno, "fread failed");
391 #  endif
392 }
393 
TEST(buffered_file_test,close_no_retry_in_dtor)394 TEST(buffered_file_test, close_no_retry_in_dtor) {
395   auto pipe = fmt::pipe();
396   std::unique_ptr<buffered_file> f(
397       new buffered_file(pipe.read_end.fdopen("r")));
398   int saved_fclose_count = 0;
399   EXPECT_WRITE(
400       stderr,
401       {
402         fclose_count = 1;
403         f.reset(nullptr);
404         saved_fclose_count = fclose_count;
405         fclose_count = 0;
406       },
407       system_error_message(EINTR, "cannot close file") + "\n");
408   EXPECT_EQ(2, saved_fclose_count);
409 }
410 
TEST(buffered_file_test,close_no_retry)411 TEST(buffered_file_test, close_no_retry) {
412   auto pipe = fmt::pipe();
413   buffered_file f = pipe.read_end.fdopen("r");
414   fclose_count = 1;
415   EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
416   EXPECT_EQ(2, fclose_count);
417   fclose_count = 0;
418 }
419 
TEST(buffered_file_test,fileno_no_retry)420 TEST(buffered_file_test, fileno_no_retry) {
421   auto pipe = fmt::pipe();
422   buffered_file f = pipe.read_end.fdopen("r");
423   fileno_count = 1;
424   EXPECT_SYSTEM_ERROR((f.descriptor)(), EINTR, "cannot get file descriptor");
425   EXPECT_EQ(2, fileno_count);
426   fileno_count = 0;
427 }
428 #endif  // FMT_USE_FCNTL
429 
430 struct test_mock {
431   static test_mock* instance;
432 }* test_mock::instance;
433 
TEST(scoped_mock,scope)434 TEST(scoped_mock, scope) {
435   {
436     scoped_mock<test_mock> mock;
437     EXPECT_EQ(&mock, test_mock::instance);
438     test_mock& copy = mock;
439     static_cast<void>(copy);
440   }
441   EXPECT_EQ(nullptr, test_mock::instance);
442 }
443