fmt/test/posix-mock-test.cc

529 lines
13 KiB
C++
Raw Normal View History

2018-03-04 20:16:51 +03: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 18:45:44 +04:00
2014-12-17 17:53:32 +03:00
// Disable bogus MSVC warnings.
#define _CRT_SECURE_NO_WARNINGS
2015-06-24 18:43:30 +03:00
#include "posix-mock.h"
2018-03-21 17:50:59 +03:00
#include "../src/posix.cc"
2014-05-15 18:45:44 +04:00
#include <errno.h>
#include <fcntl.h>
2014-05-19 19:25:08 +04:00
#include <climits>
2014-05-15 18:45:44 +04:00
2014-05-18 22:09:37 +04:00
#ifdef _WIN32
# include <io.h>
2014-09-13 02:18:11 +04:00
# undef max
2014-09-13 04:16:22 +04:00
# undef ERROR
2014-05-18 22:09:37 +04:00
#endif
2016-02-03 04:21:09 +03:00
#include "gmock/gmock.h"
2014-05-15 18:45:44 +04:00
#include "gtest-extra.h"
#include "util.h"
2014-05-15 18:45:44 +04:00
using fmt::BufferedFile;
using fmt::ErrorCode;
using fmt::File;
2015-07-31 18:24:37 +03:00
using testing::internal::scoped_ptr;
2016-02-03 04:21:09 +03:00
using testing::_;
using testing::StrEq;
using testing::Return;
2015-07-31 18:24:37 +03:00
2014-05-15 18:45:44 +04: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 21:05:29 +04:00
int pipe_count;
int fopen_count;
2014-05-18 22:09:37 +04:00
int fclose_count;
2014-05-19 18:31:05 +04:00
int fileno_count;
2014-05-19 19:25:08 +04:00
std::size_t read_nbyte;
2014-05-19 19:37:28 +04:00
std::size_t write_nbyte;
bool sysconf_error;
2014-09-13 03:52:15 +04:00
enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim;
2014-05-15 18:45:44 +04:00
}
#define EMULATE_EINTR(func, error_result) \
if (func##_count != 0) { \
if (func##_count++ != 3) { \
errno = EINTR; \
return error_result; \
} \
}
2015-05-06 17:29:58 +03:00
#ifndef _MSC_VER
2014-05-15 18:45:44 +04:00
int test::open(const char *path, int oflag, int mode) {
EMULATE_EINTR(open, -1);
return ::open(path, oflag, mode);
2014-05-15 18:45:44 +04:00
}
2015-05-06 17:29:58 +03:00
#else
errno_t test::sopen_s(
int* pfh, const char *filename, int oflag, int shflag, int pmode) {
EMULATE_EINTR(open, EINTR);
return _sopen_s(pfh, filename, oflag, shflag, pmode);
}
#endif
2015-05-06 17:29:58 +03:00
#ifndef _WIN32
2015-05-12 05:10:31 +03:00
long test::sysconf(int name) {
long result = ::sysconf(name);
if (!sysconf_error)
return result;
// Simulate an error.
errno = EINVAL;
return -1;
}
2015-05-12 05:10:31 +03:00
static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
int test::fstat(int fd, struct stat *buf) {
int result = ::fstat(fd, buf);
if (fstat_sim == MAX_SIZE)
buf->st_size = max_file_size();
return result;
}
2014-05-15 18:45:44 +04:00
#else
2015-05-12 05:10:31 +03:00
static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); }
DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
2014-09-13 03:52:15 +04:00
if (fstat_sim == ERROR) {
SetLastError(ERROR_ACCESS_DENIED);
2015-03-16 19:17:18 +03:00
return INVALID_FILE_SIZE;
2014-09-13 03:52:15 +04:00
}
if (fstat_sim == MAX_SIZE) {
DWORD max = std::numeric_limits<DWORD>::max();
2015-03-16 19:05:46 +03:00
*lpFileSizeHigh = max >> 1;
return max;
}
return ::GetFileSize(hFile, lpFileSizeHigh);
}
2015-05-12 05:10:31 +03:00
2014-05-15 18:45:44 +04:00
#endif
int test::close(int fildes) {
// Close the file first because close shouldn't be retried.
2014-05-19 18:31:05 +04: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 18:31:05 +04:00
return ::FMT_POSIX(dup(fildes));
}
int test::dup2(int fildes, int fildes2) {
EMULATE_EINTR(dup2, -1);
2014-05-19 18:31:05 +04:00
return ::FMT_POSIX(dup2(fildes, fildes2));
}
FILE *test::fdopen(int fildes, const char *mode) {
EMULATE_EINTR(fdopen, 0);
2014-05-19 18:31:05 +04:00
return ::FMT_POSIX(fdopen(fildes, mode));
}
test::ssize_t test::read(int fildes, void *buf, test::size_t nbyte) {
2014-05-19 19:25:08 +04:00
read_nbyte = nbyte;
EMULATE_EINTR(read, -1);
2014-05-19 18:31:05 +04:00
return ::FMT_POSIX(read(fildes, buf, nbyte));
}
test::ssize_t test::write(int fildes, const void *buf, test::size_t nbyte) {
2014-05-19 19:37:28 +04:00
write_nbyte = nbyte;
EMULATE_EINTR(write, -1);
2014-05-19 18:31:05 +04:00
return ::FMT_POSIX(write(fildes, buf, nbyte));
}
2014-05-18 22:09:37 +04:00
#ifndef _WIN32
2014-05-18 21:05:29 +04:00
int test::pipe(int fildes[2]) {
EMULATE_EINTR(pipe, -1);
return ::pipe(fildes);
}
2014-05-18 22:09:37 +04:00
#else
int test::pipe(int *pfds, unsigned psize, int textmode) {
EMULATE_EINTR(pipe, -1);
2014-05-18 23:38:01 +04:00
return _pipe(pfds, psize, textmode);
2014-05-18 22:09:37 +04:00
}
#endif
FILE *test::fopen(const char *filename, const char *mode) {
EMULATE_EINTR(fopen, 0);
return ::fopen(filename, mode);
}
2014-05-18 22:09:37 +04:00
int test::fclose(FILE *stream) {
EMULATE_EINTR(fclose, EOF);
return ::fclose(stream);
}
2014-05-18 21:05:29 +04:00
2015-03-19 17:39:24 +03:00
int (test::fileno)(FILE *stream) {
2014-05-19 18:31:05 +04:00
EMULATE_EINTR(fileno, -1);
#ifdef fileno
return FMT_POSIX(fileno(stream));
#else
2014-05-19 18:53:16 +04:00
return ::FMT_POSIX(fileno(stream));
#endif
2014-05-19 18:31:05 +04:00
}
2014-05-15 18:45:44 +04:00
#ifndef _WIN32
# define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \
2014-05-15 18:45:44 +04:00
statement; \
EXPECT_EQ(4, func##_count); \
func##_count = 0;
2014-05-16 20:16:29 +04:00
# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
2014-05-15 18:45:44 +04:00
#else
# define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \
EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
func##_count = 0;
2014-05-16 20:16:29 +04:00
# define EXPECT_EQ_POSIX(expected, actual)
2014-05-15 18:45:44 +04:00
#endif
2017-03-27 01:13:10 +03:00
void write_file(fmt::cstring_view filename, fmt::string_view content) {
fmt::BufferedFile f(filename, "w");
f.print("{}", content);
}
TEST(UtilTest, GetPageSize) {
#ifdef _WIN32
SYSTEM_INFO si = {};
GetSystemInfo(&si);
EXPECT_EQ(si.dwPageSize, fmt::getpagesize());
#else
EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
sysconf_error = true;
EXPECT_SYSTEM_ERROR(
fmt::getpagesize(), EINVAL, "cannot get memory page size");
sysconf_error = false;
#endif
}
2014-05-15 18:45:44 +04:00
TEST(FileTest, OpenRetry) {
write_file("test", "there must be something here");
2015-07-31 18:24:37 +03:00
scoped_ptr<File> f;
EXPECT_RETRY(f.reset(new File("test", File::RDONLY)),
open, "cannot open file test");
#ifndef _WIN32
char c = 0;
f->read(&c, 1);
#endif
2014-05-15 18:45:44 +04:00
}
2014-05-16 17:25:20 +04:00
TEST(FileTest, CloseNoRetryInDtor) {
File read_end, write_end;
File::pipe(read_end, write_end);
2015-07-31 18:24:37 +03:00
scoped_ptr<File> f(new File(std::move(read_end)));
2014-05-16 17:25:20 +04:00
int saved_close_count = 0;
EXPECT_WRITE(stderr, {
close_count = 1;
2015-07-28 17:18:14 +03:00
f.reset();
2014-05-16 17:25:20 +04:00
saved_close_count = close_count;
close_count = 0;
}, format_system_error(EINTR, "cannot close file") + "\n");
2014-05-16 17:25:20 +04:00
EXPECT_EQ(2, saved_close_count);
}
TEST(FileTest, CloseNoRetry) {
File read_end, write_end;
File::pipe(read_end, write_end);
close_count = 1;
EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file");
EXPECT_EQ(2, close_count);
close_count = 0;
}
TEST(FileTest, Size) {
std::string content = "top secret, destroy before reading";
write_file("test", content);
File f("test", File::RDONLY);
2014-10-13 19:39:38 +04:00
EXPECT_GE(f.size(), 0);
2017-08-26 19:09:43 +03:00
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
2014-09-13 03:52:15 +04:00
#ifdef _WIN32
2017-02-19 17:46:51 +03:00
fmt::memory_buffer message;
2014-09-13 04:41:04 +04:00
fmt::internal::format_windows_error(
2014-09-13 04:16:22 +04:00
message, ERROR_ACCESS_DENIED, "cannot get file size");
2014-09-13 03:52:15 +04:00
fstat_sim = ERROR;
2017-03-04 18:10:54 +03:00
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
2014-09-13 04:16:22 +04:00
fstat_sim = NONE;
2014-09-13 03:52:15 +04:00
#else
2014-09-13 05:22:45 +04:00
f.close();
EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
2014-09-13 03:52:15 +04:00
#endif
}
TEST(FileTest, MaxSize) {
write_file("test", "");
File f("test", File::RDONLY);
2014-09-13 03:52:15 +04:00
fstat_sim = MAX_SIZE;
EXPECT_GE(f.size(), 0);
EXPECT_EQ(max_file_size(), f.size());
2014-09-13 03:52:15 +04:00
fstat_sim = NONE;
}
TEST(FileTest, ReadRetry) {
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];
2016-03-02 18:53:14 +03:00
std::size_t count = 0;
EXPECT_RETRY(count = read_end.read(buffer, SIZE),
read, "cannot read from file");
2014-06-07 18:11:34 +04:00
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
}
TEST(FileTest, WriteRetry) {
File read_end, write_end;
File::pipe(read_end, write_end);
enum { SIZE = 4 };
2016-03-02 18:53:14 +03:00
std::size_t count = 0;
EXPECT_RETRY(count = write_end.write("test", SIZE),
write, "cannot write to file");
write_end.close();
#ifndef _WIN32
2014-06-07 18:11:34 +04: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);
#endif
}
2014-05-19 19:25:08 +04:00
#ifdef _WIN32
TEST(FileTest, ConvertReadCount) {
File read_end, write_end;
File::pipe(read_end, write_end);
char c;
2014-05-19 19:37:28 +04:00
std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t))
++size;
2014-05-19 19:25:08 +04:00
read_count = 1;
2014-05-19 19:37:28 +04:00
read_nbyte = 0;
2017-03-04 18:10:54 +03:00
EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
2014-05-19 19:25:08 +04:00
read_count = 0;
EXPECT_EQ(UINT_MAX, read_nbyte);
}
2014-05-19 19:37:28 +04:00
TEST(FileTest, ConvertWriteCount) {
File read_end, write_end;
File::pipe(read_end, write_end);
char c;
std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t))
++size;
write_count = 1;
write_nbyte = 0;
2017-03-04 18:10:54 +03:00
EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
2014-05-19 19:37:28 +04:00
write_count = 0;
EXPECT_EQ(UINT_MAX, write_nbyte);
}
#endif
2014-05-19 19:25:08 +04:00
2014-05-18 21:05:29 +04:00
TEST(FileTest, DupNoRetry) {
2014-05-19 18:53:16 +04:00
int stdout_fd = FMT_POSIX(fileno(stdout));
2014-05-18 21:05:29 +04:00
dup_count = 1;
EXPECT_SYSTEM_ERROR(File::dup(stdout_fd), EINTR,
fmt::format("cannot duplicate file descriptor {}", stdout_fd));
2014-05-18 21:05:29 +04:00
dup_count = 0;
}
TEST(FileTest, Dup2Retry) {
2014-05-19 18:53:16 +04:00
int stdout_fd = FMT_POSIX(fileno(stdout));
File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd);
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
fmt::format("cannot duplicate file descriptor {} to {}",
f1.descriptor(), f2.descriptor()));
}
2014-05-18 21:05:29 +04:00
TEST(FileTest, Dup2NoExceptRetry) {
2014-05-19 18:53:16 +04:00
int stdout_fd = FMT_POSIX(fileno(stdout));
File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd);
2014-05-18 21:05:29 +04:00
ErrorCode ec;
dup2_count = 1;
f1.dup2(f2.descriptor(), ec);
#ifndef _WIN32
EXPECT_EQ(4, dup2_count);
#else
EXPECT_EQ(EINTR, ec.get());
#endif
dup2_count = 0;
}
TEST(FileTest, PipeNoRetry) {
File read_end, write_end;
pipe_count = 1;
EXPECT_SYSTEM_ERROR(
File::pipe(read_end, write_end), EINTR, "cannot create pipe");
pipe_count = 0;
}
TEST(FileTest, FdopenNoRetry) {
File read_end, write_end;
File::pipe(read_end, write_end);
fdopen_count = 1;
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"),
EINTR, "cannot associate stream with file descriptor");
fdopen_count = 0;
}
TEST(BufferedFileTest, OpenRetry) {
write_file("test", "there must be something here");
2015-07-31 18:24:37 +03:00
scoped_ptr<BufferedFile> f;
EXPECT_RETRY(f.reset(new BufferedFile("test", "r")),
fopen, "cannot open file test");
#ifndef _WIN32
char c = 0;
2014-08-29 17:57:26 +04:00
if (fread(&c, 1, 1, f->get()) < 1)
2017-02-19 19:41:38 +03:00
throw fmt::system_error(errno, "fread failed");
#endif
}
2014-05-19 18:31:05 +04:00
TEST(BufferedFileTest, CloseNoRetryInDtor) {
File read_end, write_end;
File::pipe(read_end, write_end);
2015-07-31 18:24:37 +03:00
scoped_ptr<BufferedFile> f(new BufferedFile(read_end.fdopen("r")));
2014-05-19 18:31:05 +04:00
int saved_fclose_count = 0;
EXPECT_WRITE(stderr, {
fclose_count = 1;
2015-07-31 18:24:37 +03:00
f.reset();
2014-05-19 18:31:05 +04:00
saved_fclose_count = fclose_count;
fclose_count = 0;
}, format_system_error(EINTR, "cannot close file") + "\n");
2014-05-19 18:31:05 +04:00
EXPECT_EQ(2, saved_fclose_count);
}
TEST(BufferedFileTest, CloseNoRetry) {
File read_end, write_end;
File::pipe(read_end, write_end);
BufferedFile f = read_end.fdopen("r");
fclose_count = 1;
EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
EXPECT_EQ(2, fclose_count);
fclose_count = 0;
}
TEST(BufferedFileTest, FilenoNoRetry) {
File read_end, write_end;
File::pipe(read_end, write_end);
BufferedFile f = read_end.fdopen("r");
fileno_count = 1;
EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
2014-05-19 18:31:05 +04:00
EXPECT_EQ(2, fileno_count);
fileno_count = 0;
}
2016-02-03 04:21:09 +03:00
2016-02-09 22:31:04 +03:00
struct TestMock {
static TestMock *instance;
} *TestMock::instance;
2016-02-03 04:21:09 +03:00
TEST(ScopedMock, Scope) {
{
2016-02-09 22:31:04 +03:00
ScopedMock<TestMock> mock;
EXPECT_EQ(&mock, TestMock::instance);
2016-02-03 04:21:09 +03:00
TestMock &copy = mock;
}
2016-02-09 22:31:04 +03:00
EXPECT_EQ(0, TestMock::instance);
2016-02-03 04:21:09 +03:00
}
#ifdef FMT_LOCALE
typedef fmt::Locale::Type LocaleType;
struct LocaleMock {
2016-02-09 22:31:04 +03:00
static LocaleMock *instance;
2016-02-03 04:21:09 +03:00
MOCK_METHOD3(newlocale, LocaleType (int category_mask, const char *locale,
LocaleType base));
MOCK_METHOD1(freelocale, void (LocaleType locale));
MOCK_METHOD3(strtod_l, double (const char *nptr, char **endptr,
LocaleType locale));
2016-02-09 22:31:04 +03:00
} *LocaleMock::instance;
2016-02-03 04:21:09 +03:00
#ifdef _MSC_VER
2016-03-19 16:39:33 +03:00
# pragma warning(push)
# pragma warning(disable: 4273)
2016-02-03 04:21:09 +03:00
_locale_t _create_locale(int category, const char *locale) {
2016-02-09 22:31:04 +03:00
return LocaleMock::instance->newlocale(category, locale, 0);
2016-02-03 04:21:09 +03:00
}
void _free_locale(_locale_t locale) {
2016-02-09 22:31:04 +03:00
LocaleMock::instance->freelocale(locale);
2016-02-03 04:21:09 +03:00
}
double _strtod_l(const char *nptr, char **endptr, _locale_t locale) {
2016-02-09 22:31:04 +03:00
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
2016-02-03 04:21:09 +03:00
}
2016-03-19 16:39:33 +03:00
# pragma warning(pop)
2016-02-03 04:21:09 +03:00
#endif
LocaleType newlocale(int category_mask, const char *locale, LocaleType base) {
2016-02-09 22:31:04 +03:00
return LocaleMock::instance->newlocale(category_mask, locale, base);
2016-02-03 04:21:09 +03:00
}
#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 1200002)
2016-02-03 09:06:54 +03:00
typedef int FreeLocaleResult;
2016-02-03 04:21:09 +03:00
#else
2016-02-03 09:06:54 +03:00
typedef void FreeLocaleResult;
2016-02-03 04:21:09 +03:00
#endif
FreeLocaleResult freelocale(LocaleType locale) {
2016-02-09 22:31:04 +03:00
LocaleMock::instance->freelocale(locale);
return FreeLocaleResult();
2016-02-03 04:21:09 +03:00
}
double strtod_l(const char *nptr, char **endptr, LocaleType locale) {
2016-02-09 22:31:04 +03:00
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
2016-02-03 04:21:09 +03:00
}
TEST(LocaleTest, LocaleMock) {
2016-02-09 22:31:04 +03:00
ScopedMock<LocaleMock> mock;
2016-02-03 04:21:09 +03:00
LocaleType locale = reinterpret_cast<LocaleType>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
newlocale(222, "foo", locale);
}
TEST(LocaleTest, Locale) {
#ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC };
#endif
2016-02-09 22:31:04 +03:00
ScopedMock<LocaleMock> mock;
2016-02-03 04:21:09 +03:00
LocaleType impl = reinterpret_cast<LocaleType>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), 0))
.WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl));
fmt::Locale locale;
EXPECT_EQ(impl, locale.get());
}
TEST(LocaleTest, Strtod) {
2016-02-09 22:31:04 +03:00
ScopedMock<LocaleMock> mock;
2016-02-03 04:21:09 +03:00
EXPECT_CALL(mock, newlocale(_, _, _))
.WillOnce(Return(reinterpret_cast<LocaleType>(42)));
EXPECT_CALL(mock, freelocale(_));
fmt::Locale locale;
const char *str = "4.2";
char end = 'x';
EXPECT_CALL(mock, strtod_l(str, _, locale.get()))
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
EXPECT_EQ(777, locale.strtod(str));
EXPECT_EQ(&end, str);
}
#endif // FMT_LOCALE