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.
9 #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
10 # define _CRT_SECURE_NO_WARNINGS
13 #include "posix-mock.h"
21 #include "../src/os.cc"
28 #include "gmock/gmock.h"
29 #include "gtest-extra.h"
32 using fmt::buffered_file
;
35 using testing::Return
;
38 template <typename Mock
> struct scoped_mock
: testing::StrictMock
<Mock
> {
39 scoped_mock() { Mock::instance
= this; }
40 ~scoped_mock() { Mock::instance
= nullptr; }
59 enum { none
, max_size
, error
} fstat_sim
;
62 #define EMULATE_EINTR(func, error_result) \
63 if (func##_count != 0) { \
64 if (func##_count++ != 3) { \
66 return error_result; \
71 int test::open(const char* path
, int oflag
, int mode
) {
72 EMULATE_EINTR(open
, -1);
73 return ::open(path
, oflag
, mode
);
79 long test::sysconf(int name
) {
80 long result
= ::sysconf(name
);
81 if (!sysconf_error
) return result
;
87 static off_t
max_file_size() { return std::numeric_limits
<off_t
>::max(); }
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();
97 static LONGLONG
max_file_size() { return std::numeric_limits
<LONGLONG
>::max(); }
99 DWORD
test::GetFileSize(HANDLE hFile
, LPDWORD lpFileSizeHigh
) {
100 if (fstat_sim
== error
) {
101 SetLastError(ERROR_ACCESS_DENIED
);
102 return INVALID_FILE_SIZE
;
104 if (fstat_sim
== max_size
) {
105 DWORD max
= std::numeric_limits
<DWORD
>::max();
106 *lpFileSizeHigh
= max
>> 1;
109 return ::GetFileSize(hFile
, lpFileSizeHigh
);
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);
121 int test::dup(int fildes
) {
122 EMULATE_EINTR(dup
, -1);
123 return ::FMT_POSIX(dup(fildes
));
126 int test::dup2(int fildes
, int fildes2
) {
127 EMULATE_EINTR(dup2
, -1);
128 return ::FMT_POSIX(dup2(fildes
, fildes2
));
131 FILE* test::fdopen(int fildes
, const char* mode
) {
132 EMULATE_EINTR(fdopen
, nullptr);
133 return ::FMT_POSIX(fdopen(fildes
, mode
));
136 test::ssize_t
test::read(int fildes
, void* buf
, test::size_t nbyte
) {
138 EMULATE_EINTR(read
, -1);
139 return ::FMT_POSIX(read(fildes
, buf
, nbyte
));
142 test::ssize_t
test::write(int fildes
, const void* buf
, test::size_t nbyte
) {
144 EMULATE_EINTR(write
, -1);
145 return ::FMT_POSIX(write(fildes
, buf
, nbyte
));
149 int test::pipe(int fildes
[2]) {
150 EMULATE_EINTR(pipe
, -1);
151 return ::pipe(fildes
);
154 int test::pipe(int* pfds
, unsigned psize
, int textmode
) {
155 EMULATE_EINTR(pipe
, -1);
156 return _pipe(pfds
, psize
, textmode
);
160 FILE* test::fopen(const char* filename
, const char* mode
) {
161 EMULATE_EINTR(fopen
, nullptr);
162 return ::fopen(filename
, mode
);
165 int test::fclose(FILE* stream
) {
166 EMULATE_EINTR(fclose
, EOF
);
167 return ::fclose(stream
);
170 int(test::fileno
)(FILE* stream
) {
171 EMULATE_EINTR(fileno
, -1);
173 return FMT_POSIX(fileno(stream
));
175 return ::FMT_POSIX(fileno(stream
));
180 # define EXPECT_RETRY(statement, func, message) \
183 EXPECT_EQ(4, func##_count); \
185 # define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
187 # define EXPECT_RETRY(statement, func, message) \
189 EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
191 # define EXPECT_EQ_POSIX(expected, actual)
195 void write_file(fmt::cstring_view filename
, fmt::string_view content
) {
196 fmt::buffered_file
f(filename
, "w");
197 f
.print("{}", content
);
202 TEST(os_test
, getpagesize
) {
206 EXPECT_EQ(si
.dwPageSize
, fmt::getpagesize());
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;
216 TEST(file_test
, open_retry
) {
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");
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;
236 saved_close_count
= close_count
;
239 system_error_message(EINTR
, "cannot close file") + "\n");
240 EXPECT_EQ(2, saved_close_count
);
243 TEST(file_test
, close_no_retry
) {
244 auto pipe
= fmt::pipe();
246 EXPECT_SYSTEM_ERROR(pipe
.read_end
.close(), EINTR
, "cannot close file");
247 EXPECT_EQ(2, close_count
);
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()));
258 auto error_code
= std::error_code();
262 } catch (const std::system_error
& e
) {
263 error_code
= e
.code();
266 EXPECT_EQ(error_code
,
267 std::error_code(ERROR_ACCESS_DENIED
, fmt::system_category()));
270 EXPECT_SYSTEM_ERROR(f
.size(), EBADF
, "cannot get file attributes");
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());
283 TEST(file_test
, read_retry
) {
284 auto pipe
= fmt::pipe();
286 pipe
.write_end
.write("test", SIZE
);
287 pipe
.write_end
.close();
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
);
295 TEST(file_test
, write_retry
) {
296 auto pipe
= fmt::pipe();
299 EXPECT_RETRY(count
= pipe
.write_end
.write("test", SIZE
), write
,
300 "cannot write to file");
301 pipe
.write_end
.close();
303 EXPECT_EQ(static_cast<std::streamsize
>(SIZE
), count
);
304 char buffer
[SIZE
+ 1];
305 pipe
.read_end
.read(buffer
, SIZE
);
307 EXPECT_STREQ("test", buffer
);
312 TEST(file_test
, convert_read_count
) {
313 auto pipe
= fmt::pipe();
315 size_t size
= UINT_MAX
;
316 if (sizeof(unsigned) != sizeof(size_t)) ++size
;
319 EXPECT_THROW(pipe
.read_end
.read(&c
, size
), std::system_error
);
321 EXPECT_EQ(UINT_MAX
, read_nbyte
);
324 TEST(file_test
, convert_write_count
) {
325 auto pipe
= fmt::pipe();
327 size_t size
= UINT_MAX
;
328 if (sizeof(unsigned) != sizeof(size_t)) ++size
;
331 EXPECT_THROW(pipe
.write_end
.write(&c
, size
), std::system_error
);
333 EXPECT_EQ(UINT_MAX
, write_nbyte
);
337 TEST(file_test
, dup_no_retry
) {
338 int stdout_fd
= FMT_POSIX(fileno(stdout
));
341 file::dup(stdout_fd
), EINTR
,
342 fmt::format("cannot duplicate file descriptor {}", stdout_fd
));
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()));
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
);
359 f1
.dup2(f2
.descriptor(), ec
);
361 EXPECT_EQ(4, dup2_count
);
363 EXPECT_EQ(EINTR
, ec
.value());
368 TEST(file_test
, pipe_no_retry
) {
370 EXPECT_SYSTEM_ERROR(fmt::pipe(), EINTR
, "cannot create pipe");
374 TEST(file_test
, fdopen_no_retry
) {
375 auto pipe
= fmt::pipe();
377 EXPECT_SYSTEM_ERROR(pipe
.read_end
.fdopen("r"), EINTR
,
378 "cannot associate stream with file descriptor");
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");
389 if (fread(&c
, 1, 1, f
->get()) < 1)
390 throw fmt::system_error(errno
, "fread failed");
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;
404 saved_fclose_count
= fclose_count
;
407 system_error_message(EINTR
, "cannot close file") + "\n");
408 EXPECT_EQ(2, saved_fclose_count
);
411 TEST(buffered_file_test
, close_no_retry
) {
412 auto pipe
= fmt::pipe();
413 buffered_file f
= pipe
.read_end
.fdopen("r");
415 EXPECT_SYSTEM_ERROR(f
.close(), EINTR
, "cannot close file");
416 EXPECT_EQ(2, fclose_count
);
420 TEST(buffered_file_test
, fileno_no_retry
) {
421 auto pipe
= fmt::pipe();
422 buffered_file f
= pipe
.read_end
.fdopen("r");
424 EXPECT_SYSTEM_ERROR((f
.descriptor
)(), EINTR
, "cannot get file descriptor");
425 EXPECT_EQ(2, fileno_count
);
428 #endif // FMT_USE_FCNTL
431 static test_mock
* instance
;
432 }* test_mock::instance
;
434 TEST(scoped_mock
, scope
) {
436 scoped_mock
<test_mock
> mock
;
437 EXPECT_EQ(&mock
, test_mock::instance
);
438 test_mock
& copy
= mock
;
439 static_cast<void>(copy
);
441 EXPECT_EQ(nullptr, test_mock::instance
);