Move posix tests from gtest-extra-test to a separate test

This commit is contained in:
vitaut
2015-06-24 09:16:03 -07:00
parent 9769f4b9ed
commit 0a7e5141c1
8 changed files with 455 additions and 404 deletions
+4 -401
View File
@@ -36,37 +36,15 @@
# include <crtdbg.h> // for _CrtSetReportMode
#endif // _WIN32
#include "util.h"
#ifdef __MINGW32__
# undef fileno
#endif
namespace {
#if defined(_WIN32) && !defined(__MINGW32__)
// Suppresses Windows assertions on invalid file descriptors, making
// POSIX functions return proper error codes instead of crashing on Windows.
class SuppressAssert {
private:
_invalid_parameter_handler original_handler_;
int original_report_mode_;
static void handle_invalid_parameter(const wchar_t *,
const wchar_t *, const wchar_t *, unsigned , uintptr_t) {}
public:
SuppressAssert()
: original_handler_(_set_invalid_parameter_handler(handle_invalid_parameter)),
original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {
}
~SuppressAssert() {
_set_invalid_parameter_handler(original_handler_);
_CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
}
};
# define SUPPRESS_ASSERT(statement) { SuppressAssert sa; statement; }
#ifdef _MSC_VER
// Fix "secure" warning about using fopen without defining
// _CRT_SECURE_NO_WARNINGS.
FILE *safe_fopen(const char *filename, const char *mode) {
@@ -76,12 +54,8 @@ FILE *safe_fopen(const char *filename, const char *mode) {
}
#define fopen safe_fopen
#else
# define SUPPRESS_ASSERT(statement) statement
using std::fopen;
#endif // _WIN32
#define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
#endif // _MSC_VER
// Tests that assertion macros evaluate their arguments exactly once.
class SingleEvaluationTest : public ::testing::Test {
@@ -363,382 +337,11 @@ using fmt::BufferedFile;
using fmt::ErrorCode;
using fmt::File;
// Checks if the file is open by reading one character from it.
bool isopen(int fd) {
char buffer;
return FMT_POSIX(read(fd, &buffer, 1)) == 1;
}
bool isclosed(int fd) {
char buffer;
std::streamsize result = 0;
SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1)));
return result == -1 && errno == EBADF;
}
// Attempts to read count characters from a file.
std::string read(File &f, std::size_t count) {
std::string buffer(count, '\0');
std::streamsize n = 0;
std::size_t offset = 0;
do {
n = f.read(&buffer[offset], count - offset);
// We can't read more than size_t bytes since count has type size_t.
offset += static_cast<std::size_t>(n);
} while (offset < count && n != 0);
buffer.resize(offset);
return buffer;
}
// Attempts to write a string to a file.
void write(File &f, fmt::StringRef s) {
std::size_t num_chars_left = s.size();
const char *ptr = s.c_str();
do {
std::streamsize count = f.write(ptr, num_chars_left);
ptr += count;
// We can't write more than size_t bytes since num_chars_left
// has type size_t.
num_chars_left -= static_cast<std::size_t>(count);
} while (num_chars_left != 0);
}
#define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
TEST(ErrorCodeTest, Ctor) {
EXPECT_EQ(0, ErrorCode().get());
EXPECT_EQ(42, ErrorCode(42).get());
}
const char FILE_CONTENT[] = "Don't panic!";
// Opens a file for reading.
File open_file() {
File read_end, write_end;
File::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, sizeof(FILE_CONTENT) - 1);
write_end.close();
return read_end;
}
// Opens a buffered file for reading.
BufferedFile open_buffered_file(FILE **fp = 0) {
File read_end, write_end;
File::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, sizeof(FILE_CONTENT) - 1);
write_end.close();
BufferedFile f = read_end.fdopen("r");
if (fp)
*fp = f.get();
return f;
}
TEST(BufferedFileTest, DefaultCtor) {
BufferedFile f;
EXPECT_TRUE(f.get() == 0);
}
TEST(BufferedFileTest, MoveCtor) {
BufferedFile bf = open_buffered_file();
FILE *fp = bf.get();
EXPECT_TRUE(fp != 0);
BufferedFile bf2(std::move(bf));
EXPECT_EQ(fp, bf2.get());
EXPECT_TRUE(bf.get() == 0);
}
TEST(BufferedFileTest, MoveAssignment) {
BufferedFile bf = open_buffered_file();
FILE *fp = bf.get();
EXPECT_TRUE(fp != 0);
BufferedFile bf2;
bf2 = std::move(bf);
EXPECT_EQ(fp, bf2.get());
EXPECT_TRUE(bf.get() == 0);
}
TEST(BufferedFileTest, MoveAssignmentClosesFile) {
BufferedFile bf = open_buffered_file();
BufferedFile bf2 = open_buffered_file();
int old_fd = bf2.fileno();
bf2 = std::move(bf);
EXPECT_TRUE(isclosed(old_fd));
}
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
FILE *fp = 0;
BufferedFile f(open_buffered_file(&fp));
EXPECT_EQ(fp, f.get());
}
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
FILE *fp = 0;
BufferedFile f;
f = open_buffered_file(&fp);
EXPECT_EQ(fp, f.get());
}
TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
BufferedFile f = open_buffered_file();
int old_fd = f.fileno();
f = open_buffered_file();
EXPECT_TRUE(isclosed(old_fd));
}
TEST(BufferedFileTest, CloseFileInDtor) {
int fd = 0;
{
BufferedFile f = open_buffered_file();
fd = f.fileno();
}
EXPECT_TRUE(isclosed(fd));
}
TEST(BufferedFileTest, CloseErrorInDtor) {
BufferedFile *f = new BufferedFile(open_buffered_file());
EXPECT_WRITE(stderr, {
// The close function must be called inside EXPECT_WRITE, otherwise
// the system may recycle closed file descriptor when redirecting the
// output in EXPECT_STDERR and the second close will break output
// redirection.
FMT_POSIX(close(f->fileno()));
SUPPRESS_ASSERT(delete f);
}, format_system_error(EBADF, "cannot close file") + "\n");
}
TEST(BufferedFileTest, Close) {
BufferedFile f = open_buffered_file();
int fd = f.fileno();
f.close();
EXPECT_TRUE(f.get() == 0);
EXPECT_TRUE(isclosed(fd));
}
TEST(BufferedFileTest, CloseError) {
BufferedFile f = open_buffered_file();
FMT_POSIX(close(f.fileno()));
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
EXPECT_TRUE(f.get() == 0);
}
TEST(BufferedFileTest, Fileno) {
BufferedFile f;
// fileno on a null FILE pointer either crashes or returns an error.
EXPECT_DEATH_IF_SUPPORTED({
try {
f.fileno();
} catch (fmt::SystemError) {
std::exit(1);
}
}, "");
f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1);
File copy = File::dup(f.fileno());
EXPECT_READ(copy, FILE_CONTENT);
}
TEST(FileTest, DefaultCtor) {
File f;
EXPECT_EQ(-1, f.descriptor());
}
TEST(FileTest, OpenBufferedFileInCtor) {
FILE *fp = fopen("test-file", "w");
std::fputs(FILE_CONTENT, fp);
std::fclose(fp);
File f("test-file", File::RDONLY);
ASSERT_TRUE(isopen(f.descriptor()));
}
TEST(FileTest, OpenBufferedFileError) {
EXPECT_SYSTEM_ERROR(File("nonexistent", File::RDONLY),
ENOENT, "cannot open file nonexistent");
}
TEST(FileTest, MoveCtor) {
File f = open_file();
int fd = f.descriptor();
EXPECT_NE(-1, fd);
File f2(std::move(f));
EXPECT_EQ(fd, f2.descriptor());
EXPECT_EQ(-1, f.descriptor());
}
TEST(FileTest, MoveAssignment) {
File f = open_file();
int fd = f.descriptor();
EXPECT_NE(-1, fd);
File f2;
f2 = std::move(f);
EXPECT_EQ(fd, f2.descriptor());
EXPECT_EQ(-1, f.descriptor());
}
TEST(FileTest, MoveAssignmentClosesFile) {
File f = open_file();
File f2 = open_file();
int old_fd = f2.descriptor();
f2 = std::move(f);
EXPECT_TRUE(isclosed(old_fd));
}
File OpenBufferedFile(int &fd) {
File f = open_file();
fd = f.descriptor();
return std::move(f);
}
TEST(FileTest, MoveFromTemporaryInCtor) {
int fd = 0xdeadbeef;
File f(OpenBufferedFile(fd));
EXPECT_EQ(fd, f.descriptor());
}
TEST(FileTest, MoveFromTemporaryInAssignment) {
int fd = 0xdeadbeef;
File f;
f = OpenBufferedFile(fd);
EXPECT_EQ(fd, f.descriptor());
}
TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
int fd = 0xdeadbeef;
File f = open_file();
int old_fd = f.descriptor();
f = OpenBufferedFile(fd);
EXPECT_TRUE(isclosed(old_fd));
}
TEST(FileTest, CloseFileInDtor) {
int fd = 0;
{
File f = open_file();
fd = f.descriptor();
}
EXPECT_TRUE(isclosed(fd));
}
TEST(FileTest, CloseErrorInDtor) {
File *f = new File(open_file());
EXPECT_WRITE(stderr, {
// The close function must be called inside EXPECT_WRITE, otherwise
// the system may recycle closed file descriptor when redirecting the
// output in EXPECT_STDERR and the second close will break output
// redirection.
FMT_POSIX(close(f->descriptor()));
SUPPRESS_ASSERT(delete f);
}, format_system_error(EBADF, "cannot close file") + "\n");
}
TEST(FileTest, Close) {
File f = open_file();
int fd = f.descriptor();
f.close();
EXPECT_EQ(-1, f.descriptor());
EXPECT_TRUE(isclosed(fd));
}
TEST(FileTest, CloseError) {
File f = open_file();
FMT_POSIX(close(f.descriptor()));
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
EXPECT_EQ(-1, f.descriptor());
}
TEST(FileTest, Read) {
File f = open_file();
EXPECT_READ(f, FILE_CONTENT);
}
TEST(FileTest, ReadError) {
File read_end, write_end;
File::pipe(read_end, write_end);
char buf;
// We intentionally read from write_end to cause error.
EXPECT_SYSTEM_ERROR(write_end.read(&buf, 1), EBADF, "cannot read from file");
}
TEST(FileTest, Write) {
File read_end, write_end;
File::pipe(read_end, write_end);
write(write_end, "test");
write_end.close();
EXPECT_READ(read_end, "test");
}
TEST(FileTest, WriteError) {
File read_end, write_end;
File::pipe(read_end, write_end);
// We intentionally write to read_end to cause error.
EXPECT_SYSTEM_ERROR(read_end.write(" ", 1), EBADF, "cannot write to file");
}
TEST(FileTest, Dup) {
File f = open_file();
File copy = File::dup(f.descriptor());
EXPECT_NE(f.descriptor(), copy.descriptor());
EXPECT_EQ(FILE_CONTENT, read(copy, sizeof(FILE_CONTENT) - 1));
}
TEST(FileTest, DupError) {
EXPECT_SYSTEM_ERROR_NOASSERT(File::dup(-1),
EBADF, "cannot duplicate file descriptor -1");
}
TEST(FileTest, Dup2) {
File f = open_file();
File copy = open_file();
f.dup2(copy.descriptor());
EXPECT_NE(f.descriptor(), copy.descriptor());
EXPECT_READ(copy, FILE_CONTENT);
}
TEST(FileTest, Dup2Error) {
File f = open_file();
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF,
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
}
TEST(FileTest, Dup2NoExcept) {
File f = open_file();
File copy = open_file();
ErrorCode ec;
f.dup2(copy.descriptor(), ec);
EXPECT_EQ(0, ec.get());
EXPECT_NE(f.descriptor(), copy.descriptor());
EXPECT_READ(copy, FILE_CONTENT);
}
TEST(FileTest, Dup2NoExceptError) {
File f = open_file();
ErrorCode ec;
SUPPRESS_ASSERT(f.dup2(-1, ec));
EXPECT_EQ(EBADF, ec.get());
}
TEST(FileTest, Pipe) {
File read_end, write_end;
File::pipe(read_end, write_end);
EXPECT_NE(-1, read_end.descriptor());
EXPECT_NE(-1, write_end.descriptor());
write(write_end, "test");
EXPECT_READ(read_end, "test");
}
TEST(FileTest, Fdopen) {
File read_end, write_end;
File::pipe(read_end, write_end);
int read_fd = read_end.descriptor();
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
}
TEST(FileTest, FdopenError) {
File f;
EXPECT_SYSTEM_ERROR_NOASSERT(
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor");
}
TEST(OutputRedirectTest, ScopedRedirect) {
File read_end, write_end;
File::pipe(read_end, write_end);