aboutsummaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-27 23:36:07 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-27 23:36:07 -0500
commit2978356bebd38a3645e82188e94a987f7981d7a3 (patch)
treeb42268a8508b9d23e74d3fda3efb07c9335dd167 /src/tests
parent66ebc04543c1ef6c6530d0fbef6900cbda8c265a (diff)
Improve FUSE VFS performance and rename handling
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/CMakeLists.txt26
-rw-r--r--src/tests/test_vfs_file_ops.cpp96
2 files changed, 121 insertions, 1 deletions
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 24c4597..9a7972b 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -69,6 +69,30 @@ target_link_libraries(test_overwrite_duplicates PRIVATE
)
add_test(NAME test_overwrite_duplicates COMMAND test_overwrite_duplicates)
+# ---------------------------------------------------------------------------
+# VFS file-operation regression tests
+# ---------------------------------------------------------------------------
+add_executable(test_vfs_file_ops EXCLUDE_FROM_ALL
+ test_vfs_file_ops.cpp
+ ${CMAKE_SOURCE_DIR}/src/src/vfs/inodetable.cpp
+ ${CMAKE_SOURCE_DIR}/src/src/vfs/overwritemanager.cpp
+ ${CMAKE_SOURCE_DIR}/src/src/vfs/vfstree.cpp
+)
+set_target_properties(test_vfs_file_ops PROPERTIES
+ AUTOMOC OFF
+ CXX_STANDARD 23
+ CXX_STANDARD_REQUIRED ON
+)
+target_include_directories(test_vfs_file_ops PRIVATE
+ ${CMAKE_SOURCE_DIR}/src/src
+)
+target_link_libraries(test_vfs_file_ops PRIVATE
+ GTest::gtest
+ GTest::gtest_main
+)
+add_test(NAME test_vfs_file_ops COMMAND test_vfs_file_ops)
+
# Register a "fluorine-tests" umbrella target that builds every test exe.
add_custom_target(fluorine-tests
- DEPENDS test_metainiutils test_external_dir_mapping test_overwrite_duplicates)
+ DEPENDS test_metainiutils test_external_dir_mapping test_overwrite_duplicates
+ test_vfs_file_ops)
diff --git a/src/tests/test_vfs_file_ops.cpp b/src/tests/test_vfs_file_ops.cpp
new file mode 100644
index 0000000..32b82d2
--- /dev/null
+++ b/src/tests/test_vfs_file_ops.cpp
@@ -0,0 +1,96 @@
+#include "vfs/inodetable.h"
+#include "vfs/overwritemanager.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <filesystem>
+#include <fstream>
+#include <string>
+
+namespace fs = std::filesystem;
+
+namespace
+{
+
+class TempRoot
+{
+public:
+ TempRoot()
+ {
+ char tmpl[] = "/tmp/fluorine-vfs-ops-XXXXXX";
+ const char* d = mkdtemp(tmpl);
+ if (d != nullptr) {
+ m_path = d;
+ }
+ }
+
+ ~TempRoot()
+ {
+ if (!m_path.empty()) {
+ std::error_code ec;
+ fs::remove_all(m_path, ec);
+ }
+ }
+
+ const fs::path& path() const { return m_path; }
+
+private:
+ fs::path m_path;
+};
+
+void writeText(const fs::path& path, const std::string& text)
+{
+ fs::create_directories(path.parent_path());
+ std::ofstream out(path, std::ios::binary | std::ios::trunc);
+ ASSERT_TRUE(out.is_open());
+ out << text;
+}
+
+std::string readText(const fs::path& path)
+{
+ std::ifstream in(path, std::ios::binary);
+ return std::string(std::istreambuf_iterator<char>(in),
+ std::istreambuf_iterator<char>());
+}
+
+} // namespace
+
+TEST(VfsFileOps, CopyOnWriteFromFdCanCopyBackingSourceToDifferentDestination)
+{
+ TempRoot tmp;
+ ASSERT_FALSE(tmp.path().empty());
+
+ const fs::path backing = tmp.path() / "Data";
+ const fs::path staging = tmp.path() / "staging";
+ const fs::path overwrite = tmp.path() / "overwrite";
+ writeText(backing / "Plugin.esp.save_2026_05_27", "saved plugin");
+ writeText(backing / "Plugin.esp", "old plugin");
+
+ const int dirFd = open(backing.c_str(), O_RDONLY | O_DIRECTORY);
+ ASSERT_GE(dirFd, 0);
+
+ OverwriteManager overwriteManager(staging.string(), overwrite.string());
+ const std::string staged = overwriteManager.copyOnWriteFromFd(
+ dirFd, "Plugin.esp.save_2026_05_27", "Plugin.esp");
+ close(dirFd);
+
+ EXPECT_EQ(fs::path(staged), staging / "Plugin.esp");
+ EXPECT_EQ(readText(staging / "Plugin.esp"), "saved plugin");
+}
+
+TEST(VfsFileOps, InodeRenameMovesDescendants)
+{
+ InodeTable table;
+ const uint64_t parent = table.getOrCreate("meshes/actors");
+ const uint64_t child = table.getOrCreate("meshes/actors/character/body.nif");
+
+ table.rename("meshes/actors", "meshes/actors_backup");
+
+ EXPECT_EQ(table.get("meshes/actors"), 0u);
+ EXPECT_EQ(table.get("meshes/actors_backup"), parent);
+ EXPECT_EQ(table.get("meshes/actors_backup/character/body.nif"), child);
+ EXPECT_EQ(table.getPath(child), "meshes/actors_backup/character/body.nif");
+}