mirror of https://github.com/mamba-org/mamba.git
277 lines
9.6 KiB
C++
277 lines
9.6 KiB
C++
#include <vector>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "mamba/core/mamba_fs.hpp"
|
|
#include "mamba/core/util.hpp"
|
|
#include "mamba/core/util_scope.hpp"
|
|
|
|
namespace mamba
|
|
{
|
|
TEST(u8path, normalized_separators)
|
|
{
|
|
static constexpr auto value = u8"a/b/c";
|
|
std::filesystem::path x{ value };
|
|
const auto y = fs::normalized_separators(x);
|
|
#if defined(_WIN32)
|
|
ASSERT_EQ(y.u8string(), u8R"(a\b\c)");
|
|
#else
|
|
ASSERT_EQ(y.u8string(), value);
|
|
#endif
|
|
}
|
|
|
|
TEST(u8path, normalized_separators_unicode)
|
|
{
|
|
static constexpr auto value = u8"日本語";
|
|
const std::filesystem::path x = fs::from_utf8(value);
|
|
ASSERT_EQ(x.u8string(), u8"日本語"); // check assumption
|
|
const auto y = fs::normalized_separators(x);
|
|
ASSERT_EQ(y.u8string(), u8"日本語");
|
|
}
|
|
|
|
TEST(u8path, consistent_encoding)
|
|
{
|
|
const auto utf8_string = u8"日本語";
|
|
const fs::u8path filename(utf8_string);
|
|
const auto str = filename.string();
|
|
EXPECT_EQ(str, utf8_string);
|
|
|
|
const fs::u8path file_path = fs::temp_directory_path() / filename;
|
|
EXPECT_EQ(file_path.filename().string(), utf8_string);
|
|
|
|
const auto std_path = file_path.std_path();
|
|
EXPECT_EQ(std_path.filename().u8string(), utf8_string);
|
|
}
|
|
|
|
TEST(u8path, string_stream_encoding)
|
|
{
|
|
const auto utf8_string = u8"日本語";
|
|
const std::string quoted_utf8_string = std::string("\"") + utf8_string + std::string("\"");
|
|
const fs::u8path filename(utf8_string);
|
|
std::stringstream stream;
|
|
stream << filename;
|
|
EXPECT_EQ(stream.str(), quoted_utf8_string);
|
|
|
|
fs::u8path path_read;
|
|
stream.seekg(0);
|
|
stream >> path_read;
|
|
EXPECT_EQ(path_read.string(), utf8_string);
|
|
}
|
|
|
|
TEST(u8path, directory_iteration)
|
|
{
|
|
const auto tmp_dir = fs::temp_directory_path() / "mamba_fs_iteration";
|
|
mamba::on_scope_exit _([&] { fs::remove_all(tmp_dir); }); // Cleanup if not debugging.
|
|
const auto file_dir = tmp_dir / "kikoo" / "lol" / u8"日本語";
|
|
const auto file_path = file_dir / u8"joël";
|
|
|
|
fs::remove_all(tmp_dir); // Make sure it's not existing from the start.
|
|
fs::create_directories(file_dir);
|
|
|
|
{
|
|
std::ofstream file(file_path.std_path(), std::ios::binary | std::ios::trunc);
|
|
file << u8"日本語";
|
|
}
|
|
|
|
{
|
|
const auto path_to_search_from = file_dir.parent_path();
|
|
fs::recursive_directory_iterator it{ path_to_search_from };
|
|
auto first_entry = *it;
|
|
EXPECT_EQ(first_entry.path(), file_path.parent_path());
|
|
auto secibd_entry = *(++it);
|
|
EXPECT_EQ(secibd_entry.path(), file_path);
|
|
}
|
|
|
|
{
|
|
const std::vector<fs::u8path> expected_entries{
|
|
tmp_dir / "kikoo",
|
|
tmp_dir / "kikoo" / "lol",
|
|
tmp_dir / "kikoo" / "lol" / u8"日本語",
|
|
tmp_dir / "kikoo" / "lol" / u8"日本語" / u8"joël",
|
|
};
|
|
|
|
std::vector<fs::u8path> entries_found;
|
|
for (const auto& entry : fs::recursive_directory_iterator(tmp_dir))
|
|
{
|
|
static_assert(std::is_same_v<decltype(entry.path()), fs::u8path>);
|
|
entries_found.push_back(entry.path());
|
|
}
|
|
EXPECT_EQ(entries_found, expected_entries);
|
|
}
|
|
|
|
{
|
|
const std::vector<std::string> expected_entries{
|
|
(tmp_dir / "kikoo").string(),
|
|
(tmp_dir / "kikoo" / "lol").string(),
|
|
(tmp_dir / "kikoo" / "lol" / u8"日本語").string(),
|
|
(tmp_dir / "kikoo" / "lol" / u8"日本語" / u8"joël").string(),
|
|
};
|
|
|
|
std::vector<std::string> entries_found;
|
|
for (const auto& entry : fs::recursive_directory_iterator(tmp_dir))
|
|
{
|
|
static_assert(std::is_same_v<decltype(entry.path()), fs::u8path>);
|
|
entries_found.push_back(entry.path().string());
|
|
}
|
|
EXPECT_EQ(entries_found, expected_entries);
|
|
}
|
|
|
|
{
|
|
const std::vector<fs::u8path> expected_entries{
|
|
tmp_dir / "kikoo" / "lol" / u8"日本語" / u8"joël",
|
|
};
|
|
|
|
std::vector<fs::u8path> entries_found;
|
|
for (const auto& entry : fs::directory_iterator(file_dir))
|
|
{
|
|
static_assert(std::is_same_v<decltype(entry.path()), fs::u8path>);
|
|
entries_found.push_back(entry.path());
|
|
}
|
|
EXPECT_EQ(entries_found, expected_entries);
|
|
}
|
|
|
|
{
|
|
const std::vector<std::string> expected_entries{
|
|
(tmp_dir / "kikoo" / "lol" / u8"日本語" / u8"joël").string(),
|
|
};
|
|
|
|
std::vector<std::string> entries_found;
|
|
for (const auto& entry : fs::directory_iterator(file_dir))
|
|
{
|
|
static_assert(std::is_same_v<decltype(entry.path()), fs::u8path>);
|
|
entries_found.push_back(entry.path().string());
|
|
}
|
|
EXPECT_EQ(entries_found, expected_entries);
|
|
}
|
|
}
|
|
|
|
TEST(u8path, long_paths)
|
|
{
|
|
const auto tmp_dir = fs::temp_directory_path() / "mamba_fs_long_path";
|
|
|
|
fs::u8path long_path = tmp_dir;
|
|
for (int i = 0; i < 42; ++i)
|
|
{
|
|
long_path /= u8"some_very_long_prefix";
|
|
}
|
|
|
|
mamba::on_scope_exit _([&] { fs::remove_all(long_path); });
|
|
fs::create_directories(long_path);
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
TEST(u8path, append_maintains_slash_type)
|
|
{
|
|
const fs::u8path path = u8R"(a/b/c/d)";
|
|
const auto path_1 = path / u8R"(e\f\g)";
|
|
EXPECT_EQ(path_1.string(), u8R"(a\b\c\d\e\f\g)");
|
|
}
|
|
#endif
|
|
|
|
TEST(filesystem, remove_readonly_file)
|
|
{
|
|
const auto tmp_dir = fs::temp_directory_path() / "mamba-fs-delete-me";
|
|
// NOTE: the following will FAIL if the test doesnt pass (it relies on the tested function)
|
|
mamba::on_scope_exit _([&] { fs::remove_all(tmp_dir); }); // Cleanup if not debugging.
|
|
fs::create_directories(tmp_dir);
|
|
|
|
const auto readonly_file_path = tmp_dir / "fs-readonly-file";
|
|
{
|
|
std::ofstream readonly_file{ readonly_file_path.std_path(),
|
|
readonly_file.binary | readonly_file.trunc };
|
|
readonly_file << "delete me" << std::endl;
|
|
}
|
|
|
|
// set to read-only
|
|
fs::permissions(
|
|
readonly_file_path,
|
|
fs::perms::owner_read | fs::perms::group_read,
|
|
fs::perm_options::replace
|
|
);
|
|
EXPECT_EQ(
|
|
(fs::status(readonly_file_path).permissions() & fs::perms::owner_write),
|
|
fs::perms::none
|
|
);
|
|
EXPECT_EQ(
|
|
(fs::status(readonly_file_path).permissions() & fs::perms::group_write),
|
|
fs::perms::none
|
|
);
|
|
|
|
// removing should still work.
|
|
EXPECT_TRUE(fs::exists(readonly_file_path));
|
|
fs::remove(readonly_file_path);
|
|
EXPECT_FALSE(fs::exists(readonly_file_path));
|
|
}
|
|
|
|
TEST(filesystem, remove_all_readonly_files)
|
|
{
|
|
const auto tmp_dir = fs::temp_directory_path() / "mamba-fs-delete-me";
|
|
// NOTE: the following will FAIL if the test doesnt pass (it relies on the tested function)
|
|
mamba::on_scope_exit _([&] { fs::remove_all(tmp_dir); }); // Cleanup if not debugging.
|
|
fs::create_directories(tmp_dir);
|
|
|
|
static constexpr int file_count_per_directory = 3;
|
|
static constexpr int subdir_count_per_directory = 3;
|
|
static constexpr int tree_depth = 3;
|
|
|
|
const auto create_readonly_files = [](const fs::u8path& dir_path)
|
|
{
|
|
assert(fs::is_directory(dir_path));
|
|
for (int file_idx = 0; file_idx < file_count_per_directory; ++file_idx)
|
|
{
|
|
const auto readonly_file_path = dir_path / fmt::format("readonly-file-{}", file_idx);
|
|
{
|
|
std::ofstream readonly_file{ readonly_file_path.std_path(),
|
|
readonly_file.binary | readonly_file.trunc };
|
|
readonly_file << "delete me" << std::endl;
|
|
}
|
|
|
|
// set to read-only
|
|
fs::permissions(
|
|
readonly_file_path,
|
|
fs::perms::owner_read | fs::perms::group_read,
|
|
fs::perm_options::replace
|
|
);
|
|
EXPECT_EQ(
|
|
(fs::status(readonly_file_path).permissions() & fs::perms::owner_write),
|
|
fs::perms::none
|
|
);
|
|
EXPECT_EQ(
|
|
(fs::status(readonly_file_path).permissions() & fs::perms::group_write),
|
|
fs::perms::none
|
|
);
|
|
}
|
|
};
|
|
|
|
const auto create_subdirs = [&](const std::vector<fs::u8path>& dirs)
|
|
{
|
|
auto subdirs = dirs;
|
|
for (const auto& dir_path : dirs)
|
|
{
|
|
for (int subdir_idx = 0; subdir_idx < subdir_count_per_directory; ++subdir_idx)
|
|
{
|
|
subdirs.push_back(dir_path / fmt::format("{}", subdir_idx));
|
|
}
|
|
}
|
|
return subdirs;
|
|
};
|
|
|
|
std::vector<fs::u8path> dirs{ tmp_dir };
|
|
for (int depth = 0; depth < tree_depth; ++depth)
|
|
{
|
|
dirs = create_subdirs(dirs);
|
|
}
|
|
|
|
for (const auto& dir_path : dirs)
|
|
{
|
|
fs::create_directories(dir_path);
|
|
create_readonly_files(dir_path);
|
|
}
|
|
|
|
EXPECT_TRUE(fs::exists(tmp_dir));
|
|
fs::remove_all(tmp_dir);
|
|
EXPECT_FALSE(fs::exists(tmp_dir));
|
|
}
|
|
|
|
}
|