blob: 8cefd985026bc1dde643a25d1593335f24b216f2 [file] [log] [blame]
Nico Webercc890632019-08-21 00:14:12 +00001#ifndef FILESYSTEM_TEST_HELPER_H
2#define FILESYSTEM_TEST_HELPER_H
Eric Fiselierc7979582016-06-17 19:46:40 +00003
Nico Webercc890632019-08-21 00:14:12 +00004#include "filesystem_include.h"
Dan Albert5452d3f2019-01-15 19:14:15 +00005
6#include <unistd.h> // for ftruncate
7
Eric Fiselierc7979582016-06-17 19:46:40 +00008#include <cassert>
9#include <cstdio> // for printf
10#include <string>
11#include <fstream>
12#include <random>
Eric Fiselier93e32122016-06-17 21:44:26 +000013#include <chrono>
Eric Fiselierc1699862018-07-20 01:22:32 +000014#include <vector>
Eric Fiselier9158bfd2018-07-23 02:00:52 +000015#include <regex>
Eric Fiselierc1699862018-07-20 01:22:32 +000016
Eric Fiselier9158bfd2018-07-23 02:00:52 +000017#include "test_macros.h"
Nico Webercc890632019-08-21 00:14:12 +000018#include "rapid-cxx-test.h"
19#include "format_string.h"
Eric Fiselierc7979582016-06-17 19:46:40 +000020
Eric Fiselierc7979582016-06-17 19:46:40 +000021// static test helpers
22
23#ifndef LIBCXX_FILESYSTEM_STATIC_TEST_ROOT
24#warning "STATIC TESTS DISABLED"
25#else // LIBCXX_FILESYSTEM_STATIC_TEST_ROOT
26
27namespace StaticEnv {
28
29inline fs::path makePath(fs::path const& p) {
Eric Fiselier48bc8502016-06-21 21:54:23 +000030 // env_path is expected not to contain symlinks.
Eric Fiselierc7979582016-06-17 19:46:40 +000031 static const fs::path env_path = LIBCXX_FILESYSTEM_STATIC_TEST_ROOT;
32 return env_path / p;
33}
34
35static const fs::path Root = LIBCXX_FILESYSTEM_STATIC_TEST_ROOT;
36
37static const fs::path TestFileList[] = {
38 makePath("empty_file"),
39 makePath("non_empty_file"),
40 makePath("dir1/file1"),
41 makePath("dir1/file2")
42};
43const std::size_t TestFileListSize = sizeof(TestFileList) / sizeof(fs::path);
44
45static const fs::path TestDirList[] = {
46 makePath("dir1"),
47 makePath("dir1/dir2"),
48 makePath("dir1/dir2/dir3")
49};
50const std::size_t TestDirListSize = sizeof(TestDirList) / sizeof(fs::path);
51
52static const fs::path File = TestFileList[0];
53static const fs::path Dir = TestDirList[0];
54static const fs::path Dir2 = TestDirList[1];
55static const fs::path Dir3 = TestDirList[2];
56static const fs::path SymlinkToFile = makePath("symlink_to_empty_file");
57static const fs::path SymlinkToDir = makePath("symlink_to_dir");
58static const fs::path BadSymlink = makePath("bad_symlink");
59static const fs::path DNE = makePath("DNE");
60static const fs::path EmptyFile = TestFileList[0];
61static const fs::path NonEmptyFile = TestFileList[1];
62static const fs::path CharFile = "/dev/null"; // Hopefully this exists
63
64static const fs::path DirIterationList[] = {
65 makePath("dir1/dir2"),
66 makePath("dir1/file1"),
67 makePath("dir1/file2")
68};
69const std::size_t DirIterationListSize = sizeof(DirIterationList)
70 / sizeof(fs::path);
71
72static const fs::path DirIterationListDepth1[] = {
73 makePath("dir1/dir2/afile3"),
74 makePath("dir1/dir2/dir3"),
75 makePath("dir1/dir2/symlink_to_dir3"),
76 makePath("dir1/dir2/file4"),
77};
78
79static const fs::path RecDirIterationList[] = {
80 makePath("dir1/dir2"),
81 makePath("dir1/file1"),
82 makePath("dir1/file2"),
83 makePath("dir1/dir2/afile3"),
84 makePath("dir1/dir2/dir3"),
85 makePath("dir1/dir2/symlink_to_dir3"),
86 makePath("dir1/dir2/file4"),
87 makePath("dir1/dir2/dir3/file5")
88};
89
90static const fs::path RecDirFollowSymlinksIterationList[] = {
91 makePath("dir1/dir2"),
92 makePath("dir1/file1"),
93 makePath("dir1/file2"),
94 makePath("dir1/dir2/afile3"),
95 makePath("dir1/dir2/dir3"),
96 makePath("dir1/dir2/file4"),
97 makePath("dir1/dir2/dir3/file5"),
98 makePath("dir1/dir2/symlink_to_dir3"),
99 makePath("dir1/dir2/symlink_to_dir3/file5"),
100};
101
102} // namespace StaticEnv
103
104#endif // LIBCXX_FILESYSTEM_STATIC_TEST_ROOT
105
106#ifndef LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT
107#warning LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT must be defined
108#else // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT
109
110#ifndef LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER
111#error LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER must be defined
112#endif
113
Eric Fiselier7c0ed442018-07-22 02:00:53 +0000114namespace random_utils {
115inline char to_hex(int ch) {
116 return ch < 10 ? static_cast<char>('0' + ch)
117 : static_cast<char>('a' + (ch - 10));
118}
119
120inline char random_hex_char() {
121 static std::mt19937 rd{std::random_device{}()};
122 static std::uniform_int_distribution<int> mrand{0, 15};
123 return to_hex(mrand(rd));
124}
125
126} // namespace random_utils
127
Eric Fiselierc7979582016-06-17 19:46:40 +0000128struct scoped_test_env
129{
130 scoped_test_env() : test_root(random_env_path())
131 { fs_helper_run(fs_make_cmd("init_test_directory", test_root)); }
132
133 ~scoped_test_env()
134 { fs_helper_run(fs_make_cmd("destroy_test_directory", test_root)); }
135
136 scoped_test_env(scoped_test_env const &) = delete;
137 scoped_test_env & operator=(scoped_test_env const &) = delete;
138
139 fs::path make_env_path(std::string p) { return sanitize_path(p); }
140
141 std::string sanitize_path(std::string raw) {
142 assert(raw.find("..") == std::string::npos);
143 std::string const& root = test_root.native();
144 if (root.compare(0, root.size(), raw, 0, root.size()) != 0) {
145 assert(raw.front() != '\\');
146 fs::path tmp(test_root);
147 tmp /= raw;
148 return std::move(const_cast<std::string&>(tmp.native()));
149 }
150 return raw;
151 }
152
Dan Albert5452d3f2019-01-15 19:14:15 +0000153 // Purposefully using a size potentially larger than off_t here so we can
154 // test the behavior of libc++fs when it is built with _FILE_OFFSET_BITS=64
155 // but the caller is not (std::filesystem also uses uintmax_t rather than
156 // off_t). On a 32-bit system this allows us to create a file larger than
157 // 2GB.
158 std::string create_file(std::string filename, uintmax_t size = 0) {
159#if defined(__LP64__)
160 auto large_file_fopen = fopen;
161 auto large_file_ftruncate = ftruncate;
162 using large_file_offset_t = off_t;
163#else
164 auto large_file_fopen = fopen64;
165 auto large_file_ftruncate = ftruncate64;
166 using large_file_offset_t = off64_t;
167#endif
168
Eric Fiselierc7979582016-06-17 19:46:40 +0000169 filename = sanitize_path(std::move(filename));
Dan Albert5452d3f2019-01-15 19:14:15 +0000170
171 if (size > std::numeric_limits<large_file_offset_t>::max()) {
172 fprintf(stderr, "create_file(%s, %ju) too large\n",
173 filename.c_str(), size);
174 abort();
Eric Fiselierc7979582016-06-17 19:46:40 +0000175 }
Dan Albert5452d3f2019-01-15 19:14:15 +0000176
177 FILE* file = large_file_fopen(filename.c_str(), "we");
178 if (file == nullptr) {
179 fprintf(stderr, "fopen %s failed: %s\n", filename.c_str(),
180 strerror(errno));
181 abort();
182 }
183
184 if (large_file_ftruncate(
185 fileno(file), static_cast<large_file_offset_t>(size)) == -1) {
186 fprintf(stderr, "ftruncate %s %ju failed: %s\n", filename.c_str(),
187 size, strerror(errno));
188 fclose(file);
189 abort();
190 }
191
192 fclose(file);
Eric Fiselierc7979582016-06-17 19:46:40 +0000193 return filename;
194 }
195
196 std::string create_dir(std::string filename) {
197 filename = sanitize_path(std::move(filename));
198 fs_helper_run(fs_make_cmd("create_dir", filename));
199 return filename;
200 }
201
202 std::string create_symlink(std::string source, std::string to) {
203 source = sanitize_path(std::move(source));
204 to = sanitize_path(std::move(to));
205 fs_helper_run(fs_make_cmd("create_symlink", source, to));
206 return to;
207 }
208
209 std::string create_hardlink(std::string source, std::string to) {
210 source = sanitize_path(std::move(source));
211 to = sanitize_path(std::move(to));
212 fs_helper_run(fs_make_cmd("create_hardlink", source, to));
213 return to;
214 }
215
216 std::string create_fifo(std::string file) {
217 file = sanitize_path(std::move(file));
218 fs_helper_run(fs_make_cmd("create_fifo", file));
219 return file;
220 }
221
222 // OS X and FreeBSD doesn't support socket files so we shouldn't even
223 // allow tests to call this unguarded.
224#if !defined(__FreeBSD__) && !defined(__APPLE__)
225 std::string create_socket(std::string file) {
226 file = sanitize_path(std::move(file));
227 fs_helper_run(fs_make_cmd("create_socket", file));
228 return file;
229 }
230#endif
231
232 fs::path const test_root;
233
234private:
Eric Fiselierc7979582016-06-17 19:46:40 +0000235 static std::string unique_path_suffix() {
236 std::string model = "test.%%%%%%";
237 for (auto & ch : model) {
Eric Fiselier7c0ed442018-07-22 02:00:53 +0000238 if (ch == '%')
239 ch = random_utils::random_hex_char();
Eric Fiselierc7979582016-06-17 19:46:40 +0000240 }
241 return model;
242 }
243
244 // This could potentially introduce a filesystem race with other tests
245 // running at the same time, but oh well, it's just test code.
246 static inline fs::path random_env_path() {
247 static const char* env_path = LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT;
248 fs::path p = fs::path(env_path) / unique_path_suffix();
249 assert(p.parent_path() == env_path);
250 return p;
251 }
252
253 static inline std::string make_arg(std::string const& arg) {
254 return "'" + arg + "'";
255 }
256
257 static inline std::string make_arg(std::size_t arg) {
258 return std::to_string(arg);
259 }
260
261 template <class T>
262 static inline std::string
263 fs_make_cmd(std::string const& cmd_name, T const& arg) {
264 return cmd_name + "(" + make_arg(arg) + ")";
265 }
266
267 template <class T, class U>
268 static inline std::string
269 fs_make_cmd(std::string const& cmd_name, T const& arg1, U const& arg2) {
270 return cmd_name + "(" + make_arg(arg1) + ", " + make_arg(arg2) + ")";
271 }
272
273 static inline void fs_helper_run(std::string const& raw_cmd) {
Eric Fiselier78046e42017-05-09 23:47:20 +0000274 // check that the fs test root in the environment matches what we were
Eric Fiselierc7979582016-06-17 19:46:40 +0000275 // compiled with.
276 static bool checked = checkDynamicTestRoot();
Eric Fiselierfd838222016-12-23 23:37:52 +0000277 ((void)checked);
Eric Fiselierc7979582016-06-17 19:46:40 +0000278 std::string cmd = LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER;
279 cmd += " \"" + raw_cmd + "\"";
280 int ret = std::system(cmd.c_str());
281 assert(ret == 0);
282 }
283
284 static bool checkDynamicTestRoot() {
Eric Fiselier48bc8502016-06-21 21:54:23 +0000285 // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT is expected not to contain symlinks.
Eric Fiselierc7979582016-06-17 19:46:40 +0000286 char* fs_root = std::getenv("LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT");
287 if (!fs_root) {
288 std::printf("ERROR: LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT must be a defined "
289 "environment variable when running the test.\n");
290 std::abort();
291 }
292 if (std::string(fs_root) != LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT) {
Eric Fiselier78046e42017-05-09 23:47:20 +0000293 std::printf("ERROR: LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT environment variable"
Eric Fiselierc7979582016-06-17 19:46:40 +0000294 " must have the same value as when the test was compiled.\n");
295 std::printf(" Current Value: '%s'\n", fs_root);
296 std::printf(" Expected Value: '%s'\n", LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT);
297 std::abort();
298 }
299 return true;
300 }
301
302};
303
304#endif // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT
305
306// Misc test types
307
308#define CONCAT2(LHS, RHS) LHS##RHS
309#define CONCAT(LHS, RHS) CONCAT2(LHS, RHS)
310#define MKSTR(Str) {Str, CONCAT(L, Str), CONCAT(u, Str), CONCAT(U, Str)}
311
312struct MultiStringType {
313 const char* s;
314 const wchar_t* w;
315 const char16_t* u16;
316 const char32_t* u32;
317
318 operator const char* () const { return s; }
319 operator const wchar_t* () const { return w; }
320 operator const char16_t* () const { return u16; }
321 operator const char32_t* () const { return u32; }
322};
323
324const MultiStringType PathList[] = {
325 MKSTR(""),
326 MKSTR(" "),
327 MKSTR("//"),
328 MKSTR("."),
329 MKSTR(".."),
330 MKSTR("foo"),
331 MKSTR("/"),
332 MKSTR("/foo"),
333 MKSTR("foo/"),
334 MKSTR("/foo/"),
335 MKSTR("foo/bar"),
336 MKSTR("/foo/bar"),
337 MKSTR("//net"),
338 MKSTR("//net/foo"),
339 MKSTR("///foo///"),
340 MKSTR("///foo///bar"),
341 MKSTR("/."),
342 MKSTR("./"),
343 MKSTR("/.."),
344 MKSTR("../"),
345 MKSTR("foo/."),
346 MKSTR("foo/.."),
347 MKSTR("foo/./"),
348 MKSTR("foo/./bar"),
349 MKSTR("foo/../"),
350 MKSTR("foo/../bar"),
351 MKSTR("c:"),
352 MKSTR("c:/"),
353 MKSTR("c:foo"),
354 MKSTR("c:/foo"),
355 MKSTR("c:foo/"),
356 MKSTR("c:/foo/"),
357 MKSTR("c:/foo/bar"),
358 MKSTR("prn:"),
359 MKSTR("c:\\"),
360 MKSTR("c:\\foo"),
361 MKSTR("c:foo\\"),
362 MKSTR("c:\\foo\\"),
363 MKSTR("c:\\foo/"),
364 MKSTR("c:/foo\\bar"),
365 MKSTR("//"),
366 MKSTR("/finally/we/need/one/really/really/really/really/really/really/really/long/string")
367};
368const unsigned PathListSize = sizeof(PathList) / sizeof(MultiStringType);
369
370template <class Iter>
371Iter IterEnd(Iter B) {
372 using VT = typename std::iterator_traits<Iter>::value_type;
373 for (; *B != VT{}; ++B)
374 ;
375 return B;
376}
377
378template <class CharT>
379const CharT* StrEnd(CharT const* P) {
380 return IterEnd(P);
381}
382
383template <class CharT>
384std::size_t StrLen(CharT const* P) {
385 return StrEnd(P) - P;
386}
387
388// Testing the allocation behavior of the code_cvt functions requires
389// *knowing* that the allocation was not done by "path::__str_".
390// This hack forces path to allocate enough memory.
391inline void PathReserve(fs::path& p, std::size_t N) {
392 auto const& native_ref = p.native();
393 const_cast<std::string&>(native_ref).reserve(N);
394}
395
396template <class Iter1, class Iter2>
397bool checkCollectionsEqual(
398 Iter1 start1, Iter1 const end1
399 , Iter2 start2, Iter2 const end2
400 )
401{
402 while (start1 != end1 && start2 != end2) {
403 if (*start1 != *start2) {
404 return false;
405 }
406 ++start1; ++start2;
407 }
408 return (start1 == end1 && start2 == end2);
409}
410
Eric Fiselierdc62be192016-06-18 04:10:23 +0000411
412template <class Iter1, class Iter2>
413bool checkCollectionsEqualBackwards(
414 Iter1 const start1, Iter1 end1
415 , Iter2 const start2, Iter2 end2
416 )
417{
418 while (start1 != end1 && start2 != end2) {
419 --end1; --end2;
420 if (*end1 != *end2) {
421 return false;
422 }
423 }
424 return (start1 == end1 && start2 == end2);
425}
426
Eric Fiselierc7979582016-06-17 19:46:40 +0000427// We often need to test that the error_code was cleared if no error occurs
Stephan T. Lavaveja730ed32017-01-18 20:10:25 +0000428// this function returns an error_code which is set to an error that will
Eric Fiselierc7979582016-06-17 19:46:40 +0000429// never be returned by the filesystem functions.
Eric Fiselierc1699862018-07-20 01:22:32 +0000430inline std::error_code GetTestEC(unsigned Idx = 0) {
431 using std::errc;
432 auto GetErrc = [&]() {
433 switch (Idx) {
434 case 0:
435 return errc::address_family_not_supported;
436 case 1:
437 return errc::address_not_available;
438 case 2:
439 return errc::address_in_use;
440 case 3:
441 return errc::argument_list_too_long;
442 default:
443 assert(false && "Idx out of range");
444 std::abort();
445 }
446 };
447 return std::make_error_code(GetErrc());
448}
449
450inline bool ErrorIsImp(const std::error_code& ec,
451 std::vector<std::errc> const& errors) {
452 for (auto errc : errors) {
453 if (ec == std::make_error_code(errc))
454 return true;
455 }
456 return false;
457}
458
459template <class... ErrcT>
460inline bool ErrorIs(const std::error_code& ec, std::errc First, ErrcT... Rest) {
461 std::vector<std::errc> errors = {First, Rest...};
462 return ErrorIsImp(ec, errors);
Eric Fiselierc7979582016-06-17 19:46:40 +0000463}
464
Eric Fiselier93e32122016-06-17 21:44:26 +0000465// Provide our own Sleep routine since std::this_thread::sleep_for is not
466// available in single-threaded mode.
467void SleepFor(std::chrono::seconds dur) {
468 using namespace std::chrono;
Eric Fiselier824ed8c2016-06-18 18:32:26 +0000469#if defined(_LIBCPP_HAS_NO_MONOTONIC_CLOCK)
470 using Clock = system_clock;
471#else
472 using Clock = steady_clock;
473#endif
474 const auto wake_time = Clock::now() + dur;
475 while (Clock::now() < wake_time)
Eric Fiselier93e32122016-06-17 21:44:26 +0000476 ;
477}
478
Eric Fiselierd7fae182018-04-02 23:03:41 +0000479inline bool PathEq(fs::path const& LHS, fs::path const& RHS) {
480 return LHS.native() == RHS.native();
481}
482
Eric Fiselierc1699862018-07-20 01:22:32 +0000483struct ExceptionChecker {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000484 std::errc expected_err;
Eric Fiselierc1699862018-07-20 01:22:32 +0000485 fs::path expected_path1;
486 fs::path expected_path2;
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000487 unsigned num_paths;
488 const char* func_name;
489 std::string opt_message;
Eric Fiselierc1699862018-07-20 01:22:32 +0000490
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000491 explicit ExceptionChecker(std::errc first_err, const char* func_name,
492 std::string opt_msg = {})
493 : expected_err{first_err}, num_paths(0), func_name(func_name),
494 opt_message(opt_msg) {}
495 explicit ExceptionChecker(fs::path p, std::errc first_err,
496 const char* func_name, std::string opt_msg = {})
497 : expected_err(first_err), expected_path1(p), num_paths(1),
498 func_name(func_name), opt_message(opt_msg) {}
Eric Fiselierc1699862018-07-20 01:22:32 +0000499
Eric Fiselierc1699862018-07-20 01:22:32 +0000500 explicit ExceptionChecker(fs::path p1, fs::path p2, std::errc first_err,
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000501 const char* func_name, std::string opt_msg = {})
502 : expected_err(first_err), expected_path1(p1), expected_path2(p2),
503 num_paths(2), func_name(func_name), opt_message(opt_msg) {}
Eric Fiselierc1699862018-07-20 01:22:32 +0000504
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000505 void operator()(fs::filesystem_error const& Err) {
506 TEST_CHECK(ErrorIsImp(Err.code(), {expected_err}));
Eric Fiselierc1699862018-07-20 01:22:32 +0000507 TEST_CHECK(Err.path1() == expected_path1);
508 TEST_CHECK(Err.path2() == expected_path2);
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000509 LIBCPP_ONLY(check_libcxx_string(Err));
Eric Fiselierc1699862018-07-20 01:22:32 +0000510 }
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000511
512 void check_libcxx_string(fs::filesystem_error const& Err) {
513 std::string message = std::make_error_code(expected_err).message();
514
515 std::string additional_msg = "";
516 if (!opt_message.empty()) {
517 additional_msg = opt_message + ": ";
518 }
519 auto transform_path = [](const fs::path& p) {
520 if (p.native().empty())
521 return "\"\"";
522 return p.c_str();
523 };
524 std::string format = [&]() -> std::string {
525 switch (num_paths) {
526 case 0:
527 return format_string("filesystem error: in %s: %s%s", func_name,
528 additional_msg, message);
529 case 1:
530 return format_string("filesystem error: in %s: %s%s [%s]", func_name,
531 additional_msg, message,
532 transform_path(expected_path1));
533 case 2:
534 return format_string("filesystem error: in %s: %s%s [%s] [%s]",
535 func_name, additional_msg, message,
536 transform_path(expected_path1),
537 transform_path(expected_path2));
538 default:
539 TEST_CHECK(false && "unexpected case");
540 return "";
541 }
542 }();
543 TEST_CHECK(format == Err.what());
544 if (format != Err.what()) {
545 fprintf(stderr,
546 "filesystem_error::what() does not match expected output:\n");
547 fprintf(stderr, " expected: \"%s\"\n", format.c_str());
548 fprintf(stderr, " actual: \"%s\"\n\n", Err.what());
549 }
550 }
551
552 ExceptionChecker(ExceptionChecker const&) = delete;
553 ExceptionChecker& operator=(ExceptionChecker const&) = delete;
554
Eric Fiselierc1699862018-07-20 01:22:32 +0000555};
556
Eric Fiselierc7979582016-06-17 19:46:40 +0000557#endif /* FILESYSTEM_TEST_HELPER_HPP */