diff options
-rw-r--r-- | src/buildtool/file_system/file_system_manager.hpp | 28 | ||||
-rw-r--r-- | test/buildtool/file_system/file_system_manager.test.cpp | 81 |
2 files changed, 109 insertions, 0 deletions
diff --git a/src/buildtool/file_system/file_system_manager.hpp b/src/buildtool/file_system/file_system_manager.hpp index 1be50ac0..0bdfb8a6 100644 --- a/src/buildtool/file_system/file_system_manager.hpp +++ b/src/buildtool/file_system/file_system_manager.hpp @@ -118,6 +118,34 @@ class FileSystemManager { } } + template <ObjectType kType> + requires(IsFileObject(kType)) + [[nodiscard]] static auto CreateFileHardlinkAs( + std::filesystem::path const& file_path, + std::filesystem::path const& link_path) noexcept -> bool { + // Set permissions first (permissions are a property of the file) so + // that the created link has the correct permissions as soon as the link + // creation is finished. + return SetFilePermissions(file_path, IsExecutableObject(kType)) and + CreateFileHardlink(file_path, link_path); + } + + [[nodiscard]] static auto CreateFileHardlinkAs( + std::filesystem::path const& file_path, + std::filesystem::path const& link_path, + ObjectType output_type) noexcept -> bool { + switch (output_type) { + case ObjectType::File: + return CreateFileHardlinkAs<ObjectType::File>(file_path, + link_path); + case ObjectType::Executable: + return CreateFileHardlinkAs<ObjectType::Executable>(file_path, + link_path); + case ObjectType::Tree: + return false; + } + } + [[nodiscard]] static auto Rename(std::filesystem::path const& src, std::filesystem::path const& dst, bool no_clobber = false) noexcept -> bool { diff --git a/test/buildtool/file_system/file_system_manager.test.cpp b/test/buildtool/file_system/file_system_manager.test.cpp index fee5dca1..3e4bead9 100644 --- a/test/buildtool/file_system/file_system_manager.test.cpp +++ b/test/buildtool/file_system/file_system_manager.test.cpp @@ -404,3 +404,84 @@ TEST_CASE("FileSystemManager", "[file_system]") { CHECK(FileSystemManager::RemoveFile(copy_file)); CHECK(not FileSystemManager::IsFile(copy_file)); } + +TEST_CASE("CreateFileHardlink", "[file_system]") { + std::filesystem::path to{"./tmp-CreateFileHardlink/linked_file"}; + REQUIRE(FileSystemManager::CreateDirectory(to.parent_path())); + + SECTION("Existing file") { + std::filesystem::path from{ + "test/buildtool/file_system/data/example_file"}; + + CHECK(FileSystemManager::CreateFileHardlink(from, to)); + CHECK(std::filesystem::exists(to)); + + CHECK_FALSE(FileSystemManager::CreateFileHardlink(from, to)); + CHECK(std::filesystem::exists(to)); + + CHECK(FileSystemManager::RemoveFile(to)); + CHECK(not std::filesystem::exists(to)); + } + SECTION("Non-existing file") { + std::filesystem::path from{ + "test/buildtool/file_system/data/this_file_does_not_exist"}; + + CHECK_FALSE(FileSystemManager::CreateFileHardlink(from, to)); + CHECK_FALSE(std::filesystem::exists(to)); + } + SECTION("Existing but not file") { + std::filesystem::path from{"./tmp-CreateFileHardlink/dir"}; + CHECK(FileSystemManager::CreateDirectory(from)); + + CHECK_FALSE(FileSystemManager::CreateFileHardlink(from, to)); + CHECK_FALSE(std::filesystem::exists(to)); + } +} + +TEST_CASE_METHOD(CopyFileFixture, "CreateFileHardlinkAs", "[file_system]") { + auto set_perm = [&](bool is_executable) { + auto const content = FileSystemManager::ReadFile(from_); + REQUIRE(content); + REQUIRE(FileSystemManager::RemoveFile(from_)); + REQUIRE(FileSystemManager::WriteFileAs( + *content, + from_, + is_executable ? ObjectType::Executable : ObjectType::File)); + }; + auto run_test = [&](bool is_executable) { + // Hard link creation was successful + CHECK(FileSystemManager::CreateFileHardlinkAs( + from_, + to_, + is_executable ? ObjectType::Executable : ObjectType::File)); + + // file exists + CHECK(std::filesystem::exists(to_)); + CHECK(std::filesystem::is_regular_file(to_)); + CHECK(is_executable == FileSystemManager::IsExecutable(to_)); + + // permissions should be 0555 or 0444 + CHECK((is_executable ? HasExecutablePermissions(to_) + : HasFilePermissions(to_))); + }; + SECTION("as file") { + SECTION("from file") { + set_perm(false); + run_test(false); + } + SECTION("from executable") { + set_perm(true); + run_test(false); + } + } + SECTION("as executable") { + SECTION("from file") { + set_perm(false); + run_test(true); + } + SECTION("from executable") { + set_perm(true); + run_test(true); + } + } +} |