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 © = 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