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