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