1#ifndef FILESYSTEM_TEST_HELPER_HPP 2#define FILESYSTEM_TEST_HELPER_HPP 3 4#include <experimental/filesystem> 5#include <cassert> 6#include <cstdio> // for printf 7#include <string> 8#include <fstream> 9#include <random> 10#include <chrono> 11 12namespace fs = std::experimental::filesystem; 13 14// static test helpers 15 16#ifndef LIBCXX_FILESYSTEM_STATIC_TEST_ROOT 17#warning "STATIC TESTS DISABLED" 18#else // LIBCXX_FILESYSTEM_STATIC_TEST_ROOT 19 20namespace StaticEnv { 21 22inline fs::path makePath(fs::path const& p) { 23 // env_path is expected not to contain symlinks. 24 static const fs::path env_path = LIBCXX_FILESYSTEM_STATIC_TEST_ROOT; 25 return env_path / p; 26} 27 28static const fs::path Root = LIBCXX_FILESYSTEM_STATIC_TEST_ROOT; 29 30static const fs::path TestFileList[] = { 31 makePath("empty_file"), 32 makePath("non_empty_file"), 33 makePath("dir1/file1"), 34 makePath("dir1/file2") 35}; 36const std::size_t TestFileListSize = sizeof(TestFileList) / sizeof(fs::path); 37 38static const fs::path TestDirList[] = { 39 makePath("dir1"), 40 makePath("dir1/dir2"), 41 makePath("dir1/dir2/dir3") 42}; 43const std::size_t TestDirListSize = sizeof(TestDirList) / sizeof(fs::path); 44 45static const fs::path File = TestFileList[0]; 46static const fs::path Dir = TestDirList[0]; 47static const fs::path Dir2 = TestDirList[1]; 48static const fs::path Dir3 = TestDirList[2]; 49static const fs::path SymlinkToFile = makePath("symlink_to_empty_file"); 50static const fs::path SymlinkToDir = makePath("symlink_to_dir"); 51static const fs::path BadSymlink = makePath("bad_symlink"); 52static const fs::path DNE = makePath("DNE"); 53static const fs::path EmptyFile = TestFileList[0]; 54static const fs::path NonEmptyFile = TestFileList[1]; 55static const fs::path CharFile = "/dev/null"; // Hopefully this exists 56 57static const fs::path DirIterationList[] = { 58 makePath("dir1/dir2"), 59 makePath("dir1/file1"), 60 makePath("dir1/file2") 61}; 62const std::size_t DirIterationListSize = sizeof(DirIterationList) 63 / sizeof(fs::path); 64 65static const fs::path DirIterationListDepth1[] = { 66 makePath("dir1/dir2/afile3"), 67 makePath("dir1/dir2/dir3"), 68 makePath("dir1/dir2/symlink_to_dir3"), 69 makePath("dir1/dir2/file4"), 70}; 71 72static const fs::path RecDirIterationList[] = { 73 makePath("dir1/dir2"), 74 makePath("dir1/file1"), 75 makePath("dir1/file2"), 76 makePath("dir1/dir2/afile3"), 77 makePath("dir1/dir2/dir3"), 78 makePath("dir1/dir2/symlink_to_dir3"), 79 makePath("dir1/dir2/file4"), 80 makePath("dir1/dir2/dir3/file5") 81}; 82 83static const fs::path RecDirFollowSymlinksIterationList[] = { 84 makePath("dir1/dir2"), 85 makePath("dir1/file1"), 86 makePath("dir1/file2"), 87 makePath("dir1/dir2/afile3"), 88 makePath("dir1/dir2/dir3"), 89 makePath("dir1/dir2/file4"), 90 makePath("dir1/dir2/dir3/file5"), 91 makePath("dir1/dir2/symlink_to_dir3"), 92 makePath("dir1/dir2/symlink_to_dir3/file5"), 93}; 94 95} // namespace StaticEnv 96 97#endif // LIBCXX_FILESYSTEM_STATIC_TEST_ROOT 98 99#ifndef LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT 100#warning LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT must be defined 101#else // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT 102 103#ifndef LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER 104#error LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER must be defined 105#endif 106 107struct scoped_test_env 108{ 109 scoped_test_env() : test_root(random_env_path()) 110 { fs_helper_run(fs_make_cmd("init_test_directory", test_root)); } 111 112 ~scoped_test_env() 113 { fs_helper_run(fs_make_cmd("destroy_test_directory", test_root)); } 114 115 scoped_test_env(scoped_test_env const &) = delete; 116 scoped_test_env & operator=(scoped_test_env const &) = delete; 117 118 fs::path make_env_path(std::string p) { return sanitize_path(p); } 119 120 std::string sanitize_path(std::string raw) { 121 assert(raw.find("..") == std::string::npos); 122 std::string const& root = test_root.native(); 123 if (root.compare(0, root.size(), raw, 0, root.size()) != 0) { 124 assert(raw.front() != '\\'); 125 fs::path tmp(test_root); 126 tmp /= raw; 127 return std::move(const_cast<std::string&>(tmp.native())); 128 } 129 return raw; 130 } 131 132 std::string create_file(std::string filename, std::size_t size = 0) { 133 filename = sanitize_path(std::move(filename)); 134 std::string out_str(size, 'a'); 135 { 136 std::ofstream out(filename.c_str()); 137 out << out_str; 138 } 139 return filename; 140 } 141 142 std::string create_dir(std::string filename) { 143 filename = sanitize_path(std::move(filename)); 144 fs_helper_run(fs_make_cmd("create_dir", filename)); 145 return filename; 146 } 147 148 std::string create_symlink(std::string source, std::string to) { 149 source = sanitize_path(std::move(source)); 150 to = sanitize_path(std::move(to)); 151 fs_helper_run(fs_make_cmd("create_symlink", source, to)); 152 return to; 153 } 154 155 std::string create_hardlink(std::string source, std::string to) { 156 source = sanitize_path(std::move(source)); 157 to = sanitize_path(std::move(to)); 158 fs_helper_run(fs_make_cmd("create_hardlink", source, to)); 159 return to; 160 } 161 162 std::string create_fifo(std::string file) { 163 file = sanitize_path(std::move(file)); 164 fs_helper_run(fs_make_cmd("create_fifo", file)); 165 return file; 166 } 167 168 // OS X and FreeBSD doesn't support socket files so we shouldn't even 169 // allow tests to call this unguarded. 170#if !defined(__FreeBSD__) && !defined(__APPLE__) 171 std::string create_socket(std::string file) { 172 file = sanitize_path(std::move(file)); 173 fs_helper_run(fs_make_cmd("create_socket", file)); 174 return file; 175 } 176#endif 177 178 fs::path const test_root; 179 180private: 181 static char to_hex(int ch) { 182 return ch < 10 ? static_cast<char>('0' + ch) 183 : static_cast<char>('a' + (ch - 10)); 184 } 185 186 static char random_hex_char() { 187 static std::mt19937 rd { std::random_device{}() }; 188 static std::uniform_int_distribution<int> mrand{0, 15}; 189 return to_hex( mrand(rd) ); 190 } 191 192 static std::string unique_path_suffix() { 193 std::string model = "test.%%%%%%"; 194 for (auto & ch : model) { 195 if (ch == '%') ch = random_hex_char(); 196 } 197 return model; 198 } 199 200 // This could potentially introduce a filesystem race with other tests 201 // running at the same time, but oh well, it's just test code. 202 static inline fs::path random_env_path() { 203 static const char* env_path = LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT; 204 fs::path p = fs::path(env_path) / unique_path_suffix(); 205 assert(p.parent_path() == env_path); 206 return p; 207 } 208 209 static inline std::string make_arg(std::string const& arg) { 210 return "'" + arg + "'"; 211 } 212 213 static inline std::string make_arg(std::size_t arg) { 214 return std::to_string(arg); 215 } 216 217 template <class T> 218 static inline std::string 219 fs_make_cmd(std::string const& cmd_name, T const& arg) { 220 return cmd_name + "(" + make_arg(arg) + ")"; 221 } 222 223 template <class T, class U> 224 static inline std::string 225 fs_make_cmd(std::string const& cmd_name, T const& arg1, U const& arg2) { 226 return cmd_name + "(" + make_arg(arg1) + ", " + make_arg(arg2) + ")"; 227 } 228 229 static inline void fs_helper_run(std::string const& raw_cmd) { 230 // check that the fs test root in the environment matches what we were 231 // compiled with. 232 static bool checked = checkDynamicTestRoot(); 233 ((void)checked); 234 std::string cmd = LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER; 235 cmd += " \"" + raw_cmd + "\""; 236 int ret = std::system(cmd.c_str()); 237 assert(ret == 0); 238 } 239 240 static bool checkDynamicTestRoot() { 241 // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT is expected not to contain symlinks. 242 char* fs_root = std::getenv("LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT"); 243 if (!fs_root) { 244 std::printf("ERROR: LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT must be a defined " 245 "environment variable when running the test.\n"); 246 std::abort(); 247 } 248 if (std::string(fs_root) != LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT) { 249 std::printf("ERROR: LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT environment variable" 250 " must have the same value as when the test was compiled.\n"); 251 std::printf(" Current Value: '%s'\n", fs_root); 252 std::printf(" Expected Value: '%s'\n", LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT); 253 std::abort(); 254 } 255 return true; 256 } 257 258}; 259 260#endif // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT 261 262// Misc test types 263 264#define CONCAT2(LHS, RHS) LHS##RHS 265#define CONCAT(LHS, RHS) CONCAT2(LHS, RHS) 266#define MKSTR(Str) {Str, CONCAT(L, Str), CONCAT(u, Str), CONCAT(U, Str)} 267 268struct MultiStringType { 269 const char* s; 270 const wchar_t* w; 271 const char16_t* u16; 272 const char32_t* u32; 273 274 operator const char* () const { return s; } 275 operator const wchar_t* () const { return w; } 276 operator const char16_t* () const { return u16; } 277 operator const char32_t* () const { return u32; } 278}; 279 280const MultiStringType PathList[] = { 281 MKSTR(""), 282 MKSTR(" "), 283 MKSTR("//"), 284 MKSTR("."), 285 MKSTR(".."), 286 MKSTR("foo"), 287 MKSTR("/"), 288 MKSTR("/foo"), 289 MKSTR("foo/"), 290 MKSTR("/foo/"), 291 MKSTR("foo/bar"), 292 MKSTR("/foo/bar"), 293 MKSTR("//net"), 294 MKSTR("//net/foo"), 295 MKSTR("///foo///"), 296 MKSTR("///foo///bar"), 297 MKSTR("/."), 298 MKSTR("./"), 299 MKSTR("/.."), 300 MKSTR("../"), 301 MKSTR("foo/."), 302 MKSTR("foo/.."), 303 MKSTR("foo/./"), 304 MKSTR("foo/./bar"), 305 MKSTR("foo/../"), 306 MKSTR("foo/../bar"), 307 MKSTR("c:"), 308 MKSTR("c:/"), 309 MKSTR("c:foo"), 310 MKSTR("c:/foo"), 311 MKSTR("c:foo/"), 312 MKSTR("c:/foo/"), 313 MKSTR("c:/foo/bar"), 314 MKSTR("prn:"), 315 MKSTR("c:\\"), 316 MKSTR("c:\\foo"), 317 MKSTR("c:foo\\"), 318 MKSTR("c:\\foo\\"), 319 MKSTR("c:\\foo/"), 320 MKSTR("c:/foo\\bar"), 321 MKSTR("//"), 322 MKSTR("/finally/we/need/one/really/really/really/really/really/really/really/long/string") 323}; 324const unsigned PathListSize = sizeof(PathList) / sizeof(MultiStringType); 325 326template <class Iter> 327Iter IterEnd(Iter B) { 328 using VT = typename std::iterator_traits<Iter>::value_type; 329 for (; *B != VT{}; ++B) 330 ; 331 return B; 332} 333 334template <class CharT> 335const CharT* StrEnd(CharT const* P) { 336 return IterEnd(P); 337} 338 339template <class CharT> 340std::size_t StrLen(CharT const* P) { 341 return StrEnd(P) - P; 342} 343 344// Testing the allocation behavior of the code_cvt functions requires 345// *knowing* that the allocation was not done by "path::__str_". 346// This hack forces path to allocate enough memory. 347inline void PathReserve(fs::path& p, std::size_t N) { 348 auto const& native_ref = p.native(); 349 const_cast<std::string&>(native_ref).reserve(N); 350} 351 352template <class Iter1, class Iter2> 353bool checkCollectionsEqual( 354 Iter1 start1, Iter1 const end1 355 , Iter2 start2, Iter2 const end2 356 ) 357{ 358 while (start1 != end1 && start2 != end2) { 359 if (*start1 != *start2) { 360 return false; 361 } 362 ++start1; ++start2; 363 } 364 return (start1 == end1 && start2 == end2); 365} 366 367 368template <class Iter1, class Iter2> 369bool checkCollectionsEqualBackwards( 370 Iter1 const start1, Iter1 end1 371 , Iter2 const start2, Iter2 end2 372 ) 373{ 374 while (start1 != end1 && start2 != end2) { 375 --end1; --end2; 376 if (*end1 != *end2) { 377 return false; 378 } 379 } 380 return (start1 == end1 && start2 == end2); 381} 382 383// We often need to test that the error_code was cleared if no error occurs 384// this function returns an error_code which is set to an error that will 385// never be returned by the filesystem functions. 386inline std::error_code GetTestEC() { 387 return std::make_error_code(std::errc::address_family_not_supported); 388} 389 390// Provide our own Sleep routine since std::this_thread::sleep_for is not 391// available in single-threaded mode. 392void SleepFor(std::chrono::seconds dur) { 393 using namespace std::chrono; 394#if defined(_LIBCPP_HAS_NO_MONOTONIC_CLOCK) 395 using Clock = system_clock; 396#else 397 using Clock = steady_clock; 398#endif 399 const auto wake_time = Clock::now() + dur; 400 while (Clock::now() < wake_time) 401 ; 402} 403 404#endif /* FILESYSTEM_TEST_HELPER_HPP */ 405