diff --git a/test/gtest-extra-test.cc b/test/gtest-extra-test.cc index dfa13364..a9a8a4d4 100644 --- a/test/gtest-extra-test.cc +++ b/test/gtest-extra-test.cc @@ -65,8 +65,8 @@ class SuppressAssert { // Fix "secure" warning about using fopen without defining // _CRT_SECURE_NO_WARNINGS. -std::FILE *OpenFile(const char *filename, const char *mode) { - std::FILE *f = 0; +FILE *OpenFile(const char *filename, const char *mode) { + FILE *f = 0; errno = fopen_s(&f, filename, mode); return f; } diff --git a/test/gtest-extra.cc b/test/gtest-extra.cc index 5486aef7..a218803b 100644 --- a/test/gtest-extra.cc +++ b/test/gtest-extra.cc @@ -46,10 +46,6 @@ #endif // _WIN32 -#ifdef FMT_INCLUDE_POSIX_TEST -# include "posix-test.h" -#endif - // Retries the expression while it evaluates to -1 and error equals to EINTR. #define FMT_RETRY(result, expression) \ do { \ @@ -60,6 +56,7 @@ namespace { #ifdef _WIN32 // On Windows the count argument to read and write is unsigned, so convert // it from size_t preventing integer overflow. +// TODO: test inline unsigned ConvertRWCount(std::size_t count) { return count <= UINT_MAX ? static_cast(count) : UINT_MAX; } @@ -83,7 +80,7 @@ void BufferedFile::close() { } int BufferedFile::fileno() const { - int fd = ::FMT_POSIX(fileno(file_)); + int fd = FMT_POSIX_CALL(fileno(file_)); if (fd == -1) fmt::ThrowSystemError(errno, "cannot get file descriptor"); return fd; @@ -93,9 +90,9 @@ File::File(const char *path, int oflag) { int mode = S_IRUSR | S_IWUSR; #ifdef _WIN32 fd_ = -1; - _sopen_s(&fd_, path, oflag, _SH_DENYNO, mode); + FMT_POSIX_CALL(sopen_s(&fd_, path, oflag, _SH_DENYNO, mode)); #else - FMT_RETRY(fd_, open(path, oflag, mode)); + FMT_RETRY(fd_, FMT_POSIX_CALL(open(path, oflag, mode))); #endif if (fd_ == -1) fmt::ThrowSystemError(errno, "cannot open file {}") << path; @@ -104,7 +101,7 @@ File::File(const char *path, int oflag) { File::~File() FMT_NOEXCEPT(true) { // Don't retry close in case of EINTR! // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html - if (fd_ != -1 && ::FMT_POSIX(close(fd_)) != 0) + if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) fmt::ReportSystemError(errno, "cannot close file"); } @@ -113,7 +110,7 @@ void File::close() { return; // Don't retry close in case of EINTR! // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html - int result = ::FMT_POSIX(close(fd_)); + int result = FMT_POSIX_CALL(close(fd_)); fd_ = -1; if (result != 0) fmt::ThrowSystemError(errno, "cannot close file"); @@ -121,7 +118,7 @@ void File::close() { std::streamsize File::read(void *buffer, std::size_t count) { std::streamsize result = 0; - FMT_RETRY(result, ::FMT_POSIX(read(fd_, buffer, ConvertRWCount(count)))); + FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, ConvertRWCount(count)))); if (result == -1) fmt::ThrowSystemError(errno, "cannot read from file"); return result; @@ -129,7 +126,7 @@ std::streamsize File::read(void *buffer, std::size_t count) { std::streamsize File::write(const void *buffer, std::size_t count) { std::streamsize result = 0; - FMT_RETRY(result, ::FMT_POSIX(write(fd_, buffer, ConvertRWCount(count)))); + FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, ConvertRWCount(count)))); if (result == -1) fmt::ThrowSystemError(errno, "cannot write to file"); return result; @@ -137,7 +134,7 @@ std::streamsize File::write(const void *buffer, std::size_t count) { File File::dup(int fd) { int new_fd = 0; - FMT_RETRY(new_fd, ::FMT_POSIX(dup(fd))); + FMT_RETRY(new_fd, FMT_POSIX_CALL(dup(fd))); if (new_fd == -1) fmt::ThrowSystemError(errno, "cannot duplicate file descriptor {}") << fd; return File(new_fd); @@ -145,7 +142,7 @@ File File::dup(int fd) { void File::dup2(int fd) { int result = 0; - FMT_RETRY(result, ::FMT_POSIX(dup2(fd_, fd))); + FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); if (result == -1) { fmt::ThrowSystemError(errno, "cannot duplicate file descriptor {} to {}") << fd_ << fd; @@ -154,7 +151,7 @@ void File::dup2(int fd) { void File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true) { int result = 0; - FMT_RETRY(result, ::FMT_POSIX(dup2(fd_, fd))); + FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); if (result == -1) ec = ErrorCode(errno); } @@ -182,7 +179,7 @@ void File::pipe(File &read_end, File &write_end) { } BufferedFile File::fdopen(const char *mode) { - BufferedFile f(::FMT_POSIX(fdopen(fd_, mode))); + BufferedFile f(FMT_POSIX_CALL(fdopen(fd_, mode))); fd_ = -1; return f; } @@ -206,7 +203,7 @@ void OutputRedirect::Restore() { original_.close(); } -OutputRedirect::OutputRedirect(std::FILE *file) : file_(file) { +OutputRedirect::OutputRedirect(FILE *file) : file_(file) { Flush(); int fd = FMT_POSIX(fileno(file)); // Create a File object referring to the original file. diff --git a/test/gtest-extra.h b/test/gtest-extra.h index 2e2b4c0e..f5649b71 100644 --- a/test/gtest-extra.h +++ b/test/gtest-extra.h @@ -29,10 +29,11 @@ #define FMT_GTEST_EXTRA_H #include -#include #include #include +#include + #if FMT_USE_FILE_DESCRIPTORS # include #endif @@ -41,6 +42,10 @@ #include "format.h" +#ifdef FMT_INCLUDE_POSIX_TEST +# include "posix-test.h" +#endif + #define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \ @@ -93,11 +98,22 @@ std::string FormatSystemErrorMessage(int error_code, fmt::StringRef message); #if FMT_USE_FILE_DESCRIPTORS -#ifdef _WIN32 +#ifndef FMT_POSIX +# ifdef _WIN32 // Fix warnings about deprecated symbols. -# define FMT_POSIX(name) _##name -#else -# define FMT_POSIX(name) name +# define FMT_POSIX(name) _##name +# else +# define FMT_POSIX(name) name +# endif +#endif + +#ifndef FMT_POSIX_CALL +# ifdef _WIN32 +// Fix warnings about deprecated symbols. +# define FMT_POSIX_CALL(name) ::_##name +# else +# define FMT_POSIX_CALL(name) ::name +# endif #endif // An error code. @@ -114,11 +130,11 @@ class ErrorCode { // A buffered file. class BufferedFile { private: - std::FILE *file_; + FILE *file_; friend class File; - explicit BufferedFile(std::FILE *f) : file_(f) {} + explicit BufferedFile(FILE *f) : file_(f) {} public: // Constructs a BufferedFile object which doesn't represent any file. @@ -135,7 +151,7 @@ class BufferedFile { // A proxy object to emulate a move constructor. // It is private to make it impossible call operator Proxy directly. struct Proxy { - std::FILE *file; + FILE *file; }; public: @@ -190,7 +206,7 @@ class BufferedFile { void close(); // Returns the pointer to a FILE object representing this file. - std::FILE *get() const { return file_; } + FILE *get() const { return file_; } int fileno() const; }; @@ -329,7 +345,7 @@ inline File &move(File &f) { return f; } // The output it can handle is limited by the pipe capacity. class OutputRedirect { private: - std::FILE *file_; + FILE *file_; File original_; // Original file passed to redirector. File read_end_; // Read end of the pipe where the output is redirected. @@ -339,7 +355,7 @@ class OutputRedirect { void Restore(); public: - explicit OutputRedirect(std::FILE *file); + explicit OutputRedirect(FILE *file); ~OutputRedirect() FMT_NOEXCEPT(true); // Restores the original file, reads output from the pipe into a string diff --git a/test/posix-test.cc b/test/posix-test.cc index e714d987..a8f16891 100644 --- a/test/posix-test.cc +++ b/test/posix-test.cc @@ -27,8 +27,6 @@ #include "posix-test.h" -#undef open - #include #include @@ -36,29 +34,84 @@ namespace { int open_count; +int close_count; +int dup_count; +int dup2_count; +int fdopen_count; +int fileno_count; +int read_count; +int write_count; } +#define EMULATE_EINTR(func, error_result) \ + if (func##_count != 0) { \ + if (func##_count++ != 3) { \ + errno = EINTR; \ + return error_result; \ + } \ + } + #ifndef _WIN32 int test::open(const char *path, int oflag, int mode) { - if ((open_count++ % 3) == 2) - return ::open(path, oflag, mode); - errno = EINTR; - return -1; + EMULATE_EINTR(open, -1); + return ::open(path, oflag, mode); } #else -errno_t test::sopen_s(int* pfh, const char *filename, int oflag, int shflag, int pmode) { +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 +int test::close(int fildes) { + // Close the file first because close shouldn't be retried. + int result = ::close(fildes); + EMULATE_EINTR(close, -1); + return result; +} + +int test::dup(int fildes) { + EMULATE_EINTR(dup, -1); + return ::dup(fildes); +} + +int test::dup2(int fildes, int fildes2) { + EMULATE_EINTR(dup2, -1); + return ::dup2(fildes, fildes2); +} + +FILE *test::fdopen(int fildes, const char *mode) { + EMULATE_EINTR(fdopen, 0); + return ::fdopen(fildes, mode); +} + +int test::fileno(FILE *stream) { + EMULATE_EINTR(fileno, -1); + return ::fileno(stream); +} + +test::ssize_t test::read(int fildes, void *buf, test::size_t nbyte) { + EMULATE_EINTR(read, -1); + return ::read(fildes, buf, nbyte); +} + +test::ssize_t test::write(int fildes, const void *buf, test::size_t nbyte) { + EMULATE_EINTR(write, -1); + return ::write(fildes, buf, nbyte); +} + #ifndef _WIN32 # define EXPECT_RETRY(statement, func, message) \ - func##_count = 0; \ + func##_count = 1; \ statement; \ - EXPECT_EQ(3, func##_count); + EXPECT_EQ(4, func##_count); \ + func##_count = 0; #else # define EXPECT_RETRY(statement, func, message) \ - EXPECT_SYSTEM_ERROR(statement, EINTR, message); + func##_count = 1; \ + EXPECT_SYSTEM_ERROR(statement, EINTR, message); \ + func##_count = 0; #endif TEST(FileTest, OpenRetry) { diff --git a/test/posix-test.h b/test/posix-test.h index 89924f95..11d2124f 100644 --- a/test/posix-test.h +++ b/test/posix-test.h @@ -29,17 +29,35 @@ #define FMT_POSIX_TEST_H #include +#include namespace test { #ifndef _WIN32 +// Size type for read and write. +typedef size_t size_t; +typedef ssize_t ssize_t; int open(const char *path, int oflag, int mode); -#define open test::open #else -errno_t sopen_s(int* pfh, const char *filename, int oflag, int shflag, int pmode); -#define _sopen_s test::sopen_s +typedef unsigned size_t; +typedef int ssize_t; +errno_t sopen_s( + int* pfh, const char *filename, int oflag, int shflag, int pmode); #endif +int close(int fildes); + +int dup(int fildes); +int dup2(int fildes, int fildes2); + +FILE *fdopen(int fildes, const char *mode); +int fileno(FILE *stream); + +ssize_t read(int fildes, void *buf, size_t nbyte); +ssize_t write(int fildes, const void *buf, size_t nbyte); + } // namespace test +#define FMT_POSIX_CALL(call) test::call + #endif // FMT_POSIX_TEST_H