ext-fmt/test/posix-mock-test.cc

554 lines
14 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.
#ifndef _CRT_SECURE_NO_WARNINGS
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"
2019-12-15 20:25:40 +00:00
#include "../src/os.cc"
2014-05-15 14:45:44 +00:00
#include <errno.h>
#include <fcntl.h>
2019-01-13 02:27:38 +00:00
#include <climits>
#include <memory>
2014-05-15 14:45:44 +00:00
2014-05-18 18:09:37 +00:00
#ifdef _WIN32
2019-01-13 02:27:38 +00:00
# include <io.h>
# undef max
# undef ERROR
2014-05-18 18:09:37 +00:00
#endif
#include "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;
2018-05-19 17:32:53 +00:00
using fmt::error_code;
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
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;
2014-05-19 15:25:08 +00:00
std::size_t read_nbyte;
2014-05-19 15:37:28 +00:00
std::size_t write_nbyte;
bool sysconf_error;
2014-09-12 23:52:15 +00:00
enum FStatSimulation { 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);
2019-01-13 02:27:38 +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) {
2014-09-12 23:52:15 +00:00
if (fstat_sim == ERROR) {
SetLastError(ERROR_ACCESS_DENIED);
2015-03-16 16:17:18 +00:00
return INVALID_FILE_SIZE;
2014-09-12 23:52:15 +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
static 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
#if FMT_USE_FCNTL
using fmt::file;
TEST(UtilTest, 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
}
2014-05-15 14:45:44 +00:00
TEST(FileTest, OpenRetry) {
write_file("test", "there must be something here");
std::unique_ptr<file> f{nullptr};
2019-01-13 02:27:38 +00:00
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)), open,
"cannot open file test");
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
}
2014-05-16 13:25:20 +00:00
TEST(FileTest, CloseNoRetryInDtor) {
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;
2019-01-13 02:27:38 +00:00
EXPECT_WRITE(stderr,
{
close_count = 1;
f.reset(nullptr);
2019-01-13 02:27:38 +00:00
saved_close_count = close_count;
close_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
2014-05-16 13:25:20 +00:00
EXPECT_EQ(2, saved_close_count);
}
TEST(FileTest, CloseNoRetry) {
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;
}
TEST(FileTest, Size) {
std::string content = "top secret, destroy before reading";
write_file("test", content);
2018-05-19 17:32:53 +00:00
file f("test", 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
2017-02-19 14:46:51 +00:00
fmt::memory_buffer message;
2019-01-13 02:27:38 +00:00
fmt::internal::format_windows_error(message, ERROR_ACCESS_DENIED,
"cannot get file size");
2014-09-12 23:52:15 +00:00
fstat_sim = ERROR;
2017-03-04 15:10:54 +00:00
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
2014-09-13 00:16:22 +00:00
fstat_sim = NONE;
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
}
TEST(FileTest, MaxSize) {
write_file("test", "");
2018-05-19 17:32:53 +00:00
file f("test", file::RDONLY);
2014-09-12 23:52:15 +00:00
fstat_sim = MAX_SIZE;
EXPECT_GE(f.size(), 0);
EXPECT_EQ(max_file_size(), f.size());
2014-09-12 23:52:15 +00:00
fstat_sim = NONE;
}
TEST(FileTest, ReadRetry) {
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];
2016-03-02 15:53:14 +00:00
std::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);
}
TEST(FileTest, WriteRetry) {
2018-05-19 17:32:53 +00:00
file read_end, write_end;
file::pipe(read_end, write_end);
enum { SIZE = 4 };
2016-03-02 15:53:14 +00:00
std::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
2014-05-19 15:25:08 +00:00
TEST(FileTest, ConvertReadCount) {
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;
2014-05-19 15:37:28 +00:00
std::size_t size = UINT_MAX;
2019-01-13 02:27:38 +00:00
if (sizeof(unsigned) != sizeof(std::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;
2017-03-04 15:10:54 +00:00
EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
2014-05-19 15:25:08 +00:00
read_count = 0;
EXPECT_EQ(UINT_MAX, read_nbyte);
}
2014-05-19 15:37:28 +00:00
TEST(FileTest, ConvertWriteCount) {
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;
std::size_t size = UINT_MAX;
2019-01-13 02:27:38 +00:00
if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
2014-05-19 15:37:28 +00:00
write_count = 1;
write_nbyte = 0;
2017-03-04 15:10:54 +00:00
EXPECT_THROW(write_end.write(&c, size), fmt::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
2014-05-18 17:05:29 +00:00
TEST(FileTest, DupNoRetry) {
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;
}
TEST(FileTest, Dup2Retry) {
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()));
}
2014-05-18 17:05:29 +00:00
TEST(FileTest, Dup2NoExceptRetry) {
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);
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
2014-05-18 17:05:29 +00:00
EXPECT_EQ(EINTR, ec.get());
2019-12-21 21:10:45 +00:00
# endif
2014-05-18 17:05:29 +00:00
dup2_count = 0;
}
TEST(FileTest, PipeNoRetry) {
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;
}
TEST(FileTest, FdopenNoRetry) {
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;
}
TEST(BufferedFileTest, OpenRetry) {
write_file("test", "there must be something here");
std::unique_ptr<buffered_file> f{nullptr};
2019-01-13 02:27:38 +00:00
EXPECT_RETRY(f.reset(new buffered_file("test", "r")), fopen,
"cannot open file test");
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
}
2014-05-19 14:31:05 +00:00
TEST(BufferedFileTest, CloseNoRetryInDtor) {
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;
2019-01-13 02:27:38 +00:00
EXPECT_WRITE(stderr,
{
fclose_count = 1;
f.reset(nullptr);
2019-01-13 02:27:38 +00:00
saved_fclose_count = fclose_count;
fclose_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
2014-05-19 14:31:05 +00:00
EXPECT_EQ(2, saved_fclose_count);
}
TEST(BufferedFileTest, CloseNoRetry) {
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;
}
TEST(BufferedFileTest, FilenoNoRetry) {
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;
EXPECT_SYSTEM_ERROR((f.fileno)(), 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
2016-02-09 19:31:04 +00:00
struct TestMock {
2019-01-13 02:27:38 +00:00
static TestMock* instance;
} * TestMock::instance;
2016-02-03 01:21:09 +00:00
TEST(ScopedMock, Scope) {
{
2016-02-09 19:31:04 +00:00
ScopedMock<TestMock> mock;
EXPECT_EQ(&mock, TestMock::instance);
2019-01-13 02:27:38 +00:00
TestMock& copy = mock;
static_cast<void>(copy);
2016-02-03 01:21:09 +00:00
}
EXPECT_EQ(nullptr, TestMock::instance);
2016-02-03 01:21:09 +00:00
}
#ifdef FMT_LOCALE
2019-07-07 23:43:38 +00:00
typedef fmt::Locale::type LocaleType;
2016-02-03 01:21:09 +00:00
struct LocaleMock {
2019-01-13 02:27:38 +00:00
static LocaleMock* instance;
MOCK_METHOD3(newlocale, LocaleType(int category_mask, const char* locale,
LocaleType base));
MOCK_METHOD1(freelocale, void(LocaleType locale));
2016-02-03 01:21:09 +00:00
2019-01-13 02:27:38 +00:00
MOCK_METHOD3(strtod_l,
double(const char* nptr, char** endptr, LocaleType locale));
} * LocaleMock::instance;
2016-02-03 01:21:09 +00:00
2019-01-13 02:27:38 +00:00
# ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4273)
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Winconsistent-dllimport"
# endif
2016-03-19 13:39:33 +00:00
2019-01-13 02:27:38 +00:00
_locale_t _create_locale(int category, const char* locale) {
2016-02-09 19:31:04 +00:00
return LocaleMock::instance->newlocale(category, locale, 0);
2016-02-03 01:21:09 +00:00
}
void _free_locale(_locale_t locale) {
2016-02-09 19:31:04 +00:00
LocaleMock::instance->freelocale(locale);
2016-02-03 01:21:09 +00:00
}
2019-01-13 02:27:38 +00:00
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
2016-02-09 19:31:04 +00:00
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
2016-02-03 01:21:09 +00:00
}
# ifdef __clang__
# pragma clang diagnostic pop
# endif
2019-01-13 02:27:38 +00:00
# pragma warning(pop)
# endif
2016-02-03 01:21:09 +00:00
2019-01-13 02:27:38 +00:00
# if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
# define FMT_LOCALE_THROW __THROW
# else
# define FMT_LOCALE_THROW
# endif
2019-01-13 02:27:38 +00:00
LocaleType newlocale(int category_mask, const char* locale,
LocaleType base) FMT_LOCALE_THROW {
2016-02-09 19:31:04 +00:00
return LocaleMock::instance->newlocale(category_mask, locale, base);
2016-02-03 01:21:09 +00:00
}
2019-01-13 02:27:38 +00:00
# if defined(__APPLE__) || \
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
2016-02-03 06:06:54 +00:00
typedef int FreeLocaleResult;
2019-01-13 02:27:38 +00:00
# else
2016-02-03 06:06:54 +00:00
typedef void FreeLocaleResult;
2019-01-13 02:27:38 +00:00
# endif
2016-02-03 01:21:09 +00:00
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
2016-02-09 19:31:04 +00:00
LocaleMock::instance->freelocale(locale);
return FreeLocaleResult();
2016-02-03 01:21:09 +00:00
}
2019-01-13 02:27:38 +00:00
double strtod_l(const char* nptr, char** endptr,
LocaleType locale) FMT_LOCALE_THROW {
2016-02-09 19:31:04 +00:00
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
2016-02-03 01:21:09 +00:00
}
2019-01-13 02:27:38 +00:00
# undef FMT_LOCALE_THROW
2016-02-03 01:21:09 +00:00
TEST(LocaleTest, LocaleMock) {
2016-02-09 19:31:04 +00:00
ScopedMock<LocaleMock> mock;
2016-02-03 01:21:09 +00:00
LocaleType locale = reinterpret_cast<LocaleType>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
newlocale(222, "foo", locale);
}
TEST(LocaleTest, Locale) {
2019-01-13 02:27:38 +00:00
# ifndef LC_NUMERIC_MASK
2016-02-03 01:21:09 +00:00
enum { LC_NUMERIC_MASK = LC_NUMERIC };
2019-01-13 02:27:38 +00:00
# endif
2016-02-09 19:31:04 +00:00
ScopedMock<LocaleMock> mock;
2016-02-03 01:21:09 +00:00
LocaleType impl = reinterpret_cast<LocaleType>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
2016-02-03 01:21:09 +00:00
.WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl));
fmt::Locale locale;
EXPECT_EQ(impl, locale.get());
}
TEST(LocaleTest, Strtod) {
2016-02-09 19:31:04 +00:00
ScopedMock<LocaleMock> mock;
2016-02-03 01:21:09 +00:00
EXPECT_CALL(mock, newlocale(_, _, _))
.WillOnce(Return(reinterpret_cast<LocaleType>(42)));
EXPECT_CALL(mock, freelocale(_));
fmt::Locale locale;
2019-01-13 02:27:38 +00:00
const char* str = "4.2";
2016-02-03 01:21:09 +00:00
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