fmt/test/posix-mock-test.cc

460 lines
11 KiB
C++
Raw Normal View History

2018-03-04 17:16:51 +00:00
// Tests of the C++ interface to POSIX functions that require mocks
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
2014-05-15 14:45:44 +00:00
2014-12-17 14:53:32 +00:00
// Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
2019-01-13 02:27:38 +00:00
# define _CRT_SECURE_NO_WARNINGS
#endif
2014-12-17 14:53:32 +00:00
2015-06-24 15:43:30 +00:00
#include "posix-mock.h"
2014-05-15 14:45:44 +00:00
#include <errno.h>
#include <fcntl.h>
2020-04-01 15:42:14 +00:00
2019-01-13 02:27:38 +00:00
#include <climits>
#include <memory>
2014-05-15 14:45:44 +00:00
2020-04-01 15:42:14 +00:00
#include "../src/os.cc"
2014-05-18 18:09:37 +00:00
#ifdef _WIN32
2019-01-13 02:27:38 +00:00
# include <io.h>
# undef max
2014-05-18 18:09:37 +00:00
#endif
#include "gmock/gmock.h"
2014-05-15 14:45:44 +00:00
#include "gtest-extra.h"
#include "util.h"
2014-05-15 14:45:44 +00:00
2018-05-19 15:57:31 +00:00
using fmt::buffered_file;
2016-02-03 01:21:09 +00:00
using testing::_;
using testing::Return;
2019-01-13 02:27:38 +00:00
using testing::StrEq;
2015-07-31 15:24:37 +00:00
2021-05-04 23:54:20 +00:00
template <typename Mock> struct scoped_mock : testing::StrictMock<Mock> {
scoped_mock() { Mock::instance = this; }
~scoped_mock() { Mock::instance = nullptr; }
2021-05-02 12:55:55 +00:00
};
2014-05-15 14:45:44 +00:00
namespace {
int open_count;
int close_count;
int dup_count;
int dup2_count;
int fdopen_count;
int read_count;
int write_count;
2014-05-18 17:05:29 +00:00
int pipe_count;
int fopen_count;
2014-05-18 18:09:37 +00:00
int fclose_count;
2014-05-19 14:31:05 +00:00
int fileno_count;
2020-05-07 22:59:46 +00:00
size_t read_nbyte;
size_t write_nbyte;
bool sysconf_error;
2014-09-12 23:52:15 +00:00
2021-05-04 23:54:20 +00:00
enum { none, max_size, error } fstat_sim;
2019-01-13 02:27:38 +00:00
} // namespace
2014-05-15 14:45:44 +00:00
#define EMULATE_EINTR(func, error_result) \
2019-01-13 02:27:38 +00:00
if (func##_count != 0) { \
if (func##_count++ != 3) { \
errno = EINTR; \
return error_result; \
} \
}
2015-05-06 14:29:58 +00:00
#ifndef _MSC_VER
2019-01-13 02:27:38 +00:00
int test::open(const char* path, int oflag, int mode) {
EMULATE_EINTR(open, -1);
return ::open(path, oflag, mode);
2014-05-15 14:45:44 +00:00
}
2015-05-06 14:29:58 +00:00
#else
2019-01-13 02:27:38 +00:00
errno_t test::sopen_s(int* pfh, const char* filename, int oflag, int shflag,
int pmode) {
2015-05-06 14:29:58 +00:00
EMULATE_EINTR(open, EINTR);
return _sopen_s(pfh, filename, oflag, shflag, pmode);
}
#endif
2015-05-06 14:29:58 +00:00
#ifndef _WIN32
2015-05-12 02:10:31 +00:00
long test::sysconf(int name) {
long result = ::sysconf(name);
2019-01-13 02:27:38 +00:00
if (!sysconf_error) return result;
// Simulate an error.
errno = EINVAL;
return -1;
}
2015-05-12 02:10:31 +00:00
static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
2019-01-13 02:27:38 +00:00
int test::fstat(int fd, struct stat* buf) {
2015-05-12 02:10:31 +00:00
int result = ::fstat(fd, buf);
2021-05-04 23:54:20 +00:00
if (fstat_sim == max_size) buf->st_size = max_file_size();
2015-05-12 02:10:31 +00:00
return result;
}
2014-05-15 14:45:44 +00:00
#else
2015-05-12 02:10:31 +00:00
static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); }
DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
2021-05-04 23:54:20 +00:00
if (fstat_sim == error) {
2014-09-12 23:52:15 +00:00
SetLastError(ERROR_ACCESS_DENIED);
2015-03-16 16:17:18 +00:00
return INVALID_FILE_SIZE;
2014-09-12 23:52:15 +00:00
}
2021-05-04 23:54:20 +00:00
if (fstat_sim == max_size) {
DWORD max = std::numeric_limits<DWORD>::max();
2015-03-16 16:05:46 +00:00
*lpFileSizeHigh = max >> 1;
return max;
}
return ::GetFileSize(hFile, lpFileSizeHigh);
}
2015-05-12 02:10:31 +00:00
2014-05-15 14:45:44 +00:00
#endif
int test::close(int fildes) {
// Close the file first because close shouldn't be retried.
2014-05-19 14:31:05 +00:00
int result = ::FMT_POSIX(close(fildes));
EMULATE_EINTR(close, -1);
return result;
}
int test::dup(int fildes) {
EMULATE_EINTR(dup, -1);
2014-05-19 14:31:05 +00:00
return ::FMT_POSIX(dup(fildes));
}
int test::dup2(int fildes, int fildes2) {
EMULATE_EINTR(dup2, -1);
2014-05-19 14:31:05 +00:00
return ::FMT_POSIX(dup2(fildes, fildes2));
}
2019-01-13 02:27:38 +00:00
FILE* test::fdopen(int fildes, const char* mode) {
EMULATE_EINTR(fdopen, nullptr);
2014-05-19 14:31:05 +00:00
return ::FMT_POSIX(fdopen(fildes, mode));
}
2019-01-13 02:27:38 +00:00
test::ssize_t test::read(int fildes, void* buf, test::size_t nbyte) {
2014-05-19 15:25:08 +00:00
read_nbyte = nbyte;
EMULATE_EINTR(read, -1);
2014-05-19 14:31:05 +00:00
return ::FMT_POSIX(read(fildes, buf, nbyte));
}
2019-01-13 02:27:38 +00:00
test::ssize_t test::write(int fildes, const void* buf, test::size_t nbyte) {
2014-05-19 15:37:28 +00:00
write_nbyte = nbyte;
EMULATE_EINTR(write, -1);
2014-05-19 14:31:05 +00:00
return ::FMT_POSIX(write(fildes, buf, nbyte));
}
2014-05-18 18:09:37 +00:00
#ifndef _WIN32
2014-05-18 17:05:29 +00:00
int test::pipe(int fildes[2]) {
EMULATE_EINTR(pipe, -1);
return ::pipe(fildes);
}
2014-05-18 18:09:37 +00:00
#else
2019-01-13 02:27:38 +00:00
int test::pipe(int* pfds, unsigned psize, int textmode) {
2014-05-18 18:09:37 +00:00
EMULATE_EINTR(pipe, -1);
2014-05-18 19:38:01 +00:00
return _pipe(pfds, psize, textmode);
2014-05-18 18:09:37 +00:00
}
#endif
2019-01-13 02:27:38 +00:00
FILE* test::fopen(const char* filename, const char* mode) {
EMULATE_EINTR(fopen, nullptr);
return ::fopen(filename, mode);
}
2019-01-13 02:27:38 +00:00
int test::fclose(FILE* stream) {
2014-05-18 18:09:37 +00:00
EMULATE_EINTR(fclose, EOF);
return ::fclose(stream);
}
2014-05-18 17:05:29 +00:00
2019-01-13 02:27:38 +00:00
int(test::fileno)(FILE* stream) {
2014-05-19 14:31:05 +00:00
EMULATE_EINTR(fileno, -1);
#ifdef fileno
return FMT_POSIX(fileno(stream));
#else
2014-05-19 14:53:16 +00:00
return ::FMT_POSIX(fileno(stream));
#endif
2014-05-19 14:31:05 +00:00
}
2014-05-15 14:45:44 +00:00
#ifndef _WIN32
2019-01-13 02:27:38 +00:00
# define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \
statement; \
EXPECT_EQ(4, func##_count); \
func##_count = 0;
2019-01-13 02:27:38 +00:00
# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
2014-05-15 14:45:44 +00:00
#else
2019-01-13 02:27:38 +00:00
# define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \
EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
func##_count = 0;
2019-01-13 02:27:38 +00:00
# define EXPECT_EQ_POSIX(expected, actual)
2014-05-15 14:45:44 +00:00
#endif
#if FMT_USE_FCNTL
2021-05-04 23:54:20 +00:00
void write_file(fmt::cstring_view filename, fmt::string_view content) {
2018-05-19 15:57:31 +00:00
fmt::buffered_file f(filename, "w");
f.print("{}", content);
}
2019-11-15 13:41:14 +00:00
using fmt::file;
2021-05-04 23:54:20 +00:00
TEST(os_test, getpagesize) {
2019-12-21 21:10:45 +00:00
# ifdef _WIN32
SYSTEM_INFO si = {};
GetSystemInfo(&si);
EXPECT_EQ(si.dwPageSize, fmt::getpagesize());
2019-12-21 21:10:45 +00:00
# else
EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
sysconf_error = true;
2019-01-13 02:27:38 +00:00
EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL,
"cannot get memory page size");
sysconf_error = false;
2019-12-21 21:10:45 +00:00
# endif
}
2021-05-04 23:54:20 +00:00
TEST(file_test, open_retry) {
2020-04-01 15:42:14 +00:00
write_file("temp", "there must be something here");
std::unique_ptr<file> f{nullptr};
2020-04-01 15:42:14 +00:00
EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
"cannot open file temp");
2019-12-21 21:10:45 +00:00
# ifndef _WIN32
char c = 0;
f->read(&c, 1);
2019-12-21 21:10:45 +00:00
# endif
2014-05-15 14:45:44 +00:00
}
2021-05-04 23:54:20 +00:00
TEST(file_test, close_no_retry_in_dtor) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
std::unique_ptr<file> f(new file(std::move(read_end)));
2014-05-16 13:25:20 +00:00
int saved_close_count = 0;
2020-04-01 15:42:14 +00:00
EXPECT_WRITE(
stderr,
{
close_count = 1;
f.reset(nullptr);
saved_close_count = close_count;
close_count = 0;
},
system_error_message(EINTR, "cannot close file") + "\n");
2014-05-16 13:25:20 +00:00
EXPECT_EQ(2, saved_close_count);
}
2021-05-04 23:54:20 +00:00
TEST(file_test, close_no_retry) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
2014-05-16 13:25:20 +00:00
close_count = 1;
EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file");
EXPECT_EQ(2, close_count);
close_count = 0;
}
2021-05-04 23:54:20 +00:00
TEST(file_test, size) {
std::string content = "top secret, destroy before reading";
2020-04-01 15:42:14 +00:00
write_file("temp", content);
file f("temp", file::RDONLY);
2014-10-13 15:39:38 +00:00
EXPECT_GE(f.size(), 0);
2017-08-26 16:09:43 +00:00
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
2019-12-21 21:10:45 +00:00
# ifdef _WIN32
auto error_code = std::error_code();
2021-05-04 23:54:20 +00:00
fstat_sim = error;
try {
f.size();
} catch (const std::system_error& e) {
error_code = e.code();
}
2021-05-04 23:54:20 +00:00
fstat_sim = none;
EXPECT_EQ(error_code,
2021-05-09 20:33:14 +00:00
std::error_code(ERROR_ACCESS_DENIED, fmt::system_category()));
2019-12-21 21:10:45 +00:00
# else
2014-09-13 01:22:45 +00:00
f.close();
EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
2019-12-21 21:10:45 +00:00
# endif
}
2021-05-04 23:54:20 +00:00
TEST(file_test, max_size) {
2020-04-01 15:42:14 +00:00
write_file("temp", "");
file f("temp", file::RDONLY);
2021-05-04 23:54:20 +00:00
fstat_sim = max_size;
EXPECT_GE(f.size(), 0);
EXPECT_EQ(max_file_size(), f.size());
2021-05-04 23:54:20 +00:00
fstat_sim = none;
}
2021-05-04 23:54:20 +00:00
TEST(file_test, read_retry) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
enum { SIZE = 4 };
write_end.write("test", SIZE);
write_end.close();
char buffer[SIZE];
2020-05-07 22:59:46 +00:00
size_t count = 0;
2019-01-13 02:27:38 +00:00
EXPECT_RETRY(count = read_end.read(buffer, SIZE), read,
"cannot read from file");
2014-06-07 14:11:34 +00:00
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
}
2021-05-04 23:54:20 +00:00
TEST(file_test, write_retry) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
enum { SIZE = 4 };
2020-05-07 22:59:46 +00:00
size_t count = 0;
2019-01-13 02:27:38 +00:00
EXPECT_RETRY(count = write_end.write("test", SIZE), write,
"cannot write to file");
write_end.close();
2019-12-21 21:10:45 +00:00
# ifndef _WIN32
2014-06-07 14:11:34 +00:00
EXPECT_EQ(static_cast<std::streamsize>(SIZE), count);
char buffer[SIZE + 1];
read_end.read(buffer, SIZE);
buffer[SIZE] = '\0';
EXPECT_STREQ("test", buffer);
2019-12-21 21:10:45 +00:00
# endif
}
2019-12-21 21:10:45 +00:00
# ifdef _WIN32
2021-05-04 23:54:20 +00:00
TEST(file_test, convert_read_count) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
2014-05-19 15:25:08 +00:00
char c;
2020-05-07 22:59:46 +00:00
size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(size_t)) ++size;
2014-05-19 15:25:08 +00:00
read_count = 1;
2014-05-19 15:37:28 +00:00
read_nbyte = 0;
EXPECT_THROW(read_end.read(&c, size), std::system_error);
2014-05-19 15:25:08 +00:00
read_count = 0;
EXPECT_EQ(UINT_MAX, read_nbyte);
}
2021-05-04 23:54:20 +00:00
TEST(file_test, convert_write_count) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
2014-05-19 15:37:28 +00:00
char c;
2020-05-07 22:59:46 +00:00
size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(size_t)) ++size;
2014-05-19 15:37:28 +00:00
write_count = 1;
write_nbyte = 0;
EXPECT_THROW(write_end.write(&c, size), std::system_error);
2014-05-19 15:37:28 +00:00
write_count = 0;
EXPECT_EQ(UINT_MAX, write_nbyte);
}
2019-12-21 21:10:45 +00:00
# endif
2014-05-19 15:25:08 +00:00
2021-05-04 23:54:20 +00:00
TEST(file_test, dup_no_retry) {
2014-05-19 14:53:16 +00:00
int stdout_fd = FMT_POSIX(fileno(stdout));
2014-05-18 17:05:29 +00:00
dup_count = 1;
2019-01-13 02:27:38 +00:00
EXPECT_SYSTEM_ERROR(
file::dup(stdout_fd), EINTR,
fmt::format("cannot duplicate file descriptor {}", stdout_fd));
2014-05-18 17:05:29 +00:00
dup_count = 0;
}
2021-05-04 23:54:20 +00:00
TEST(file_test, dup2_retry) {
2014-05-19 14:53:16 +00:00
int stdout_fd = FMT_POSIX(fileno(stdout));
2018-05-19 17:32:53 +00:00
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
2019-01-13 02:27:38 +00:00
fmt::format("cannot duplicate file descriptor {} to {}",
f1.descriptor(), f2.descriptor()));
}
2021-05-04 23:54:20 +00:00
TEST(file_test, dup2_no_except_retry) {
2014-05-19 14:53:16 +00:00
int stdout_fd = FMT_POSIX(fileno(stdout));
2018-05-19 17:32:53 +00:00
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
std::error_code ec;
2014-05-18 17:05:29 +00:00
dup2_count = 1;
f1.dup2(f2.descriptor(), ec);
2019-12-21 21:10:45 +00:00
# ifndef _WIN32
2014-05-18 17:05:29 +00:00
EXPECT_EQ(4, dup2_count);
2019-12-21 21:10:45 +00:00
# else
EXPECT_EQ(EINTR, ec.value());
2019-12-21 21:10:45 +00:00
# endif
2014-05-18 17:05:29 +00:00
dup2_count = 0;
}
2021-05-04 23:54:20 +00:00
TEST(file_test, pipe_no_retry) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
2014-05-18 17:05:29 +00:00
pipe_count = 1;
2019-01-13 02:27:38 +00:00
EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR,
"cannot create pipe");
2014-05-18 17:05:29 +00:00
pipe_count = 0;
}
2021-05-04 23:54:20 +00:00
TEST(file_test, fdopen_no_retry) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
2014-05-18 17:05:29 +00:00
fdopen_count = 1;
2019-01-13 02:27:38 +00:00
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EINTR,
"cannot associate stream with file descriptor");
2014-05-18 17:05:29 +00:00
fdopen_count = 0;
}
2021-05-04 23:54:20 +00:00
TEST(buffered_file_test, open_retry) {
2020-04-01 15:42:14 +00:00
write_file("temp", "there must be something here");
std::unique_ptr<buffered_file> f{nullptr};
2020-04-01 15:42:14 +00:00
EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
"cannot open file temp");
2019-12-21 21:10:45 +00:00
# ifndef _WIN32
char c = 0;
2014-08-29 13:57:26 +00:00
if (fread(&c, 1, 1, f->get()) < 1)
2017-02-19 16:41:38 +00:00
throw fmt::system_error(errno, "fread failed");
2019-12-21 21:10:45 +00:00
# endif
}
2021-05-04 23:54:20 +00:00
TEST(buffered_file_test, close_no_retry_in_dtor) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
2014-05-19 14:31:05 +00:00
int saved_fclose_count = 0;
2020-04-01 15:42:14 +00:00
EXPECT_WRITE(
stderr,
{
fclose_count = 1;
f.reset(nullptr);
saved_fclose_count = fclose_count;
fclose_count = 0;
},
system_error_message(EINTR, "cannot close file") + "\n");
2014-05-19 14:31:05 +00:00
EXPECT_EQ(2, saved_fclose_count);
}
2021-05-04 23:54:20 +00:00
TEST(buffered_file_test, close_no_retry) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
2018-05-19 15:57:31 +00:00
buffered_file f = read_end.fdopen("r");
2014-05-19 14:31:05 +00:00
fclose_count = 1;
EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
EXPECT_EQ(2, fclose_count);
fclose_count = 0;
}
2021-05-04 23:54:20 +00:00
TEST(buffered_file_test, fileno_no_retry) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
2018-05-19 15:57:31 +00:00
buffered_file f = read_end.fdopen("r");
2014-05-19 14:31:05 +00:00
fileno_count = 1;
2022-05-22 13:49:31 +00:00
EXPECT_SYSTEM_ERROR((f.descriptor)(), EINTR, "cannot get file descriptor");
2014-05-19 14:31:05 +00:00
EXPECT_EQ(2, fileno_count);
fileno_count = 0;
}
2019-11-15 13:41:14 +00:00
#endif // FMT_USE_FCNTL
2016-02-03 01:21:09 +00:00
2020-04-01 16:30:28 +00:00
struct test_mock {
static test_mock* instance;
} * test_mock::instance;
2016-02-03 01:21:09 +00:00
2021-05-04 23:54:20 +00:00
TEST(scoped_mock, scope) {
2016-02-03 01:21:09 +00:00
{
2021-05-04 23:54:20 +00:00
scoped_mock<test_mock> mock;
2020-04-01 16:30:28 +00:00
EXPECT_EQ(&mock, test_mock::instance);
test_mock& copy = mock;
static_cast<void>(copy);
2016-02-03 01:21:09 +00:00
}
2020-04-01 16:30:28 +00:00
EXPECT_EQ(nullptr, test_mock::instance);
2016-02-03 01:21:09 +00:00
}