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