util.h (3684B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef util_h__ 8 #define util_h__ 9 10 #include <cassert> 11 #include <cstdlib> 12 #include <iomanip> 13 #include <iostream> 14 #include <sstream> 15 #include <sys/stat.h> 16 #include <vector> 17 #if defined(_WIN32) 18 #include <windows.h> 19 #include <codecvt> 20 #include <direct.h> 21 #else 22 #include <unistd.h> 23 #endif 24 25 #include "nspr.h" 26 27 static inline std::vector<uint8_t> hex_string_to_bytes(std::string s) { 28 std::vector<uint8_t> bytes; 29 for (size_t i = 0; i < s.length(); i += 2) { 30 bytes.push_back(std::stoul(s.substr(i, 2), nullptr, 16)); 31 } 32 return bytes; 33 } 34 35 // Given a prefix, attempts to create a unique directory that the user can do 36 // work in without impacting other tests. For example, if given the prefix 37 // "scratch", a directory like "scratch05c17b25" will be created in the current 38 // working directory (or the location specified by NSS_GTEST_WORKDIR, if 39 // defined). 40 // Upon destruction, the implementation will attempt to delete the directory. 41 // However, no attempt is made to first remove files in the directory - the 42 // user is responsible for this. If the directory is not empty, deleting it will 43 // fail. 44 // Statistically, it is technically possible to fail to create a unique 45 // directory name, but this is extremely unlikely given the expected workload of 46 // this implementation. 47 class ScopedUniqueDirectory { 48 public: 49 explicit ScopedUniqueDirectory(const std::string &prefix) { 50 std::string path; 51 const char *workingDirectory = PR_GetEnvSecure("NSS_GTEST_WORKDIR"); 52 if (workingDirectory) { 53 path.assign(workingDirectory); 54 } 55 path.append(prefix); 56 for (int i = 0; i < RETRY_LIMIT; i++) { 57 std::string pathCopy(path); 58 // TryMakingDirectory will modify its input. If it fails, we want to throw 59 // away the modified result. 60 if (TryMakingDirectory(pathCopy)) { 61 mPath.assign(pathCopy); 62 break; 63 } 64 } 65 assert(mPath.length() > 0); 66 #if defined(_WIN32) 67 // sqldb always uses UTF-8 regardless of the current system locale. 68 DWORD len = 69 MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), nullptr, 0); 70 std::vector<wchar_t> buf(len, L'\0'); 71 MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), buf.data(), 72 buf.size()); 73 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; 74 mUTF8Path = converter.to_bytes(std::wstring(buf.begin(), buf.end())); 75 #else 76 mUTF8Path = mPath; 77 #endif 78 } 79 80 // NB: the directory must be empty upon destruction 81 ~ScopedUniqueDirectory() { assert(rmdir(mPath.c_str()) == 0); } 82 83 const std::string &GetPath() { return mPath; } 84 const std::string &GetUTF8Path() { return mUTF8Path; } 85 86 private: 87 static const int RETRY_LIMIT = 5; 88 89 static void GenerateRandomName(/*in/out*/ std::string &prefix) { 90 std::stringstream ss; 91 ss << prefix; 92 // RAND_MAX is at least 32767. 93 ss << std::setfill('0') << std::setw(4) << std::hex << rand() << rand(); 94 // This will overwrite the value of prefix. This is a little inefficient, 95 // but at least it makes the code simple. 96 ss >> prefix; 97 } 98 99 static bool TryMakingDirectory(/*in/out*/ std::string &prefix) { 100 GenerateRandomName(prefix); 101 #if defined(_WIN32) 102 return _mkdir(prefix.c_str()) == 0; 103 #else 104 return mkdir(prefix.c_str(), 0777) == 0; 105 #endif 106 } 107 108 std::string mPath; 109 std::string mUTF8Path; 110 }; 111 112 #endif // util_h__