• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
12 namespace 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 
20 namespace StaticEnv {
21 
makePath(fs::path const & p)22 inline 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 
28 static const fs::path Root = LIBCXX_FILESYSTEM_STATIC_TEST_ROOT;
29 
30 static const fs::path TestFileList[] = {
31         makePath("empty_file"),
32         makePath("non_empty_file"),
33         makePath("dir1/file1"),
34         makePath("dir1/file2")
35 };
36 const std::size_t TestFileListSize = sizeof(TestFileList) / sizeof(fs::path);
37 
38 static const fs::path TestDirList[] = {
39         makePath("dir1"),
40         makePath("dir1/dir2"),
41         makePath("dir1/dir2/dir3")
42 };
43 const std::size_t TestDirListSize = sizeof(TestDirList) / sizeof(fs::path);
44 
45 static const fs::path File          = TestFileList[0];
46 static const fs::path Dir           = TestDirList[0];
47 static const fs::path Dir2          = TestDirList[1];
48 static const fs::path Dir3          = TestDirList[2];
49 static const fs::path SymlinkToFile = makePath("symlink_to_empty_file");
50 static const fs::path SymlinkToDir  = makePath("symlink_to_dir");
51 static const fs::path BadSymlink    = makePath("bad_symlink");
52 static const fs::path DNE           = makePath("DNE");
53 static const fs::path EmptyFile     = TestFileList[0];
54 static const fs::path NonEmptyFile  = TestFileList[1];
55 static const fs::path CharFile      = "/dev/null"; // Hopefully this exists
56 
57 static const fs::path DirIterationList[] = {
58     makePath("dir1/dir2"),
59     makePath("dir1/file1"),
60     makePath("dir1/file2")
61 };
62 const std::size_t DirIterationListSize = sizeof(DirIterationList)
63                                         / sizeof(fs::path);
64 
65 static 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 
72 static 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 
83 static 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 
107 struct scoped_test_env
108 {
scoped_test_envscoped_test_env109     scoped_test_env() : test_root(random_env_path())
110         { fs_helper_run(fs_make_cmd("init_test_directory", test_root)); }
111 
~scoped_test_envscoped_test_env112     ~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 
make_env_pathscoped_test_env118     fs::path make_env_path(std::string p) { return sanitize_path(p); }
119 
sanitize_pathscoped_test_env120     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 
create_filescoped_test_env132     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 
create_dirscoped_test_env142     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 
create_symlinkscoped_test_env148     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 
create_hardlinkscoped_test_env155     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 
create_fifoscoped_test_env162     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__)
create_socketscoped_test_env171     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 
180 private:
to_hexscoped_test_env181     static char to_hex(int ch) {
182         return ch < 10 ? static_cast<char>('0' + ch)
183                        : static_cast<char>('a' + (ch - 10));
184     }
185 
random_hex_charscoped_test_env186     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 
unique_path_suffixscoped_test_env192     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.
random_env_pathscoped_test_env202     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 
make_argscoped_test_env209     static inline std::string make_arg(std::string const& arg) {
210         return "'" + arg + "'";
211     }
212 
make_argscoped_test_env213     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
fs_make_cmdscoped_test_env219     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
fs_make_cmdscoped_test_env225     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 
fs_helper_runscoped_test_env229     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 
checkDynamicTestRootscoped_test_env240     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 
268 struct MultiStringType {
269   const char* s;
270   const wchar_t* w;
271   const char16_t* u16;
272   const char32_t* u32;
273 
operator const char*MultiStringType274   operator const char* () const { return s; }
operator const wchar_t*MultiStringType275   operator const wchar_t* () const { return w; }
operator const char16_t*MultiStringType276   operator const char16_t* () const { return u16; }
operator const char32_t*MultiStringType277   operator const char32_t* () const { return u32; }
278 };
279 
280 const 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 };
324 const unsigned PathListSize = sizeof(PathList) / sizeof(MultiStringType);
325 
326 template <class Iter>
IterEnd(Iter B)327 Iter IterEnd(Iter B) {
328   using VT = typename std::iterator_traits<Iter>::value_type;
329   for (; *B != VT{}; ++B)
330     ;
331   return B;
332 }
333 
334 template <class CharT>
StrEnd(CharT const * P)335 const CharT* StrEnd(CharT const* P) {
336     return IterEnd(P);
337 }
338 
339 template <class CharT>
StrLen(CharT const * P)340 std::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.
PathReserve(fs::path & p,std::size_t N)347 inline 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 
352 template <class Iter1, class Iter2>
checkCollectionsEqual(Iter1 start1,Iter1 const end1,Iter2 start2,Iter2 const end2)353 bool 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 
368 template <class Iter1, class Iter2>
checkCollectionsEqualBackwards(Iter1 const start1,Iter1 end1,Iter2 const start2,Iter2 end2)369 bool 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.
GetTestEC()386 inline 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.
SleepFor(std::chrono::seconds dur)392 void 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