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// recursive_directory_iterator
17
18#include <filesystem>
19#include <type_traits>
20#include <set>
21#include <cassert>
22
23#include "test_macros.h"
24#include "filesystem_test_helper.h"
25namespace fs = std::filesystem;
26
27#if defined(_WIN32)
28static void set_last_write_time_in_iteration(const fs::path& dir) {
29 // Windows can postpone updating last write time for file especially for
30 // directory because last write time of directory depends of its childs.
31 // See
32 // https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times
33 // To force updating file entries calls "last_write_time" with own value.
34 const fs::recursive_directory_iterator end_it{};
35
36 std::error_code ec;
37 fs::recursive_directory_iterator it(dir, ec);
38 assert(!ec);
39
40 fs::file_time_type now_time = fs::file_time_type::clock::now();
41 for (; it != end_it; ++it) {
42 const fs::path entry = *it;
43 fs::last_write_time(entry, now_time, ec);
44 assert(!ec);
45 }
46
47 assert(it == end_it);
48}
49
50struct directory_entry_and_values {
51 fs::directory_entry entry;
52
53 fs::file_status symlink_status;
54 fs::file_status status;
55 std::uintmax_t file_size;
56 fs::file_time_type last_write_time;
57};
58
59std::vector<directory_entry_and_values>
60get_directory_entries_for(const fs::path& dir, const std::set<fs::path>& dir_contents) {
61 const fs::recursive_directory_iterator end_it{};
62
63 std::error_code ec;
64 fs::recursive_directory_iterator it(dir, ec);
65 assert(!ec);
66
67 std::vector<directory_entry_and_values> dir_entries;
68 std::set<fs::path> unseen_entries = dir_contents;
69 while (!unseen_entries.empty()) {
70 assert(it != end_it);
71 const fs::directory_entry& entry = *it;
72
73 assert(unseen_entries.erase(entry.path()) == 1);
74
75 dir_entries.push_back(directory_entry_and_values{
76 .entry = entry,
77 .symlink_status = entry.symlink_status(),
78 .status = entry.status(),
79 .file_size = entry.is_regular_file() ? entry.file_size() : 0,
80 .last_write_time = entry.last_write_time()});
81
82 fs::recursive_directory_iterator& it_ref = it.increment(ec);
83 assert(!ec);
84 assert(&it_ref == &it);
85 }
86 return dir_entries;
87}
88#endif // _WIN32
89
90// Checks that the directory_entry properties will be the same before and after
91// calling "refresh" in case of iteration.
92// In case of Windows expects that directory_entry caches the properties during
93// iteration.
94static void test_cache_and_refresh_in_iteration() {
95 static_test_env static_env;
96 const fs::path test_dir = static_env.Dir;
97#if defined(_WIN32)
98 set_last_write_time_in_iteration(test_dir);
99#endif
100 const std::set<fs::path> dir_contents(static_env.RecDirIterationList.begin(), static_env.RecDirIterationList.end());
101 const fs::recursive_directory_iterator end_it{};
102
103 std::error_code ec;
104 fs::recursive_directory_iterator it(test_dir, ec);
105 assert(!ec);
106
107 std::set<fs::path> unseen_entries = dir_contents;
108 while (!unseen_entries.empty()) {
109 assert(it != end_it);
110 const fs::directory_entry& entry = *it;
111
112 assert(unseen_entries.erase(entry.path()) == 1);
113
114 fs::file_status symlink_status = entry.symlink_status();
115 fs::file_status status = entry.status();
116 std::uintmax_t file_size = entry.is_regular_file() ? entry.file_size() : 0;
117 fs::file_time_type last_write_time = entry.last_write_time();
118
119 fs::directory_entry mutable_entry = *it;
120 mutable_entry.refresh();
121 fs::file_status upd_symlink_status = mutable_entry.symlink_status();
122 fs::file_status upd_status = mutable_entry.status();
123 std::uintmax_t upd_file_size = mutable_entry.is_regular_file() ? mutable_entry.file_size() : 0;
124 fs::file_time_type upd_last_write_time = mutable_entry.last_write_time();
125 assert(upd_symlink_status.type() == symlink_status.type() &&
126 upd_symlink_status.permissions() == symlink_status.permissions());
127 assert(upd_status.type() == status.type() && upd_status.permissions() == status.permissions());
128 assert(upd_file_size == file_size);
129 assert(upd_last_write_time == last_write_time);
130
131 fs::recursive_directory_iterator& it_ref = it.increment(ec&: ec);
132 assert(!ec);
133 assert(&it_ref == &it);
134 }
135}
136
137#if defined(_WIN32)
138// In case of Windows expects that the directory_entry caches the properties
139// during iteration and the properties don't change after deleting folders
140// and files.
141static void test_cached_values_in_iteration() {
142 std::vector<directory_entry_and_values> dir_entries;
143 {
144 static_test_env static_env;
145 const fs::path testDir = static_env.Dir;
146 set_last_write_time_in_iteration(testDir);
147 const std::set<fs::path> dir_contents(static_env.RecDirIterationList.begin(), static_env.RecDirIterationList.end());
148 dir_entries = get_directory_entries_for(testDir, dir_contents);
149 }
150 // Testing folder should be deleted after destoying static_test_env.
151
152 for (const auto& dir_entry : dir_entries) {
153 // During iteration Windows provides information only about symlink itself
154 // not about file/folder which symlink points to.
155 if (dir_entry.entry.is_symlink()) {
156 // Check that symlink is not using cached value about existing file.
157 assert(!dir_entry.entry.exists());
158 } else {
159 // Check that entry uses cached value about existing file.
160 assert(dir_entry.entry.exists());
161 }
162 fs::file_status symlink_status = dir_entry.entry.symlink_status();
163 assert(dir_entry.symlink_status.type() == symlink_status.type() &&
164 dir_entry.symlink_status.permissions() == symlink_status.permissions());
165
166 if (!dir_entry.entry.is_symlink()) {
167 fs::file_status status = dir_entry.entry.status();
168 assert(dir_entry.status.type() == status.type() && dir_entry.status.permissions() == status.permissions());
169
170 std::uintmax_t file_size = dir_entry.entry.is_regular_file() ? dir_entry.entry.file_size() : 0;
171 assert(dir_entry.file_size == file_size);
172
173 fs::file_time_type last_write_time = dir_entry.entry.last_write_time();
174 assert(dir_entry.last_write_time == last_write_time);
175 }
176 }
177}
178#endif // _WIN32
179
180int main(int, char**) {
181 test_cache_and_refresh_in_iteration();
182#if defined(_WIN32)
183 test_cached_values_in_iteration();
184#endif
185
186 return 0;
187}
188

source code of libcxx/test/std/input.output/filesystems/class.rec.dir.itr/cache_refresh_iter.pass.cpp