]> git-server-git.apps.pok.os.sepia.ceph.com Git - googletest.git/commitdiff
Improves thread-safe death tests by changing to the original working directory before...
authorshiqian <shiqian@861a406c-534a-0410-8894-cb66d6ee9925>
Fri, 12 Sep 2008 04:01:37 +0000 (04:01 +0000)
committershiqian <shiqian@861a406c-534a-0410-8894-cb66d6ee9925>
Fri, 12 Sep 2008 04:01:37 +0000 (04:01 +0000)
include/gtest/gtest-death-test.h
include/gtest/gtest.h
include/gtest/internal/gtest-filepath.h
src/gtest-death-test.cc
src/gtest-filepath.cc
src/gtest-internal-inl.h
src/gtest.cc
test/gtest-death-test_test.cc
test/gtest-filepath_test.cc
test/gtest_unittest.cc

index cbd41fe6daeb038fb7c0995c532694b72a35ca5a..6fae47fbd9cc25eb50578086a6036616204e57b3 100644 (file)
@@ -56,29 +56,19 @@ GTEST_DECLARE_string(death_test_style);
 // Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is
 // executed:
 //
-//   1. The assertion fails immediately if there are more than one
-//   active threads.  This is because it's safe to fork() only when
-//   there is a single thread.
+//   1. It generates a warning if there is more than one active
+//   thread.  This is because it's safe to fork() or clone() only
+//   when there is a single thread.
 //
-//   2. The parent process forks a sub-process and runs the death test
-//   in it; the sub-process exits with code 0 at the end of the death
-//   test, if it hasn't exited already.
+//   2. The parent process clone()s a sub-process and runs the death
+//   test in it; the sub-process exits with code 0 at the end of the
+//   death test, if it hasn't exited already.
 //
 //   3. The parent process waits for the sub-process to terminate.
 //
 //   4. The parent process checks the exit code and error message of
 //   the sub-process.
 //
-// Note:
-//
-// It's not safe to call exit() if the current process is forked from
-// a multi-threaded process, so people usually call _exit() instead in
-// such a case.  However, we are not concerned with this as we run
-// death tests only when there is a single thread.  Since exit() has a
-// cleaner semantics (it also calls functions registered with atexit()
-// and on_exit()), this macro calls exit() instead of _exit() to
-// terminate the child process.
-//
 // Examples:
 //
 //   ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number");
@@ -95,6 +85,20 @@ GTEST_DECLARE_string(death_test_style);
 //   }
 //
 //   ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
+//
+// Known caveats:
+//
+//   A "threadsafe" style death test obtains the path to the test
+//   program from argv[0] and re-executes it in the sub-process.  For
+//   simplicity, the current implementation doesn't search the PATH
+//   when launching the sub-process.  This means that the user must
+//   invoke the test program via a path that contains at least one
+//   path separator (e.g. path/to/foo_test and
+//   /absolute/path/to/bar_test are fine, but foo_test is not).  This
+//   is rarely a problem as people usually don't put the test binary
+//   directory in PATH.
+//
+// TODO(wan@google.com): make thread-safe death tests search the PATH.
 
 // Asserts that a given statement causes the program to exit, with an
 // integer exit status that satisfies predicate, and emitting error output
index 37ea6b04110e949d5c55bc82b202a9b36fbb3577..057efa26e36eda506cbe5dd5df8c9176b860ae1a 100644 (file)
@@ -479,6 +479,10 @@ class UnitTest {
   // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
   int Run() GTEST_MUST_USE_RESULT;
 
+  // Returns the working directory when the first TEST() or TEST_F()
+  // was executed.  The UnitTest object owns the string.
+  const char* original_working_dir() const;
+
   // Returns the TestCase object for the test that's currently running,
   // or NULL if no test is running.
   const TestCase* current_test_case() const;
index 308a2c64c3725b2ed5b713d79a3ecbff8af84ae3..fecdddcc78d5c5c392a8e846c30c45badc270b19 100644 (file)
@@ -70,6 +70,9 @@ class FilePath {
   String ToString() const { return pathname_; }
   const char* c_str() const { return pathname_.c_str(); }
 
+  // Returns the current working directory, or "" if unsuccessful.
+  static FilePath GetCurrentDir();
+
   // Given directory = "dir", base_name = "test", number = 0,
   // extension = "xml", returns "dir/test.xml". If number is greater
   // than zero (e.g., 12), returns "dir/test_12.xml".
@@ -91,6 +94,9 @@ class FilePath {
                                          const FilePath& base_name,
                                          const char* extension);
 
+  // Returns true iff the path is NULL or "".
+  bool IsEmpty() const { return c_str() == NULL || *c_str() == '\0'; }
+
   // If input name has a trailing separator character, removes it and returns
   // the name, otherwise return the name string unmodified.
   // On Windows platform, uses \ as the separator, other platforms use /.
index cb0d3cd73b79843ea88280f9ce135bfce7ea57a3..971c3005bb949e404bf40c0d4a95c3738623d142 100644 (file)
@@ -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;
 }
 
index 3c32c705becbbef00672e9b42bc016c08420b29c..2a5be8ce2af17c45ccd6f3f1a8d28bf5c6ad7663 100644 (file)
@@ -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
index 6aafc7bff737e741037ba451739ff5d06165bdc1..d488948328f243bfbc6501948ead8d300dcee0cd 100644 (file)
@@ -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_;
 
index 09f6bae40c3d784c3be62fdf4cbaa1235d5d74d3..8ca6ac8e1d2ea6786285545a0098d80e73a174c9 100644 (file)
@@ -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_
index 4bfd9d63e1303b91c3e0c369c2c97e71379871da..9d69b2cd17b607c1ea60c589068f7bf8be1f5ea3 100644 (file)
@@ -36,6 +36,8 @@
 
 #ifdef GTEST_HAS_DEATH_TEST
 
+#include <stdio.h>
+#include <unistd.h>
 #include <gtest/gtest-spi.h>
 
 // Indicates that this translation unit is part of Google Test's
@@ -85,13 +87,19 @@ class TestForDeathTest : public testing::Test {
  protected:
   // A static member function that's expected to die.
   static void StaticMemberFunction() {
-    GTEST_LOG(FATAL, "death inside StaticMemberFunction().");
+    fprintf(stderr, "%s", "death inside StaticMemberFunction().");
+    // We call _exit() instead of exit(), as the former is a direct
+    // system call and thus safer in the presence of threads.  exit()
+    // will invoke user-defined exit-hooks, which may do dangerous
+    // things that conflict with death tests.
+    _exit(1);
   }
 
   // A method of the test fixture that may die.
   void MemberFunction() {
     if (should_die_) {
-      GTEST_LOG(FATAL, "death inside MemberFunction().");
+      fprintf(stderr, "%s", "death inside MemberFunction().");
+      _exit(1);
     }
   }
 
@@ -157,13 +165,13 @@ int DieInDebugElse12(int* sideeffect) {
   return 12;
 }
 
-// Returns the exit status of a process that calls exit(2) with a
+// Returns the exit status of a process that calls _exit(2) with a
 // given exit code.  This is a helper function for the
 // ExitStatusPredicateTest test suite.
 static int NormalExitStatus(int exit_code) {
   pid_t child_pid = fork();
   if (child_pid == 0) {
-    exit(exit_code);
+    _exit(exit_code);
   }
   int status;
   waitpid(child_pid, &status, 0);
@@ -179,7 +187,7 @@ static int KilledExitStatus(int signum) {
   pid_t child_pid = fork();
   if (child_pid == 0) {
     raise(signum);
-    exit(1);
+    _exit(1);
   }
   int status;
   waitpid(child_pid, &status, 0);
@@ -223,7 +231,7 @@ TEST_F(TestForDeathTest, SingleStatement) {
     ASSERT_DEATH(return, "");
 
   if (true)
-    EXPECT_DEATH(exit(1), "");
+    EXPECT_DEATH(_exit(1), "");
   else
     // This empty "else" branch is meant to ensure that EXPECT_DEATH
     // doesn't expand into an "if" statement without an "else"
@@ -235,12 +243,12 @@ TEST_F(TestForDeathTest, SingleStatement) {
   if (false)
     ;
   else
-    EXPECT_DEATH(exit(1), "") << 1 << 2 << 3;
+    EXPECT_DEATH(_exit(1), "") << 1 << 2 << 3;
 }
 
 void DieWithEmbeddedNul() {
   fprintf(stderr, "Hello%cworld.\n", '\0');
-  abort();
+  _exit(1);
 }
 
 // Tests that EXPECT_DEATH and ASSERT_DEATH work when the error
@@ -257,24 +265,40 @@ TEST_F(TestForDeathTest, DISABLED_EmbeddedNulInMessage) {
 TEST_F(TestForDeathTest, SwitchStatement) {
   switch (0)
     default:
-      ASSERT_DEATH(exit(1), "") << "exit in default switch handler";
+      ASSERT_DEATH(_exit(1), "") << "exit in default switch handler";
 
   switch (0)
     case 0:
-      EXPECT_DEATH(exit(1), "") << "exit in switch case";
+      EXPECT_DEATH(_exit(1), "") << "exit in switch case";
 }
 
-// Tests that a static member function can be used in a death test.
-TEST_F(TestForDeathTest, StaticMemberFunction) {
+// Tests that a static member function can be used in a "fast" style
+// death test.
+TEST_F(TestForDeathTest, StaticMemberFunctionFastStyle) {
+  testing::GTEST_FLAG(death_test_style) = "fast";
   ASSERT_DEATH(StaticMemberFunction(), "death.*StaticMember");
 }
 
-// Tests that a method of the test fixture can be used in a death test.
-TEST_F(TestForDeathTest, MemberFunction) {
+// Tests that a method of the test fixture can be used in a "fast"
+// style death test.
+TEST_F(TestForDeathTest, MemberFunctionFastStyle) {
+  testing::GTEST_FLAG(death_test_style) = "fast";
   should_die_ = true;
   EXPECT_DEATH(MemberFunction(), "inside.*MemberFunction");
 }
 
+// Tests that death tests work even if the current directory has been
+// changed.
+TEST_F(TestForDeathTest, FastDeathTestInChangedDir) {
+  testing::GTEST_FLAG(death_test_style) = "fast";
+
+  chdir("/");
+  EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), "");
+
+  chdir("/");
+  ASSERT_DEATH(_exit(1), "");
+}
+
 // Repeats a representative sample of death tests in the "threadsafe" style:
 
 TEST_F(TestForDeathTest, StaticMemberFunctionThreadsafeStyle) {
@@ -292,14 +316,24 @@ TEST_F(TestForDeathTest, ThreadsafeDeathTestInLoop) {
   testing::GTEST_FLAG(death_test_style) = "threadsafe";
 
   for (int i = 0; i < 3; ++i)
-    EXPECT_EXIT(exit(i), testing::ExitedWithCode(i), "") << ": i = " << i;
+    EXPECT_EXIT(_exit(i), testing::ExitedWithCode(i), "") << ": i = " << i;
+}
+
+TEST_F(TestForDeathTest, ThreadsafeDeathTestInChangedDir) {
+  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+
+  chdir("/");
+  EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), "");
+
+  chdir("/");
+  ASSERT_DEATH(_exit(1), "");
 }
 
 TEST_F(TestForDeathTest, MixedStyles) {
   testing::GTEST_FLAG(death_test_style) = "threadsafe";
-  EXPECT_DEATH(exit(1), "");
+  EXPECT_DEATH(_exit(1), "");
   testing::GTEST_FLAG(death_test_style) = "fast";
-  EXPECT_DEATH(exit(1), "");
+  EXPECT_DEATH(_exit(1), "");
 }
 
 namespace {
@@ -316,7 +350,7 @@ TEST_F(TestForDeathTest, DoesNotExecuteAtforkHooks) {
   testing::GTEST_FLAG(death_test_style) = "threadsafe";
   pthread_flag = false;
   ASSERT_EQ(0, pthread_atfork(&SetPthreadFlag, NULL, NULL));
-  ASSERT_DEATH(exit(1), "");
+  ASSERT_DEATH(_exit(1), "");
   ASSERT_FALSE(pthread_flag);
 }
 
@@ -528,8 +562,8 @@ TEST_F(TestForDeathTest, AssertDebugDeathAborts) {
 
 // Tests the *_EXIT family of macros, using a variety of predicates.
 TEST_F(TestForDeathTest, ExitMacros) {
-  EXPECT_EXIT(exit(1),  testing::ExitedWithCode(1),  "");
-  ASSERT_EXIT(exit(42), testing::ExitedWithCode(42), "");
+  EXPECT_EXIT(_exit(1),  testing::ExitedWithCode(1),  "");
+  ASSERT_EXIT(_exit(42), testing::ExitedWithCode(42), "");
   EXPECT_EXIT(raise(SIGKILL), testing::KilledBySignal(SIGKILL), "") << "foo";
   ASSERT_EXIT(raise(SIGUSR2), testing::KilledBySignal(SIGUSR2), "") << "bar";
 
@@ -539,7 +573,7 @@ TEST_F(TestForDeathTest, ExitMacros) {
   }, "This failure is expected.");
 
   EXPECT_FATAL_FAILURE({  // NOLINT
-    ASSERT_EXIT(exit(0), testing::KilledBySignal(SIGSEGV), "")
+    ASSERT_EXIT(_exit(0), testing::KilledBySignal(SIGSEGV), "")
         << "This failure is expected, too.";
   }, "This failure is expected, too.");
 }
@@ -547,7 +581,7 @@ TEST_F(TestForDeathTest, ExitMacros) {
 TEST_F(TestForDeathTest, InvalidStyle) {
   testing::GTEST_FLAG(death_test_style) = "rococo";
   EXPECT_NONFATAL_FAILURE({  // NOLINT
-    EXPECT_DEATH(exit(0), "") << "This failure is expected.";
+    EXPECT_DEATH(_exit(0), "") << "This failure is expected.";
   }, "This failure is expected.");
 }
 
@@ -794,7 +828,7 @@ TEST_F(MacroLogicDeathTest, ChildDoesNotDie) {
   // This time there are two calls to Abort: one since the test didn't
   // die, and another from the ReturnSentinel when it's destroyed.  The
   // sentinel normally isn't destroyed if a test doesn't die, since
-  // exit(2) is called in that case by ForkingDeathTest, but not by
+  // _exit(2) is called in that case by ForkingDeathTest, but not by
   // our MockDeathTest.
   ASSERT_EQ(2, factory_->AbortCalls());
   EXPECT_EQ(DeathTest::TEST_DID_NOT_DIE,
@@ -813,18 +847,18 @@ static size_t GetSuccessfulTestPartCount() {
 // Tests that a successful death test does not register a successful
 // test part.
 TEST(SuccessRegistrationDeathTest, NoSuccessPart) {
-  EXPECT_DEATH(exit(1), "");
+  EXPECT_DEATH(_exit(1), "");
   EXPECT_EQ(0u, GetSuccessfulTestPartCount());
 }
 
 TEST(StreamingAssertionsDeathTest, DeathTest) {
-  EXPECT_DEATH(exit(1), "") << "unexpected failure";
-  ASSERT_DEATH(exit(1), "") << "unexpected failure";
+  EXPECT_DEATH(_exit(1), "") << "unexpected failure";
+  ASSERT_DEATH(_exit(1), "") << "unexpected failure";
   EXPECT_NONFATAL_FAILURE({  // NOLINT
-    EXPECT_DEATH(exit(0), "") << "expected failure";
+    EXPECT_DEATH(_exit(0), "") << "expected failure";
   }, "expected failure");
   EXPECT_FATAL_FAILURE({  // NOLINT
-    ASSERT_DEATH(exit(0), "") << "expected failure";
+    ASSERT_DEATH(_exit(0), "") << "expected failure";
   }, "expected failure");
 }
 
index f4b70c3638eeb45989f393849042261c610de6a5..603427c1ff8d83f50ee5030e8be4ee06c2a38a39 100644 (file)
@@ -91,6 +91,38 @@ const char* MakeTempDir() {
 }
 #endif  // _WIN32_WCE
 
+#ifndef _WIN32_WCE
+
+TEST(GetCurrentDirTest, ReturnsCurrentDir) {
+  EXPECT_FALSE(FilePath::GetCurrentDir().IsEmpty());
+
+#ifdef GTEST_OS_WINDOWS
+  _chdir(PATH_SEP);
+  const FilePath cwd = FilePath::GetCurrentDir();
+  // Skips the ":".
+  const char* const cwd_without_drive = strchr(cwd.c_str(), ':');
+  ASSERT_TRUE(cwd_without_drive != NULL);
+  EXPECT_STREQ(PATH_SEP, cwd_without_drive + 1);
+#else
+  chdir(PATH_SEP);
+  EXPECT_STREQ(PATH_SEP, FilePath::GetCurrentDir().c_str());
+#endif
+}
+
+#endif  // _WIN32_WCE
+
+TEST(IsEmptyTest, ReturnsTrueForEmptyPath) {
+  EXPECT_TRUE(FilePath("").IsEmpty());
+  EXPECT_TRUE(FilePath(NULL).IsEmpty());
+}
+
+TEST(IsEmptyTest, ReturnsFalseForNonEmptyPath) {
+  EXPECT_FALSE(FilePath("a").IsEmpty());
+  EXPECT_FALSE(FilePath(".").IsEmpty());
+  EXPECT_FALSE(FilePath("a/b").IsEmpty());
+  EXPECT_FALSE(FilePath("a\\b\\").IsEmpty());
+}
+
 // FilePath's functions used by UnitTestOptions::GetOutputFile.
 
 // RemoveDirectoryName "" -> ""
index 7c5123c238f3a1c62f956593499c218b69b1476a..8343795aa90cfeb4eb4f27eb1e2ad41328bc4ad7 100644 (file)
@@ -1142,7 +1142,8 @@ TEST(ParseInt32FlagTest, ParsesAndReturnsValidValue) {
 }
 
 // For the same reason we are not explicitly testing everything in the
-// Test class, there are no separate tests for the following classes:
+// Test class, there are no separate tests for the following classes
+// (except for some trivial cases):
 //
 //   TestCase, UnitTest, UnitTestResultPrinter.
 //
@@ -1150,6 +1151,11 @@ TEST(ParseInt32FlagTest, ParsesAndReturnsValidValue) {
 //
 //   TEST, TEST_F, RUN_ALL_TESTS
 
+TEST(UnitTestTest, CanGetOriginalWorkingDir) {
+  ASSERT_TRUE(UnitTest::GetInstance()->original_working_dir() != NULL);
+  EXPECT_STRNE(UnitTest::GetInstance()->original_working_dir(), "");
+}
+
 // This group of tests is for predicate assertions (ASSERT_PRED*, etc)
 // of various arities.  They do not attempt to be exhaustive.  Rather,
 // view them as smoke tests that can be easily reviewed and verified.