Improves thread-safe death tests by changing to the original working directory before they are executed; also fixes out-dated comments about death tests.
This commit is contained in:
@@ -546,11 +546,32 @@ struct ExecDeathTestArgs {
|
||||
};
|
||||
|
||||
// The main function for a threadsafe-style death test child process.
|
||||
// This function is called in a clone()-ed process and thus must avoid
|
||||
// any potentially unsafe operations like malloc or libc functions.
|
||||
static int ExecDeathTestChildMain(void* child_arg) {
|
||||
ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg);
|
||||
GTEST_DEATH_TEST_CHECK_SYSCALL(close(args->close_fd));
|
||||
|
||||
// We need to execute the test program in the same environment where
|
||||
// it was originally invoked. Therefore we change to the original
|
||||
// working directory first.
|
||||
const char* const original_dir =
|
||||
UnitTest::GetInstance()->original_working_dir();
|
||||
// We can safely call chdir() as it's a direct system call.
|
||||
if (chdir(original_dir) != 0) {
|
||||
DeathTestAbort("chdir(\"%s\") failed: %s",
|
||||
original_dir, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// We can safely call execve() as it's a direct system call. We
|
||||
// cannot use execvp() as it's a libc function and thus potentially
|
||||
// unsafe. Since execve() doesn't search the PATH, the user must
|
||||
// invoke the test program via a valid path that contains at least
|
||||
// one path separator.
|
||||
execve(args->argv[0], args->argv, environ);
|
||||
DeathTestAbort("execve failed: %s", strerror(errno));
|
||||
DeathTestAbort("execve(%s, ...) in %s failed: %s",
|
||||
args->argv[0], original_dir, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include <gtest/internal/gtest-filepath.h>
|
||||
#include <gtest/internal/gtest-port.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _WIN32_WCE
|
||||
#include <windows.h>
|
||||
#elif defined(_WIN32)
|
||||
@@ -40,6 +42,7 @@
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif // _WIN32_WCE or _WIN32
|
||||
|
||||
#include <gtest/internal/gtest-string.h>
|
||||
@@ -66,6 +69,21 @@ const char kPathSeparatorString[] = "/";
|
||||
const char kCurrentDirectoryString[] = "./";
|
||||
#endif // GTEST_OS_WINDOWS
|
||||
|
||||
// Returns the current working directory, or "" if unsuccessful.
|
||||
FilePath FilePath::GetCurrentDir() {
|
||||
#ifdef _WIN32_WCE
|
||||
// Windows CE doesn't have a current directory, so we just return
|
||||
// something reasonable.
|
||||
return FilePath(kCurrentDirectoryString);
|
||||
#elif defined(GTEST_OS_WINDOWS)
|
||||
char cwd[_MAX_PATH + 1] = {};
|
||||
return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
|
||||
#else
|
||||
char cwd[PATH_MAX + 1] = {};
|
||||
return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns a copy of the FilePath with the case-insensitive extension removed.
|
||||
// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
|
||||
// FilePath("dir/file"). If a case-insensitive extension is not
|
||||
|
||||
@@ -1003,6 +1003,21 @@ class UnitTestImpl : public TestPartResultReporterInterface {
|
||||
void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
|
||||
Test::TearDownTestCaseFunc tear_down_tc,
|
||||
TestInfo * test_info) {
|
||||
// In order to support thread-safe death tests, we need to
|
||||
// remember the original working directory when the test program
|
||||
// was first invoked. We cannot do this in RUN_ALL_TESTS(), as
|
||||
// the user may have changed the current directory before calling
|
||||
// RUN_ALL_TESTS(). Therefore we capture the current directory in
|
||||
// AddTestInfo(), which is called to register a TEST or TEST_F
|
||||
// before main() is reached.
|
||||
if (original_working_dir_.IsEmpty()) {
|
||||
original_working_dir_.Set(FilePath::GetCurrentDir());
|
||||
if (original_working_dir_.IsEmpty()) {
|
||||
printf("%s\n", "Failed to get the current working directory.");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
GetTestCase(test_info->test_case_name(),
|
||||
test_info->test_case_comment(),
|
||||
set_up_tc,
|
||||
@@ -1083,9 +1098,15 @@ class UnitTestImpl : public TestPartResultReporterInterface {
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
private:
|
||||
friend class ::testing::UnitTest;
|
||||
|
||||
// The UnitTest object that owns this implementation object.
|
||||
UnitTest* const parent_;
|
||||
|
||||
// The working directory when the first TEST() or TEST_F() was
|
||||
// executed.
|
||||
internal::FilePath original_working_dir_;
|
||||
|
||||
// Points to (but doesn't own) the test part result reporter.
|
||||
TestPartResultReporterInterface* test_part_result_reporter_;
|
||||
|
||||
|
||||
@@ -3211,6 +3211,12 @@ int UnitTest::Run() {
|
||||
#endif // GTEST_OS_WINDOWS
|
||||
}
|
||||
|
||||
// Returns the working directory when the first TEST() or TEST_F() was
|
||||
// executed.
|
||||
const char* UnitTest::original_working_dir() const {
|
||||
return impl_->original_working_dir_.c_str();
|
||||
}
|
||||
|
||||
// Returns the TestCase object for the test that's currently running,
|
||||
// or NULL if no test is running.
|
||||
// L < mutex_
|
||||
|
||||
Reference in New Issue
Block a user