diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gtest-death-test.cc | 23 | ||||
-rw-r--r-- | src/gtest-filepath.cc | 18 | ||||
-rw-r--r-- | src/gtest-internal-inl.h | 21 | ||||
-rw-r--r-- | src/gtest.cc | 6 |
4 files changed, 67 insertions, 1 deletions
diff --git a/src/gtest-death-test.cc b/src/gtest-death-test.cc index cb0d3cd7..971c3005 100644 --- a/src/gtest-death-test.cc +++ b/src/gtest-death-test.cc @@ -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; } diff --git a/src/gtest-filepath.cc b/src/gtest-filepath.cc index 3c32c705..2a5be8ce 100644 --- a/src/gtest-filepath.cc +++ b/src/gtest-filepath.cc @@ -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 diff --git a/src/gtest-internal-inl.h b/src/gtest-internal-inl.h index 6aafc7bf..d4889483 100644 --- a/src/gtest-internal-inl.h +++ b/src/gtest-internal-inl.h @@ -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_; diff --git a/src/gtest.cc b/src/gtest.cc index 09f6bae4..8ca6ac8e 100644 --- a/src/gtest.cc +++ b/src/gtest.cc @@ -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_ |