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