• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===----------------------------------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // UNSUPPORTED: c++98, c++03
11 
12 // <experimental/filesystem>
13 
14 // class recursive_directory_iterator
15 
16 // recursive_directory_iterator& operator++();
17 // recursive_directory_iterator& increment(error_code& ec) noexcept;
18 
19 #include <experimental/filesystem>
20 #include <type_traits>
21 #include <set>
22 #include <cassert>
23 
24 #include "test_macros.h"
25 #include "rapid-cxx-test.hpp"
26 #include "filesystem_test_helper.hpp"
27 
28 using namespace std::experimental::filesystem;
29 
30 TEST_SUITE(recursive_directory_iterator_increment_tests)
31 
TEST_CASE(test_increment_signatures)32 TEST_CASE(test_increment_signatures)
33 {
34     using D = recursive_directory_iterator;
35     recursive_directory_iterator d; ((void)d);
36     std::error_code ec; ((void)ec);
37 
38     ASSERT_SAME_TYPE(decltype(++d), recursive_directory_iterator&);
39     ASSERT_NOT_NOEXCEPT(++d);
40 
41     ASSERT_SAME_TYPE(decltype(d.increment(ec)), recursive_directory_iterator&);
42     ASSERT_NOT_NOEXCEPT(d.increment(ec));
43 }
44 
TEST_CASE(test_prefix_increment)45 TEST_CASE(test_prefix_increment)
46 {
47     const path testDir = StaticEnv::Dir;
48     const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
49                                       std::end(  StaticEnv::RecDirIterationList));
50     const recursive_directory_iterator endIt{};
51 
52     std::error_code ec;
53     recursive_directory_iterator it(testDir, ec);
54     TEST_REQUIRE(!ec);
55 
56     std::set<path> unseen_entries = dir_contents;
57     while (!unseen_entries.empty()) {
58         TEST_REQUIRE(it != endIt);
59         const path entry = *it;
60         TEST_REQUIRE(unseen_entries.erase(entry) == 1);
61         recursive_directory_iterator& it_ref = ++it;
62         TEST_CHECK(&it_ref == &it);
63     }
64 
65     TEST_CHECK(it == endIt);
66 }
67 
TEST_CASE(test_postfix_increment)68 TEST_CASE(test_postfix_increment)
69 {
70     const path testDir = StaticEnv::Dir;
71     const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
72                                       std::end(  StaticEnv::RecDirIterationList));
73     const recursive_directory_iterator endIt{};
74 
75     std::error_code ec;
76     recursive_directory_iterator it(testDir, ec);
77     TEST_REQUIRE(!ec);
78 
79     std::set<path> unseen_entries = dir_contents;
80     while (!unseen_entries.empty()) {
81         TEST_REQUIRE(it != endIt);
82         const path entry = *it;
83         TEST_REQUIRE(unseen_entries.erase(entry) == 1);
84         const path entry2 = *it++;
85         TEST_CHECK(entry2 == entry);
86     }
87     TEST_CHECK(it == endIt);
88 }
89 
90 
TEST_CASE(test_increment_method)91 TEST_CASE(test_increment_method)
92 {
93     const path testDir = StaticEnv::Dir;
94     const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
95                                       std::end(  StaticEnv::RecDirIterationList));
96     const recursive_directory_iterator endIt{};
97 
98     std::error_code ec;
99     recursive_directory_iterator it(testDir, ec);
100     TEST_REQUIRE(!ec);
101 
102     std::set<path> unseen_entries = dir_contents;
103     while (!unseen_entries.empty()) {
104         TEST_REQUIRE(it != endIt);
105         const path entry = *it;
106         TEST_REQUIRE(unseen_entries.erase(entry) == 1);
107         recursive_directory_iterator& it_ref = it.increment(ec);
108         TEST_REQUIRE(!ec);
109         TEST_CHECK(&it_ref == &it);
110     }
111 
112     TEST_CHECK(it == endIt);
113 }
114 
TEST_CASE(test_follow_symlinks)115 TEST_CASE(test_follow_symlinks)
116 {
117     const path testDir = StaticEnv::Dir;
118     auto const& IterList = StaticEnv::RecDirFollowSymlinksIterationList;
119 
120     const std::set<path> dir_contents(std::begin(IterList), std::end(IterList));
121     const recursive_directory_iterator endIt{};
122 
123     std::error_code ec;
124     recursive_directory_iterator it(testDir,
125                               directory_options::follow_directory_symlink, ec);
126     TEST_REQUIRE(!ec);
127 
128     std::set<path> unseen_entries = dir_contents;
129     while (!unseen_entries.empty()) {
130         TEST_REQUIRE(it != endIt);
131         const path entry = *it;
132 
133         TEST_REQUIRE(unseen_entries.erase(entry) == 1);
134         recursive_directory_iterator& it_ref = it.increment(ec);
135         TEST_REQUIRE(!ec);
136         TEST_CHECK(&it_ref == &it);
137     }
138     TEST_CHECK(it == endIt);
139 }
140 
TEST_CASE(access_denied_on_recursion_test_case)141 TEST_CASE(access_denied_on_recursion_test_case)
142 {
143     using namespace std::experimental::filesystem;
144     scoped_test_env env;
145     const path testFiles[] = {
146         env.create_dir("dir1"),
147         env.create_dir("dir1/dir2"),
148         env.create_file("dir1/dir2/file1"),
149         env.create_file("dir1/file2")
150     };
151     const path startDir = testFiles[0];
152     const path permDeniedDir = testFiles[1];
153     const path otherFile = testFiles[3];
154     auto SkipEPerm = directory_options::skip_permission_denied;
155 
156     // Change the permissions so we can no longer iterate
157     permissions(permDeniedDir, perms::none);
158 
159     const recursive_directory_iterator endIt;
160 
161     // Test that recursion resulting in a "EACCESS" error is not ignored
162     // by default.
163     {
164         std::error_code ec = GetTestEC();
165         recursive_directory_iterator it(startDir, ec);
166         TEST_REQUIRE(ec != GetTestEC());
167         TEST_REQUIRE(!ec);
168         while (it != endIt && it->path() != permDeniedDir)
169             ++it;
170         TEST_REQUIRE(it != endIt);
171         TEST_REQUIRE(*it == permDeniedDir);
172 
173         it.increment(ec);
174         TEST_CHECK(ec);
175         TEST_CHECK(it == endIt);
176     }
177     // Same as above but test operator++().
178     {
179         std::error_code ec = GetTestEC();
180         recursive_directory_iterator it(startDir, ec);
181         TEST_REQUIRE(!ec);
182         while (it != endIt && it->path() != permDeniedDir)
183             ++it;
184         TEST_REQUIRE(it != endIt);
185         TEST_REQUIRE(*it == permDeniedDir);
186 
187         TEST_REQUIRE_THROW(filesystem_error, ++it);
188     }
189     // Test that recursion resulting in a "EACCESS" error is ignored when the
190     // correct options are given to the constructor.
191     {
192         std::error_code ec = GetTestEC();
193         recursive_directory_iterator it(startDir, SkipEPerm, ec);
194         TEST_REQUIRE(!ec);
195         TEST_REQUIRE(it != endIt);
196 
197         bool seenOtherFile = false;
198         if (*it == otherFile) {
199             ++it;
200             seenOtherFile = true;
201             TEST_REQUIRE (it != endIt);
202         }
203         TEST_REQUIRE(*it == permDeniedDir);
204 
205         ec = GetTestEC();
206         it.increment(ec);
207         TEST_REQUIRE(!ec);
208 
209         if (seenOtherFile) {
210             TEST_CHECK(it == endIt);
211         } else {
212             TEST_CHECK(it != endIt);
213             TEST_CHECK(*it == otherFile);
214         }
215     }
216     // Test that construction resulting in a "EACCESS" error is not ignored
217     // by default.
218     {
219         std::error_code ec;
220         recursive_directory_iterator it(permDeniedDir, ec);
221         TEST_REQUIRE(ec);
222         TEST_REQUIRE(it == endIt);
223     }
224     // Same as above but testing the throwing constructors
225     {
226         TEST_REQUIRE_THROW(filesystem_error,
227                            recursive_directory_iterator(permDeniedDir));
228     }
229     // Test that construction resulting in a "EACCESS" error constructs the
230     // end iterator when the correct options are given.
231     {
232         std::error_code ec = GetTestEC();
233         recursive_directory_iterator it(permDeniedDir, SkipEPerm, ec);
234         TEST_REQUIRE(!ec);
235         TEST_REQUIRE(it == endIt);
236     }
237 }
238 
239 // See llvm.org/PR35078
TEST_CASE(test_PR35078)240 TEST_CASE(test_PR35078)
241 {
242   using namespace std::experimental::filesystem;
243     scoped_test_env env;
244     const path testFiles[] = {
245         env.create_dir("dir1"),
246         env.create_dir("dir1/dir2"),
247         env.create_dir("dir1/dir2/dir3"),
248         env.create_file("dir1/file1"),
249         env.create_file("dir1/dir2/dir3/file2")
250     };
251     const path startDir = testFiles[0];
252     const path permDeniedDir = testFiles[1];
253     const path nestedDir = testFiles[2];
254     const path nestedFile = testFiles[3];
255 
256     // Change the permissions so we can no longer iterate
257     permissions(permDeniedDir,
258                 perms::remove_perms|perms::group_exec
259                |perms::owner_exec|perms::others_exec);
260 
261     const std::error_code eacess_ec =
262         std::make_error_code(std::errc::permission_denied);
263     std::error_code ec = GetTestEC();
264 
265     const recursive_directory_iterator endIt;
266 
267     auto SetupState = [&](bool AllowEAccess, bool& SeenFile3) {
268       SeenFile3 = false;
269       auto Opts = AllowEAccess ? directory_options::skip_permission_denied
270           : directory_options::none;
271       recursive_directory_iterator it(startDir, Opts, ec);
272       while (!ec && it != endIt && *it != nestedDir) {
273         if (*it == nestedFile)
274           SeenFile3 = true;
275         it.increment(ec);
276       }
277       return it;
278     };
279 
280     {
281       bool SeenNestedFile = false;
282       recursive_directory_iterator it = SetupState(false, SeenNestedFile);
283       TEST_REQUIRE(it != endIt);
284       TEST_REQUIRE(*it == nestedDir);
285       ec = GetTestEC();
286       it.increment(ec);
287       TEST_CHECK(ec);
288       TEST_CHECK(ec == eacess_ec);
289       TEST_CHECK(it == endIt);
290     }
291     {
292       bool SeenNestedFile = false;
293       recursive_directory_iterator it = SetupState(true, SeenNestedFile);
294       TEST_REQUIRE(it != endIt);
295       TEST_REQUIRE(*it == nestedDir);
296       ec = GetTestEC();
297       it.increment(ec);
298       TEST_CHECK(!ec);
299       if (SeenNestedFile) {
300         TEST_CHECK(it == endIt);
301       } else {
302         TEST_REQUIRE(it != endIt);
303         TEST_CHECK(*it == nestedFile);
304       }
305     }
306 }
307 
308 
309 // See llvm.org/PR35078
TEST_CASE(test_PR35078_with_symlink)310 TEST_CASE(test_PR35078_with_symlink)
311 {
312   using namespace std::experimental::filesystem;
313     scoped_test_env env;
314     const path testFiles[] = {
315         env.create_dir("dir1"),
316         env.create_file("dir1/file1"),
317         env.create_dir("sym_dir"),
318         env.create_dir("sym_dir/nested_sym_dir"),
319         env.create_symlink("sym_dir/nested_sym_dir", "dir1/dir2"),
320         env.create_dir("sym_dir/dir1"),
321         env.create_dir("sym_dir/dir1/dir2"),
322 
323     };
324    // const unsigned TestFilesSize = sizeof(testFiles) / sizeof(testFiles[0]);
325     const path startDir = testFiles[0];
326     const path nestedFile = testFiles[1];
327     const path permDeniedDir = testFiles[2];
328     const path symDir = testFiles[4];
329 
330     // Change the permissions so we can no longer iterate
331     permissions(permDeniedDir,
332                 perms::remove_perms|perms::group_exec
333                |perms::owner_exec|perms::others_exec);
334 
335     const std::error_code eacess_ec =
336         std::make_error_code(std::errc::permission_denied);
337     std::error_code ec = GetTestEC();
338 
339     const recursive_directory_iterator endIt;
340 
341     auto SetupState = [&](bool AllowEAccess, bool FollowSym, bool& SeenFile3) {
342       SeenFile3 = false;
343       auto Opts = AllowEAccess ? directory_options::skip_permission_denied
344           : directory_options::none;
345       if (FollowSym)
346         Opts |= directory_options::follow_directory_symlink;
347       recursive_directory_iterator it(startDir, Opts, ec);
348       while (!ec && it != endIt && *it != symDir) {
349         if (*it == nestedFile)
350           SeenFile3 = true;
351         it.increment(ec);
352       }
353       return it;
354     };
355 
356     struct {
357       bool SkipPermDenied;
358       bool FollowSymlinks;
359       bool ExpectSuccess;
360     } TestCases[]  = {
361         // Passing cases
362         {false, false, true}, {true, true, true}, {true, false, true},
363         // Failing cases
364         {false, true, false}
365     };
366     for (auto TC : TestCases) {
367       bool SeenNestedFile = false;
368       recursive_directory_iterator it = SetupState(TC.SkipPermDenied,
369                                                    TC.FollowSymlinks,
370                                                    SeenNestedFile);
371       TEST_REQUIRE(!ec);
372       TEST_REQUIRE(it != endIt);
373       TEST_REQUIRE(*it == symDir);
374       ec = GetTestEC();
375       it.increment(ec);
376       if (TC.ExpectSuccess) {
377         TEST_CHECK(!ec);
378         if (SeenNestedFile) {
379           TEST_CHECK(it == endIt);
380         } else {
381           TEST_REQUIRE(it != endIt);
382           TEST_CHECK(*it == nestedFile);
383         }
384       } else {
385         TEST_CHECK(ec);
386         TEST_CHECK(ec == eacess_ec);
387         TEST_CHECK(it == endIt);
388       }
389     }
390 }
391 
392 
393 // See llvm.org/PR35078
TEST_CASE(test_PR35078_with_symlink_file)394 TEST_CASE(test_PR35078_with_symlink_file)
395 {
396   using namespace std::experimental::filesystem;
397     scoped_test_env env;
398     const path testFiles[] = {
399         env.create_dir("dir1"),
400         env.create_dir("dir1/dir2"),
401         env.create_file("dir1/file2"),
402         env.create_dir("sym_dir"),
403         env.create_dir("sym_dir/sdir1"),
404         env.create_file("sym_dir/sdir1/sfile1"),
405         env.create_symlink("sym_dir/sdir1/sfile1", "dir1/dir2/file1")
406     };
407     const unsigned TestFilesSize = sizeof(testFiles) / sizeof(testFiles[0]);
408     const path startDir = testFiles[0];
409     const path nestedDir = testFiles[1];
410     const path nestedFile = testFiles[2];
411     const path permDeniedDir = testFiles[3];
412     const path symFile = testFiles[TestFilesSize - 1];
413 
414     // Change the permissions so we can no longer iterate
415     permissions(permDeniedDir,
416                 perms::remove_perms|perms::group_exec
417                |perms::owner_exec|perms::others_exec);
418 
419     const std::error_code eacess_ec =
420         std::make_error_code(std::errc::permission_denied);
421     std::error_code ec = GetTestEC();
422 
423     const recursive_directory_iterator EndIt;
424 
425     auto SetupState = [&](bool AllowEAccess, bool FollowSym, bool& SeenNestedFile) {
426       SeenNestedFile = false;
427       auto Opts = AllowEAccess ? directory_options::skip_permission_denied
428           : directory_options::none;
429       if (FollowSym)
430         Opts |= directory_options::follow_directory_symlink;
431       recursive_directory_iterator it(startDir, Opts, ec);
432       while (!ec && it != EndIt && *it != nestedDir) {
433         if (*it == nestedFile)
434           SeenNestedFile = true;
435         it.increment(ec);
436       }
437       return it;
438     };
439 
440     struct {
441       bool SkipPermDenied;
442       bool FollowSymlinks;
443       bool ExpectSuccess;
444     } TestCases[]  = {
445         // Passing cases
446         {false, false, true}, {true, true, true}, {true, false, true},
447         // Failing cases
448         {false, true, false}
449     };
450     for (auto TC : TestCases){
451       bool SeenNestedFile = false;
452       recursive_directory_iterator it = SetupState(TC.SkipPermDenied,
453                                                    TC.FollowSymlinks,
454                                                    SeenNestedFile);
455       TEST_REQUIRE(!ec);
456       TEST_REQUIRE(it != EndIt);
457       TEST_REQUIRE(*it == nestedDir);
458       ec = GetTestEC();
459       it.increment(ec);
460       TEST_REQUIRE(it != EndIt);
461       TEST_CHECK(!ec);
462       TEST_CHECK(*it == symFile);
463       ec = GetTestEC();
464       it.increment(ec);
465       if (TC.ExpectSuccess) {
466         if (!SeenNestedFile) {
467           TEST_CHECK(!ec);
468           TEST_REQUIRE(it != EndIt);
469           TEST_CHECK(*it == nestedFile);
470           ec = GetTestEC();
471           it.increment(ec);
472         }
473         TEST_CHECK(!ec);
474         TEST_CHECK(it == EndIt);
475       } else {
476         TEST_CHECK(ec);
477         TEST_CHECK(ec == eacess_ec);
478         TEST_CHECK(it == EndIt);
479       }
480     }
481 }
482 
483 
484 TEST_SUITE_END()
485