diff --git a/googletest/include/gtest/gtest.h b/googletest/include/gtest/gtest.h index ffff87662e..1184337a86 100644 --- a/googletest/include/gtest/gtest.h +++ b/googletest/include/gtest/gtest.h @@ -2201,10 +2201,17 @@ constexpr bool StaticAssertTypeEq() noexcept { #define TEST_F(test_fixture, test_name) GTEST_TEST_F(test_fixture, test_name) #endif -// Returns a path to temporary directory. -// Tries to determine an appropriate directory for the platform. +// Returns a path to a temporary directory, which should be writable. It is +// implementation-dependent whether or not the path is terminated by the +// directory-separator character. GTEST_API_ std::string TempDir(); +// Returns a path to a directory that contains ancillary data files that might +// be used by tests. It is implementation dependent whether or not the path is +// terminated by the directory-separator character. The directory and the files +// in it should be considered read-only. +GTEST_API_ std::string SrcDir(); + #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc index 2276d0778a..7669d22877 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -6754,12 +6754,13 @@ void InitGoogleTest() { #endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) } -#if !defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) -// Return value of first environment variable that is set and contains -// a non-empty string. If there are none, return the "fallback" string. -// Since we like the temporary directory to have a directory separator suffix, -// add it if not provided in the environment variable value. -static std::string GetTempDirFromEnv( +#if !defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) || \ + !defined(GTEST_CUSTOM_SRCDIR_FUNCTION_) +// Returns the value of the first environment variable that is set and contains +// a non-empty string. If there are none, returns the "fallback" string. Adds +// the director-separator character as a suffix if not provided in the +// environment variable value. +static std::string GetDirFromEnv( std::initializer_list environment_variables, const char* fallback, char separator) { for (const char* variable_name : environment_variables) { @@ -6779,11 +6780,35 @@ std::string TempDir() { #if defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) return GTEST_CUSTOM_TEMPDIR_FUNCTION_(); #elif GTEST_OS_WINDOWS || GTEST_OS_WINDOWS_MOBILE - return GetTempDirFromEnv({"TEST_TMPDIR", "TEMP"}, "\\temp\\", '\\'); + return GetDirFromEnv({"TEST_TMPDIR", "TEMP"}, "\\temp\\", '\\'); #elif GTEST_OS_LINUX_ANDROID - return GetTempDirFromEnv({"TEST_TMPDIR", "TMPDIR"}, "/data/local/tmp/", '/'); + return GetDirFromEnv({"TEST_TMPDIR", "TMPDIR"}, "/data/local/tmp/", '/'); #else - return GetTempDirFromEnv({"TEST_TMPDIR", "TMPDIR"}, "/tmp/", '/'); + return GetDirFromEnv({"TEST_TMPDIR", "TMPDIR"}, "/tmp/", '/'); +#endif +} + +#if !defined(GTEST_CUSTOM_SRCDIR_FUNCTION_) +// Returns the directory path (including terminating separator) of the current +// executable as derived from argv[0]. +static std::string GetCurrentExecutableDirectory() { + internal::FilePath argv_0(internal::GetArgvs()[0]); + return argv_0.RemoveFileName().string(); +} +#endif + +std::string SrcDir() { +#if defined(GTEST_CUSTOM_SRCDIR_FUNCTION_) + return GTEST_CUSTOM_SRCDIR_FUNCTION_(); +#elif GTEST_OS_WINDOWS || GTEST_OS_WINDOWS_MOBILE + return GetDirFromEnv({"TEST_SRCDIR"}, GetCurrentExecutableDirectory().c_str(), + '\\'); +#elif GTEST_OS_LINUX_ANDROID + return GetDirFromEnv({"TEST_SRCDIR"}, GetCurrentExecutableDirectory().c_str(), + '/'); +#else + return GetDirFromEnv({"TEST_SRCDIR"}, GetCurrentExecutableDirectory().c_str(), + '/'); #endif } diff --git a/googletest/test/gtest_dirs_test.cc b/googletest/test/gtest_dirs_test.cc new file mode 100644 index 0000000000..c0da9ac463 --- /dev/null +++ b/googletest/test/gtest_dirs_test.cc @@ -0,0 +1,97 @@ +#include + +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/internal/gtest-port.h" + +namespace { + +class SetEnv { + public: + // Sets the environment value with name `name` to `value`, unless `value` is + // nullptr, in which case it unsets it. Restores the original value on + // destruction. + SetEnv(const char* name, const char* value) : name_(name) { + const char* old_value = getenv(name); + if (old_value != nullptr) { + saved_value_ = old_value; + have_saved_value_ = true; + } + if (value == nullptr) { + GTEST_CHECK_POSIX_SUCCESS_(unsetenv(name)); + } else { + GTEST_CHECK_POSIX_SUCCESS_(setenv(name, value, 1 /*overwrite*/)); + } + } + + ~SetEnv() { + if (have_saved_value_) { + GTEST_CHECK_POSIX_SUCCESS_( + setenv(name_.c_str(), saved_value_.c_str(), 1 /*overwrite*/)); + } else { + GTEST_CHECK_POSIX_SUCCESS_(unsetenv(name_.c_str())); + } + } + + private: + std::string name_; + bool have_saved_value_ = false; + std::string saved_value_; +}; + +class MakeTempDir { + public: + // Creates a directory with a unique name including `testname`. + // The destructor removes it. + explicit MakeTempDir(const std::string& testname) { + // mkdtemp requires that the last 6 characters of the input pattern + // are Xs, and the string is modified by replacing those characters. + std::string pattern = "/tmp/" + testname + "_XXXXXX"; + GTEST_CHECK_(mkdtemp(pattern.data()) != nullptr); + dirname_ = pattern; + } + + ~MakeTempDir() { GTEST_CHECK_POSIX_SUCCESS_(rmdir(dirname_.c_str())); } + + const char* DirName() const { return dirname_.c_str(); } + + private: + std::string dirname_; +}; + +bool StartsWith(const std::string& str, const std::string& prefix) { + return str.substr(0, prefix.size()) == prefix; +} + +TEST(TempDirTest, InEnvironment) { + // Since the test infrastructure might be verifying directory existence or + // even creating subdirectories, we need to be careful that the directories we + // specify are actually valid. + MakeTempDir temp_dir("TempDirTest_InEnvironment"); + SetEnv set_env("TEST_TMPDIR", temp_dir.DirName()); + EXPECT_TRUE(StartsWith(testing::TempDir(), temp_dir.DirName())); +} + +TEST(TempDirTest, NotInEnvironment) { + SetEnv set_env("TEST_TMPDIR", nullptr); + EXPECT_NE(testing::TempDir(), ""); +} + +TEST(SrcDirTest, InEnvironment) { + // Since the test infrastructure might be verifying directory existence or + // even creating subdirectories, we need to be careful that the directories we + // specify are actually valid. + MakeTempDir temp_dir("SrcDirTest_InEnvironment"); + SetEnv set_env("TEST_SRCDIR", temp_dir.DirName()); + EXPECT_TRUE(StartsWith(testing::SrcDir(), temp_dir.DirName())); +} + +TEST(SrcDirTest, NotInEnvironment) { + SetEnv set_env("TEST_SRCDIR", nullptr); + EXPECT_NE(testing::SrcDir(), ""); +} + +} // namespace