From bf7a57fc55493247a624ecfd65e4d73964e7d8d2 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Sun, 12 Apr 2026 02:40:16 -0500 Subject: Remove vendored usvfs and Windows-only usvfs code paths The Linux port uses its own in-process FUSE VFS (FuseConnector), so usvfs was dead weight: libs/usvfs/ was never built, usvfsconnector.cpp was explicitly REMOVE_ITEM'd from the source list, and every call site was already guarded by #ifdef _WIN32 with a working Linux branch (the Windows build is also broken on this branch, since FUSE3 is a hard find_package dependency). - Drop libs/usvfs/ and src/src/usvfsconnector.{cpp,h} - Collapse #ifdef _WIN32 / usvfs include branches in organizercore, mainwindow, settings, util, spawn - Remove getUsvfsVersionString() and the "usvfs: ..." log field - Remove vfs32/64DLLName APPPARAMs (nothing reads them) - Relabel About dialog "usvfs:" row to "VFS: FUSE 3" --- .../test/usvfs_global_test_runner/CMakeLists.txt | 23 --- .../usvfs_global_test/usvfs_global_test.cpp | 216 --------------------- .../usvfs_global_test_fixture.cpp | 205 ------------------- .../usvfs_global_test_fixture.h | 82 -------- .../usvfs_global_test_runner.cpp | 58 ------ 5 files changed, 584 deletions(-) delete mode 100644 libs/usvfs/test/usvfs_global_test_runner/CMakeLists.txt delete mode 100644 libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp delete mode 100644 libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp delete mode 100644 libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.h delete mode 100644 libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp (limited to 'libs/usvfs/test/usvfs_global_test_runner') diff --git a/libs/usvfs/test/usvfs_global_test_runner/CMakeLists.txt b/libs/usvfs/test/usvfs_global_test_runner/CMakeLists.txt deleted file mode 100644 index 88ff0c2..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -find_package(GTest CONFIG REQUIRED) -find_package(Boost CONFIG REQUIRED COMPONENTS filesystem) - -# other executables -add_executable(usvfs_global_test - usvfs_global_test/usvfs_global_test.cpp -) -usvfs_set_test_properties(usvfs_global_test FOLDER usvfs_global_test) -target_link_libraries(usvfs_global_test PRIVATE gtest_utils GTest::gtest_main GTest::gmock Boost::filesystem) - -# actual test executable -add_executable(usvfs_global_test_runner - usvfs_global_test_fixture.cpp - usvfs_global_test_fixture.h - usvfs_global_test_runner.cpp -) -usvfs_set_test_properties(usvfs_global_test_runner FOLDER usvfs_global_test) -usvfs_target_link_usvfs(usvfs_global_test_runner) -target_link_libraries(usvfs_global_test_runner - PRIVATE test_utils gtest_utils GTest::gtest_main) -add_dependencies(usvfs_global_test_runner usvfs_global_test) diff --git a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp b/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp deleted file mode 100644 index 79c6c4c..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include - -#define WIN32_LEAN_AND_MEAN -#include - -#include -#include - -#include - -#include -#include - -// anonymous class that allow tests to access parameters (currently only where the -// virtualized data folder is) -// -class -{ - bool initialize(int argc, char* argv[]) - { - // extract the data path - if (argc != 2) { - std::cerr << "missing data path, aborting\n"; - return false; - } - m_data = std::filesystem::path{argv[1]}; - - if (!exists(m_data)) { - std::cerr << "data path '" << m_data.string() << "'does not exist\n"; - return false; - } - - return true; - } - - friend int main(int argc, char* argv[]); - std::filesystem::path m_data; - -public: - const auto& data() const { return m_data; } -} parameters; - -// simple guard for handle -class HandleGuard -{ - HANDLE m_handle = INVALID_HANDLE_VALUE; - -public: - HandleGuard() = default; - HandleGuard(HANDLE handle) : m_handle{handle} {} - - ~HandleGuard() { close(); } - - operator HANDLE() { return m_handle; } - - void close() - { - if (m_handle != INVALID_HANDLE_VALUE) { - ::CloseHandle(m_handle); - m_handle = INVALID_HANDLE_VALUE; - } - } -}; - -// simple function to write content to a specified path -void write_content(const std::filesystem::path& path, const std::string_view content) -{ - std::ofstream ofs{path}; - ofs << content; -} - -TEST(BasicTest, SimpleTest) -{ - const auto data = parameters.data(); - - ASSERT_TRUE(exists(data)); - ASSERT_TRUE(exists(data / "docs")); - ASSERT_TRUE(exists(data / "empty")); - ASSERT_TRUE(exists(data / "docs" / "doc.txt")); - ASSERT_TRUE(exists(data / "readme.txt")); - - // should remove mods/mod1/info.txt - ASSERT_TRUE(exists(data / "info.txt")); - remove(data / "info.txt"); - ASSERT_FALSE(exists(data / "info.txt")); - - { - // retrieve the path of data using GetFinalPathNameByHandleW() to - // compare later on - std::filesystem::path dataPathFromHandle; - { - HandleGuard hdl = CreateFileW(data.c_str(), GENERIC_READ, 0, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - ASSERT_NE(INVALID_HANDLE_VALUE, (HANDLE)hdl); - - WCHAR filepath[1024]; - const auto length = GetFinalPathNameByHandleW( - hdl, filepath, sizeof(filepath) / sizeof(WCHAR), FILE_NAME_NORMALIZED); - ASSERT_NE(0, length); - - dataPathFromHandle = std::filesystem::path(std::wstring(filepath, length)); - } - - const auto doc_txt = data / "docs" / "doc.txt"; - HandleGuard hdl = CreateFileW(doc_txt.c_str(), GENERIC_READ, 0, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - ASSERT_NE(INVALID_HANDLE_VALUE, (HANDLE)hdl); - - WCHAR filepath[1024]; - const auto length = GetFinalPathNameByHandleW( - hdl, filepath, sizeof(filepath) / sizeof(WCHAR), FILE_NAME_NORMALIZED); - ASSERT_NE(0, length); - - ASSERT_EQ(dataPathFromHandle / "docs" / "doc.txt", - std::filesystem::path(std::wstring(filepath, length))); - } - - { - // retrieve the path of data using GetFileInformationByHandleEx() to - // compare later on - std::filesystem::path dataPathFromHandle; - { - HandleGuard hdl = CreateFileW(data.c_str(), GENERIC_READ, 0, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - ASSERT_NE(INVALID_HANDLE_VALUE, (HANDLE)hdl); - - char buffer[4096]; - ASSERT_TRUE( - GetFileInformationByHandleEx(hdl, FileNameInfo, buffer, sizeof(buffer))); - auto* info = reinterpret_cast(buffer); - - dataPathFromHandle = std::filesystem::path( - std::wstring_view(info->FileName, info->FileNameLength / 2)); - } - - const auto info_txt = data / "readme.txt"; - HandleGuard hdl = CreateFileW(info_txt.c_str(), GENERIC_READ, 0, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - ASSERT_NE(INVALID_HANDLE_VALUE, (HANDLE)hdl); - - char buffer[4096]; - ASSERT_TRUE( - GetFileInformationByHandleEx(hdl, FileNameInfo, buffer, sizeof(buffer))); - - auto* info = reinterpret_cast(buffer); - - ASSERT_EQ(dataPathFromHandle / "readme.txt", - std::filesystem::path( - std::wstring_view(info->FileName, info->FileNameLength / 2))); - } -} - -TEST(BoostFilesystemTest, BoostFilesystemTest) -{ - using namespace boost::filesystem; - - const path data{parameters.data().native()}; - - std::vector contents; - for (recursive_directory_iterator it{data}; it != recursive_directory_iterator{}; - ++it) { - contents.push_back(relative(it->path(), data).string()); - } - ASSERT_THAT(contents, ::testing::UnorderedElementsAre( - ".gitkeep", "docs", "docs\\doc.txt", "docs\\subdocs", - "docs\\subdocs\\.gitkeep")); -} - -// see https://github.com/ModOrganizer2/modorganizer/issues/2039 for context -// -TEST(RedFileSystemTest, RedFileSystemTest) -{ - const auto data = parameters.data(); - - const auto storages_path = data / "r6" / "storages"; - const auto hudpainter_path = storages_path / "HUDPainter"; - - ASSERT_TRUE(exists(hudpainter_path / "DEFAULT.json")); - - // TEST.json does not exist, so will be created in overwrite - this mainly check that - // weakly_canonical returns the path under data/ and not the actual path under - // overwrite/ - // - // this relies on the hook for NtQueryInformationFile - // - ASSERT_FALSE(exists(hudpainter_path / "TEST.json")); - ASSERT_EQ(hudpainter_path / "TEST.json", - weakly_canonical(hudpainter_path / "TEST.json")); - write_content(hudpainter_path / "TEST.json", "{}"); -} - -TEST(SkipFilesTest, SkipFilesTest) -{ - const auto data = parameters.data(); - - // file in mod1 should have been skipped - ASSERT_FALSE(exists(data / "readme.skip")); - - // docs/doc.skip should come from data, not mods1/docs/doc.skip - ASSERT_CONTENT_EQ("doc.skip in data/docs", data / "docs" / "doc.skip"); - - // write to readme.skip should create a file in overwrite - write_content(data / "readme.skip", "readme.skip in overwrite"); -} - -int main(int argc, char* argv[]) -{ - testing::InitGoogleTest(&argc, argv); - - // initialize parameters from remaining arguments - if (!parameters.initialize(argc, argv)) { - return -1; - } - - return RUN_ALL_TESTS(); -} diff --git a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp b/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp deleted file mode 100644 index 4f6e672..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp +++ /dev/null @@ -1,205 +0,0 @@ -#include "usvfs_global_test_fixture.h" - -#include -#include -#include - -#include - -#include -#include -#include -#include - -// find the path to the executable that contains gtest entries -// -std::filesystem::path path_to_usvfs_global_test() -{ - return test::path_of_test_bin( - test::platform_dependant_executable("usvfs_global_test", "exe")); -} - -// path to the fixture for the given test group -// -std::filesystem::path path_to_usvfs_global_test_figures(std::wstring_view group) -{ - return test::path_of_test_fixtures() / "usvfs_global_test" / group; -} - -// spawn the an hook version of the given -// -DWORD spawn_usvfs_hooked_process( - const std::filesystem::path& app, const std::vector& arguments = {}, - const std::optional& working_directory = {}) -{ - using namespace usvfs::shared; - - std::wstring command = app; - std::filesystem::path cwd = working_directory.value_or(app.parent_path()); - std::vector env; - - if (!arguments.empty()) { - command += L" " + boost::algorithm::join(arguments, L" "); - } - - STARTUPINFO si{0}; - si.cb = sizeof(si); - PROCESS_INFORMATION pi{0}; - -#pragma warning(push) -#pragma warning(disable : 6387) - - if (!usvfsCreateProcessHooked(nullptr, command.data(), nullptr, nullptr, FALSE, 0, - nullptr, cwd.c_str(), &si, &pi)) { - test::throw_testWinFuncFailed( - "CreateProcessHooked", - string_cast(command, CodePage::UTF8).c_str()); - } - - WaitForSingleObject(pi.hProcess, INFINITE); - - DWORD exit = 99; - if (!GetExitCodeProcess(pi.hProcess, &exit)) { - test::WinFuncFailedGenerator failed; - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - throw failed("GetExitCodeProcess"); - } - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - -#pragma warning(pop) - - return exit; -} - -bool UsvfsGlobalTest::m_force_usvfs_logs = false; - -void UsvfsGlobalTest::ForceUsvfsLogs() -{ - m_force_usvfs_logs = true; -} - -class UsvfsGlobalTest::UsvfsGuard -{ -public: - UsvfsGuard(std::string_view instance_name = "usvfs_test", bool logging = false) - : m_parameters(usvfsCreateParameters(), &usvfsFreeParameters) - { - usvfsSetInstanceName(m_parameters.get(), instance_name.data()); - usvfsSetDebugMode(m_parameters.get(), false); - usvfsSetLogLevel(m_parameters.get(), LogLevel::Debug); - usvfsSetCrashDumpType(m_parameters.get(), CrashDumpsType::None); - usvfsSetCrashDumpPath(m_parameters.get(), ""); - - usvfsInitLogging(logging); - usvfsCreateVFS(m_parameters.get()); - } - - ~UsvfsGuard() { usvfsDisconnectVFS(); } - -private: - std::unique_ptr m_parameters; -}; - -UsvfsGlobalTest::UsvfsGlobalTest() - : m_usvfs{std::make_unique()}, - m_temporary_folder{test::path_of_test_temp()} -{ - std::string name{testing::UnitTest::GetInstance()->current_test_info()->name()}; - m_group = {name.begin(), name.end()}; -} - -UsvfsGlobalTest::~UsvfsGlobalTest() -{ - CleanUp(); -} - -std::filesystem::path UsvfsGlobalTest::ActualFolder() const -{ - return m_temporary_folder; -} - -std::filesystem::path UsvfsGlobalTest::ExpectedFolder() const -{ - return path_to_usvfs_global_test_figures(m_group) / "expected"; -} - -void UsvfsGlobalTest::CleanUp() const -{ - if (exists(m_temporary_folder)) { - remove_all(m_temporary_folder); - } -} - -void UsvfsGlobalTest::PrepareFileSystem() const -{ - // cleanup in case a previous tests failed to delete its stuff - CleanUp(); - - // copy fixtures - const auto fixtures = path_to_usvfs_global_test_figures(m_group) / "source"; - if (exists(fixtures)) { - copy(fixtures, m_temporary_folder, std::filesystem::copy_options::recursive); - } -} - -void UsvfsGlobalTest::SetUpOverwrite(bool force) const -{ - if (force && !exists(m_overwrite_folder)) { - create_directory(m_overwrite_folder); - } - - if (exists(m_overwrite_folder)) { - usvfsVirtualLinkDirectoryStatic(m_overwrite_folder.c_str(), m_data_folder.c_str(), - LINKFLAG_CREATETARGET | LINKFLAG_RECURSIVE); - } -} - -void UsvfsGlobalTest::PrepareMapping() const -{ - // should not be needed, but just to be safe - usvfsClearVirtualMappings(); - - if (!exists(m_data_folder)) { - throw std::runtime_error{ - std::format("data path missing at {}", m_data_folder.string())}; - } - - if (exists(m_mods_folder)) { - for (const auto& mod : std::filesystem::directory_iterator(m_mods_folder)) { - if (!is_directory(mod)) { - continue; - } - usvfsVirtualLinkDirectoryStatic(mod.path().c_str(), m_data_folder.c_str(), - LINKFLAG_RECURSIVE); - } - } - - // by default, only create overwrite if present - SetUpOverwrite(false); -} - -int UsvfsGlobalTest::Run() const -{ - PrepareMapping(); - - const auto res = spawn_usvfs_hooked_process( - path_to_usvfs_global_test(), {std::format(L"--gtest_filter={}.*", m_group), - L"--gtest_brief=1", m_data_folder.native()}); - - // TODO: try to do this with gtest itself? - if (m_force_usvfs_logs || res != 0) { - const auto log_path = test::path_of_test_bin(m_group + L".log"); - std::ofstream os{log_path}; - std::string buffer(1024, '\0'); - std::cout << "process returned " << std::hex << res << ", usvfs logs dumped to " - << log_path.string() << '\n'; - while (usvfsGetLogMessages(buffer.data(), buffer.size(), false)) { - os << " " << buffer.c_str() << "\n"; - } - } - - return res; -} diff --git a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.h b/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.h deleted file mode 100644 index aeafc25..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include - -#include - -class UsvfsGlobalTest : public testing::Test -{ -public: - // enable log mode - this will generate USVFS log file for all tests regardless of - // success or failure (default is to only generate for failure) - // - static void ForceUsvfsLogs(); - -public: - UsvfsGlobalTest(); - - void SetUp() override { PrepareFileSystem(); } - - void TearDown() override { CleanUp(); } - - ~UsvfsGlobalTest(); - - // setup the overwrite folder - // - // if force is true, force creation of the overwrite folder even if not present, this - // is useful when the overwrite is initially empty and thus cannot be committed to git - // but need to contain files after the run - // - void SetUpOverwrite(bool force = true) const; - - // run the test, return the exit code of the google test process - // - int Run() const; - - // return the path to the folder containing the expected results - // - std::filesystem::path ActualFolder() const; - - // return the path to the folder containing the expected results - // - std::filesystem::path ExpectedFolder() const; - -private: - class UsvfsGuard; - - // always generate usvfs logs - static bool m_force_usvfs_logs; - - // prepare the filesystem by copying files and folders from the relevant fixtures - // folder to the temporary folder - // - // after this operations, the temporary folder will contain - // - a data folder - // - [optional] a mods folder containing a set of folders that should be mounted - // - [optional] an overwrite folder that should be mounted as overwrite - // - void PrepareFileSystem() const; - - // prepare mapping using the given set of paths - // - void PrepareMapping() const; - - // cleanup the temporary path - // - void CleanUp() const; - - // usvfs_guard - std::unique_ptr m_usvfs; - - // name of GTest group (first argument of the TEST macro) to run - std::wstring m_group; - - // path to the folder containing temporary files - std::filesystem::path m_temporary_folder; - - // path to the subfolder inside the temporary folder - std::filesystem::path m_data_folder = m_temporary_folder / "data"; - std::filesystem::path m_mods_folder = m_temporary_folder / "mods"; - std::filesystem::path m_overwrite_folder = m_temporary_folder / "overwrite"; -}; diff --git a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp b/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp deleted file mode 100644 index 2fa6cc8..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include - -#include -#include -#include - -#include "usvfs_global_test_fixture.h" - -TEST_F(UsvfsGlobalTest, BasicTest) -{ - ASSERT_EQ(0, Run()); - ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); -} - -TEST_F(UsvfsGlobalTest, BoostFilesystemTest) -{ - ASSERT_EQ(0, Run()); - ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); -} - -TEST_F(UsvfsGlobalTest, RedFileSystemTest) -{ - SetUpOverwrite(true); - ASSERT_EQ(0, Run()); - ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); -} - -TEST_F(UsvfsGlobalTest, SkipFilesTest) -{ - SetUpOverwrite(true); - - usvfsAddSkipFileSuffix(L".skip"); - - ASSERT_EQ(0, Run()); - ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); -} - -int main(int argc, char* argv[]) -{ - // load the USVFS DLL - // - const auto usvfs_dll = - test::path_of_usvfs_lib(test::platform_dependant_executable("usvfs", "dll")); - test::ScopedLoadLibrary loadDll(usvfs_dll.c_str()); - if (!loadDll) { - std::wcerr << L"ERROR: failed to load usvfs dll: " << usvfs_dll.c_str() << L", " - << GetLastError() << L"\n"; - return 3; - } - - testing::InitGoogleTest(&argc, argv); - - UsvfsGlobalTest::ForceUsvfsLogs(); - - usvfsInitLogging(false); - - return RUN_ALL_TESTS(); -} -- cgit v1.3.1