Suppress asserts locally.

This commit is contained in:
Victor Zverovich
2014-05-05 17:07:21 -07:00
parent 2ecce39ca9
commit cada26d226

View File

@@ -45,38 +45,51 @@ std::string FormatSystemErrorMessage(int error_code, fmt::StringRef message) {
return str(out); return str(out);
} }
// Suppresses Windows assertions on invalid file descriptors, making
// POSIX functions return proper error codes instead of crashing on Windows.
class SuppressAssert {
#ifdef _WIN32
private:
_invalid_parameter_handler original_handler_;
int original_report_mode_;
static void InvalidParameterHandler(const wchar_t *,
const wchar_t *, const wchar_t *, unsigned , uintptr_t) {}
public:
SuppressAssert()
: original_handler_(_set_invalid_parameter_handler(InvalidParameterHandler)),
original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {
}
~SuppressAssert() {
_set_invalid_parameter_handler(original_handler_);
_CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
}
#endif // _WIN32
};
#define SUPPRESS_ASSERT(statement) { SuppressAssert sa; statement; }
#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \ #define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
EXPECT_THROW_MSG(statement, fmt::SystemError, \ EXPECT_THROW_MSG(statement, fmt::SystemError, \
FormatSystemErrorMessage(error_code, message)) FormatSystemErrorMessage(error_code, message))
#define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
// Checks if the file is open by reading one character from it. // Checks if the file is open by reading one character from it.
bool IsOpen(int fd) { bool IsOpen(int fd) {
char buffer; char buffer;
return FMT_POSIX(read(fd, &buffer, 1)) == 1; return FMT_POSIX(read(fd, &buffer, 1)) == 1;
} }
bool IsClosedInternal(int fd) { bool IsClosed(int fd) {
char buffer; char buffer;
std::streamsize result = FMT_POSIX(read(fd, &buffer, 1)); std::streamsize result = 0;
SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1)));
return result == -1 && errno == EBADF; return result == -1 && errno == EBADF;
} }
#ifndef _WIN32
// Checks if the file is closed.
# define EXPECT_CLOSED(fd) EXPECT_TRUE(IsClosedInternal(fd))
#else
// Reading from a closed file causes death on Windows.
# define EXPECT_CLOSED(fd) EXPECT_DEATH(IsClosedInternal(fd), "")
#endif
#ifndef _WIN32
# define EXPECT_SYSTEM_ERROR_OR_DEATH(statement, error_code, message) \
EXPECT_SYSTEM_ERROR(statement, error_code, message)
#else
# define EXPECT_SYSTEM_ERROR_OR_DEATH(statement, error_code, message) \
EXPECT_DEATH(statement, "")
#endif
// Tests that assertion macros evaluate their arguments exactly once. // Tests that assertion macros evaluate their arguments exactly once.
class SingleEvaluationTest : public ::testing::Test { class SingleEvaluationTest : public ::testing::Test {
protected: protected:
@@ -234,8 +247,6 @@ TEST(ExpectTest, EXPECT_WRITE) {
" Actual: that"); " Actual: that");
} }
// TODO: test EXPECT_WRITE
TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) { TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
EXPECT_THROW_MSG(ThrowException(), std::exception, "test") EXPECT_THROW_MSG(ThrowException(), std::exception, "test")
<< "unexpected failure"; << "unexpected failure";
@@ -244,6 +255,14 @@ TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
<< "expected failure", "expected failure"); << "expected failure", "expected failure");
} }
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test")
<< "unexpected failure";
EXPECT_NONFATAL_FAILURE(
EXPECT_WRITE(stdout, std::printf("test"), "other")
<< "expected failure", "expected failure");
}
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FILE_DESCRIPTORS
TEST(ErrorCodeTest, Ctor) { TEST(ErrorCodeTest, Ctor) {
@@ -251,33 +270,6 @@ TEST(ErrorCodeTest, Ctor) {
EXPECT_EQ(42, ErrorCode(42).get()); EXPECT_EQ(42, ErrorCode(42).get());
} }
// Enables standard POSIX mode of handling errors, making functions return
// proper error codes instead of crashing on Windows.
class ScopedPOSIXMode {
#ifdef _WIN32
private:
_invalid_parameter_handler original_handler_;
int original_report_mode_;
static void InvalidParameterHandler(const wchar_t *,
const wchar_t *, const wchar_t *, unsigned , uintptr_t) {}
public:
ScopedPOSIXMode()
: original_handler_(_set_invalid_parameter_handler(InvalidParameterHandler)),
original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {
}
~ScopedPOSIXMode() {
_set_invalid_parameter_handler(original_handler_);
_CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
}
#endif // _WIN32
};
class BufferedFileTest : public ::testing::Test {
ScopedPOSIXMode mode_;
};
BufferedFile OpenFile(const char *name, FILE **fp = 0) { BufferedFile OpenFile(const char *name, FILE **fp = 0) {
BufferedFile f = File(".travis.yml", File::RDONLY).fdopen("r"); BufferedFile f = File(".travis.yml", File::RDONLY).fdopen("r");
if (fp) if (fp)
@@ -285,12 +277,12 @@ BufferedFile OpenFile(const char *name, FILE **fp = 0) {
return f; return f;
} }
TEST_F(BufferedFileTest, DefaultCtor) { TEST(BufferedFileTest, DefaultCtor) {
BufferedFile f; BufferedFile f;
EXPECT_TRUE(f.get() == 0); EXPECT_TRUE(f.get() == 0);
} }
TEST_F(BufferedFileTest, MoveCtor) { TEST(BufferedFileTest, MoveCtor) {
BufferedFile bf = OpenFile(".travis.yml"); BufferedFile bf = OpenFile(".travis.yml");
FILE *fp = bf.get(); FILE *fp = bf.get();
EXPECT_TRUE(fp != 0); EXPECT_TRUE(fp != 0);
@@ -299,7 +291,7 @@ TEST_F(BufferedFileTest, MoveCtor) {
EXPECT_TRUE(bf.get() == 0); EXPECT_TRUE(bf.get() == 0);
} }
TEST_F(BufferedFileTest, MoveAssignment) { TEST(BufferedFileTest, MoveAssignment) {
BufferedFile bf = OpenFile(".travis.yml"); BufferedFile bf = OpenFile(".travis.yml");
FILE *fp = bf.get(); FILE *fp = bf.get();
EXPECT_TRUE(fp != 0); EXPECT_TRUE(fp != 0);
@@ -309,51 +301,53 @@ TEST_F(BufferedFileTest, MoveAssignment) {
EXPECT_TRUE(bf.get() == 0); EXPECT_TRUE(bf.get() == 0);
} }
TEST_F(BufferedFileTest, MoveAssignmentClosesFile) { TEST(BufferedFileTest, MoveAssignmentClosesFile) {
BufferedFile bf = OpenFile(".travis.yml"); BufferedFile bf = OpenFile(".travis.yml");
BufferedFile bf2 = OpenFile("CMakeLists.txt"); BufferedFile bf2 = OpenFile("CMakeLists.txt");
int old_fd = fileno(bf2.get()); int old_fd = fileno(bf2.get());
bf2 = std::move(bf); bf2 = std::move(bf);
EXPECT_TRUE(IsClosedInternal(old_fd)); EXPECT_TRUE(IsClosed(old_fd));
} }
TEST_F(BufferedFileTest, MoveFromTemporaryInCtor) { TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
FILE *fp = 0; FILE *fp = 0;
BufferedFile f(OpenFile(".travis.yml", &fp)); BufferedFile f(OpenFile(".travis.yml", &fp));
EXPECT_EQ(fp, f.get()); EXPECT_EQ(fp, f.get());
} }
TEST_F(BufferedFileTest, MoveFromTemporaryInAssignment) { TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
FILE *fp = 0; FILE *fp = 0;
BufferedFile f; BufferedFile f;
f = OpenFile(".travis.yml", &fp); f = OpenFile(".travis.yml", &fp);
EXPECT_EQ(fp, f.get()); EXPECT_EQ(fp, f.get());
} }
TEST_F(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) { TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
BufferedFile f = OpenFile(".travis.yml"); BufferedFile f = OpenFile(".travis.yml");
int old_fd = fileno(f.get()); int old_fd = fileno(f.get());
f = OpenFile(".travis.yml"); f = OpenFile(".travis.yml");
EXPECT_TRUE(IsClosedInternal(old_fd)); EXPECT_TRUE(IsClosed(old_fd));
} }
TEST_F(BufferedFileTest, CloseFileInDtor) { TEST(BufferedFileTest, CloseFileInDtor) {
int fd = 0; int fd = 0;
{ {
BufferedFile f = OpenFile(".travis.yml"); BufferedFile f = OpenFile(".travis.yml");
fd = fileno(f.get()); fd = fileno(f.get());
} }
EXPECT_TRUE(IsClosedInternal(fd)); EXPECT_TRUE(IsClosed(fd));
} }
TEST_F(BufferedFileTest, CloseErrorInDtor) { TEST(BufferedFileTest, CloseErrorInDtor) {
BufferedFile *f = new BufferedFile(OpenFile(".travis.yml")); BufferedFile *f = new BufferedFile(OpenFile(".travis.yml"));
// The close function must be called inside EXPECT_WRITE, otherwise EXPECT_WRITE(stderr, {
// the system may recycle closed file descriptor when redirecting the // The close function must be called inside EXPECT_WRITE, otherwise
// output in EXPECT_STDERR and the second close will break output // the system may recycle closed file descriptor when redirecting the
// redirection. // output in EXPECT_STDERR and the second close will break output
EXPECT_WRITE(stderr, close(fileno(f->get())); delete f, // redirection.
FormatSystemErrorMessage(EBADF, "cannot close file") + "\n"); close(fileno(f->get()));
SUPPRESS_ASSERT(delete f);
}, FormatSystemErrorMessage(EBADF, "cannot close file") + "\n");
} }
TEST(FileTest, DefaultCtor) { TEST(FileTest, DefaultCtor) {
@@ -395,7 +389,7 @@ TEST(FileTest, MoveAssignmentClosesFile) {
File f2("CMakeLists.txt", File::RDONLY); File f2("CMakeLists.txt", File::RDONLY);
int old_fd = f2.descriptor(); int old_fd = f2.descriptor();
f2 = std::move(f); f2 = std::move(f);
EXPECT_CLOSED(old_fd); EXPECT_TRUE(IsClosed(old_fd));
} }
File OpenFile(int &fd) { File OpenFile(int &fd) {
@@ -422,7 +416,7 @@ TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
File f(".travis.yml", File::RDONLY); File f(".travis.yml", File::RDONLY);
int old_fd = f.descriptor(); int old_fd = f.descriptor();
f = OpenFile(fd); f = OpenFile(fd);
EXPECT_CLOSED(old_fd); EXPECT_TRUE(IsClosed(old_fd));
} }
TEST(FileTest, CloseFileInDtor) { TEST(FileTest, CloseFileInDtor) {
@@ -431,23 +425,19 @@ TEST(FileTest, CloseFileInDtor) {
File f(".travis.yml", File::RDONLY); File f(".travis.yml", File::RDONLY);
fd = f.descriptor(); fd = f.descriptor();
} }
EXPECT_CLOSED(fd); EXPECT_TRUE(IsClosed(fd));
} }
TEST(FileTest, CloseErrorInDtor) { TEST(FileTest, CloseErrorInDtor) {
File *f = new File(".travis.yml", File::RDONLY); File *f = new File(".travis.yml", File::RDONLY);
#ifndef _WIN32 EXPECT_WRITE(stderr, {
// The close function must be called inside EXPECT_WRITE, otherwise // The close function must be called inside EXPECT_WRITE, otherwise
// the system may recycle closed file descriptor when redirecting the // the system may recycle closed file descriptor when redirecting the
// output in EXPECT_STDERR and the second close will break output // output in EXPECT_STDERR and the second close will break output
// redirection. // redirection.
EXPECT_WRITE(stderr, FMT_POSIX(close(f->descriptor())); delete f, FMT_POSIX(close(f->descriptor()));
FormatSystemErrorMessage(EBADF, "cannot close file") + "\n"); SUPPRESS_ASSERT(delete f);
#else }, FormatSystemErrorMessage(EBADF, "cannot close file") + "\n");
close(f->descriptor());
// Closing file twice causes death on Windows.
EXPECT_DEATH(delete f, "");
#endif
} }
TEST(FileTest, Close) { TEST(FileTest, Close) {
@@ -455,22 +445,14 @@ TEST(FileTest, Close) {
int fd = f.descriptor(); int fd = f.descriptor();
f.close(); f.close();
EXPECT_EQ(-1, f.descriptor()); EXPECT_EQ(-1, f.descriptor());
EXPECT_CLOSED(fd); EXPECT_TRUE(IsClosed(fd));
} }
TEST(FileTest, CloseError) { TEST(FileTest, CloseError) {
File f(".travis.yml", File::RDONLY); File f(".travis.yml", File::RDONLY);
#ifndef _WIN32
close(f.descriptor()); close(f.descriptor());
EXPECT_SYSTEM_ERROR(f.close(), EBADF, "cannot close file"); EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
EXPECT_EQ(-1, f.descriptor()); EXPECT_EQ(-1, f.descriptor());
#else
File dup = f.dup(f.descriptor());
close(f.descriptor());
// Closing file twice causes death on Windows.
EXPECT_DEATH(f.close(), "");
dup.dup2(f.descriptor()); // "undo" close or dtor will fail
#endif
} }
// Attempts to read count characters from a file. // Attempts to read count characters from a file.
@@ -536,7 +518,7 @@ TEST(FileTest, Dup) {
} }
TEST(FileTest, DupError) { TEST(FileTest, DupError) {
EXPECT_SYSTEM_ERROR_OR_DEATH(File::dup(-1), EXPECT_SYSTEM_ERROR_NOASSERT(File::dup(-1),
EBADF, "cannot duplicate file descriptor -1"); EBADF, "cannot duplicate file descriptor -1");
} }
@@ -550,7 +532,7 @@ TEST(FileTest, Dup2) {
TEST(FileTest, Dup2Error) { TEST(FileTest, Dup2Error) {
File f(".travis.yml", File::RDONLY); File f(".travis.yml", File::RDONLY);
EXPECT_SYSTEM_ERROR_OR_DEATH(f.dup2(-1), EBADF, EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF,
fmt::Format("cannot duplicate file descriptor {} to -1") << f.descriptor()); fmt::Format("cannot duplicate file descriptor {} to -1") << f.descriptor());
} }
@@ -567,12 +549,8 @@ TEST(FileTest, Dup2NoExcept) {
TEST(FileTest, Dup2NoExceptError) { TEST(FileTest, Dup2NoExceptError) {
File f(".travis.yml", File::RDONLY); File f(".travis.yml", File::RDONLY);
ErrorCode ec; ErrorCode ec;
#ifndef _WIN32 SUPPRESS_ASSERT(f.dup2(-1, ec));
f.dup2(-1, ec);
EXPECT_EQ(EBADF, ec.get()); EXPECT_EQ(EBADF, ec.get());
#else
EXPECT_DEATH(f.dup2(-1, ec), "");
#endif
} }
TEST(FileTest, Pipe) { TEST(FileTest, Pipe) {
@@ -610,7 +588,7 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
close(write_fd); close(write_fd);
OutputRedirect *redir = 0; OutputRedirect *redir = 0;
EXPECT_SYSTEM_ERROR_OR_DEATH(redir = new OutputRedirect(f.get()), EXPECT_SYSTEM_ERROR_NOASSERT(redir = new OutputRedirect(f.get()),
EBADF, fmt::Format("cannot flush stream")); EBADF, fmt::Format("cannot flush stream"));
delete redir; delete redir;
write_dup.dup2(write_fd); // "undo" close or dtor will fail write_dup.dup2(write_fd); // "undo" close or dtor will fail
@@ -622,7 +600,7 @@ TEST(OutputRedirectTest, DupErrorInCtor) {
File dup = File::dup(fd); File dup = File::dup(fd);
close(fd); close(fd);
OutputRedirect *redir = 0; OutputRedirect *redir = 0;
EXPECT_SYSTEM_ERROR_OR_DEATH(redir = new OutputRedirect(f.get()), EXPECT_SYSTEM_ERROR_NOASSERT(redir = new OutputRedirect(f.get()),
EBADF, fmt::Format("cannot duplicate file descriptor {}") << fd); EBADF, fmt::Format("cannot duplicate file descriptor {}") << fd);
dup.dup2(fd); // "undo" close or dtor will fail dup.dup2(fd); // "undo" close or dtor will fail
delete redir; delete redir;
@@ -653,7 +631,7 @@ TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
close(write_fd); close(write_fd);
EXPECT_SYSTEM_ERROR_OR_DEATH(redir.RestoreAndRead(), EXPECT_SYSTEM_ERROR_NOASSERT(redir.RestoreAndRead(),
EBADF, fmt::Format("cannot flush stream")); EBADF, fmt::Format("cannot flush stream"));
write_dup.dup2(write_fd); // "undo" close or dtor will fail write_dup.dup2(write_fd); // "undo" close or dtor will fail
} }
@@ -667,17 +645,14 @@ TEST(OutputRedirectTest, ErrorInDtor) {
OutputRedirect *redir = new OutputRedirect(f.get()); OutputRedirect *redir = new OutputRedirect(f.get());
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
#ifndef _WIN32 EXPECT_WRITE(stderr, {
// The close function must be called inside EXPECT_WRITE, otherwise // The close function must be called inside EXPECT_WRITE, otherwise
// the system may recycle closed file descriptor when redirecting the // the system may recycle closed file descriptor when redirecting the
// output in EXPECT_STDERR and the second close will break output // output in EXPECT_STDERR and the second close will break output
// redirection. // redirection.
EXPECT_WRITE(stderr, close(write_fd); delete redir, close(write_fd);
FormatSystemErrorMessage(EBADF, "cannot flush stream")); SUPPRESS_ASSERT(delete redir);
#else }, FormatSystemErrorMessage(EBADF, "cannot flush stream"));
close(write_fd);
EXPECT_DEATH(delete redir, "");
#endif
write_dup.dup2(write_fd); // "undo" close or dtor of BufferedFile will fail write_dup.dup2(write_fd); // "undo" close or dtor of BufferedFile will fail
} }