// Copyright (C) 2015 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include "base/Compiler.h" #include "base/StringFormat.h" #include "base/PathUtils.h" #include "file_io.h" #ifdef _WIN32 #include #include #include #include #undef ERROR #include #include #endif #include #include #include #include #ifndef _MSC_VER #include #include #endif namespace android { namespace base { // A class used to model a temporary directory used during testing. // Usage is simple: // // { // TestTempDir myDir("my_test"); // creates new temp directory. // ASSERT_TRUE(myDir.path()); // NULL if error during creation. // ... write files into directory path myDir->path() // ... do your test // } // destructor removes temp directory and all files under it. #ifdef _MSC_VER // From https://stackoverflow.com/questions/24365331/how-can-i-generate-uuid-in-c-without-using-boost-library namespace uuid { static std::random_device rd; static std::mt19937 gen(rd()); static std::uniform_int_distribution<> dis(0, 15); static std::uniform_int_distribution<> dis2(8, 11); static inline std::string generate_uuid_v4() { std::stringstream ss; int i; ss << std::hex; for (i = 0; i < 8; i++) { ss << dis(gen); } ss << "-"; for (i = 0; i < 4; i++) { ss << dis(gen); } ss << "-4"; for (i = 0; i < 3; i++) { ss << dis(gen); } ss << "-"; ss << dis2(gen); for (i = 0; i < 3; i++) { ss << dis(gen); } ss << "-"; for (i = 0; i < 12; i++) { ss << dis(gen); }; return ss.str(); } } #endif class TestTempDir { public: // Create new instance. This also tries to create a new temporary // directory. |debugPrefix| is an optional name prefix and can be NULL. TestTempDir(const std::string& debugName) { std::string temp_dir = getTempPath(); if (!debugName.empty()) { temp_dir += debugName; temp_dir += "."; } #if defined(_MSC_VER) || defined(_WIN32) temp_dir += uuid::generate_uuid_v4(); if (android_mkdir(temp_dir.c_str(), 0755) != 0) { fprintf(stderr, "Unable to create %s, falling back to tmp dir", temp_dir.c_str()); temp_dir = getTempPath(); } auto parts = PathUtils::decompose(temp_dir); mPath = PathUtils::recompose(parts); #else temp_dir += "XXXXXX"; if (mkdtemp(&temp_dir[0])) { // Fix any Win32/Linux naming issues auto parts = PathUtils::decompose(temp_dir); mPath = PathUtils::recompose(parts); } else { fprintf(stderr, "%s: mkdtemp failed!\n", __func__); } #endif } // Return the path to the temporary directory, or NULL if it could not // be created for some reason. const char* path() const { return mPath.size() ? mPath.c_str() : NULL; } // Return the path as a string. It will be empty if the directory could // not be created for some reason. const std::string& pathString() const { return mPath; } // Destroy instance, and removes the temporary directory and all files // inside it. ~TestTempDir() { if (mPath.size()) { DeleteRecursive(mPath); } } // Create the path of a directory entry under the temporary directory. std::string makeSubPath(const std::string& subpath) { return StringFormat("%s/%s", mPath.c_str(), subpath.c_str()); } // Create an empty directory under the temporary directory. bool makeSubDir(const std::string& subdir) { std::string path = makeSubPath(subdir); if (android_mkdir(path.c_str(), 0755) < 0) { // PLOG(ERROR) << "Can't create " << path.c_str() << ": "; return false; } return true; } // Create an empty file under the temporary directory. bool makeSubFile(const std::string& file) { std::string path = makeSubPath(file); int fd = ::android_open(path.c_str(), O_WRONLY | O_CREAT, 0744); if (fd < 0) { // PLOG(ERROR) << "Can't create " << path.c_str() << ": "; return false; } ::close(fd); return true; } private: DISALLOW_COPY_AND_ASSIGN(TestTempDir); void DeleteRecursive(const std::string& path) { #ifdef _WIN32 std::filesystem::remove_all(path); #else // First remove any files in the dir DIR* dir = opendir(path.c_str()); if (!dir) { return; } dirent* entry; while ((entry = readdir(dir)) != NULL) { if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { continue; } std::string entry_path = StringFormat("%s/%s", path, entry->d_name); #ifdef _WIN32 struct _stati64 stats; android_lstat(entry_path.c_str(), reinterpret_cast(&stats)); #else struct stat stats; android_lstat(entry_path.c_str(), &stats); #endif if (S_ISDIR(stats.st_mode)) { DeleteRecursive(entry_path); } else { android_unlink(entry_path.c_str()); } } closedir(dir); android_rmdir(path.c_str()); #endif } #ifdef _WIN32 std::string getTempPath() { return std::filesystem::temp_directory_path().string(); } char* mkdtemp(char* path) { char tmpnamBuf[2048]; if (!std::tmpnam(tmpnamBuf)) return nullptr; char* path_end = tmpnamBuf + ::strlen(path); // Loop. On each iteration, replace the XXXXXX suffix with a random // number. const size_t kSuffixLen = 6U; for (int tries = 128; tries > 0; tries--) { int random = rand() % 1000000; snprintf(path_end - kSuffixLen, kSuffixLen + 1, "%0d", random); if (android_mkdir(path, 0755) == 0) { return path; // Success } if (errno != EEXIST) { return NULL; } } return NULL; } #else // !_WIN32 std::string getTempPath() { std::string result; // Only check TMPDIR if we're not root. if (getuid() != 0 && getgid() != 0) { const char* tmpdir = ::getenv("TMPDIR"); if (tmpdir && tmpdir[0]) { result = tmpdir; } } // Otherwise use P_tmpdir, which defaults to /tmp if (result.empty()) { #ifndef P_tmpdir #define P_tmpdir "/tmp" #endif result = P_tmpdir; } // Check that it exists and is a directory. struct stat st; int ret = android_stat(result.c_str(), &st); if (ret < 0 || !S_ISDIR(st.st_mode)) { fprintf(stderr, "%s: Can't find temp path %s\n", __func__, result.c_str()); abort(); // LOG(FATAL) << "Can't find temporary path: [" << result.c_str() // << "]"; } // Ensure there is a trailing directory separator. if (result.size() && result[result.size() - 1] != '/') { result += '/'; } return result; } #endif // !_WIN32 std::string mPath; }; } // namespace base } // namespace android