gtest-death-test.cc [plain text]
#include <gtest/gtest-death-test.h>
#include <gtest/internal/gtest-port.h>
#ifdef GTEST_HAS_DEATH_TEST
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#endif // GTEST_HAS_DEATH_TEST
#include <gtest/gtest-message.h>
#include <gtest/internal/gtest-string.h>
#define GTEST_IMPLEMENTATION
#include "gtest/internal/gtest-internal-inl.h"
#undef GTEST_IMPLEMENTATION
namespace testing {
static const char kDefaultDeathTestStyle[] = "fast";
GTEST_DEFINE_string_(
death_test_style,
internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle),
"Indicates how to run a death test in a forked child process: "
"\"threadsafe\" (child process re-executes the test binary "
"from the beginning, running only the specific death test) or "
"\"fast\" (child process runs the death test immediately "
"after forking).");
namespace internal {
GTEST_DEFINE_string_(
internal_run_death_test, "",
"Indicates the file, line number, temporal index of "
"the single death test to run, and a file descriptor to "
"which a success code may be sent, all separated by "
"colons. This flag is specified if and only if the current "
"process is a sub-process launched for running a thread-safe "
"death test. FOR INTERNAL USE ONLY.");
}
#ifdef GTEST_HAS_DEATH_TEST
ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) {
}
bool ExitedWithCode::operator()(int exit_status) const {
return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_;
}
KilledBySignal::KilledBySignal(int signum) : signum_(signum) {
}
bool KilledBySignal::operator()(int exit_status) const {
return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_;
}
namespace internal {
static String ExitSummary(int exit_code) {
Message m;
if (WIFEXITED(exit_code)) {
m << "Exited with exit status " << WEXITSTATUS(exit_code);
} else if (WIFSIGNALED(exit_code)) {
m << "Terminated by signal " << WTERMSIG(exit_code);
}
#ifdef WCOREDUMP
if (WCOREDUMP(exit_code)) {
m << " (core dumped)";
}
#endif
return m.GetString();
}
bool ExitedUnsuccessfully(int exit_status) {
return !ExitedWithCode(0)(exit_status);
}
static String DeathTestThreadWarning(size_t thread_count) {
Message msg;
msg << "Death tests use fork(), which is unsafe particularly"
<< " in a threaded context. For this test, " << GTEST_NAME << " ";
if (thread_count == 0)
msg << "couldn't detect the number of threads.";
else
msg << "detected " << thread_count << " threads.";
return msg.GetString();
}
static String last_death_test_message;
static const char kDeathTestLived = 'L';
static const char kDeathTestReturned = 'R';
static const char kDeathTestInternalError = 'I';
enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED };
void DeathTestAbort(const char* format, ...) {
const InternalRunDeathTestFlag* const flag =
GetUnitTestImpl()->internal_run_death_test_flag();
va_list args;
va_start(args, format);
if (flag != NULL) {
FILE* parent = fdopen(flag->status_fd, "w");
fputc(kDeathTestInternalError, parent);
vfprintf(parent, format, args);
fclose(parent);
va_end(args);
_exit(1);
} else {
vfprintf(stderr, format, args);
va_end(args);
abort();
}
}
#define GTEST_DEATH_TEST_CHECK_(expression) \
do { \
if (!(expression)) { \
DeathTestAbort("CHECK failed: File %s, line %d: %s", \
__FILE__, __LINE__, #expression); \
} \
} while (0)
#define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \
do { \
int retval; \
do { \
retval = (expression); \
} while (retval == -1 && errno == EINTR); \
if (retval == -1) { \
DeathTestAbort("CHECK failed: File %s, line %d: %s != -1", \
__FILE__, __LINE__, #expression); \
} \
} while (0)
DeathTest::DeathTest() {
TestInfo* const info = GetUnitTestImpl()->current_test_info();
if (info == NULL) {
DeathTestAbort("Cannot run a death test outside of a TEST or "
"TEST_F construct");
}
}
bool DeathTest::Create(const char* statement, const RE* regex,
const char* file, int line, DeathTest** test) {
return GetUnitTestImpl()->death_test_factory()->Create(
statement, regex, file, line, test);
}
const char* DeathTest::LastMessage() {
return last_death_test_message.c_str();
}
class ForkingDeathTest : public DeathTest {
public:
ForkingDeathTest(const char* statement, const RE* regex);
virtual int Wait();
virtual bool Passed(bool status_ok);
virtual void Abort(AbortReason reason);
protected:
void set_forked(bool forked) { forked_ = forked; }
void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; }
void set_read_fd(int fd) { read_fd_ = fd; }
void set_write_fd(int fd) { write_fd_ = fd; }
private:
const char* const statement_;
const RE* const regex_;
bool forked_;
pid_t child_pid_;
int read_fd_; int write_fd_; int status_;
DeathTestOutcome outcome_;
};
ForkingDeathTest::ForkingDeathTest(const char* statement, const RE* regex)
: DeathTest(),
statement_(statement),
regex_(regex),
forked_(false),
child_pid_(-1),
read_fd_(-1),
write_fd_(-1),
status_(-1),
outcome_(IN_PROGRESS) {
}
static void FailFromInternalError(int fd) {
Message error;
char buffer[256];
ssize_t num_read;
do {
while ((num_read = read(fd, buffer, 255)) > 0) {
buffer[num_read] = '\0';
error << buffer;
}
} while (num_read == -1 && errno == EINTR);
if (num_read == 0) {
GTEST_LOG_(FATAL, error);
} else {
GTEST_LOG_(FATAL,
Message() << "Error while reading death test internal: "
<< strerror(errno) << " [" << errno << "]");
}
}
int ForkingDeathTest::Wait() {
if (!forked_)
return 0;
char flag;
ssize_t bytes_read;
do {
bytes_read = read(read_fd_, &flag, 1);
} while (bytes_read == -1 && errno == EINTR);
if (bytes_read == 0) {
outcome_ = DIED;
} else if (bytes_read == 1) {
switch (flag) {
case kDeathTestReturned:
outcome_ = RETURNED;
break;
case kDeathTestLived:
outcome_ = LIVED;
break;
case kDeathTestInternalError:
FailFromInternalError(read_fd_); break;
default:
GTEST_LOG_(FATAL,
Message() << "Death test child process reported unexpected "
<< "status byte (" << static_cast<unsigned int>(flag)
<< ")");
}
} else {
GTEST_LOG_(FATAL,
Message() << "Read from death test child process failed: "
<< strerror(errno));
}
GTEST_DEATH_TEST_CHECK_SYSCALL_(close(read_fd_));
GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_, 0));
return status_;
}
bool ForkingDeathTest::Passed(bool status_ok) {
if (!forked_)
return false;
#if GTEST_HAS_GLOBAL_STRING
const ::string error_message = GetCapturedStderr();
#else
const ::std::string error_message = GetCapturedStderr();
#endif // GTEST_HAS_GLOBAL_STRING
bool success = false;
Message buffer;
buffer << "Death test: " << statement_ << "\n";
switch (outcome_) {
case LIVED:
buffer << " Result: failed to die.\n"
<< " Error msg: " << error_message;
break;
case RETURNED:
buffer << " Result: illegal return in test statement.\n"
<< " Error msg: " << error_message;
break;
case DIED:
if (status_ok) {
if (RE::PartialMatch(error_message, *regex_)) {
success = true;
} else {
buffer << " Result: died but not with expected error.\n"
<< " Expected: " << regex_->pattern() << "\n"
<< "Actual msg: " << error_message;
}
} else {
buffer << " Result: died but not with expected exit code:\n"
<< " " << ExitSummary(status_) << "\n";
}
break;
case IN_PROGRESS:
default:
GTEST_LOG_(FATAL,
"DeathTest::Passed somehow called before conclusion of test");
}
last_death_test_message = buffer.GetString();
return success;
}
void ForkingDeathTest::Abort(AbortReason reason) {
const char flag =
reason == TEST_DID_NOT_DIE ? kDeathTestLived : kDeathTestReturned;
GTEST_DEATH_TEST_CHECK_SYSCALL_(write(write_fd_, &flag, 1));
GTEST_DEATH_TEST_CHECK_SYSCALL_(close(write_fd_));
_exit(1); }
class NoExecDeathTest : public ForkingDeathTest {
public:
NoExecDeathTest(const char* statement, const RE* regex) :
ForkingDeathTest(statement, regex) { }
virtual TestRole AssumeRole();
};
DeathTest::TestRole NoExecDeathTest::AssumeRole() {
const size_t thread_count = GetThreadCount();
if (thread_count != 1) {
GTEST_LOG_(WARNING, DeathTestThreadWarning(thread_count));
}
int pipe_fd[2];
GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1);
last_death_test_message = "";
CaptureStderr();
FlushInfoLog();
const pid_t child_pid = fork();
GTEST_DEATH_TEST_CHECK_(child_pid != -1);
set_child_pid(child_pid);
if (child_pid == 0) {
GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0]));
set_write_fd(pipe_fd[1]);
LogToStderr();
return EXECUTE_TEST;
} else {
GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1]));
set_read_fd(pipe_fd[0]);
set_forked(true);
return OVERSEE_TEST;
}
}
class ExecDeathTest : public ForkingDeathTest {
public:
ExecDeathTest(const char* statement, const RE* regex,
const char* file, int line) :
ForkingDeathTest(statement, regex), file_(file), line_(line) { }
virtual TestRole AssumeRole();
private:
const char* const file_;
const int line_;
};
class Arguments {
public:
Arguments() {
args_.push_back(NULL);
}
~Arguments() {
for (std::vector<char*>::iterator i = args_.begin();
i + 1 != args_.end();
++i) {
free(*i);
}
}
void AddArgument(const char* argument) {
args_.insert(args_.end() - 1, strdup(argument));
}
template <typename Str>
void AddArguments(const ::std::vector<Str>& arguments) {
for (typename ::std::vector<Str>::const_iterator i = arguments.begin();
i != arguments.end();
++i) {
args_.insert(args_.end() - 1, strdup(i->c_str()));
}
}
char* const* Argv() {
return &args_[0];
}
private:
std::vector<char*> args_;
};
struct ExecDeathTestArgs {
char* const* argv; int close_fd; };
static int ExecDeathTestChildMain(void* child_arg) {
ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg);
GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd));
const char* const original_dir =
UnitTest::GetInstance()->original_working_dir();
if (chdir(original_dir) != 0) {
DeathTestAbort("chdir(\"%s\") failed: %s",
original_dir, strerror(errno));
return EXIT_FAILURE;
}
execve(args->argv[0], args->argv, environ);
DeathTestAbort("execve(%s, ...) in %s failed: %s",
args->argv[0], original_dir, strerror(errno));
return EXIT_FAILURE;
}
static bool StackLowerThanAddress(const void* ptr) {
int dummy;
return &dummy < ptr;
}
static bool StackGrowsDown() {
int dummy;
return StackLowerThanAddress(&dummy);
}
static pid_t ExecDeathTestFork(char* const* argv, int close_fd) {
static const bool stack_grows_down = StackGrowsDown();
const size_t stack_size = getpagesize();
void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED);
void* const stack_top =
static_cast<char*>(stack) + (stack_grows_down ? stack_size : 0);
ExecDeathTestArgs args = { argv, close_fd };
const pid_t child_pid = clone(&ExecDeathTestChildMain, stack_top,
SIGCHLD, &args);
GTEST_DEATH_TEST_CHECK_(child_pid != -1);
GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1);
return child_pid;
}
DeathTest::TestRole ExecDeathTest::AssumeRole() {
const UnitTestImpl* const impl = GetUnitTestImpl();
const InternalRunDeathTestFlag* const flag =
impl->internal_run_death_test_flag();
const TestInfo* const info = impl->current_test_info();
const int death_test_index = info->result()->death_test_count();
if (flag != NULL) {
set_write_fd(flag->status_fd);
return EXECUTE_TEST;
}
int pipe_fd[2];
GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1);
GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1);
const String filter_flag =
String::Format("--%s%s=%s.%s",
GTEST_FLAG_PREFIX, kFilterFlag,
info->test_case_name(), info->name());
const String internal_flag =
String::Format("--%s%s=%s:%d:%d:%d",
GTEST_FLAG_PREFIX, kInternalRunDeathTestFlag, file_, line_,
death_test_index, pipe_fd[1]);
Arguments args;
args.AddArguments(GetArgvs());
args.AddArgument("--logtostderr");
args.AddArgument(filter_flag.c_str());
args.AddArgument(internal_flag.c_str());
last_death_test_message = "";
CaptureStderr();
FlushInfoLog();
const pid_t child_pid = ExecDeathTestFork(args.Argv(), pipe_fd[0]);
GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1]));
set_child_pid(child_pid);
set_read_fd(pipe_fd[0]);
set_forked(true);
return OVERSEE_TEST;
}
bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
const char* file, int line,
DeathTest** test) {
UnitTestImpl* const impl = GetUnitTestImpl();
const InternalRunDeathTestFlag* const flag =
impl->internal_run_death_test_flag();
const int death_test_index = impl->current_test_info()
->increment_death_test_count();
if (flag != NULL) {
if (death_test_index > flag->index) {
last_death_test_message = String::Format(
"Death test count (%d) somehow exceeded expected maximum (%d)",
death_test_index, flag->index);
return false;
}
if (!(flag->file == file && flag->line == line &&
flag->index == death_test_index)) {
*test = NULL;
return true;
}
}
if (GTEST_FLAG(death_test_style) == "threadsafe") {
*test = new ExecDeathTest(statement, regex, file, line);
} else if (GTEST_FLAG(death_test_style) == "fast") {
*test = new NoExecDeathTest(statement, regex);
} else {
last_death_test_message = String::Format(
"Unknown death test style \"%s\" encountered",
GTEST_FLAG(death_test_style).c_str());
return false;
}
return true;
}
static void SplitString(const ::std::string& str, char delimiter,
::std::vector< ::std::string>* dest) {
::std::vector< ::std::string> parsed;
::std::string::size_type pos = 0;
while (true) {
const ::std::string::size_type colon = str.find(delimiter, pos);
if (colon == ::std::string::npos) {
parsed.push_back(str.substr(pos));
break;
} else {
parsed.push_back(str.substr(pos, colon - pos));
pos = colon + 1;
}
}
dest->swap(parsed);
}
static bool ParsePositiveInt(const ::std::string& str, int* number) {
if (str.empty() || !isdigit(str[0])) {
return false;
}
char* endptr;
const long parsed = strtol(str.c_str(), &endptr, 10); if (*endptr == '\0' && parsed <= INT_MAX) {
*number = static_cast<int>(parsed);
return true;
} else {
return false;
}
}
InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
if (GTEST_FLAG(internal_run_death_test) == "") return NULL;
InternalRunDeathTestFlag* const internal_run_death_test_flag =
new InternalRunDeathTestFlag;
::std::vector< ::std::string> fields;
SplitString(GTEST_FLAG(internal_run_death_test).c_str(), ':', &fields);
if (fields.size() != 4
|| !ParsePositiveInt(fields[1], &internal_run_death_test_flag->line)
|| !ParsePositiveInt(fields[2], &internal_run_death_test_flag->index)
|| !ParsePositiveInt(fields[3],
&internal_run_death_test_flag->status_fd)) {
DeathTestAbort("Bad --gtest_internal_run_death_test flag: %s",
GTEST_FLAG(internal_run_death_test).c_str());
}
internal_run_death_test_flag->file = fields[0].c_str();
return internal_run_death_test_flag;
}
}
#endif // GTEST_HAS_DEATH_TEST
}