[lld][WebAssembly] Add `--table-base` setting
[llvm-project.git] / libcxx / test / support / filesystem_test_helper.h
blobd63b1e61b5f9b53ee462affbd413fa277b2ae445
1 #ifndef FILESYSTEM_TEST_HELPER_H
2 #define FILESYSTEM_TEST_HELPER_H
4 #include "filesystem_include.h"
6 #include <sys/stat.h> // for stat, mkdir, mkfifo
7 #ifndef _WIN32
8 #include <unistd.h> // for ftruncate, link, symlink, getcwd, chdir
9 #include <sys/statvfs.h>
10 #else
11 #include <io.h>
12 #include <direct.h>
13 #include <windows.h> // for CreateSymbolicLink, CreateHardLink
14 #endif
16 #include <cassert>
17 #include <cerrno>
18 #include <chrono>
19 #include <cstdint>
20 #include <cstdio> // for printf
21 #include <string>
22 #include <system_error>
23 #include <type_traits>
24 #include <vector>
26 #include "assert_macros.h"
27 #include "make_string.h"
28 #include "test_macros.h"
29 #include "format_string.h"
31 // For creating socket files
32 #if !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(_WIN32)
33 # include <sys/socket.h>
34 # include <sys/un.h>
35 #endif
37 namespace utils {
38 #ifdef _WIN32
39 inline int mkdir(const char* path, int mode) { (void)mode; return ::_mkdir(path); }
40 inline int symlink(const char* oldname, const char* newname, bool is_dir) {
41 DWORD flags = is_dir ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
42 if (CreateSymbolicLinkA(newname, oldname,
43 flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
44 return 0;
45 if (GetLastError() != ERROR_INVALID_PARAMETER)
46 return 1;
47 return !CreateSymbolicLinkA(newname, oldname, flags);
49 inline int link(const char *oldname, const char* newname) {
50 return !CreateHardLinkA(newname, oldname, NULL);
52 inline int setenv(const char *var, const char *val, int overwrite) {
53 (void)overwrite;
54 return ::_putenv((std::string(var) + "=" + std::string(val)).c_str());
56 inline int unsetenv(const char *var) {
57 return ::_putenv((std::string(var) + "=").c_str());
59 inline bool space(std::string path, std::uintmax_t &capacity,
60 std::uintmax_t &free, std::uintmax_t &avail) {
61 ULARGE_INTEGER FreeBytesAvailableToCaller, TotalNumberOfBytes,
62 TotalNumberOfFreeBytes;
63 if (!GetDiskFreeSpaceExA(path.c_str(), &FreeBytesAvailableToCaller,
64 &TotalNumberOfBytes, &TotalNumberOfFreeBytes))
65 return false;
66 capacity = TotalNumberOfBytes.QuadPart;
67 free = TotalNumberOfFreeBytes.QuadPart;
68 avail = FreeBytesAvailableToCaller.QuadPart;
69 assert(capacity > 0);
70 assert(free > 0);
71 assert(avail > 0);
72 return true;
74 #else
75 using ::mkdir;
76 inline int symlink(const char* oldname, const char* newname, bool is_dir) { (void)is_dir; return ::symlink(oldname, newname); }
77 using ::link;
78 using ::setenv;
79 using ::unsetenv;
80 inline bool space(std::string path, std::uintmax_t &capacity,
81 std::uintmax_t &free, std::uintmax_t &avail) {
82 struct statvfs expect;
83 if (::statvfs(path.c_str(), &expect) == -1)
84 return false;
85 assert(expect.f_bavail > 0);
86 assert(expect.f_bfree > 0);
87 assert(expect.f_bsize > 0);
88 assert(expect.f_blocks > 0);
89 assert(expect.f_frsize > 0);
90 auto do_mult = [&](std::uintmax_t val) {
91 std::uintmax_t fsize = expect.f_frsize;
92 std::uintmax_t new_val = val * fsize;
93 assert(new_val / fsize == val); // Test for overflow
94 return new_val;
96 capacity = do_mult(expect.f_blocks);
97 free = do_mult(expect.f_bfree);
98 avail = do_mult(expect.f_bavail);
99 return true;
101 #endif
103 // N.B. libc might define some of the foo[64] identifiers using macros from
104 // foo64 -> foo or vice versa.
105 #if defined(_WIN32)
106 using off64_t = std::int64_t;
107 #elif defined(__MVS__) || defined(__LP64__)
108 using off64_t = ::off_t;
109 #else
110 using ::off64_t;
111 #endif
113 inline FILE* fopen64(const char* pathname, const char* mode) {
114 // Bionic does not distinguish between fopen and fopen64, but fopen64
115 // wasn't added until API 24.
116 #if defined(_WIN32) || defined(__MVS__) || defined(__LP64__) || defined(__BIONIC__)
117 return ::fopen(pathname, mode);
118 #else
119 return ::fopen64(pathname, mode);
120 #endif
123 inline int ftruncate64(int fd, off64_t length) {
124 #if defined(_WIN32)
125 // _chsize_s sets errno on failure and also returns the error number.
126 return ::_chsize_s(fd, length) ? -1 : 0;
127 #elif defined(__MVS__) || defined(__LP64__)
128 return ::ftruncate(fd, length);
129 #else
130 return ::ftruncate64(fd, length);
131 #endif
134 inline std::string getcwd() {
135 // Assume that path lengths are not greater than this.
136 // This should be fine for testing purposes.
137 char buf[4096];
138 char* ret = ::getcwd(buf, sizeof(buf));
139 assert(ret && "getcwd failed");
140 return std::string(ret);
143 inline bool exists(std::string const& path) {
144 struct ::stat tmp;
145 return ::stat(path.c_str(), &tmp) == 0;
147 } // end namespace utils
149 struct scoped_test_env
151 scoped_test_env() : test_root(available_cwd_path()) {
152 #ifdef _WIN32
153 // Windows mkdir can create multiple recursive directories
154 // if needed.
155 std::string cmd = "mkdir " + test_root.string();
156 #else
157 std::string cmd = "mkdir -p " + test_root.string();
158 #endif
159 int ret = std::system(cmd.c_str());
160 assert(ret == 0);
162 // Ensure that the root_path is fully resolved, i.e. it contains no
163 // symlinks. The filesystem tests depend on that. We do this after
164 // creating the root_path, because `fs::canonical` requires the
165 // path to exist.
166 test_root = fs::canonical(test_root);
169 ~scoped_test_env() {
170 #ifdef _WIN32
171 std::string cmd = "rmdir /s /q " + test_root.string();
172 int ret = std::system(cmd.c_str());
173 assert(ret == 0);
174 #else
175 #if defined(__MVS__)
176 // The behaviour of chmod -R on z/OS prevents recursive
177 // permission change for directories that do not have read permission.
178 std::string cmd = "find " + test_root.string() + " -exec chmod 777 {} \\;";
179 #else
180 std::string cmd = "chmod -R 777 " + test_root.string();
181 #endif // defined(__MVS__)
182 int ret = std::system(cmd.c_str());
183 #if !defined(_AIX)
184 // On AIX the chmod command will return non-zero when trying to set
185 // the permissions on a directory that contains a bad symlink. This triggers
186 // the assert, despite being able to delete everything with the following
187 // `rm -r` command.
188 assert(ret == 0);
189 #endif
191 cmd = "rm -rf " + test_root.string();
192 ret = std::system(cmd.c_str());
193 assert(ret == 0);
194 #endif
197 scoped_test_env(scoped_test_env const &) = delete;
198 scoped_test_env & operator=(scoped_test_env const &) = delete;
200 fs::path make_env_path(std::string p) { return sanitize_path(p); }
202 std::string sanitize_path(std::string raw) {
203 assert(raw.find("..") == std::string::npos);
204 std::string root = test_root.string();
205 if (root.compare(0, root.size(), raw, 0, root.size()) != 0) {
206 assert(raw.front() != '\\');
207 fs::path tmp(test_root);
208 tmp /= raw;
209 return tmp.string();
211 return raw;
214 // Purposefully using a size potentially larger than off_t here so we can
215 // test the behavior of libc++fs when it is built with _FILE_OFFSET_BITS=64
216 // but the caller is not (std::filesystem also uses uintmax_t rather than
217 // off_t). On a 32-bit system this allows us to create a file larger than
218 // 2GB.
219 std::string create_file(fs::path filename_path, std::uintmax_t size = 0) {
220 std::string filename = sanitize_path(filename_path.string());
222 if (size >
223 static_cast<typename std::make_unsigned<utils::off64_t>::type>(
224 std::numeric_limits<utils::off64_t>::max())) {
225 fprintf(stderr, "create_file(%s, %ju) too large\n",
226 filename.c_str(), size);
227 abort();
230 #if defined(_WIN32) || defined(__MVS__)
231 # define FOPEN_CLOEXEC_FLAG ""
232 #else
233 # define FOPEN_CLOEXEC_FLAG "e"
234 #endif
235 FILE* file = utils::fopen64(filename.c_str(), "w" FOPEN_CLOEXEC_FLAG);
236 if (file == nullptr) {
237 fprintf(stderr, "fopen %s failed: %s\n", filename.c_str(),
238 strerror(errno));
239 abort();
242 if (utils::ftruncate64(
243 fileno(file), static_cast<utils::off64_t>(size)) == -1) {
244 fprintf(stderr, "ftruncate %s %ju failed: %s\n", filename.c_str(),
245 size, strerror(errno));
246 fclose(file);
247 abort();
250 fclose(file);
251 return filename;
254 std::string create_dir(fs::path filename_path) {
255 std::string filename = filename_path.string();
256 filename = sanitize_path(std::move(filename));
257 int ret = utils::mkdir(filename.c_str(), 0777); // rwxrwxrwx mode
258 assert(ret == 0);
259 return filename;
262 std::string create_file_dir_symlink(fs::path source_path,
263 fs::path to_path,
264 bool sanitize_source = true,
265 bool is_dir = false) {
266 std::string source = source_path.string();
267 std::string to = to_path.string();
268 if (sanitize_source)
269 source = sanitize_path(std::move(source));
270 to = sanitize_path(std::move(to));
271 int ret = utils::symlink(source.c_str(), to.c_str(), is_dir);
272 assert(ret == 0);
273 return to;
276 std::string create_symlink(fs::path source_path,
277 fs::path to_path,
278 bool sanitize_source = true) {
279 return create_file_dir_symlink(source_path, to_path, sanitize_source,
280 false);
283 std::string create_directory_symlink(fs::path source_path,
284 fs::path to_path,
285 bool sanitize_source = true) {
286 return create_file_dir_symlink(source_path, to_path, sanitize_source,
287 true);
290 std::string create_hardlink(fs::path source_path, fs::path to_path) {
291 std::string source = source_path.string();
292 std::string to = to_path.string();
293 source = sanitize_path(std::move(source));
294 to = sanitize_path(std::move(to));
295 int ret = utils::link(source.c_str(), to.c_str());
296 assert(ret == 0);
297 return to;
300 #ifndef _WIN32
301 std::string create_fifo(std::string file) {
302 file = sanitize_path(std::move(file));
303 int ret = ::mkfifo(file.c_str(), 0666); // rw-rw-rw- mode
304 assert(ret == 0);
305 return file;
307 #endif
309 // Some platforms doesn't support socket files so we shouldn't even
310 // allow tests to call this unguarded.
311 #if !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(_WIN32)
312 std::string create_socket(std::string file) {
313 file = sanitize_path(std::move(file));
315 ::sockaddr_un address;
316 address.sun_family = AF_UNIX;
317 assert(file.size() <= sizeof(address.sun_path));
318 ::strncpy(address.sun_path, file.c_str(), sizeof(address.sun_path));
319 int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
320 ::bind(fd, reinterpret_cast<::sockaddr*>(&address), sizeof(address));
321 return file;
323 #endif
325 fs::path test_root;
327 private:
328 // This could potentially introduce a filesystem race if multiple
329 // scoped_test_envs were created concurrently in the same test (hence
330 // sharing the same cwd). However, it is fairly unlikely to happen as
331 // we generally don't use scoped_test_env from multiple threads, so
332 // this is deemed acceptable.
333 // The cwd.filename() itself isn't unique across all tests in the suite,
334 // so start the numbering from a hash of the full cwd, to avoid
335 // different tests interfering with each other.
336 static inline fs::path available_cwd_path() {
337 fs::path const cwd = utils::getcwd();
338 fs::path const tmp = fs::temp_directory_path();
339 std::string base = cwd.filename().string();
340 std::size_t i = std::hash<std::string>()(cwd.string());
341 fs::path p = tmp / (base + "-static_env." + std::to_string(i));
342 while (utils::exists(p.string())) {
343 p = tmp / (base + "-static_env." + std::to_string(++i));
345 return p;
349 /// This class generates the following tree:
351 /// static_test_env
352 /// |-- bad_symlink -> dne
353 /// |-- dir1
354 /// | |-- dir2
355 /// | | |-- afile3
356 /// | | |-- dir3
357 /// | | | `-- file5
358 /// | | |-- file4
359 /// | | `-- symlink_to_dir3 -> dir3
360 /// | `-- file1
361 /// | `-- file2
362 /// |-- empty_file
363 /// |-- non_empty_file
364 /// |-- symlink_to_dir -> dir1
365 /// `-- symlink_to_empty_file -> empty_file
367 class static_test_env {
368 scoped_test_env env_;
369 public:
370 static_test_env() {
371 env_.create_symlink("dne", "bad_symlink", false);
372 env_.create_dir("dir1");
373 env_.create_dir("dir1/dir2");
374 env_.create_file("dir1/dir2/afile3");
375 env_.create_dir("dir1/dir2/dir3");
376 env_.create_file("dir1/dir2/dir3/file5");
377 env_.create_file("dir1/dir2/file4");
378 env_.create_directory_symlink("dir3", "dir1/dir2/symlink_to_dir3", false);
379 env_.create_file("dir1/file1");
380 env_.create_file("dir1/file2", 42);
381 env_.create_file("empty_file");
382 env_.create_file("non_empty_file", 42);
383 env_.create_directory_symlink("dir1", "symlink_to_dir", false);
384 env_.create_symlink("empty_file", "symlink_to_empty_file", false);
387 const fs::path Root = env_.test_root;
389 fs::path makePath(fs::path const& p) const {
390 // env_path is expected not to contain symlinks.
391 fs::path const& env_path = Root;
392 return env_path / p;
395 const std::vector<fs::path> TestFileList = {
396 makePath("empty_file"),
397 makePath("non_empty_file"),
398 makePath("dir1/file1"),
399 makePath("dir1/file2")
402 const std::vector<fs::path> TestDirList = {
403 makePath("dir1"),
404 makePath("dir1/dir2"),
405 makePath("dir1/dir2/dir3")
408 const fs::path File = TestFileList[0];
409 const fs::path Dir = TestDirList[0];
410 const fs::path Dir2 = TestDirList[1];
411 const fs::path Dir3 = TestDirList[2];
412 const fs::path SymlinkToFile = makePath("symlink_to_empty_file");
413 const fs::path SymlinkToDir = makePath("symlink_to_dir");
414 const fs::path BadSymlink = makePath("bad_symlink");
415 const fs::path DNE = makePath("DNE");
416 const fs::path EmptyFile = TestFileList[0];
417 const fs::path NonEmptyFile = TestFileList[1];
418 const fs::path CharFile = "/dev/null"; // Hopefully this exists
420 const std::vector<fs::path> DirIterationList = {
421 makePath("dir1/dir2"),
422 makePath("dir1/file1"),
423 makePath("dir1/file2")
426 const std::vector<fs::path> DirIterationListDepth1 = {
427 makePath("dir1/dir2/afile3"),
428 makePath("dir1/dir2/dir3"),
429 makePath("dir1/dir2/symlink_to_dir3"),
430 makePath("dir1/dir2/file4"),
433 const std::vector<fs::path> RecDirIterationList = {
434 makePath("dir1/dir2"),
435 makePath("dir1/file1"),
436 makePath("dir1/file2"),
437 makePath("dir1/dir2/afile3"),
438 makePath("dir1/dir2/dir3"),
439 makePath("dir1/dir2/symlink_to_dir3"),
440 makePath("dir1/dir2/file4"),
441 makePath("dir1/dir2/dir3/file5")
444 const std::vector<fs::path> RecDirFollowSymlinksIterationList = {
445 makePath("dir1/dir2"),
446 makePath("dir1/file1"),
447 makePath("dir1/file2"),
448 makePath("dir1/dir2/afile3"),
449 makePath("dir1/dir2/dir3"),
450 makePath("dir1/dir2/file4"),
451 makePath("dir1/dir2/dir3/file5"),
452 makePath("dir1/dir2/symlink_to_dir3"),
453 makePath("dir1/dir2/symlink_to_dir3/file5"),
457 struct CWDGuard {
458 std::string oldCwd_;
459 CWDGuard() : oldCwd_(utils::getcwd()) { }
460 ~CWDGuard() {
461 int ret = ::chdir(oldCwd_.c_str());
462 assert(ret == 0 && "chdir failed");
465 CWDGuard(CWDGuard const&) = delete;
466 CWDGuard& operator=(CWDGuard const&) = delete;
469 // We often need to test that the error_code was cleared if no error occurs
470 // this function returns an error_code which is set to an error that will
471 // never be returned by the filesystem functions.
472 inline std::error_code GetTestEC(unsigned Idx = 0) {
473 using std::errc;
474 auto GetErrc = [&]() {
475 switch (Idx) {
476 case 0:
477 return errc::address_family_not_supported;
478 case 1:
479 return errc::address_not_available;
480 case 2:
481 return errc::address_in_use;
482 case 3:
483 return errc::argument_list_too_long;
484 default:
485 assert(false && "Idx out of range");
486 std::abort();
489 return std::make_error_code(GetErrc());
492 inline bool ErrorIsImp(const std::error_code& ec,
493 std::vector<std::errc> const& errors) {
494 std::error_condition cond = ec.default_error_condition();
495 for (auto errc : errors) {
496 if (cond.value() == static_cast<int>(errc))
497 return true;
499 return false;
502 template <class... ErrcT>
503 inline bool ErrorIs(const std::error_code& ec, std::errc First, ErrcT... Rest) {
504 std::vector<std::errc> errors = {First, Rest...};
505 return ErrorIsImp(ec, errors);
508 // Provide our own Sleep routine since std::this_thread::sleep_for is not
509 // available in single-threaded mode.
510 template <class Dur> void SleepFor(Dur dur) {
511 using namespace std::chrono;
512 #if defined(_LIBCPP_HAS_NO_MONOTONIC_CLOCK)
513 using Clock = system_clock;
514 #else
515 using Clock = steady_clock;
516 #endif
517 const auto wake_time = Clock::now() + dur;
518 while (Clock::now() < wake_time)
522 inline fs::perms NormalizeExpectedPerms(fs::perms P) {
523 #ifdef _WIN32
524 // On Windows, fs::perms only maps down to one bit stored in the filesystem,
525 // a boolean readonly flag.
526 // Normalize permissions to the format it gets returned; all fs entries are
527 // read+exec for all users; writable ones also have the write bit set for
528 // all users.
529 P |= fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read;
530 P |= fs::perms::owner_exec | fs::perms::group_exec | fs::perms::others_exec;
531 fs::perms Write =
532 fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
533 if ((P & Write) != fs::perms::none)
534 P |= Write;
535 #endif
536 return P;
539 struct ExceptionChecker {
540 std::errc expected_err;
541 fs::path expected_path1;
542 fs::path expected_path2;
543 unsigned num_paths;
544 const char* func_name;
545 std::string opt_message;
547 explicit ExceptionChecker(std::errc first_err, const char* fun_name,
548 std::string opt_msg = {})
549 : expected_err{first_err}, num_paths(0), func_name(fun_name),
550 opt_message(opt_msg) {}
551 explicit ExceptionChecker(fs::path p, std::errc first_err,
552 const char* fun_name, std::string opt_msg = {})
553 : expected_err(first_err), expected_path1(p), num_paths(1),
554 func_name(fun_name), opt_message(opt_msg) {}
556 explicit ExceptionChecker(fs::path p1, fs::path p2, std::errc first_err,
557 const char* fun_name, std::string opt_msg = {})
558 : expected_err(first_err), expected_path1(p1), expected_path2(p2),
559 num_paths(2), func_name(fun_name), opt_message(opt_msg) {}
561 void operator()(fs::filesystem_error const& Err) {
562 assert(ErrorIsImp(Err.code(), {expected_err}));
563 assert(Err.path1() == expected_path1);
564 assert(Err.path2() == expected_path2);
565 LIBCPP_ONLY(check_libcxx_string(Err));
568 void check_libcxx_string(fs::filesystem_error const& Err) {
569 std::string message = std::make_error_code(expected_err).message();
571 std::string additional_msg = "";
572 if (!opt_message.empty()) {
573 additional_msg = opt_message + ": ";
575 auto transform_path = [](const fs::path& p) {
576 return "\"" + p.string() + "\"";
578 std::string format = [&]() -> std::string {
579 switch (num_paths) {
580 case 0:
581 return format_string("filesystem error: in %s: %s%s", func_name,
582 additional_msg, message);
583 case 1:
584 return format_string("filesystem error: in %s: %s%s [%s]", func_name,
585 additional_msg, message,
586 transform_path(expected_path1).c_str());
587 case 2:
588 return format_string("filesystem error: in %s: %s%s [%s] [%s]",
589 func_name, additional_msg, message,
590 transform_path(expected_path1).c_str(),
591 transform_path(expected_path2).c_str());
592 default:
593 TEST_FAIL("unexpected case");
594 return "";
596 }();
597 assert(format == Err.what());
598 if (format != Err.what()) {
599 fprintf(stderr,
600 "filesystem_error::what() does not match expected output:\n");
601 fprintf(stderr, " expected: \"%s\"\n", format.c_str());
602 fprintf(stderr, " actual: \"%s\"\n\n", Err.what());
606 ExceptionChecker(ExceptionChecker const&) = delete;
607 ExceptionChecker& operator=(ExceptionChecker const&) = delete;
611 inline fs::path GetWindowsInaccessibleDir() {
612 // Only makes sense on windows, but the code can be compiled for
613 // any platform.
614 const fs::path dir("C:\\System Volume Information");
615 std::error_code ec;
616 const fs::path root("C:\\");
617 for (const auto &ent : fs::directory_iterator(root, ec)) {
618 if (ent != dir)
619 continue;
620 // Basic sanity checks on the directory_entry
621 if (!ent.exists() || !ent.is_directory()) {
622 fprintf(stderr, "The expected inaccessible directory \"%s\" was found "
623 "but doesn't behave as expected, skipping tests "
624 "regarding it\n", dir.string().c_str());
625 return fs::path();
627 // Check that it indeed is inaccessible as expected
628 (void)fs::exists(ent, ec);
629 if (!ec) {
630 fprintf(stderr, "The expected inaccessible directory \"%s\" was found "
631 "but seems to be accessible, skipping tests "
632 "regarding it\n", dir.string().c_str());
633 return fs::path();
635 return ent;
637 fprintf(stderr, "No inaccessible directory \"%s\" found, skipping tests "
638 "regarding it\n", dir.string().c_str());
639 return fs::path();
642 #endif /* FILESYSTEM_TEST_HELPER_H */