1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // REQUIRES: can-create-symlinks
10 // UNSUPPORTED: c++03, c++11, c++14
11 // UNSUPPORTED: no-filesystem
12 // UNSUPPORTED: availability-filesystem-missing
13 
14 // <filesystem>
15 
16 // class directory_iterator
17 
18 //
19 // explicit recursive_directory_iterator(const path& p);
20 // recursive_directory_iterator(const path& p, directory_options options);
21 // recursive_directory_iterator(const path& p, error_code& ec);
22 // recursive_directory_iterator(const path& p, directory_options options, error_code& ec);
23 
24 #include <filesystem>
25 #include <type_traits>
26 #include <set>
27 #include <cassert>
28 
29 #include "assert_macros.h"
30 #include "test_macros.h"
31 #include "filesystem_test_helper.h"
32 namespace fs = std::filesystem;
33 using namespace fs;
34 
35 using RDI = recursive_directory_iterator;
36 
test_constructor_signatures()37 static void test_constructor_signatures()
38 {
39     using D = recursive_directory_iterator;
40 
41     // explicit directory_iterator(path const&);
42     static_assert(!std::is_convertible<path, D>::value, "");
43     static_assert(std::is_constructible<D, path>::value, "");
44     static_assert(!std::is_nothrow_constructible<D, path>::value, "");
45 
46     // directory_iterator(path const&, error_code&)
47     static_assert(std::is_constructible<D, path,
48         std::error_code&>::value, "");
49     static_assert(!std::is_nothrow_constructible<D, path,
50         std::error_code&>::value, "");
51 
52     // directory_iterator(path const&, directory_options);
53     static_assert(std::is_constructible<D, path, directory_options>::value, "");
54     static_assert(!std::is_nothrow_constructible<D, path, directory_options>::value, "");
55 
56     // directory_iterator(path const&, directory_options, error_code&)
57     static_assert(std::is_constructible<D, path, directory_options, std::error_code&>::value, "");
58     static_assert(!std::is_nothrow_constructible<D, path, directory_options, std::error_code&>::value, "");
59 }
60 
test_construction_from_bad_path()61 static void test_construction_from_bad_path()
62 {
63     static_test_env static_env;
64     std::error_code ec;
65     directory_options opts = directory_options::none;
66     const RDI endIt;
67 
68     const path testPaths[] = { static_env.DNE, static_env.BadSymlink };
69     for (path const& testPath : testPaths)
70     {
71         {
72             RDI it(testPath, ec);
73             assert(ec);
74             assert(it == endIt);
75         }
76         {
77             RDI it(testPath, opts, ec);
78             assert(ec);
79             assert(it == endIt);
80         }
81         {
82             TEST_THROWS_TYPE(filesystem_error, RDI(testPath));
83             TEST_THROWS_TYPE(filesystem_error, RDI(testPath, opts));
84         }
85     }
86 }
87 
access_denied_test_case()88 static void access_denied_test_case()
89 {
90     using namespace fs;
91 #ifdef _WIN32
92     // Windows doesn't support setting perms::none to trigger failures
93     // reading directories; test using a special inaccessible directory
94     // instead.
95     const path testDir = GetWindowsInaccessibleDir();
96     if (testDir.empty())
97         return;
98 #else
99     scoped_test_env env;
100     path const testDir = env.make_env_path("dir1");
101     path const testFile = testDir / "testFile";
102     env.create_dir(testDir);
103     env.create_file(testFile, 42);
104 
105     // Test that we can iterator over the directory before changing the perms
106     {
107         RDI it(testDir);
108         assert(it != RDI{});
109     }
110 
111     // Change the permissions so we can no longer iterate
112     permissions(testDir, perms::none);
113 #endif
114 
115     // Check that the construction fails when skip_permissions_denied is
116     // not given.
117     {
118         std::error_code ec;
119         RDI it(testDir, ec);
120         assert(ec);
121         assert(it == RDI{});
122     }
123     // Check that construction does not report an error when
124     // 'skip_permissions_denied' is given.
125     {
126         std::error_code ec;
127         RDI it(testDir, directory_options::skip_permission_denied, ec);
128         assert(!ec);
129         assert(it == RDI{});
130     }
131 }
132 
133 
access_denied_to_file_test_case()134 static void access_denied_to_file_test_case()
135 {
136     using namespace fs;
137 #ifdef _WIN32
138     // Windows doesn't support setting perms::none to trigger failures
139     // reading directories; test using a special inaccessible directory
140     // instead.
141     const path testDir = GetWindowsInaccessibleDir();
142     if (testDir.empty())
143         return;
144     path const testFile = testDir / "inaccessible_file";
145 #else
146     scoped_test_env env;
147     path const testFile = env.make_env_path("file1");
148     env.create_file(testFile, 42);
149 
150     // Change the permissions so we can no longer iterate
151     permissions(testFile, perms::none);
152 #endif
153 
154     // Check that the construction fails when skip_permissions_denied is
155     // not given.
156     {
157         std::error_code ec;
158         RDI it(testFile, ec);
159         assert(ec);
160         assert(it == RDI{});
161     }
162     // Check that construction still fails when 'skip_permissions_denied' is given
163     // because we tried to open a file and not a directory.
164     {
165         std::error_code ec;
166         RDI it(testFile, directory_options::skip_permission_denied, ec);
167         assert(ec);
168         assert(it == RDI{});
169     }
170 }
171 
test_open_on_empty_directory_equals_end()172 static void test_open_on_empty_directory_equals_end()
173 {
174     scoped_test_env env;
175     const path testDir = env.make_env_path("dir1");
176     env.create_dir(testDir);
177 
178     const RDI endIt;
179     {
180         std::error_code ec;
181         RDI it(testDir, ec);
182         assert(!ec);
183         assert(it == endIt);
184     }
185     {
186         RDI it(testDir);
187         assert(it == endIt);
188     }
189 }
190 
test_open_on_directory_succeeds()191 static void test_open_on_directory_succeeds()
192 {
193     static_test_env static_env;
194     const path testDir = static_env.Dir;
195     std::set<path> dir_contents(static_env.DirIterationList.begin(),
196                                 static_env.DirIterationList.end());
197     const RDI endIt{};
198 
199     {
200         std::error_code ec;
201         RDI it(testDir, ec);
202         assert(!ec);
203         assert(it != endIt);
204         assert(dir_contents.count(*it));
205     }
206     {
207         RDI it(testDir);
208         assert(it != endIt);
209         assert(dir_contents.count(*it));
210     }
211 }
212 
test_open_on_file_fails()213 static void test_open_on_file_fails()
214 {
215     static_test_env static_env;
216     const path testFile = static_env.File;
217     const RDI endIt{};
218     {
219         std::error_code ec;
220         RDI it(testFile, ec);
221         assert(ec);
222         assert(it == endIt);
223     }
224     {
225         TEST_THROWS_TYPE(filesystem_error, RDI(testFile));
226     }
227 }
228 
test_options_post_conditions()229 static void test_options_post_conditions()
230 {
231     static_test_env static_env;
232     const path goodDir = static_env.Dir;
233     const path badDir = static_env.DNE;
234 
235     {
236         std::error_code ec;
237 
238         RDI it1(goodDir, ec);
239         assert(!ec);
240         assert(it1.options() == directory_options::none);
241 
242         RDI it2(badDir, ec);
243         assert(ec);
244         assert(it2 == RDI{});
245     }
246     {
247         std::error_code ec;
248         const directory_options opts = directory_options::skip_permission_denied;
249 
250         RDI it1(goodDir, opts, ec);
251         assert(!ec);
252         assert(it1.options() == opts);
253 
254         RDI it2(badDir, opts, ec);
255         assert(ec);
256         assert(it2 == RDI{});
257     }
258     {
259         RDI it(goodDir);
260         assert(it.options() == directory_options::none);
261     }
262     {
263         const directory_options opts = directory_options::follow_directory_symlink;
264         RDI it(goodDir, opts);
265         assert(it.options() == opts);
266     }
267 }
268 
main(int,char **)269 int main(int, char**) {
270     test_constructor_signatures();
271     test_construction_from_bad_path();
272     access_denied_test_case();
273     access_denied_to_file_test_case();
274     test_open_on_empty_directory_equals_end();
275     test_open_on_directory_succeeds();
276     test_open_on_file_fails();
277     test_options_post_conditions();
278 
279     return 0;
280 }
281