Suppress asserts locally.
This commit is contained in:
parent
2ecce39ca9
commit
cada26d226
@ -45,38 +45,51 @@ std::string FormatSystemErrorMessage(int error_code, fmt::StringRef message) {
|
||||
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) \
|
||||
EXPECT_THROW_MSG(statement, fmt::SystemError, \
|
||||
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.
|
||||
bool IsOpen(int fd) {
|
||||
char buffer;
|
||||
return FMT_POSIX(read(fd, &buffer, 1)) == 1;
|
||||
}
|
||||
|
||||
bool IsClosedInternal(int fd) {
|
||||
bool IsClosed(int fd) {
|
||||
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;
|
||||
}
|
||||
|
||||
#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.
|
||||
class SingleEvaluationTest : public ::testing::Test {
|
||||
protected:
|
||||
@ -234,8 +247,6 @@ TEST(ExpectTest, EXPECT_WRITE) {
|
||||
" Actual: that");
|
||||
}
|
||||
|
||||
// TODO: test EXPECT_WRITE
|
||||
|
||||
TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
|
||||
EXPECT_THROW_MSG(ThrowException(), std::exception, "test")
|
||||
<< "unexpected failure";
|
||||
@ -244,6 +255,14 @@ TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
|
||||
<< "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
|
||||
|
||||
TEST(ErrorCodeTest, Ctor) {
|
||||
@ -251,33 +270,6 @@ TEST(ErrorCodeTest, Ctor) {
|
||||
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 f = File(".travis.yml", File::RDONLY).fdopen("r");
|
||||
if (fp)
|
||||
@ -285,12 +277,12 @@ BufferedFile OpenFile(const char *name, FILE **fp = 0) {
|
||||
return f;
|
||||
}
|
||||
|
||||
TEST_F(BufferedFileTest, DefaultCtor) {
|
||||
TEST(BufferedFileTest, DefaultCtor) {
|
||||
BufferedFile f;
|
||||
EXPECT_TRUE(f.get() == 0);
|
||||
}
|
||||
|
||||
TEST_F(BufferedFileTest, MoveCtor) {
|
||||
TEST(BufferedFileTest, MoveCtor) {
|
||||
BufferedFile bf = OpenFile(".travis.yml");
|
||||
FILE *fp = bf.get();
|
||||
EXPECT_TRUE(fp != 0);
|
||||
@ -299,7 +291,7 @@ TEST_F(BufferedFileTest, MoveCtor) {
|
||||
EXPECT_TRUE(bf.get() == 0);
|
||||
}
|
||||
|
||||
TEST_F(BufferedFileTest, MoveAssignment) {
|
||||
TEST(BufferedFileTest, MoveAssignment) {
|
||||
BufferedFile bf = OpenFile(".travis.yml");
|
||||
FILE *fp = bf.get();
|
||||
EXPECT_TRUE(fp != 0);
|
||||
@ -309,51 +301,53 @@ TEST_F(BufferedFileTest, MoveAssignment) {
|
||||
EXPECT_TRUE(bf.get() == 0);
|
||||
}
|
||||
|
||||
TEST_F(BufferedFileTest, MoveAssignmentClosesFile) {
|
||||
TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
||||
BufferedFile bf = OpenFile(".travis.yml");
|
||||
BufferedFile bf2 = OpenFile("CMakeLists.txt");
|
||||
int old_fd = fileno(bf2.get());
|
||||
bf2 = std::move(bf);
|
||||
EXPECT_TRUE(IsClosedInternal(old_fd));
|
||||
EXPECT_TRUE(IsClosed(old_fd));
|
||||
}
|
||||
|
||||
TEST_F(BufferedFileTest, MoveFromTemporaryInCtor) {
|
||||
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
|
||||
FILE *fp = 0;
|
||||
BufferedFile f(OpenFile(".travis.yml", &fp));
|
||||
EXPECT_EQ(fp, f.get());
|
||||
}
|
||||
|
||||
TEST_F(BufferedFileTest, MoveFromTemporaryInAssignment) {
|
||||
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
|
||||
FILE *fp = 0;
|
||||
BufferedFile f;
|
||||
f = OpenFile(".travis.yml", &fp);
|
||||
EXPECT_EQ(fp, f.get());
|
||||
}
|
||||
|
||||
TEST_F(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
||||
TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
||||
BufferedFile f = OpenFile(".travis.yml");
|
||||
int old_fd = fileno(f.get());
|
||||
f = OpenFile(".travis.yml");
|
||||
EXPECT_TRUE(IsClosedInternal(old_fd));
|
||||
EXPECT_TRUE(IsClosed(old_fd));
|
||||
}
|
||||
|
||||
TEST_F(BufferedFileTest, CloseFileInDtor) {
|
||||
TEST(BufferedFileTest, CloseFileInDtor) {
|
||||
int fd = 0;
|
||||
{
|
||||
BufferedFile f = OpenFile(".travis.yml");
|
||||
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"));
|
||||
// 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.
|
||||
EXPECT_WRITE(stderr, close(fileno(f->get())); delete f,
|
||||
FormatSystemErrorMessage(EBADF, "cannot close file") + "\n");
|
||||
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.
|
||||
close(fileno(f->get()));
|
||||
SUPPRESS_ASSERT(delete f);
|
||||
}, FormatSystemErrorMessage(EBADF, "cannot close file") + "\n");
|
||||
}
|
||||
|
||||
TEST(FileTest, DefaultCtor) {
|
||||
@ -395,7 +389,7 @@ TEST(FileTest, MoveAssignmentClosesFile) {
|
||||
File f2("CMakeLists.txt", File::RDONLY);
|
||||
int old_fd = f2.descriptor();
|
||||
f2 = std::move(f);
|
||||
EXPECT_CLOSED(old_fd);
|
||||
EXPECT_TRUE(IsClosed(old_fd));
|
||||
}
|
||||
|
||||
File OpenFile(int &fd) {
|
||||
@ -422,7 +416,7 @@ TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
||||
File f(".travis.yml", File::RDONLY);
|
||||
int old_fd = f.descriptor();
|
||||
f = OpenFile(fd);
|
||||
EXPECT_CLOSED(old_fd);
|
||||
EXPECT_TRUE(IsClosed(old_fd));
|
||||
}
|
||||
|
||||
TEST(FileTest, CloseFileInDtor) {
|
||||
@ -431,23 +425,19 @@ TEST(FileTest, CloseFileInDtor) {
|
||||
File f(".travis.yml", File::RDONLY);
|
||||
fd = f.descriptor();
|
||||
}
|
||||
EXPECT_CLOSED(fd);
|
||||
EXPECT_TRUE(IsClosed(fd));
|
||||
}
|
||||
|
||||
TEST(FileTest, CloseErrorInDtor) {
|
||||
File *f = new File(".travis.yml", File::RDONLY);
|
||||
#ifndef _WIN32
|
||||
// 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.
|
||||
EXPECT_WRITE(stderr, FMT_POSIX(close(f->descriptor())); delete f,
|
||||
FormatSystemErrorMessage(EBADF, "cannot close file") + "\n");
|
||||
#else
|
||||
close(f->descriptor());
|
||||
// Closing file twice causes death on Windows.
|
||||
EXPECT_DEATH(delete f, "");
|
||||
#endif
|
||||
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);
|
||||
}, FormatSystemErrorMessage(EBADF, "cannot close file") + "\n");
|
||||
}
|
||||
|
||||
TEST(FileTest, Close) {
|
||||
@ -455,22 +445,14 @@ TEST(FileTest, Close) {
|
||||
int fd = f.descriptor();
|
||||
f.close();
|
||||
EXPECT_EQ(-1, f.descriptor());
|
||||
EXPECT_CLOSED(fd);
|
||||
EXPECT_TRUE(IsClosed(fd));
|
||||
}
|
||||
|
||||
TEST(FileTest, CloseError) {
|
||||
File f(".travis.yml", File::RDONLY);
|
||||
#ifndef _WIN32
|
||||
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());
|
||||
#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.
|
||||
@ -536,7 +518,7 @@ TEST(FileTest, Dup) {
|
||||
}
|
||||
|
||||
TEST(FileTest, DupError) {
|
||||
EXPECT_SYSTEM_ERROR_OR_DEATH(File::dup(-1),
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(File::dup(-1),
|
||||
EBADF, "cannot duplicate file descriptor -1");
|
||||
}
|
||||
|
||||
@ -550,7 +532,7 @@ TEST(FileTest, Dup2) {
|
||||
|
||||
TEST(FileTest, Dup2Error) {
|
||||
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());
|
||||
}
|
||||
|
||||
@ -567,12 +549,8 @@ TEST(FileTest, Dup2NoExcept) {
|
||||
TEST(FileTest, Dup2NoExceptError) {
|
||||
File f(".travis.yml", File::RDONLY);
|
||||
ErrorCode ec;
|
||||
#ifndef _WIN32
|
||||
f.dup2(-1, ec);
|
||||
SUPPRESS_ASSERT(f.dup2(-1, ec));
|
||||
EXPECT_EQ(EBADF, ec.get());
|
||||
#else
|
||||
EXPECT_DEATH(f.dup2(-1, ec), "");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(FileTest, Pipe) {
|
||||
@ -610,7 +588,7 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
|
||||
EXPECT_EQ('x', fputc('x', f.get()));
|
||||
close(write_fd);
|
||||
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"));
|
||||
delete redir;
|
||||
write_dup.dup2(write_fd); // "undo" close or dtor will fail
|
||||
@ -622,7 +600,7 @@ TEST(OutputRedirectTest, DupErrorInCtor) {
|
||||
File dup = File::dup(fd);
|
||||
close(fd);
|
||||
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);
|
||||
dup.dup2(fd); // "undo" close or dtor will fail
|
||||
delete redir;
|
||||
@ -653,7 +631,7 @@ TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
||||
// Put a character in a file buffer.
|
||||
EXPECT_EQ('x', fputc('x', f.get()));
|
||||
close(write_fd);
|
||||
EXPECT_SYSTEM_ERROR_OR_DEATH(redir.RestoreAndRead(),
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.RestoreAndRead(),
|
||||
EBADF, fmt::Format("cannot flush stream"));
|
||||
write_dup.dup2(write_fd); // "undo" close or dtor will fail
|
||||
}
|
||||
@ -667,17 +645,14 @@ TEST(OutputRedirectTest, ErrorInDtor) {
|
||||
OutputRedirect *redir = new OutputRedirect(f.get());
|
||||
// Put a character in a file buffer.
|
||||
EXPECT_EQ('x', fputc('x', f.get()));
|
||||
#ifndef _WIN32
|
||||
// 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.
|
||||
EXPECT_WRITE(stderr, close(write_fd); delete redir,
|
||||
FormatSystemErrorMessage(EBADF, "cannot flush stream"));
|
||||
#else
|
||||
close(write_fd);
|
||||
EXPECT_DEATH(delete redir, "");
|
||||
#endif
|
||||
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.
|
||||
close(write_fd);
|
||||
SUPPRESS_ASSERT(delete redir);
|
||||
}, FormatSystemErrorMessage(EBADF, "cannot flush stream"));
|
||||
write_dup.dup2(write_fd); // "undo" close or dtor of BufferedFile will fail
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user