Use stdio.h instead of cstdio for fdopen. Wrap all used POSIX functions for testing.

This commit is contained in:
Victor Zverovich 2014-05-15 08:58:10 -07:00
parent d758dbb33a
commit fb17316d77
5 changed files with 126 additions and 42 deletions

View File

@ -65,8 +65,8 @@ class SuppressAssert {
// Fix "secure" warning about using fopen without defining // Fix "secure" warning about using fopen without defining
// _CRT_SECURE_NO_WARNINGS. // _CRT_SECURE_NO_WARNINGS.
std::FILE *OpenFile(const char *filename, const char *mode) { FILE *OpenFile(const char *filename, const char *mode) {
std::FILE *f = 0; FILE *f = 0;
errno = fopen_s(&f, filename, mode); errno = fopen_s(&f, filename, mode);
return f; return f;
} }

View File

@ -46,10 +46,6 @@
#endif // _WIN32 #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. // Retries the expression while it evaluates to -1 and error equals to EINTR.
#define FMT_RETRY(result, expression) \ #define FMT_RETRY(result, expression) \
do { \ do { \
@ -60,6 +56,7 @@ namespace {
#ifdef _WIN32 #ifdef _WIN32
// On Windows the count argument to read and write is unsigned, so convert // On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow. // it from size_t preventing integer overflow.
// TODO: test
inline unsigned ConvertRWCount(std::size_t count) { inline unsigned ConvertRWCount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX; return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
} }
@ -83,7 +80,7 @@ void BufferedFile::close() {
} }
int BufferedFile::fileno() const { int BufferedFile::fileno() const {
int fd = ::FMT_POSIX(fileno(file_)); int fd = FMT_POSIX_CALL(fileno(file_));
if (fd == -1) if (fd == -1)
fmt::ThrowSystemError(errno, "cannot get file descriptor"); fmt::ThrowSystemError(errno, "cannot get file descriptor");
return fd; return fd;
@ -93,9 +90,9 @@ File::File(const char *path, int oflag) {
int mode = S_IRUSR | S_IWUSR; int mode = S_IRUSR | S_IWUSR;
#ifdef _WIN32 #ifdef _WIN32
fd_ = -1; fd_ = -1;
_sopen_s(&fd_, path, oflag, _SH_DENYNO, mode); FMT_POSIX_CALL(sopen_s(&fd_, path, oflag, _SH_DENYNO, mode));
#else #else
FMT_RETRY(fd_, open(path, oflag, mode)); FMT_RETRY(fd_, FMT_POSIX_CALL(open(path, oflag, mode)));
#endif #endif
if (fd_ == -1) if (fd_ == -1)
fmt::ThrowSystemError(errno, "cannot open file {}") << path; fmt::ThrowSystemError(errno, "cannot open file {}") << path;
@ -104,7 +101,7 @@ File::File(const char *path, int oflag) {
File::~File() FMT_NOEXCEPT(true) { File::~File() FMT_NOEXCEPT(true) {
// Don't retry close in case of EINTR! // Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // 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"); fmt::ReportSystemError(errno, "cannot close file");
} }
@ -113,7 +110,7 @@ void File::close() {
return; return;
// Don't retry close in case of EINTR! // Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // 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; fd_ = -1;
if (result != 0) if (result != 0)
fmt::ThrowSystemError(errno, "cannot close file"); 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 File::read(void *buffer, std::size_t count) {
std::streamsize result = 0; 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) if (result == -1)
fmt::ThrowSystemError(errno, "cannot read from file"); fmt::ThrowSystemError(errno, "cannot read from file");
return result; 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 File::write(const void *buffer, std::size_t count) {
std::streamsize result = 0; 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) if (result == -1)
fmt::ThrowSystemError(errno, "cannot write to file"); fmt::ThrowSystemError(errno, "cannot write to file");
return result; return result;
@ -137,7 +134,7 @@ std::streamsize File::write(const void *buffer, std::size_t count) {
File File::dup(int fd) { File File::dup(int fd) {
int new_fd = 0; 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) if (new_fd == -1)
fmt::ThrowSystemError(errno, "cannot duplicate file descriptor {}") << fd; fmt::ThrowSystemError(errno, "cannot duplicate file descriptor {}") << fd;
return File(new_fd); return File(new_fd);
@ -145,7 +142,7 @@ File File::dup(int fd) {
void File::dup2(int fd) { void File::dup2(int fd) {
int result = 0; int result = 0;
FMT_RETRY(result, ::FMT_POSIX(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) { if (result == -1) {
fmt::ThrowSystemError(errno, fmt::ThrowSystemError(errno,
"cannot duplicate file descriptor {} to {}") << fd_ << fd; "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) { void File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true) {
int result = 0; int result = 0;
FMT_RETRY(result, ::FMT_POSIX(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) if (result == -1)
ec = ErrorCode(errno); ec = ErrorCode(errno);
} }
@ -182,7 +179,7 @@ void File::pipe(File &read_end, File &write_end) {
} }
BufferedFile File::fdopen(const char *mode) { BufferedFile File::fdopen(const char *mode) {
BufferedFile f(::FMT_POSIX(fdopen(fd_, mode))); BufferedFile f(FMT_POSIX_CALL(fdopen(fd_, mode)));
fd_ = -1; fd_ = -1;
return f; return f;
} }
@ -206,7 +203,7 @@ void OutputRedirect::Restore() {
original_.close(); original_.close();
} }
OutputRedirect::OutputRedirect(std::FILE *file) : file_(file) { OutputRedirect::OutputRedirect(FILE *file) : file_(file) {
Flush(); Flush();
int fd = FMT_POSIX(fileno(file)); int fd = FMT_POSIX(fileno(file));
// Create a File object referring to the original file. // Create a File object referring to the original file.

View File

@ -29,10 +29,11 @@
#define FMT_GTEST_EXTRA_H #define FMT_GTEST_EXTRA_H
#include <cstddef> #include <cstddef>
#include <cstdio>
#include <ios> #include <ios>
#include <string> #include <string>
#include <stdio.h>
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FILE_DESCRIPTORS
# include <fcntl.h> # include <fcntl.h>
#endif #endif
@ -41,6 +42,10 @@
#include "format.h" #include "format.h"
#ifdef FMT_INCLUDE_POSIX_TEST
# include "posix-test.h"
#endif
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \ #define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \ 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 #if FMT_USE_FILE_DESCRIPTORS
#ifdef _WIN32 #ifndef FMT_POSIX
# ifdef _WIN32
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX(name) _##name # define FMT_POSIX(name) _##name
#else # else
# define FMT_POSIX(name) name # 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 #endif
// An error code. // An error code.
@ -114,11 +130,11 @@ class ErrorCode {
// A buffered file. // A buffered file.
class BufferedFile { class BufferedFile {
private: private:
std::FILE *file_; FILE *file_;
friend class File; friend class File;
explicit BufferedFile(std::FILE *f) : file_(f) {} explicit BufferedFile(FILE *f) : file_(f) {}
public: public:
// Constructs a BufferedFile object which doesn't represent any file. // Constructs a BufferedFile object which doesn't represent any file.
@ -135,7 +151,7 @@ class BufferedFile {
// A proxy object to emulate a move constructor. // A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly. // It is private to make it impossible call operator Proxy directly.
struct Proxy { struct Proxy {
std::FILE *file; FILE *file;
}; };
public: public:
@ -190,7 +206,7 @@ class BufferedFile {
void close(); void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
std::FILE *get() const { return file_; } FILE *get() const { return file_; }
int fileno() const; 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. // The output it can handle is limited by the pipe capacity.
class OutputRedirect { class OutputRedirect {
private: private:
std::FILE *file_; FILE *file_;
File original_; // Original file passed to redirector. File original_; // Original file passed to redirector.
File read_end_; // Read end of the pipe where the output is redirected. File read_end_; // Read end of the pipe where the output is redirected.
@ -339,7 +355,7 @@ class OutputRedirect {
void Restore(); void Restore();
public: public:
explicit OutputRedirect(std::FILE *file); explicit OutputRedirect(FILE *file);
~OutputRedirect() FMT_NOEXCEPT(true); ~OutputRedirect() FMT_NOEXCEPT(true);
// Restores the original file, reads output from the pipe into a string // Restores the original file, reads output from the pipe into a string

View File

@ -27,8 +27,6 @@
#include "posix-test.h" #include "posix-test.h"
#undef open
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@ -36,29 +34,84 @@
namespace { namespace {
int open_count; 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 #ifndef _WIN32
int test::open(const char *path, int oflag, int mode) { int test::open(const char *path, int oflag, int mode) {
if ((open_count++ % 3) == 2) EMULATE_EINTR(open, -1);
return ::open(path, oflag, mode); return ::open(path, oflag, mode);
errno = EINTR;
return -1;
} }
#else #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); return _sopen_s(pfh, filename, oflag, shflag, pmode);
} }
#endif #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 #ifndef _WIN32
# define EXPECT_RETRY(statement, func, message) \ # define EXPECT_RETRY(statement, func, message) \
func##_count = 0; \ func##_count = 1; \
statement; \ statement; \
EXPECT_EQ(3, func##_count); EXPECT_EQ(4, func##_count); \
func##_count = 0;
#else #else
# define EXPECT_RETRY(statement, func, message) \ # 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 #endif
TEST(FileTest, OpenRetry) { TEST(FileTest, OpenRetry) {

View File

@ -29,17 +29,35 @@
#define FMT_POSIX_TEST_H #define FMT_POSIX_TEST_H
#include <errno.h> #include <errno.h>
#include <stdio.h>
namespace test { namespace test {
#ifndef _WIN32 #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); int open(const char *path, int oflag, int mode);
#define open test::open
#else #else
errno_t sopen_s(int* pfh, const char *filename, int oflag, int shflag, int pmode); typedef unsigned size_t;
#define _sopen_s test::sopen_s typedef int ssize_t;
errno_t sopen_s(
int* pfh, const char *filename, int oflag, int shflag, int pmode);
#endif #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 } // namespace test
#define FMT_POSIX_CALL(call) test::call
#endif // FMT_POSIX_TEST_H #endif // FMT_POSIX_TEST_H