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#include <iostream>
28
29using namespace std::experimental::filesystem;
30
31TEST_SUITE(recursive_directory_iterator_increment_tests)
32
33TEST_CASE(test_increment_signatures)
34{
35    using D = recursive_directory_iterator;
36    recursive_directory_iterator d; ((void)d);
37    std::error_code ec; ((void)ec);
38
39    ASSERT_SAME_TYPE(decltype(++d), recursive_directory_iterator&);
40    ASSERT_NOT_NOEXCEPT(++d);
41
42    ASSERT_SAME_TYPE(decltype(d.increment(ec)), recursive_directory_iterator&);
43    ASSERT_NOEXCEPT(d.increment(ec));
44}
45
46TEST_CASE(test_prefix_increment)
47{
48    const path testDir = StaticEnv::Dir;
49    const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
50                                      std::end(  StaticEnv::RecDirIterationList));
51    const recursive_directory_iterator endIt{};
52
53    std::error_code ec;
54    recursive_directory_iterator it(testDir, ec);
55    TEST_REQUIRE(!ec);
56
57    std::set<path> unseen_entries = dir_contents;
58    while (!unseen_entries.empty()) {
59        TEST_REQUIRE(it != endIt);
60        const path entry = *it;
61        TEST_REQUIRE(unseen_entries.erase(entry) == 1);
62        recursive_directory_iterator& it_ref = ++it;
63        TEST_CHECK(&it_ref == &it);
64    }
65
66    TEST_CHECK(it == endIt);
67}
68
69TEST_CASE(test_postfix_increment)
70{
71    const path testDir = StaticEnv::Dir;
72    const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
73                                      std::end(  StaticEnv::RecDirIterationList));
74    const recursive_directory_iterator endIt{};
75
76    std::error_code ec;
77    recursive_directory_iterator it(testDir, ec);
78    TEST_REQUIRE(!ec);
79
80    std::set<path> unseen_entries = dir_contents;
81    while (!unseen_entries.empty()) {
82        TEST_REQUIRE(it != endIt);
83        const path entry = *it;
84        TEST_REQUIRE(unseen_entries.erase(entry) == 1);
85        const path entry2 = *it++;
86        TEST_CHECK(entry2 == entry);
87    }
88    TEST_CHECK(it == endIt);
89}
90
91
92TEST_CASE(test_increment_method)
93{
94    const path testDir = StaticEnv::Dir;
95    const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
96                                      std::end(  StaticEnv::RecDirIterationList));
97    const recursive_directory_iterator endIt{};
98
99    std::error_code ec;
100    recursive_directory_iterator it(testDir, ec);
101    TEST_REQUIRE(!ec);
102
103    std::set<path> unseen_entries = dir_contents;
104    while (!unseen_entries.empty()) {
105        TEST_REQUIRE(it != endIt);
106        const path entry = *it;
107        TEST_REQUIRE(unseen_entries.erase(entry) == 1);
108        recursive_directory_iterator& it_ref = it.increment(ec);
109        TEST_REQUIRE(!ec);
110        TEST_CHECK(&it_ref == &it);
111    }
112
113    TEST_CHECK(it == endIt);
114}
115
116TEST_CASE(test_follow_symlinks)
117{
118    const path testDir = StaticEnv::Dir;
119    auto const& IterList = StaticEnv::RecDirFollowSymlinksIterationList;
120
121    const std::set<path> dir_contents(std::begin(IterList), std::end(IterList));
122    const recursive_directory_iterator endIt{};
123
124    std::error_code ec;
125    recursive_directory_iterator it(testDir,
126                              directory_options::follow_directory_symlink, ec);
127    TEST_REQUIRE(!ec);
128
129    std::set<path> unseen_entries = dir_contents;
130    while (!unseen_entries.empty()) {
131        TEST_REQUIRE(it != endIt);
132        const path entry = *it;
133
134        TEST_REQUIRE(unseen_entries.erase(entry) == 1);
135        recursive_directory_iterator& it_ref = it.increment(ec);
136        TEST_REQUIRE(!ec);
137        TEST_CHECK(&it_ref == &it);
138    }
139    TEST_CHECK(it == endIt);
140}
141
142TEST_CASE(access_denied_on_recursion_test_case)
143{
144    using namespace std::experimental::filesystem;
145    scoped_test_env env;
146    const path testFiles[] = {
147        env.create_dir("dir1"),
148        env.create_dir("dir1/dir2"),
149        env.create_file("dir1/dir2/file1"),
150        env.create_file("dir1/file2")
151    };
152    const path startDir = testFiles[0];
153    const path permDeniedDir = testFiles[1];
154    const path otherFile = testFiles[3];
155    auto SkipEPerm = directory_options::skip_permission_denied;
156
157    // Change the permissions so we can no longer iterate
158    permissions(permDeniedDir, perms::none);
159
160    const recursive_directory_iterator endIt;
161
162    // Test that recursion resulting in a "EACCESS" error is not ignored
163    // by default.
164    {
165        std::error_code ec = GetTestEC();
166        recursive_directory_iterator it(startDir, ec);
167        TEST_REQUIRE(ec != GetTestEC());
168        TEST_REQUIRE(!ec);
169        while (it != endIt && it->path() != permDeniedDir)
170            ++it;
171        TEST_REQUIRE(it != endIt);
172        TEST_REQUIRE(*it == permDeniedDir);
173
174        it.increment(ec);
175        TEST_CHECK(ec);
176        TEST_CHECK(it == endIt);
177    }
178    // Same as above but test operator++().
179    {
180        std::error_code ec = GetTestEC();
181        recursive_directory_iterator it(startDir, ec);
182        TEST_REQUIRE(!ec);
183        while (it != endIt && it->path() != permDeniedDir)
184            ++it;
185        TEST_REQUIRE(it != endIt);
186        TEST_REQUIRE(*it == permDeniedDir);
187
188        TEST_REQUIRE_THROW(filesystem_error, ++it);
189    }
190    // Test that recursion resulting in a "EACCESS" error is ignored when the
191    // correct options are given to the constructor.
192    {
193        std::error_code ec = GetTestEC();
194        recursive_directory_iterator it(startDir, SkipEPerm, ec);
195        TEST_REQUIRE(!ec);
196        TEST_REQUIRE(it != endIt);
197
198        bool seenOtherFile = false;
199        if (*it == otherFile) {
200            ++it;
201            seenOtherFile = true;
202            TEST_REQUIRE (it != endIt);
203        }
204        TEST_REQUIRE(*it == permDeniedDir);
205
206        ec = GetTestEC();
207        it.increment(ec);
208        TEST_REQUIRE(!ec);
209
210        if (seenOtherFile) {
211            TEST_CHECK(it == endIt);
212        } else {
213            TEST_CHECK(it != endIt);
214            TEST_CHECK(*it == otherFile);
215        }
216    }
217    // Test that construction resulting in a "EACCESS" error is not ignored
218    // by default.
219    {
220        std::error_code ec;
221        recursive_directory_iterator it(permDeniedDir, ec);
222        TEST_REQUIRE(ec);
223        TEST_REQUIRE(it == endIt);
224    }
225    // Same as above but testing the throwing constructors
226    {
227        TEST_REQUIRE_THROW(filesystem_error,
228                           recursive_directory_iterator(permDeniedDir));
229    }
230    // Test that construction resulting in a "EACCESS" error constructs the
231    // end iterator when the correct options are given.
232    {
233        std::error_code ec = GetTestEC();
234        recursive_directory_iterator it(permDeniedDir, SkipEPerm, ec);
235        TEST_REQUIRE(!ec);
236        TEST_REQUIRE(it == endIt);
237    }
238}
239
240TEST_SUITE_END()
241