]>
git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/fmt/test/posix-mock-test.cc
1 // Tests of the C++ interface to POSIX functions that require mocks
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
6 // For the license information refer to format.h.
8 // Disable bogus MSVC warnings.
10 # define _CRT_SECURE_NO_WARNINGS
13 #include "posix-mock.h"
14 #include "../src/posix.cc"
27 #include "gtest-extra.h"
30 using fmt::buffered_file
;
31 using fmt::error_code
;
34 using testing::internal::scoped_ptr
;
37 using testing::Return
;
51 std::size_t read_nbyte
;
52 std::size_t write_nbyte
;
55 enum FStatSimulation
{ NONE
, MAX_SIZE
, ERROR
} fstat_sim
;
58 #define EMULATE_EINTR(func, error_result) \
59 if (func##_count != 0) { \
60 if (func##_count++ != 3) { \
62 return error_result; \
67 int test::open(const char *path
, int oflag
, int mode
) {
68 EMULATE_EINTR(open
, -1);
69 return ::open(path
, oflag
, mode
);
72 errno_t
test::sopen_s(
73 int* pfh
, const char *filename
, int oflag
, int shflag
, int pmode
) {
74 EMULATE_EINTR(open
, EINTR
);
75 return _sopen_s(pfh
, filename
, oflag
, shflag
, pmode
);
81 long test::sysconf(int name
) {
82 long result
= ::sysconf(name
);
90 static off_t
max_file_size() { return std::numeric_limits
<off_t
>::max(); }
92 int test::fstat(int fd
, struct stat
*buf
) {
93 int result
= ::fstat(fd
, buf
);
94 if (fstat_sim
== MAX_SIZE
)
95 buf
->st_size
= max_file_size();
101 static LONGLONG
max_file_size() { return std::numeric_limits
<LONGLONG
>::max(); }
103 DWORD
test::GetFileSize(HANDLE hFile
, LPDWORD lpFileSizeHigh
) {
104 if (fstat_sim
== ERROR
) {
105 SetLastError(ERROR_ACCESS_DENIED
);
106 return INVALID_FILE_SIZE
;
108 if (fstat_sim
== MAX_SIZE
) {
109 DWORD max
= std::numeric_limits
<DWORD
>::max();
110 *lpFileSizeHigh
= max
>> 1;
113 return ::GetFileSize(hFile
, lpFileSizeHigh
);
118 int test::close(int fildes
) {
119 // Close the file first because close shouldn't be retried.
120 int result
= ::FMT_POSIX(close(fildes
));
121 EMULATE_EINTR(close
, -1);
125 int test::dup(int fildes
) {
126 EMULATE_EINTR(dup
, -1);
127 return ::FMT_POSIX(dup(fildes
));
130 int test::dup2(int fildes
, int fildes2
) {
131 EMULATE_EINTR(dup2
, -1);
132 return ::FMT_POSIX(dup2(fildes
, fildes2
));
135 FILE *test::fdopen(int fildes
, const char *mode
) {
136 EMULATE_EINTR(fdopen
, FMT_NULL
);
137 return ::FMT_POSIX(fdopen(fildes
, mode
));
140 test::ssize_t
test::read(int fildes
, void *buf
, test::size_t nbyte
) {
142 EMULATE_EINTR(read
, -1);
143 return ::FMT_POSIX(read(fildes
, buf
, nbyte
));
146 test::ssize_t
test::write(int fildes
, const void *buf
, test::size_t nbyte
) {
148 EMULATE_EINTR(write
, -1);
149 return ::FMT_POSIX(write(fildes
, buf
, nbyte
));
153 int test::pipe(int fildes
[2]) {
154 EMULATE_EINTR(pipe
, -1);
155 return ::pipe(fildes
);
158 int test::pipe(int *pfds
, unsigned psize
, int textmode
) {
159 EMULATE_EINTR(pipe
, -1);
160 return _pipe(pfds
, psize
, textmode
);
164 FILE *test::fopen(const char *filename
, const char *mode
) {
165 EMULATE_EINTR(fopen
, FMT_NULL
);
166 return ::fopen(filename
, mode
);
169 int test::fclose(FILE *stream
) {
170 EMULATE_EINTR(fclose
, EOF
);
171 return ::fclose(stream
);
174 int (test::fileno
)(FILE *stream
) {
175 EMULATE_EINTR(fileno
, -1);
177 return FMT_POSIX(fileno(stream
));
179 return ::FMT_POSIX(fileno(stream
));
184 # define EXPECT_RETRY(statement, func, message) \
187 EXPECT_EQ(4, func##_count); \
189 # define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
191 # define EXPECT_RETRY(statement, func, message) \
193 EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
195 # define EXPECT_EQ_POSIX(expected, actual)
198 static void write_file(fmt::cstring_view filename
, fmt::string_view content
) {
199 fmt::buffered_file
f(filename
, "w");
200 f
.print("{}", content
);
203 TEST(UtilTest
, GetPageSize
) {
207 EXPECT_EQ(si
.dwPageSize
, fmt::getpagesize());
209 EXPECT_EQ(sysconf(_SC_PAGESIZE
), fmt::getpagesize());
210 sysconf_error
= true;
212 fmt::getpagesize(), EINVAL
, "cannot get memory page size");
213 sysconf_error
= false;
217 TEST(FileTest
, OpenRetry
) {
218 write_file("test", "there must be something here");
219 scoped_ptr
<file
> f
{FMT_NULL
};
220 EXPECT_RETRY(f
.reset(new file("test", file::RDONLY
)),
221 open
, "cannot open file test");
228 TEST(FileTest
, CloseNoRetryInDtor
) {
229 file read_end
, write_end
;
230 file::pipe(read_end
, write_end
);
231 scoped_ptr
<file
> f(new file(std::move(read_end
)));
232 int saved_close_count
= 0;
233 EXPECT_WRITE(stderr
, {
236 saved_close_count
= close_count
;
238 }, format_system_error(EINTR
, "cannot close file") + "\n");
239 EXPECT_EQ(2, saved_close_count
);
242 TEST(FileTest
, CloseNoRetry
) {
243 file read_end
, write_end
;
244 file::pipe(read_end
, write_end
);
246 EXPECT_SYSTEM_ERROR(read_end
.close(), EINTR
, "cannot close file");
247 EXPECT_EQ(2, close_count
);
251 TEST(FileTest
, Size
) {
252 std::string content
= "top secret, destroy before reading";
253 write_file("test", content
);
254 file
f("test", file::RDONLY
);
255 EXPECT_GE(f
.size(), 0);
256 EXPECT_EQ(content
.size(), static_cast<unsigned long long>(f
.size()));
258 fmt::memory_buffer message
;
259 fmt::internal::format_windows_error(
260 message
, ERROR_ACCESS_DENIED
, "cannot get file size");
262 EXPECT_THROW_MSG(f
.size(), fmt::windows_error
, fmt::to_string(message
));
266 EXPECT_SYSTEM_ERROR(f
.size(), EBADF
, "cannot get file attributes");
270 TEST(FileTest
, MaxSize
) {
271 write_file("test", "");
272 file
f("test", file::RDONLY
);
273 fstat_sim
= MAX_SIZE
;
274 EXPECT_GE(f
.size(), 0);
275 EXPECT_EQ(max_file_size(), f
.size());
279 TEST(FileTest
, ReadRetry
) {
280 file read_end
, write_end
;
281 file::pipe(read_end
, write_end
);
283 write_end
.write("test", SIZE
);
286 std::size_t count
= 0;
287 EXPECT_RETRY(count
= read_end
.read(buffer
, SIZE
),
288 read
, "cannot read from file");
289 EXPECT_EQ_POSIX(static_cast<std::streamsize
>(SIZE
), count
);
292 TEST(FileTest
, WriteRetry
) {
293 file read_end
, write_end
;
294 file::pipe(read_end
, write_end
);
296 std::size_t count
= 0;
297 EXPECT_RETRY(count
= write_end
.write("test", SIZE
),
298 write
, "cannot write to file");
301 EXPECT_EQ(static_cast<std::streamsize
>(SIZE
), count
);
302 char buffer
[SIZE
+ 1];
303 read_end
.read(buffer
, SIZE
);
305 EXPECT_STREQ("test", buffer
);
310 TEST(FileTest
, ConvertReadCount
) {
311 file read_end
, write_end
;
312 file::pipe(read_end
, write_end
);
314 std::size_t size
= UINT_MAX
;
315 if (sizeof(unsigned) != sizeof(std::size_t))
319 EXPECT_THROW(read_end
.read(&c
, size
), fmt::system_error
);
321 EXPECT_EQ(UINT_MAX
, read_nbyte
);
324 TEST(FileTest
, ConvertWriteCount
) {
325 file read_end
, write_end
;
326 file::pipe(read_end
, write_end
);
328 std::size_t size
= UINT_MAX
;
329 if (sizeof(unsigned) != sizeof(std::size_t))
333 EXPECT_THROW(write_end
.write(&c
, size
), fmt::system_error
);
335 EXPECT_EQ(UINT_MAX
, write_nbyte
);
339 TEST(FileTest
, DupNoRetry
) {
340 int stdout_fd
= FMT_POSIX(fileno(stdout
));
342 EXPECT_SYSTEM_ERROR(file::dup(stdout_fd
), EINTR
,
343 fmt::format("cannot duplicate file descriptor {}", stdout_fd
));
347 TEST(FileTest
, Dup2Retry
) {
348 int stdout_fd
= FMT_POSIX(fileno(stdout
));
349 file f1
= file::dup(stdout_fd
), f2
= file::dup(stdout_fd
);
350 EXPECT_RETRY(f1
.dup2(f2
.descriptor()), dup2
,
351 fmt::format("cannot duplicate file descriptor {} to {}",
352 f1
.descriptor(), f2
.descriptor()));
355 TEST(FileTest
, Dup2NoExceptRetry
) {
356 int stdout_fd
= FMT_POSIX(fileno(stdout
));
357 file f1
= file::dup(stdout_fd
), f2
= file::dup(stdout_fd
);
360 f1
.dup2(f2
.descriptor(), ec
);
362 EXPECT_EQ(4, dup2_count
);
364 EXPECT_EQ(EINTR
, ec
.get());
369 TEST(FileTest
, PipeNoRetry
) {
370 file read_end
, write_end
;
373 file::pipe(read_end
, write_end
), EINTR
, "cannot create pipe");
377 TEST(FileTest
, FdopenNoRetry
) {
378 file read_end
, write_end
;
379 file::pipe(read_end
, write_end
);
381 EXPECT_SYSTEM_ERROR(read_end
.fdopen("r"),
382 EINTR
, "cannot associate stream with file descriptor");
386 TEST(BufferedFileTest
, OpenRetry
) {
387 write_file("test", "there must be something here");
388 scoped_ptr
<buffered_file
> f
{FMT_NULL
};
389 EXPECT_RETRY(f
.reset(new buffered_file("test", "r")),
390 fopen
, "cannot open file test");
393 if (fread(&c
, 1, 1, f
->get()) < 1)
394 throw fmt::system_error(errno
, "fread failed");
398 TEST(BufferedFileTest
, CloseNoRetryInDtor
) {
399 file read_end
, write_end
;
400 file::pipe(read_end
, write_end
);
401 scoped_ptr
<buffered_file
> f(new buffered_file(read_end
.fdopen("r")));
402 int saved_fclose_count
= 0;
403 EXPECT_WRITE(stderr
, {
406 saved_fclose_count
= fclose_count
;
408 }, format_system_error(EINTR
, "cannot close file") + "\n");
409 EXPECT_EQ(2, saved_fclose_count
);
412 TEST(BufferedFileTest
, CloseNoRetry
) {
413 file read_end
, write_end
;
414 file::pipe(read_end
, write_end
);
415 buffered_file f
= read_end
.fdopen("r");
417 EXPECT_SYSTEM_ERROR(f
.close(), EINTR
, "cannot close file");
418 EXPECT_EQ(2, fclose_count
);
422 TEST(BufferedFileTest
, FilenoNoRetry
) {
423 file read_end
, write_end
;
424 file::pipe(read_end
, write_end
);
425 buffered_file f
= read_end
.fdopen("r");
427 EXPECT_SYSTEM_ERROR((f
.fileno
)(), EINTR
, "cannot get file descriptor");
428 EXPECT_EQ(2, fileno_count
);
433 static TestMock
*instance
;
434 } *TestMock::instance
;
436 TEST(ScopedMock
, Scope
) {
438 ScopedMock
<TestMock
> mock
;
439 EXPECT_EQ(&mock
, TestMock::instance
);
440 TestMock
©
= mock
;
441 static_cast<void>(copy
);
443 EXPECT_EQ(FMT_NULL
, TestMock::instance
);
448 typedef fmt::Locale::Type LocaleType
;
451 static LocaleMock
*instance
;
452 MOCK_METHOD3(newlocale
, LocaleType (int category_mask
, const char *locale
,
454 MOCK_METHOD1(freelocale
, void (LocaleType locale
));
456 MOCK_METHOD3(strtod_l
, double (const char *nptr
, char **endptr
,
458 } *LocaleMock::instance
;
461 # pragma warning(push)
462 # pragma warning(disable: 4273)
464 _locale_t
_create_locale(int category
, const char *locale
) {
465 return LocaleMock::instance
->newlocale(category
, locale
, 0);
468 void _free_locale(_locale_t locale
) {
469 LocaleMock::instance
->freelocale(locale
);
472 double _strtod_l(const char *nptr
, char **endptr
, _locale_t locale
) {
473 return LocaleMock::instance
->strtod_l(nptr
, endptr
, locale
);
475 # pragma warning(pop)
478 #if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
479 #define FMT_LOCALE_THROW __THROW
481 #define FMT_LOCALE_THROW
484 LocaleType
newlocale(int category_mask
, const char *locale
, LocaleType base
) FMT_LOCALE_THROW
{
485 return LocaleMock::instance
->newlocale(category_mask
, locale
, base
);
488 #if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 1200002)
489 typedef int FreeLocaleResult
;
491 typedef void FreeLocaleResult
;
494 FreeLocaleResult
freelocale(LocaleType locale
) FMT_LOCALE_THROW
{
495 LocaleMock::instance
->freelocale(locale
);
496 return FreeLocaleResult();
499 double strtod_l(const char *nptr
, char **endptr
, LocaleType locale
) FMT_LOCALE_THROW
{
500 return LocaleMock::instance
->strtod_l(nptr
, endptr
, locale
);
503 #undef FMT_LOCALE_THROW
505 TEST(LocaleTest
, LocaleMock
) {
506 ScopedMock
<LocaleMock
> mock
;
507 LocaleType locale
= reinterpret_cast<LocaleType
>(11);
508 EXPECT_CALL(mock
, newlocale(222, StrEq("foo"), locale
));
509 newlocale(222, "foo", locale
);
512 TEST(LocaleTest
, Locale
) {
513 #ifndef LC_NUMERIC_MASK
514 enum { LC_NUMERIC_MASK
= LC_NUMERIC
};
516 ScopedMock
<LocaleMock
> mock
;
517 LocaleType impl
= reinterpret_cast<LocaleType
>(42);
518 EXPECT_CALL(mock
, newlocale(LC_NUMERIC_MASK
, StrEq("C"), FMT_NULL
))
519 .WillOnce(Return(impl
));
520 EXPECT_CALL(mock
, freelocale(impl
));
522 EXPECT_EQ(impl
, locale
.get());
525 TEST(LocaleTest
, Strtod
) {
526 ScopedMock
<LocaleMock
> mock
;
527 EXPECT_CALL(mock
, newlocale(_
, _
, _
))
528 .WillOnce(Return(reinterpret_cast<LocaleType
>(42)));
529 EXPECT_CALL(mock
, freelocale(_
));
531 const char *str
= "4.2";
533 EXPECT_CALL(mock
, strtod_l(str
, _
, locale
.get()))
534 .WillOnce(testing::DoAll(testing::SetArgPointee
<1>(&end
), Return(777)));
535 EXPECT_EQ(777, locale
.strtod(str
));
536 EXPECT_EQ(&end
, str
);