diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c8f9f4..ac58db4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,9 +31,6 @@ jobs: # checkout USVFS and vcpkg - uses: actions/checkout@v4 - uses: lukka/run-vcpkg@v11 - with: - vcpkgGitCommitId: f61a294e765b257926ae9e9d85f96468a0af74e7 - vcpkgJsonGlob: ${{ github.workspace }}/usvfs/vcpkg.json # configure - run: cmake --preset vs2022-windows-${{ matrix.arch }} -B build_${{ matrix.arch }} "-DCMAKE_INSTALL_PREFIX=install/${{ matrix.config }}" @@ -140,6 +137,8 @@ jobs: if: always() - run: ./test/bin/usvfs_test_runner_${{ matrix.arch }}.exe if: always() + - run: ./test/bin/usvfs_global_test_runner_${{ matrix.arch }}.exe + if: always() - uses: actions/upload-artifact@master if: always() with: diff --git a/include/usvfs/usvfs.h b/include/usvfs/usvfs.h index 03e8111..97d7ad3 100644 --- a/include/usvfs/usvfs.h +++ b/include/usvfs/usvfs.h @@ -144,7 +144,7 @@ DLLEXPORT BOOL WINAPI usvfsCreateVFSDump(LPSTR buffer, size_t *size); * file system * @param executableName name of the executable */ -DLLEXPORT VOID WINAPI usvfsBlacklistExecutable(LPWSTR executableName); +DLLEXPORT VOID WINAPI usvfsBlacklistExecutable(LPCWSTR executableName); /** * clears the executable blacklist @@ -157,7 +157,7 @@ DLLEXPORT VOID WINAPI usvfsClearExecutableBlacklist(); * not to be confused with file extensions * @param fileSuffix a valid file suffix */ -DLLEXPORT VOID WINAPI usvfsAddSkipFileSuffix(LPWSTR fileSuffix); +DLLEXPORT VOID WINAPI usvfsAddSkipFileSuffix(LPCWSTR fileSuffix); /** * clears the file suffix skip-list @@ -172,7 +172,7 @@ DLLEXPORT VOID WINAPI usvfsClearSkipFileSuffixes(); * will have the .git directory skipped during directory linking * @param directory name of the directory */ -DLLEXPORT VOID WINAPI usvfsAddSkipDirectory(LPWSTR directory); +DLLEXPORT VOID WINAPI usvfsAddSkipDirectory(LPCWSTR directory); /** * clears the directory skip-list @@ -183,7 +183,7 @@ DLLEXPORT VOID WINAPI usvfsClearSkipDirectories(); * adds a library to be force loaded when the given process is injected * @param */ -DLLEXPORT VOID WINAPI usvfsForceLoadLibrary(LPWSTR processName, LPWSTR libraryPath); +DLLEXPORT VOID WINAPI usvfsForceLoadLibrary(LPCWSTR processName, LPCWSTR libraryPath); /** * clears all previous calls to ForceLoadLibrary diff --git a/include/usvfs/usvfs_version.h b/include/usvfs/usvfs_version.h index e998df3..ddfee6e 100644 --- a/include/usvfs/usvfs_version.h +++ b/include/usvfs/usvfs_version.h @@ -2,8 +2,8 @@ #define USVFS_VERSION_MAJOR 0 #define USVFS_VERSION_MINOR 5 -#define USVFS_VERSION_BUILD 6 -#define USVFS_VERSION_REVISION 2 +#define USVFS_VERSION_BUILD 7 +#define USVFS_VERSION_REVISION 0 #define USVFS_BUILD_STRING "" #define USVFS_BUILD_WSTRING L"" diff --git a/src/shared/formatters.h b/src/shared/formatters.h index 829422f..caebe10 100644 --- a/src/shared/formatters.h +++ b/src/shared/formatters.h @@ -74,6 +74,16 @@ struct std::formatter : std::formatter +struct std::formatter : std::formatter +{ + template + FmtContext::iterator format(UNICODE_STRING v, FmtContext& ctx) const + { + return std::formatter::format(usvfs::log::to_string(&v), ctx); + } +}; + template requires (std::is_pointer_v && !std::is_same_v diff --git a/src/shared/ntdll_declarations.cpp b/src/shared/ntdll_declarations.cpp index c2ca05b..b1119f1 100644 --- a/src/shared/ntdll_declarations.cpp +++ b/src/shared/ntdll_declarations.cpp @@ -27,6 +27,9 @@ NtQueryDirectoryFile_type NtQueryDirectoryFile; NtQueryDirectoryFileEx_type NtQueryDirectoryFileEx; NtQueryFullAttributesFile_type NtQueryFullAttributesFile; NtQueryAttributesFile_type NtQueryAttributesFile; +NtQueryObject_type NtQueryObject; +NtQueryInformationFile_type NtQueryInformationFile; +NtQueryInformationByName_type NtQueryInformationByName; NtOpenFile_type NtOpenFile; NtCreateFile_type NtCreateFile; NtClose_type NtClose; @@ -46,6 +49,9 @@ void ntdll_declarations_init() { LOAD_EXT(ntDLLMod, NtQueryDirectoryFileEx); LOAD_EXT(ntDLLMod, NtQueryFullAttributesFile); LOAD_EXT(ntDLLMod, NtQueryAttributesFile); + LOAD_EXT(ntDLLMod, NtQueryObject); + LOAD_EXT(ntDLLMod, NtQueryInformationFile); + LOAD_EXT(ntDLLMod, NtQueryInformationByName); LOAD_EXT(ntDLLMod, NtCreateFile); LOAD_EXT(ntDLLMod, NtOpenFile); LOAD_EXT(ntDLLMod, NtClose); diff --git a/src/shared/ntdll_declarations.h b/src/shared/ntdll_declarations.h index 74ca5ec..ca39f77 100644 --- a/src/shared/ntdll_declarations.h +++ b/src/shared/ntdll_declarations.h @@ -116,6 +116,24 @@ typedef struct _FILE_ID_BOTH_DIR_INFORMATION { WCHAR FileName[1]; } FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION; +typedef struct _FILE_BASIC_INFORMATION +{ + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + ULONG FileAttributes; +} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; + +typedef struct _FILE_STANDARD_INFORMATION +{ + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG NumberOfLinks; + BOOLEAN DeletePending; + BOOLEAN Directory; +} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION; + typedef struct _FILE_NAMES_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; @@ -123,6 +141,55 @@ typedef struct _FILE_NAMES_INFORMATION { WCHAR FileName[1]; } FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION; +typedef struct _FILE_INTERNAL_INFORMATION +{ + LARGE_INTEGER IndexNumber; +} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION; + +typedef struct _FILE_EA_INFORMATION +{ + ULONG EaSize; +} FILE_EA_INFORMATION, *PFILE_EA_INFORMATION; + +typedef struct _FILE_ACCESS_INFORMATION +{ + ACCESS_MASK AccessFlags; +} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION; + +typedef struct _FILE_POSITION_INFORMATION +{ + LARGE_INTEGER CurrentByteOffset; +} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION; + +typedef struct _FILE_MODE_INFORMATION +{ + ULONG Mode; +} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; + +typedef struct _FILE_ALIGNMENT_INFORMATION +{ + ULONG AlignmentRequirement; +} FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION; + +typedef struct _FILE_NAME_INFORMATION +{ + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; + +typedef struct _FILE_ALL_INFORMATION +{ + FILE_BASIC_INFORMATION BasicInformation; + FILE_STANDARD_INFORMATION StandardInformation; + FILE_INTERNAL_INFORMATION InternalInformation; + FILE_EA_INFORMATION EaInformation; + FILE_ACCESS_INFORMATION AccessInformation; + FILE_POSITION_INFORMATION PositionInformation; + FILE_MODE_INFORMATION ModeInformation; + FILE_ALIGNMENT_INFORMATION AlignmentInformation; + FILE_NAME_INFORMATION NameInformation; +} FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; + typedef struct _FILE_OBJECTID_INFORMATION { LONGLONG FileReference; UCHAR ObjectId[16]; @@ -142,10 +209,11 @@ typedef struct _FILE_REPARSE_POINT_INFORMATION { } FILE_REPARSE_POINT_INFORMATION, *PFILE_REPARSE_POINT_INFORMATION; // copied from ntstatus.h -#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) -#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) -#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L) -#define STATUS_NO_SUCH_FILE ((NTSTATUS)0xC000000FL) +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) +#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L) +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +#define STATUS_NO_SUCH_FILE ((NTSTATUS)0xC000000FL) #define SL_RESTART_SCAN 0x01 #define SL_RETURN_SINGLE_ENTRY 0x02 @@ -158,13 +226,17 @@ typedef enum _FILE_INFORMATION_CLASS { FileDirectoryInformation = 1, FileFullDirectoryInformation = 2, FileBothDirectoryInformation = 3, + FileStandardInformation = 5, + FileNameInformation = 9, + FileRenameInformation = 10, FileNamesInformation = 12, + FileAllInformation = 18, FileObjectIdInformation = 29, FileReparsePointInformation = 33, FileIdBothDirectoryInformation = 37, - FileIdFullDirectoryInformation = 38 -} FILE_INFORMATION_CLASS, - *PFILE_INFORMATION_CLASS; + FileIdFullDirectoryInformation = 38, + FileNormalizedNameInformation = 48, +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; typedef enum _MODE { KernelMode, UserMode, MaximumMode } MODE; @@ -253,6 +325,18 @@ typedef struct _OBJECT_HANDLE_INFORMATION { ACCESS_MASK GrantedAccess; } OBJECT_HANDLE_INFORMATION, *POBJECT_HANDLE_INFORMATION; +typedef struct _OBJECT_NAME_INFORMATION +{ + UNICODE_STRING Name; +} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; + +typedef enum _OBJECT_INFORMATION_CLASS +{ + ObjectBasicInformation = 0, + ObjectNameInformation = 1, + ObjectTypeInformation = 2 +} OBJECT_INFORMATION_CLASS; + typedef struct _RTL_RELATIVE_NAME { UNICODE_STRING RelativeName; HANDLE ContainingDirectory; @@ -269,14 +353,6 @@ typedef struct _FILE_NETWORK_OPEN_INFORMATION { ULONG FileAttributes; } FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION; -typedef struct _FILE_BASIC_INFORMATION { - LARGE_INTEGER CreationTime; - LARGE_INTEGER LastAccessTime; - LARGE_INTEGER LastWriteTime; - LARGE_INTEGER ChangeTime; - ULONG FileAttributes; -} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; - #define FILE_DIRECTORY_FILE 0x00000001 #define FILE_WRITE_THROUGH 0x00000002 #define FILE_SEQUENTIAL_ONLY 0x00000004 @@ -302,55 +378,67 @@ typedef struct _FILE_BASIC_INFORMATION { #define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000 #define FILE_CONTAINS_EXTENDED_CREATE_INFORMATION 0x10000000 -typedef NTSTATUS(WINAPI *NtQueryDirectoryFile_type)( +// Nt + +using NtQueryDirectoryFile_type = NTSTATUS(WINAPI *)( HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS, BOOLEAN, PUNICODE_STRING, BOOLEAN); - -typedef NTSTATUS(WINAPI *NtQueryDirectoryFileEx_type)( +using NtQueryDirectoryFileEx_type = NTSTATUS(WINAPI *)( HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS, ULONG, PUNICODE_STRING); -typedef NTSTATUS(WINAPI *NtQueryFullAttributesFile_type)( +using NtQueryFullAttributesFile_type = NTSTATUS(WINAPI *)( POBJECT_ATTRIBUTES, PFILE_NETWORK_OPEN_INFORMATION); - -typedef NTSTATUS(WINAPI *NtQueryAttributesFile_type)(POBJECT_ATTRIBUTES, +using NtQueryAttributesFile_type = NTSTATUS(WINAPI *)(POBJECT_ATTRIBUTES, PFILE_BASIC_INFORMATION); -typedef NTSTATUS(WINAPI *NtOpenFile_type)(PHANDLE, ACCESS_MASK, +using NtQueryObject_type = NTSTATUS (WINAPI *)( + HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, + PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength); +using NtQueryInformationFile_type = NTSTATUS(WINAPI*)( + HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, + ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); + +using NtQueryInformationByName_type = NTSTATUS(WINAPI*)( + HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, + ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); + +using NtOpenFile_type = NTSTATUS(WINAPI *)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG); - -typedef NTSTATUS(WINAPI *NtCreateFile_type)(PHANDLE, ACCESS_MASK, +using NtCreateFile_type = NTSTATUS(WINAPI *)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG); -typedef NTSTATUS(WINAPI *NtClose_type)(HANDLE); - -typedef NTSYSAPI BOOLEAN(NTAPI *RtlDoesFileExists_U_type)(PCWSTR); - -typedef NTSTATUS(NTAPI *RtlDosPathNameToRelativeNtPathName_U_WithStatus_type)( - PCWSTR DosFileName, PUNICODE_STRING NtFileName, PWSTR* FilePath, PRTL_RELATIVE_NAME RelativeName); +using NtClose_type = NTSTATUS(WINAPI *)(HANDLE); -typedef void (NTAPI *RtlReleaseRelativeName_type)(PRTL_RELATIVE_NAME RelativeName); +using NtTerminateProcess_type = NTSTATUS(WINAPI *)(HANDLE ProcessHandle, NTSTATUS ExitStatus); -typedef NTSTATUS (NTAPI *RtlGetVersion_type)(PRTL_OSVERSIONINFOW); +// Rtl -typedef NTSTATUS(WINAPI *NtTerminateProcess_type)(HANDLE ProcessHandle, NTSTATUS ExitStatus); +using RtlDoesFileExists_U_type = NTSYSAPI BOOLEAN(NTAPI *)(PCWSTR); +using RtlDosPathNameToRelativeNtPathName_U_WithStatus_type = NTSTATUS(NTAPI *)( + PCWSTR DosFileName, PUNICODE_STRING NtFileName, PWSTR* FilePath, PRTL_RELATIVE_NAME RelativeName); +using RtlReleaseRelativeName_type = void (NTAPI *)(PRTL_RELATIVE_NAME RelativeName); +using RtlGetVersion_type = NTSTATUS (NTAPI *)(PRTL_OSVERSIONINFOW); extern NtQueryDirectoryFile_type NtQueryDirectoryFile; extern NtQueryDirectoryFileEx_type NtQueryDirectoryFileEx; extern NtQueryFullAttributesFile_type NtQueryFullAttributesFile; extern NtQueryAttributesFile_type NtQueryAttributesFile; +extern NtQueryObject_type NtQueryObject; +extern NtQueryInformationFile_type NtQueryInformationFile; +extern NtQueryInformationByName_type NtQueryInformationByName; extern NtOpenFile_type NtOpenFile; extern NtCreateFile_type NtCreateFile; extern NtClose_type NtClose; +extern NtTerminateProcess_type NtTerminateProcess; extern RtlDoesFileExists_U_type RtlDoesFileExists_U; extern RtlDosPathNameToRelativeNtPathName_U_WithStatus_type RtlDosPathNameToRelativeNtPathName_U_WithStatus; extern RtlReleaseRelativeName_type RtlReleaseRelativeName; extern RtlGetVersion_type RtlGetVersion; -extern NtTerminateProcess_type NtTerminateProcess; // ensures ntdll functions have been initialized (only needed during static objects initialization) void ntdll_declarations_init(); diff --git a/src/shared/tree_container.h b/src/shared/tree_container.h index 598c846..16a3e5f 100644 --- a/src/shared/tree_container.h +++ b/src/shared/tree_container.h @@ -437,7 +437,6 @@ class TreeContainer m_SHM.reset(shm); std::pair res = m_SHM->find("Meta"); - bool lastUser = false; if (res.first == nullptr) { res.first = m_SHM->construct("Meta")(createEmpty(), m_SHM->get_segment_manager()); diff --git a/src/thooklib/hooklib.cpp b/src/thooklib/hooklib.cpp index 10b2b61..b76d92d 100644 --- a/src/thooklib/hooklib.cpp +++ b/src/thooklib/hooklib.cpp @@ -251,7 +251,7 @@ BOOL HookRIPIndirection(THookInfo &hookInfo, HookError *error) throw std::runtime_error("expected rip-relative addressing"); } - uintptr_t chainNext = disasm().jumpTarget(); + auto chainNext = disasm().jumpTarget(); if (hookInfo.stub) { hookInfo.trampoline = TrampolinePool::instance().storeStub(hookInfo.replacementFunction , reinterpret_cast(hookInfo.originalFunction) @@ -294,7 +294,7 @@ BOOL HookChainHook(THookInfo &hookInfo, LPBYTE jumpPos, HookError *error) throw std::runtime_error("failed to find jump in patch"); } - uintptr_t chainTarget = disasm().jumpTarget(); + auto chainTarget = disasm().jumpTarget(); size_t size = ud_insn_len(disasm()); diff --git a/src/usvfs_dll/hookmanager.cpp b/src/usvfs_dll/hookmanager.cpp index eccceea..e588103 100644 --- a/src/usvfs_dll/hookmanager.cpp +++ b/src/usvfs_dll/hookmanager.cpp @@ -267,6 +267,9 @@ void HookManager::initHooks() installHook(ntdllMod, nullptr, "NtQueryAttributesFile", hook_NtQueryAttributesFile); installHook(ntdllMod, nullptr, "NtQueryDirectoryFile", hook_NtQueryDirectoryFile); installHook(ntdllMod, nullptr, "NtQueryDirectoryFileEx", hook_NtQueryDirectoryFileEx); + installHook(ntdllMod, nullptr, "NtQueryObject", hook_NtQueryObject); + installHook(ntdllMod, nullptr, "NtQueryInformationFile", hook_NtQueryInformationFile); + installHook(ntdllMod, nullptr, "NtQueryInformationByName", hook_NtQueryInformationByName); installHook(ntdllMod, nullptr, "NtOpenFile", hook_NtOpenFile); installHook(ntdllMod, nullptr, "NtCreateFile", hook_NtCreateFile); installHook(ntdllMod, nullptr, "NtClose", hook_NtClose); diff --git a/src/usvfs_dll/hooks/ntdll.cpp b/src/usvfs_dll/hooks/ntdll.cpp index 5b7083d..94107e4 100644 --- a/src/usvfs_dll/hooks/ntdll.cpp +++ b/src/usvfs_dll/hooks/ntdll.cpp @@ -331,11 +331,6 @@ void GetInfoData(LPCVOID address, FILE_INFORMATION_CLASS infoClass, template void SetInfoFilenameImpl(T *info, const std::wstring &fileName) { - // not sure if the filename is supposed to be zero terminated but I did get - // invalid - // filenames when the buffer wasn't zeroed - memset(info->FileName, L'\0', info->FileNameLength); - info->FileNameLength = static_cast(fileName.length() * sizeof(WCHAR)); memcpy(info->FileName, fileName.c_str(), info->FileNameLength + 1); } @@ -372,6 +367,10 @@ void SetInfoFilename(LPVOID address, FILE_INFORMATION_CLASS infoClass, const std::wstring &fileName) { switch (infoClass) { + case FileAllInformation: { + SetInfoFilenameImpl( + &reinterpret_cast(address)->NameInformation, fileName); + } break; case FileBothDirectoryInformation: { SetInfoFilenameImplSN( reinterpret_cast(address), fileName); @@ -380,9 +379,17 @@ void SetInfoFilename(LPVOID address, FILE_INFORMATION_CLASS infoClass, SetInfoFilenameImpl( reinterpret_cast(address), fileName); } break; + case FileNameInformation: { + SetInfoFilenameImpl( + reinterpret_cast(address), fileName); + } break; case FileNamesInformation: { - SetInfoFilenameImpl(reinterpret_cast(address), - fileName); + SetInfoFilenameImpl( + reinterpret_cast(address), fileName); + } break; + case FileNormalizedNameInformation: { + SetInfoFilenameImpl( + reinterpret_cast(address), fileName); } break; case FileIdFullDirectoryInformation: { SetInfoFilenameImpl( @@ -394,8 +401,7 @@ void SetInfoFilename(LPVOID address, FILE_INFORMATION_CLASS infoClass, } break; case FileIdBothDirectoryInformation: { SetInfoFilenameImplSN( - reinterpret_cast(address), - fileName); + reinterpret_cast(address), fileName); } break; default: { // NOP @@ -1040,24 +1046,266 @@ NTSTATUS WINAPI usvfs::hook_NtQueryDirectoryFileEx( return res; } +DLLEXPORT NTSTATUS WINAPI usvfs::hook_NtQueryObject( + HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, + PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength) +{ + NTSTATUS res = STATUS_SUCCESS; + + HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) + if (!callContext.active()) { + return ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, + ObjectInformationLength, ReturnLength); + } + + PRE_REALCALL + res = ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, + ObjectInformationLength, ReturnLength); + POST_REALCALL + + // we handle both SUCCESS and BUFFER_OVERFLOW since the fixed name might be + // smaller than the original one + // + // we only handle STATUS_INFO_LENGTH_MISMATCH if ReturnLength is not NULL since + // this is only returned if the length is too small to hold the structure itself + // (regardless of the name), in which case, we need to compute our own ReturnLength + // + if ((res == STATUS_SUCCESS + || res == STATUS_BUFFER_OVERFLOW + || (res == STATUS_INFO_LENGTH_MISMATCH && ReturnLength)) + && (ObjectInformationClass == ObjectNameInformation)) { + const auto trackerInfo = ntdllHandleTracker.lookup(Handle); + const auto redir = applyReroute(READ_CONTEXT(), callContext, trackerInfo); + + OBJECT_NAME_INFORMATION* info = + reinterpret_cast(ObjectInformation); + + if (redir.redirected) { + // https://learn.microsoft.com/en-us/windows/win32/fileio/displaying-volume-paths + // + + // TODO: is that always true? + // path should start with \??\X: - we need to replace this by device name + // + WCHAR deviceName[MAX_PATH]; + std::wstring buffer(static_cast(trackerInfo)); + buffer[6] = L'\0'; + + QueryDosDeviceW(buffer.data() + 4, deviceName, ARRAYSIZE(deviceName)); + + buffer = + std::wstring(deviceName) + L'\\' + std::wstring(buffer.data() + 7, buffer.size() - 7); + + // the name is put in the buffer AFTER the struct, so the required size if + // sizeof(OBJECT_NAME_INFORMATION) + the number of bytes for the name + 2 bytes for a wide null character + const auto requiredLength = sizeof(OBJECT_NAME_INFORMATION) + buffer.size() * 2 + 2; + if (ObjectInformationLength < requiredLength) { + // if the status was info length mismatch, we keep it, we are just going to update + // *ReturnLength + if (res != STATUS_INFO_LENGTH_MISMATCH) { + res = STATUS_BUFFER_OVERFLOW; + } + + if (ReturnLength) { + *ReturnLength = static_cast(requiredLength); + } + } else { + // put the unicode buffer at the end of the object + const USHORT unicodeBufferLength = static_cast(std::min( + static_cast(std::numeric_limits::max()), + static_cast(ObjectInformationLength - sizeof(OBJECT_NAME_INFORMATION)))); + LPWSTR unicodeBuffer = reinterpret_cast( + static_cast(ObjectInformation) + sizeof(OBJECT_NAME_INFORMATION)); + + // copy the path into the buffer + wmemcpy_s(unicodeBuffer, unicodeBufferLength, buffer.data(), buffer.size()); + + // set the null character + unicodeBuffer[buffer.size()] = L'\0'; + + // update the actual unicode string + info->Name.Buffer = unicodeBuffer; + info->Name.Length = static_cast(buffer.size() * 2); + info->Name.MaximumLength = unicodeBufferLength; + + res = STATUS_SUCCESS; + } + } + + auto logger = LOG_CALL() + .PARAMWRAP(res) + .PARAM(ObjectInformationLength) + .addParam("return_length", ReturnLength ? *ReturnLength : -1) + .addParam("tracker_path", trackerInfo) + .PARAM(ObjectInformationClass) + .PARAM(redir.redirected) + .PARAM(redir.path); + + if (res == STATUS_SUCCESS) { + logger.addParam("name_info", info->Name); + } + else { + logger.addParam("name_info", ""); + } + } + + HOOK_END + return res; +} + +DLLEXPORT NTSTATUS WINAPI usvfs::hook_NtQueryInformationFile( + HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, + ULONG Length, FILE_INFORMATION_CLASS FileInformationClass) +{ + NTSTATUS res = STATUS_SUCCESS; + + HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) + if (!callContext.active()) { + return ::NtQueryInformationFile(FileHandle, IoStatusBlock, FileInformation, + Length, FileInformationClass); + } + + PRE_REALCALL + res = ::NtQueryInformationFile(FileHandle, IoStatusBlock, FileInformation, Length, + FileInformationClass); + POST_REALCALL + + // we handle both SUCCESS and BUFFER_OVERFLOW since the fixed name might be + // smaller than the original one + // + // we do not handle STATUS_INFO_LENGTH_MISMATCH because this is only returned if + // the length is too small to old the structure itself (regardless of the name) + // + // TODO: currently, this does not handle STATUS_BUFFER_OVERLOW for ALL information + // because most of the structures would need to be manually filled, which is very + // complicated - this should not pose huge issue + // + if (((res == STATUS_SUCCESS || res == STATUS_BUFFER_OVERFLOW) && + (FileInformationClass == FileNameInformation || + FileInformationClass == FileNormalizedNameInformation)) || + (res == STATUS_SUCCESS && FileInformationClass == FileAllInformation)) { + + const auto trackerInfo = ntdllHandleTracker.lookup(FileHandle); + const auto redir = applyReroute(READ_CONTEXT(), callContext, trackerInfo); + + // TODO: difference between FileNameInformation and FileNormalizedNameInformation + + // maximum length in bytes - the required length is + // - for ALL, 100 + the number of bytes in the name (not account for null character) + // - for NAME, 4 + the number of bytes in the name (not accounting for null character) + // + // it is close to the sizeof the structure + the number of bytes in the name, except + // for the alignment that gives us a bit more space + // + ULONG prefixStructLength; + FILE_NAME_INFORMATION *info; + if (FileInformationClass == FileAllInformation) { + info = &reinterpret_cast(FileInformation)->NameInformation; + prefixStructLength = sizeof(FILE_ALL_INFORMATION) - 4; + } else { + info = reinterpret_cast(FileInformation); + prefixStructLength = sizeof(FILE_NAME_INFORMATION) - 4; + } + + if (redir.redirected) + { + auto requiredLength = prefixStructLength + 2 * (trackerInfo.size() - 6); + if (Length < requiredLength) { + res = STATUS_BUFFER_OVERFLOW; + } else { + // strip the \??\X: prefix (X being the drive name) + LPCWSTR filenameFixed = static_cast(trackerInfo) + 6; + + // not using SetInfoFilename because the length is not set and we do not need to + // 0-out the memory here + info->FileNameLength = static_cast((trackerInfo.size() - 6) * 2); + wmemcpy(info->FileName, filenameFixed, trackerInfo.size() - 6); + res = STATUS_SUCCESS; + + // update status block, Information is the number of bytes written, basically + // the required length + IoStatusBlock->Status = STATUS_SUCCESS; + IoStatusBlock->Information = static_cast(requiredLength); + } + } + + LOG_CALL() + .PARAMWRAP(res) + .addParam("tracker_path", trackerInfo) + .PARAM(FileInformationClass) + .PARAM(redir.redirected) + .PARAM(redir.path) + .addParam("name_info", res == STATUS_SUCCESS ? std::wstring{info->FileName, + info->FileNameLength / sizeof(WCHAR)} + : std::wstring{}); + + } + + HOOK_END + return res; +} + unique_ptr_deleter -makeObjectAttributes(RedirectionInfo &redirInfo, - POBJECT_ATTRIBUTES attributeTemplate) +makeObjectAttributes(RedirectionInfo& redirInfo, + POBJECT_ATTRIBUTES attributeTemplate) { if (redirInfo.redirected) { unique_ptr_deleter result( - new OBJECT_ATTRIBUTES, [](OBJECT_ATTRIBUTES *ptr) { delete ptr; }); + new OBJECT_ATTRIBUTES, [](OBJECT_ATTRIBUTES* ptr) { delete ptr; }); memcpy(result.get(), attributeTemplate, sizeof(OBJECT_ATTRIBUTES)); result->RootDirectory = nullptr; - result->ObjectName = static_cast(redirInfo.path); + result->ObjectName = static_cast(redirInfo.path); return result; - } else { + } + else { // just reuse the template with a dummy deleter return unique_ptr_deleter(attributeTemplate, - [](OBJECT_ATTRIBUTES *) {}); + [](OBJECT_ATTRIBUTES*) {}); } } +DLLEXPORT NTSTATUS WINAPI usvfs::hook_NtQueryInformationByName( + POBJECT_ATTRIBUTES ObjectAttributes, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass +) +{ + NTSTATUS res = STATUS_SUCCESS; + + HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) + + if (!callContext.active()) { + res = ::NtQueryInformationByName(ObjectAttributes, IoStatusBlock, FileInformation, Length, FileInformationClass); + callContext.updateLastError(); + return res; + } + + RedirectionInfo redir = applyReroute(READ_CONTEXT(), callContext, CreateUnicodeString(ObjectAttributes)); + + if (redir.redirected) { + auto newObjectAttributes = makeObjectAttributes(redir, ObjectAttributes); + + PRE_REALCALL + res = ::NtQueryInformationByName(newObjectAttributes.get(), IoStatusBlock, FileInformation, Length, FileInformationClass); + POST_REALCALL + + LOG_CALL() + .PARAMWRAP(res) + .addParam("input_path", *ObjectAttributes->ObjectName) + .addParam("reroute_path", redir.path); + } + else { + PRE_REALCALL + res = ::NtQueryInformationByName(ObjectAttributes, IoStatusBlock, FileInformation, Length, FileInformationClass); + POST_REALCALL + } + + HOOK_END + return res; +} + NTSTATUS ntdll_mess_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, diff --git a/src/usvfs_dll/hooks/ntdll.h b/src/usvfs_dll/hooks/ntdll.h index befe7b1..a87b00c 100644 --- a/src/usvfs_dll/hooks/ntdll.h +++ b/src/usvfs_dll/hooks/ntdll.h @@ -40,6 +40,31 @@ hook_NtQueryDirectoryFileEx(HANDLE FileHandle, ULONG QueryFlags, PUNICODE_STRING FileName); +DLLEXPORT NTSTATUS WINAPI +hook_NtQueryObject( + HANDLE Handle, + OBJECT_INFORMATION_CLASS ObjectInformationClass, + PVOID ObjectInformation, + ULONG ObjectInformationLength, + PULONG ReturnLength); + +DLLEXPORT NTSTATUS WINAPI +hook_NtQueryInformationFile( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +DLLEXPORT NTSTATUS WINAPI +hook_NtQueryInformationByName( + POBJECT_ATTRIBUTES ObjectAttributes, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass +); + DLLEXPORT NTSTATUS WINAPI hook_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, diff --git a/src/usvfs_dll/usvfs.cpp b/src/usvfs_dll/usvfs.cpp index 5024975..7b38143 100644 --- a/src/usvfs_dll/usvfs.cpp +++ b/src/usvfs_dll/usvfs.cpp @@ -50,8 +50,6 @@ PVOID exceptionHandler = nullptr; CrashDumpsType usvfs_dump_type = CrashDumpsType::None; std::wstring usvfs_dump_path; -typedef std::codecvt_utf8_utf16 u8u16_convert; - // this is called for every single file, so it's a bit long winded, but it's // as fast as it gets, probably // @@ -869,7 +867,7 @@ BOOL WINAPI usvfsCreateVFSDump(LPSTR buffer, size_t *size) } -VOID WINAPI usvfsBlacklistExecutable(LPWSTR executableName) +VOID WINAPI usvfsBlacklistExecutable(LPCWSTR executableName) { context->blacklistExecutable(executableName); } @@ -881,7 +879,7 @@ VOID WINAPI usvfsClearExecutableBlacklist() } -VOID WINAPI usvfsAddSkipFileSuffix(LPWSTR fileSuffix) +VOID WINAPI usvfsAddSkipFileSuffix(LPCWSTR fileSuffix) { context->addSkipFileSuffix(fileSuffix); } @@ -893,7 +891,7 @@ VOID WINAPI usvfsClearSkipFileSuffixes() } -VOID WINAPI usvfsAddSkipDirectory(LPWSTR directory) +VOID WINAPI usvfsAddSkipDirectory(LPCWSTR directory) { context->addSkipDirectory(directory); } @@ -905,7 +903,7 @@ VOID WINAPI usvfsClearSkipDirectories() } -VOID WINAPI usvfsForceLoadLibrary(LPWSTR processName, LPWSTR libraryPath) +VOID WINAPI usvfsForceLoadLibrary(LPCWSTR processName, LPCWSTR libraryPath) { context->forceLoadLibrary(processName, libraryPath); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4314802..3881d9c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -58,7 +58,7 @@ foreach(directory ${directories}) add_subdirectory(${directory}) get_filename_component(dirname ${directory} NAME) - if(dirname STREQUAL "test_utils") + if((dirname STREQUAL "test_utils") OR (dirname STREQUAL "gtest_utils")) continue() endif() diff --git a/test/fixtures/usvfs_global_test/BasicTest/expected/data/file.txt b/test/fixtures/usvfs_global_test/BasicTest/expected/data/file.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/docs/doc.txt b/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/docs/doc.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/empty/.gitkeep b/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/empty/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/readme.txt b/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/BasicTest/source/data/file.txt b/test/fixtures/usvfs_global_test/BasicTest/source/data/file.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/docs/doc.txt b/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/docs/doc.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/empty/.gitkeep b/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/empty/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/info.txt b/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/info.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/readme.txt b/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/RedFileSystemTest/example.cpp b/test/fixtures/usvfs_global_test/RedFileSystemTest/example.cpp new file mode 100644 index 0000000..9a79d16 --- /dev/null +++ b/test/fixtures/usvfs_global_test/RedFileSystemTest/example.cpp @@ -0,0 +1,139 @@ +#include +#include + +std::filesystem::path storages_path; +std::filesystem::path hudpainter_path; +bool has_mo2; + +// See below main() for implementation. +void request_directory(std::filesystem::path); +std::filesystem::path restrict_path(std::filesystem::path); +std::vector get_files(); +std::string read_file(std::filesystem::path); +void write_file(std::filesystem::path); + +// Expected path when not using MO2 +int main() +{ + // RedFileSystem plugin (DLL) is loaded. + has_mo2 = GetModuleHandle(TEXT("usvfs_x64.dll")) != nullptr; + + // Setup paths + auto path = std::filesystem::absolute("."); // \bin\x64 + auto game_path = path.parent_path().parent_path(); // + + storages_path = game_path / "r6" / "storages"; // \r6\storages + + // Create storages directory if it is no present. + request_directory(storages_path); // \r6\storages + + // Game is running... + + // HUD Painter request its storage endpoint. + hudpainter_path = storages_path / "HUDPainter"; + request_directory(hudpainter_path); // \r6\storages\HUDPainter + + // HUD Painter request a file (present in archive). + auto default_path = + restrict_path("DEFAULT.json"); // \r6\storages\HUDPainter\DEFAULT.json + + // HUD Painter read file in `default_path` with success. + auto data = read_file(default_path); // \r6\storages\HUDPainter\DEFAULT.json + + // HUD Painter request another file. + auto test_path = + restrict_path("TEST.json"); // \r6\storages\HUDPainter\TEST.json + + // (Bug A) HUD Painter write file in `test_path`. + write_file(test_path); // \r6\storages\HUDPainter\TEST.json + // file is created with workaround, not created otherwise. + + // (Bug B) HUD Painter request list of files. + // TEST.json is successfully created when using workaround. + auto files = get_files(); // [0] \r6\storages\HUDPainter\DEFAULT.json + + // files.size() == 1 + + // Expect: + // files.size() == 2 + // + // [0] \r6\storages\HUDPainter\DEFAULT.json + // [1] \r6\storages\HUDPainter\TEST.json +} + +// Create directory if it is no present +void request_directory(std::filesystem::path path) +{ + bool is_present = std::filesystem::exists(path); + + if (is_present) { + return; + } + std::filesystem::create_directory(path); +} + +// Resolve path for HUDPainter (security layer) +std::filesystem::path restrict_path(std::filesystem::path path) +{ + auto real_path = std::filesystem::weakly_canonical(hudpainter_path / path); + + // Workaround when using MO2 + if (has_mo2) { + return real_path; + } + // Expected implementation without MO2 + if (real_path.string().find(hudpainter_path.string() + "\\") != 0) { + // "throw" error + } + return real_path; +} + +// List files in storage of HUDPainter. +std::vector get_files() +{ + std::vector files; + auto entries = std::filesystem::directory_iterator(hudpainter_path); + + for (const auto& entry : entries) { + if (entry.is_regular_file()) { + auto file_name = entry.path().filename(); + auto file_path = + hudpainter_path / + file_name; // Culprit of bug B. When using entry.path() directly, it works. + + files.emplace_back(file_path); + } + } + return files; +} + +std::string read_file(std::filesystem::path path) +{ + std::ifstream stream; + + // With workaround: + // std::filesystem::create_directories(path.parent_path()); + stream.open(path); + if (!stream.is_open()) { + return ""; + } + std::stringstream data; + + data << stream.rdbuf(); + stream.close(); + return data.str(); +} + +void write_file(std::filesystem::path path) +{ + std::ofstream stream; + + // With workaround: + // std::filesystem::create_directories(path.parent_path()); + stream.open(path, std::ios_base::trunc); + if (!stream.is_open()) { + return; + } + stream << ""; + stream.close(); +} diff --git a/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/data/.gitkeep b/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json b/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json new file mode 100644 index 0000000..dd85d0a --- /dev/null +++ b/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json @@ -0,0 +1,11 @@ +{ + "propertiesDefault": [ + { + "name": "MainColors.Red", + "red": 1.1761, + "green": 0.3809, + "blue": 0.3476, + "alpha": 1 + } + ] +} diff --git a/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/overwrite/r6/storages/HUDPainter/TEST.json b/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/overwrite/r6/storages/HUDPainter/TEST.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/overwrite/r6/storages/HUDPainter/TEST.json @@ -0,0 +1 @@ +{} diff --git a/test/fixtures/usvfs_global_test/RedFileSystemTest/source/data/.gitkeep b/test/fixtures/usvfs_global_test/RedFileSystemTest/source/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/RedFileSystemTest/source/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json b/test/fixtures/usvfs_global_test/RedFileSystemTest/source/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json new file mode 100644 index 0000000..dd85d0a --- /dev/null +++ b/test/fixtures/usvfs_global_test/RedFileSystemTest/source/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json @@ -0,0 +1,11 @@ +{ + "propertiesDefault": [ + { + "name": "MainColors.Red", + "red": 1.1761, + "green": 0.3809, + "blue": 0.3476, + "alpha": 1 + } + ] +} diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/docs/doc.skip b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/docs/doc.skip new file mode 100644 index 0000000..09a24fd --- /dev/null +++ b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/docs/doc.skip @@ -0,0 +1 @@ +doc.skip in data/docs diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/file.txt b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/file.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.skip b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.skip new file mode 100644 index 0000000..4b8bde6 --- /dev/null +++ b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.skip @@ -0,0 +1 @@ +doc.skip in mod1/docs diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.txt b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/empty/.gitkeep b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/empty/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.skip b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.skip new file mode 100644 index 0000000..1d1857a --- /dev/null +++ b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.skip @@ -0,0 +1 @@ +readme.skip in mod1 diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.txt b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/expected/overwrite/readme.skip b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/overwrite/readme.skip new file mode 100644 index 0000000..0062dba --- /dev/null +++ b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/overwrite/readme.skip @@ -0,0 +1 @@ +readme.skip in overwrite diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/docs/doc.skip b/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/docs/doc.skip new file mode 100644 index 0000000..09a24fd --- /dev/null +++ b/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/docs/doc.skip @@ -0,0 +1 @@ +doc.skip in data/docs diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/file.txt b/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/file.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.skip b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.skip new file mode 100644 index 0000000..4b8bde6 --- /dev/null +++ b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.skip @@ -0,0 +1 @@ +doc.skip in mod1/docs diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.txt b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/empty/.gitkeep b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/empty/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.skip b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.skip new file mode 100644 index 0000000..1d1857a --- /dev/null +++ b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.skip @@ -0,0 +1 @@ +readme.skip in mod1 diff --git a/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.txt b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/gtest_utils/CMakeLists.txt b/test/gtest_utils/CMakeLists.txt new file mode 100644 index 0000000..8dcda70 --- /dev/null +++ b/test/gtest_utils/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16) + +find_package(GTest CONFIG REQUIRED) + +add_library(gtest_utils STATIC + gtest_utils.cpp + gtest_utils.h +) +set_target_properties(gtest_utils PROPERTIES FOLDER tests) +target_link_libraries(gtest_utils PUBLIC GTest::gtest) +target_include_directories(gtest_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/test/gtest_utils/gtest_utils.cpp b/test/gtest_utils/gtest_utils.cpp new file mode 100644 index 0000000..28fd3a9 --- /dev/null +++ b/test/gtest_utils/gtest_utils.cpp @@ -0,0 +1,122 @@ +#include "gtest_utils.h" + +// this file is shared by both usvfs_global_test and usvfs_global_test_runner +// + +#include +#include +#include + +#include + +#include + +std::string trimmed(std::string content) +{ + boost::algorithm::trim(content); + return content; +} + +// small utility to read files +std::optional read_content(const std::filesystem::path& path, + bool trim = true) +{ + std::ifstream ifs(path, std::ios::binary | std::ios::ate); + + if (!ifs) { + return {}; + } + + const auto count = ifs.tellg(); + + std::string buffer(static_cast(count), '\0'); + + ifs.seekg(0, std::ios::beg); + ifs.read(buffer.data(), count); + + if (trim) { + boost::algorithm::trim(buffer); + } + + return buffer; +} + +::testing::AssertionResult AssertDirectoryEquals(const std::filesystem::path& expected, + const std::filesystem::path& actual, + bool content) +{ + std::vector failure_messages; + std::vector in_both; + + // iterate left, check on right + for (const auto& it : std::filesystem::recursive_directory_iterator{expected}) { + const auto relpath = relative(it.path(), expected); + if (!exists(actual / relpath)) { + failure_messages.push_back( + std::format("{} expected but not found", relpath.string())); + } else { + in_both.push_back(relpath); + } + } + + // iterate right, check on left + for (const auto& it : std::filesystem::recursive_directory_iterator{actual}) { + const auto relpath = relative(it.path(), actual); + if (!exists(expected / relpath)) { + failure_messages.push_back( + std::format("{} found but not expected", relpath.string())); + } + } + + // check contents + if (content) { + for (const auto& relpath : in_both) { + const auto expected_path = expected / relpath, actual_path = actual / relpath; + + if (is_directory(expected_path) != is_directory(actual_path)) { + failure_messages.push_back( + std::format("{} type mismatch, expected {} but found {}", relpath.string(), + is_directory(expected_path) ? "directory" : "file", + is_directory(expected_path) ? "file" : "directory")); + continue; + } + + if (is_directory(expected_path)) { + continue; + } + + if (read_content(expected_path) != read_content(actual_path)) { + failure_messages.push_back( + std::format("{} content mismatch", relpath.string())); + } + } + } + + if (failure_messages.empty()) { + return ::testing::AssertionSuccess(); + } + + return ::testing::AssertionFailure() + << "\n" + << boost::algorithm::join(failure_messages, "\n") << "\n"; +} + +::testing::AssertionResult AssertContentEquals(std::string_view expected, + const std::filesystem::path& path, + bool trim) +{ + const auto content = read_content(path, trim); + + if (!content) { + return ::testing::AssertionFailure() + << "failed to open path '" << path.string() << "'"; + } + + if (*content != expected) { + return ::testing::AssertionFailure() + << "mismatch content for '" << path.string() << "', expected '" << expected + << "', found '" << *content << "'"; + } + + return ::testing::AssertionSuccess(); +} \ No newline at end of file diff --git a/test/gtest_utils/gtest_utils.h b/test/gtest_utils/gtest_utils.h new file mode 100644 index 0000000..5837ca6 --- /dev/null +++ b/test/gtest_utils/gtest_utils.h @@ -0,0 +1,29 @@ +#pragma once + +// this file is shared by both usvfs_global_test and usvfs_global_test_runner +// + +#include + +#include + +::testing::AssertionResult AssertDirectoryEquals(const std::filesystem::path& expected, + const std::filesystem::path& actual, + bool content = true); + +::testing::AssertionResult AssertContentEquals(std::string_view expected, + const std::filesystem::path& path, + bool trim = true); + +// macro to assert that the contents of two directories are identical - directories are +// compared recursively and file contents are compared (excluding extra spaces or lines +// in file) +// +#define ASSERT_DIRECTORY_EQ(Expected, Actual) \ + ASSERT_TRUE(AssertDirectoryEquals(Expected, Actual)) + +// macro to assert that the contents of two files are identical (excluding extra space +// or lines in file) +// +#define ASSERT_CONTENT_EQ(Expected, Path) \ + ASSERT_TRUE(AssertContentEquals(Expected, Path)) \ No newline at end of file diff --git a/test/test_utils/CMakeLists.txt b/test/test_utils/CMakeLists.txt index cdb86d9..88c75d6 100644 --- a/test/test_utils/CMakeLists.txt +++ b/test/test_utils/CMakeLists.txt @@ -2,7 +2,10 @@ cmake_minimum_required(VERSION 3.16) find_package(GTest CONFIG REQUIRED) -add_library(test_utils STATIC test_helpers.cpp test_helpers.h) +add_library(test_utils STATIC + test_helpers.cpp + test_helpers.h +) set_target_properties(test_utils PROPERTIES FOLDER tests) target_link_libraries(test_utils PUBLIC shared) target_include_directories(test_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/test/test_utils/test_helpers.cpp b/test/test_utils/test_helpers.cpp index ef008e9..bcf762f 100644 --- a/test/test_utils/test_helpers.cpp +++ b/test/test_utils/test_helpers.cpp @@ -7,7 +7,7 @@ namespace test { - std::string FuncFailed::msg(const char* func, const char* arg1, const unsigned long* res, const char* what) + std::string FuncFailed::msg(std::string_view func, const char* arg1, const unsigned long* res, const char* what) { std::string buffer; buffer.reserve(128); @@ -24,6 +24,25 @@ namespace test { return buffer; } + + ScopedFILE ScopedFILE::open(const std::filesystem::path& filepath, std::wstring_view mode, errno_t& err) { + FILE* fp = nullptr; + err = _wfopen_s(&fp, filepath.c_str(), mode.data()); + if (err || !fp) { + return ScopedFILE(); + } + return ScopedFILE(fp); + } + + ScopedFILE ScopedFILE::open(const std::filesystem::path& filepath, std::wstring_view mode) { + errno_t err; + auto file = open(filepath, mode, err); + if (err || !file) { + throw_testWinFuncFailed("_wfopen_s", filepath.string().c_str(), err); + } + return file; + } + path path_of_test_bin(const path& relative) { path base(winapi::wide::getModuleFileName(nullptr)); return relative.empty() ? base.parent_path() : base.parent_path() / relative; @@ -85,10 +104,7 @@ namespace test { { using namespace std; - ScopedFILE f; - errno_t err = _wfopen_s(f, file.c_str(), binary ? L"rb" : L"rt"); - if (err || !f) - throw_testWinFuncFailed("_wfopen_s", file.string().c_str(), err); + const auto f = ScopedFILE::open(file, binary ? L"rb" : L"rt"); if (fseek(f, 0, SEEK_END)) throw_testWinFuncFailed("fseek", (unsigned long) 0); @@ -105,7 +121,7 @@ namespace test { std::vector content(static_cast(size)); content.resize(fread(content.data(), sizeof(char), content.size(), f)); - return std::move(content); + return content; } bool compare_files(const path& file1, const path& file2, bool binary) diff --git a/test/test_utils/test_helpers.h b/test/test_utils/test_helpers.h index ad69f22..1bc846a 100644 --- a/test/test_utils/test_helpers.h +++ b/test/test_utils/test_helpers.h @@ -2,6 +2,8 @@ #include "windows_sane.h" +#include +#include #include namespace test @@ -24,7 +26,7 @@ namespace test : std::runtime_error(msg(func, arg1, &res, what)) {} private: - std::string msg(const char *func, const char *arg1 = nullptr, const unsigned long *res = nullptr, const char *what = nullptr); + std::string msg(std::string_view func, const char *arg1 = nullptr, const unsigned long *res = nullptr, const char *what = nullptr); }; class WinFuncFailed : public std::runtime_error @@ -51,12 +53,12 @@ namespace test return WinFuncFailed(std::format("{} failed : result=({:#x}), lastError={}", func, res, m_gle)); } - WinFuncFailed operator()(std::basic_string_view func, std::basic_string_view arg1) + WinFuncFailed operator()(std::string_view func, std::basic_string_view arg1) { return WinFuncFailed(std::format("{} failed : {}, lastError={}", func, arg1, m_gle)); } - WinFuncFailed operator()(std::basic_string_view func, std::basic_string_view arg1, unsigned long res) + WinFuncFailed operator()(std::string_view func, std::basic_string_view arg1, unsigned long res) { return WinFuncFailed(std::format("{} failed : {}, result=({:#x}), lastError={}", func, arg1, res, m_gle)); } @@ -67,7 +69,7 @@ namespace test // trick to guarantee the evalutation of GetLastError() before the evalution of the parameters to the WinFuncFailed message generation template - [[noreturn]] void throw_testWinFuncFailed(std::basic_string_view func, Args &&...args) + [[noreturn]] void throw_testWinFuncFailed(std::string_view func, Args &&...args) { ::test::WinFuncFailedGenerator exceptionGenerator; throw exceptionGenerator(func, std::forward(args)...); @@ -76,30 +78,30 @@ namespace test class ScopedFILE { public: - ScopedFILE(FILE *f = nullptr) : m_f(f) {} + // try to open the given filepath with the given mode, if it fails, set err to + // the return code of _wfopen_s and return a nulled scoped file + // + static ScopedFILE open(const std::filesystem::path &filepath, std::wstring_view mode, errno_t &err); + + // same as above but throw a WinFuncFailed() exception if opening the file failed + // + static ScopedFILE open(const std::filesystem::path &filepath, std::wstring_view mode); + + public: + ScopedFILE(FILE *f = nullptr) : m_f(f, &fclose) {} + + ScopedFILE(ScopedFILE &&other) noexcept = default; + ~ScopedFILE() = default; + ScopedFILE(const ScopedFILE &) = delete; - ScopedFILE(ScopedFILE &&other) noexcept : m_f(other.m_f) { other.m_f = nullptr; } - ~ScopedFILE() - { - if (m_f) - fclose(m_f); - } - void close() - { - if (m_f) - { - fclose(m_f); - m_f = nullptr; - } - } + void close() { m_f = nullptr; } - operator bool() const { return m_f; } - operator FILE *() const { return m_f; } - operator FILE **() { return &m_f; } + operator bool() const { return static_cast(m_f); } + operator FILE *() const { return m_f.get(); } private: - FILE *m_f; + std::unique_ptr m_f; }; using std::filesystem::path; diff --git a/test/thooklib_test/main.cpp b/test/thooklib_test/main.cpp index 0efc836..e679488 100644 --- a/test/thooklib_test/main.cpp +++ b/test/thooklib_test/main.cpp @@ -75,15 +75,6 @@ class HookingTest : public testing::Test private: }; -static shared_ptr logger() -{ - shared_ptr result = spdlog::get("test"); - if (result.get() == nullptr) { - result = spdlog::stdout_logger_mt("test"); - } - return result; -} - TEST(GetProcAddressTest, ReturnsValidResults) { HMODULE mh = GetModuleHandleA("KernelBase.dll"); diff --git a/test/tvfs_test/main.cpp b/test/tvfs_test/main.cpp index 253b75c..0c61a43 100644 --- a/test/tvfs_test/main.cpp +++ b/test/tvfs_test/main.cpp @@ -25,48 +25,44 @@ along with usvfs. If not, see . #include -#pragma warning (push, 3) -#include +#pragma warning(push, 3) #include +#include #include -#pragma warning (pop) - +#pragma warning(pop) #include -#include #include +#include -#include #include +#include #include -#include -#include #include #include -#include #include - - +#include +#include +#include namespace spd = spdlog; namespace ush = usvfs::shared; - -// name of a file to be created in the virtual fs. Shouldn't exist on disc but the directory must exist -static LPCSTR VIRTUAL_FILEA = "C:/np.exe"; +// name of a file to be created in the virtual fs. Shouldn't exist on disc but the +// directory must exist +static LPCSTR VIRTUAL_FILEA = "C:/np.exe"; static LPCWSTR VIRTUAL_FILEW = L"C:/np.exe"; // a real file on disc that has to exist -static LPCSTR REAL_FILEA = "C:/windows/notepad.exe"; +static LPCSTR REAL_FILEA = "C:/windows/notepad.exe"; static LPCWSTR REAL_FILEW = L"C:/windows/notepad.exe"; -static LPCSTR REAL_DIRA = "C:/windows/Logs"; +static LPCSTR REAL_DIRA = "C:/windows/Logs"; static LPCWSTR REAL_DIRW = L"C:/windows/Logs"; - static std::shared_ptr logger() { std::shared_ptr result = spdlog::get("test"); @@ -76,9 +72,10 @@ static std::shared_ptr logger() return result; } -auto defaultUsvfsParams(const char* instanceName = "usvfs_test") { - std::unique_ptr parameters{ - usvfsCreateParameters(), &usvfsFreeParameters }; +auto defaultUsvfsParams(const char* instanceName = "usvfs_test") +{ + std::unique_ptr parameters{ + usvfsCreateParameters(), &usvfsFreeParameters}; usvfsSetInstanceName(parameters.get(), instanceName); usvfsSetDebugMode(parameters.get(), true); @@ -92,13 +89,15 @@ auto defaultUsvfsParams(const char* instanceName = "usvfs_test") { class USVFSTest : public testing::Test { public: - void SetUp() { + void SetUp() + { SHMLogger::create("usvfs"); // need to initialize logging in the context of the dll usvfsInitLogging(); } - void TearDown() { + void TearDown() + { std::array buffer; while (SHMLogger::instance().tryGet(buffer.data(), buffer.size())) { std::cout << buffer.data() << std::endl; @@ -112,19 +111,22 @@ class USVFSTest : public testing::Test class USVFSTestWithReroute : public testing::Test { public: - void SetUp() { + void SetUp() + { SHMLogger::create("usvfs"); // need to initialize logging in the context of the dll usvfsInitLogging(); auto params = defaultUsvfsParams(); m_Context.reset(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer &tree = m_Context->redirectionTable(); - tree.addFile(ush::string_cast(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str() - , usvfs::RedirectionDataLocal(REAL_FILEA)); + usvfs::RedirectionTreeContainer& tree = m_Context->redirectionTable(); + tree.addFile( + ush::string_cast(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str(), + usvfs::RedirectionDataLocal(REAL_FILEA)); } - void TearDown() { + void TearDown() + { std::array buffer; while (SHMLogger::instance().tryGet(buffer.data(), buffer.size())) { std::cout << buffer.data() << std::endl; @@ -132,6 +134,7 @@ class USVFSTestWithReroute : public testing::Test m_Context.reset(); SHMLogger::free(); } + private: std::unique_ptr m_Context; }; @@ -139,13 +142,15 @@ class USVFSTestWithReroute : public testing::Test class USVFSTestAuto : public testing::Test { public: - void SetUp() { + void SetUp() + { auto params = defaultUsvfsParams(); usvfsConnectVFS(params.get()); SHMLogger::create("usvfs"); } - void TearDown() { + void TearDown() + { usvfsDisconnectVFS(); std::array buffer; @@ -158,21 +163,28 @@ class USVFSTestAuto : public testing::Test private: }; - TEST_F(USVFSTest, CanResizeRedirectiontree) { using usvfs::shared::MissingThrow; ASSERT_NO_THROW({ - usvfs::RedirectionTreeContainer container("treetest_shm", 1024); - for (char i = 'a'; i <= 'z'; ++i) { - for (char j = 'a'; j <= 'z'; ++j) { - std::string name = std::string(R"(C:\temp\)") + i + j; - container.addFile(name, usvfs::RedirectionDataLocal("gaga"), false); - } + usvfs::RedirectionTreeContainer container("treetest_shm", 1024); + for (char i = 'a'; i <= 'z'; ++i) { + for (char j = 'a'; j <= 'z'; ++j) { + std::string name = std::string(R"(C:\temp\)") + i + j; + container.addFile(name, usvfs::RedirectionDataLocal("gaga"), false); } + } - ASSERT_EQ("gaga", container->node("C:")->node("temp")->node("aa", MissingThrow)->data().linkTarget); - ASSERT_EQ("gaga", container->node("C:")->node("temp")->node("az", MissingThrow)->data().linkTarget); + ASSERT_EQ("gaga", container->node("C:") + ->node("temp") + ->node("aa", MissingThrow) + ->data() + .linkTarget); + ASSERT_EQ("gaga", container->node("C:") + ->node("temp") + ->node("az", MissingThrow) + ->data() + .linkTarget); }); } @@ -181,15 +193,11 @@ TEST_F(USVFSTest, CreateFileHookReportsCorrectErrorOnMissingFile) { ASSERT_NO_THROW({ USVFSParameters params; - USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, CrashDumpsType::None, ""); - std::unique_ptr ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); - HANDLE res = usvfs::hook_CreateFileW(VIRTUAL_FILEW - , GENERIC_READ - , FILE_SHARE_READ | FILE_SHARE_WRITE - , nullptr - , OPEN_EXISTING - , FILE_ATTRIBUTE_NORMAL - , nullptr); + USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, +CrashDumpsType::None, ""); std::unique_ptr +ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); HANDLE res = +usvfs::hook_CreateFileW(VIRTUAL_FILEW , GENERIC_READ , FILE_SHARE_READ | +FILE_SHARE_WRITE , nullptr , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , nullptr); ASSERT_EQ(INVALID_HANDLE_VALUE, res); ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); @@ -211,20 +219,20 @@ TEST_F(USVFSTestWithReroute, CreateFileHookRedirectsFile) } */ - TEST_F(USVFSTest, GetFileAttributesHookReportsCorrectErrorOnMissingFile) { ASSERT_NO_THROW({ try { - auto params = defaultUsvfsParams(); - std::unique_ptr ctx(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - DWORD res = usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW); + auto params = defaultUsvfsParams(); + std::unique_ptr ctx( + usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); + DWORD res = usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW); - ASSERT_EQ(INVALID_FILE_ATTRIBUTES, res); - ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); + ASSERT_EQ(INVALID_FILE_ATTRIBUTES, res); + ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); } catch (const std::exception& e) { - logger()->error("Exception: {}", e.what()); - throw; + logger()->error("Exception: {}", e.what()); + throw; } }); } @@ -232,21 +240,24 @@ TEST_F(USVFSTest, GetFileAttributesHookReportsCorrectErrorOnMissingFile) TEST_F(USVFSTest, GetFileAttributesHookRedirectsFile) { auto params = defaultUsvfsParams(); - std::unique_ptr ctx(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer &tree = ctx->redirectionTable(); + std::unique_ptr ctx( + usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); + usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable(); - tree.addFile(ush::string_cast(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str() - , usvfs::RedirectionDataLocal(REAL_FILEA)); + tree.addFile( + ush::string_cast(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str(), + usvfs::RedirectionDataLocal(REAL_FILEA)); - ASSERT_EQ(::GetFileAttributesW(REAL_FILEW) - , usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW)); + ASSERT_EQ(::GetFileAttributesW(REAL_FILEW), + usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW)); } /* TEST_F(USVFSTest, GetFullPathNameOnRegularCurrentDirectory) { USVFSParameters params; - USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, CrashDumpsType::None, ""); - std::unique_ptr ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); + USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, +CrashDumpsType::None, ""); std::unique_ptr +ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); std::wstring expected = winapi::wide::getCurrentDirectory() + L"\\filename.txt"; @@ -254,7 +265,8 @@ TEST_F(USVFSTest, GetFullPathNameOnRegularCurrentDirectory) std::unique_ptr buffer(new wchar_t[bufferLength]); LPWSTR filePart = nullptr; - DWORD res = usvfs::hook_GetFullPathNameW(L"filename.txt", bufferLength, buffer.get(), &filePart); + DWORD res = usvfs::hook_GetFullPathNameW(L"filename.txt", bufferLength, buffer.get(), +&filePart); ASSERT_NE(0UL, res); ASSERT_EQ(expected, std::wstring(buffer.get())); @@ -263,38 +275,37 @@ TEST_F(USVFSTest, GetFullPathNameOnRegularCurrentDirectory) // small wrapper to call usvfs::hook_NtOpenFile with a path // // at some point in time, changes were made to USVFS such that calling a hooked -// function from a handle obtained from a non-hooked function would not work anymore, +// function from a handle obtained from a non-hooked function would not work anymore, // meaning that function such as CreateFileW that have no hook equivalent cannot // be used to test hook functions -// +// // this function is useful to simulate a CreateFileW by internally using the hook // version of NtOpenFile // -HANDLE hooked_NtOpenFile(LPCWSTR path, ACCESS_MASK accessMask, ULONG shareAccess, ULONG openOptions) +HANDLE hooked_NtOpenFile(LPCWSTR path, ACCESS_MASK accessMask, ULONG shareAccess, + ULONG openOptions) { constexpr size_t BUFFER_SIZE = 2048; IO_STATUS_BLOCK statusBlock; OBJECT_ATTRIBUTES attributes; - attributes.SecurityDescriptor = 0; + attributes.SecurityDescriptor = 0; attributes.SecurityQualityOfService = 0; - attributes.RootDirectory = 0; - attributes.Attributes = 0; - attributes.Length = sizeof(OBJECT_ATTRIBUTES); + attributes.RootDirectory = 0; + attributes.Attributes = 0; + attributes.Length = sizeof(OBJECT_ATTRIBUTES); WCHAR stringBuffer[BUFFER_SIZE]; - UNICODE_STRING string; + UNICODE_STRING string; string.Buffer = stringBuffer; lstrcpyW(stringBuffer, L"\\??\\"); lstrcatW(stringBuffer, path); - string.Length = lstrlenW(stringBuffer) * 2; - string.MaximumLength = BUFFER_SIZE; + string.Length = lstrlenW(stringBuffer) * 2; + string.MaximumLength = BUFFER_SIZE; attributes.ObjectName = &string; - + HANDLE ret = INVALID_HANDLE_VALUE; - if (usvfs::hook_NtOpenFile(&ret, accessMask | SYNCHRONIZE, - &attributes, &statusBlock, shareAccess, - openOptions | FILE_SYNCHRONOUS_IO_NONALERT) != STATUS_SUCCESS) - { + if (usvfs::hook_NtOpenFile(&ret, accessMask, &attributes, &statusBlock, shareAccess, + openOptions) != STATUS_SUCCESS) { return INVALID_HANDLE_VALUE; } @@ -304,46 +315,37 @@ HANDLE hooked_NtOpenFile(LPCWSTR path, ACCESS_MASK accessMask, ULONG shareAccess TEST_F(USVFSTest, NtQueryDirectoryFileRegularFile) { auto params = defaultUsvfsParams(); - std::unique_ptr ctx(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); + std::unique_ptr ctx( + usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - HANDLE hdl = hooked_NtOpenFile( - L"C:\\" - , FILE_GENERIC_READ - , FILE_SHARE_READ | FILE_SHARE_WRITE - , OPEN_EXISTING); + HANDLE hdl = + hooked_NtOpenFile(L"C:\\", FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); ASSERT_NE(INVALID_HANDLE_VALUE, hdl); IO_STATUS_BLOCK status; char buffer[1024]; - usvfs::hook_NtQueryDirectoryFile(hdl - , nullptr - , nullptr - , nullptr - , &status - , buffer - , 1024 - , FileDirectoryInformation - , TRUE - , nullptr - , TRUE); + usvfs::hook_NtQueryDirectoryFile(hdl, nullptr, nullptr, nullptr, &status, buffer, + 1024, FileDirectoryInformation, TRUE, nullptr, TRUE); ASSERT_EQ(STATUS_SUCCESS, status.Status); + + usvfs::hook_NtClose(hdl); } TEST_F(USVFSTest, NtQueryDirectoryFileFindsVirtualFile) { auto params = defaultUsvfsParams(); - std::unique_ptr ctx(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer &tree = ctx->redirectionTable(); + std::unique_ptr ctx( + usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); + usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable(); tree.addFile(L"C:\\np.exe", usvfs::RedirectionDataLocal(REAL_FILEA)); - HANDLE hdl = hooked_NtOpenFile( - L"C:\\" - , FILE_GENERIC_READ - , FILE_SHARE_READ | FILE_SHARE_WRITE - , OPEN_EXISTING); + HANDLE hdl = + hooked_NtOpenFile(L"C:\\", FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); ASSERT_NE(INVALID_HANDLE_VALUE, hdl); IO_STATUS_BLOCK status; @@ -351,32 +353,149 @@ TEST_F(USVFSTest, NtQueryDirectoryFileFindsVirtualFile) usvfs::UnicodeString fileName(L"np.exe"); - usvfs::hook_NtQueryDirectoryFile(hdl - , nullptr - , nullptr - , nullptr - , &status - , buffer - , 1024 - , FileDirectoryInformation - , TRUE - , static_cast(fileName) - , TRUE); - - FILE_DIRECTORY_INFORMATION *info = reinterpret_cast(buffer); + usvfs::hook_NtQueryDirectoryFile(hdl, nullptr, nullptr, nullptr, &status, buffer, + 1024, FileDirectoryInformation, TRUE, + static_cast(fileName), TRUE); + + FILE_DIRECTORY_INFORMATION* info = + reinterpret_cast(buffer); ASSERT_EQ(STATUS_SUCCESS, status.Status); ASSERT_EQ(0, wcscmp(info->FileName, L"np.exe")); + + usvfs::hook_NtClose(hdl); +} + +TEST_F(USVFSTest, NtQueryObjectVirtualFile) +{ + std::wstring c_drive_device; + { + // find the device path for C: + HANDLE hdl = ::CreateFileW( + L"C:\\", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nullptr); + ASSERT_NE(INVALID_HANDLE_VALUE, hdl); + + char buffer[2048]; + ASSERT_EQ(STATUS_SUCCESS, ::NtQueryObject(hdl, ObjectNameInformation, buffer, + sizeof(buffer), nullptr)); + + OBJECT_NAME_INFORMATION* information = + reinterpret_cast(buffer); + + c_drive_device = + std::wstring(information->Name.Buffer, information->Name.Length / 2); + + ::CloseHandle(hdl); + } + + auto params = defaultUsvfsParams(); + std::unique_ptr ctx( + usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); + usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable(); + + tree.addFile(L"C:\\np.exe", usvfs::RedirectionDataLocal(REAL_FILEA)); + + HANDLE hdl = hooked_NtOpenFile(L"C:\\np.exe", FILE_GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); + ASSERT_NE(INVALID_HANDLE_VALUE, hdl) << "last error=" << ::GetLastError(); + + { + char buffer[1024]; + IO_STATUS_BLOCK status; + const auto res = usvfs::hook_NtQueryInformationFile( + hdl, &status, buffer, sizeof(buffer), FileNameInformation); + ASSERT_EQ(STATUS_SUCCESS, status.Status); + + FILE_NAME_INFORMATION* fileNameInfo = + reinterpret_cast(buffer); + ASSERT_EQ(L"\\np.exe", std::wstring(fileNameInfo->FileName, fileNameInfo->FileNameLength / 2)); + } + + { + char buffer[1024]; + IO_STATUS_BLOCK status; + const auto res = usvfs::hook_NtQueryInformationFile( + hdl, &status, buffer, sizeof(buffer), FileNormalizedNameInformation); + ASSERT_EQ(STATUS_SUCCESS, res); + ASSERT_EQ(STATUS_SUCCESS, status.Status); + ASSERT_EQ(sizeof(ULONG) + 7 * 2, status.Information); + + FILE_NAME_INFORMATION* fileNameInfo = + reinterpret_cast(buffer); + ASSERT_EQ(L"\\np.exe", std::wstring(fileNameInfo->FileName, fileNameInfo->FileNameLength / 2)); + } + + // buffer of size should be too small for the original path (\Windows\notepad.exe) + // but not for \np.exe + { + // the required size should be sizeof(ULONG) + 7 * 2 but apparently that is + // not enough for the CI so using 16 * 2 which should be large enough for + // the hooked version, but still too short for the non-hooked one + char buffer[sizeof(ULONG) + 7 * 2]; + IO_STATUS_BLOCK status; + NTSTATUS res; + + res = ::NtQueryInformationFile( + hdl, &status, buffer, sizeof(buffer), FileNameInformation); + ASSERT_EQ(STATUS_BUFFER_OVERFLOW, res); + ASSERT_EQ(STATUS_BUFFER_OVERFLOW, status.Status); + + res = usvfs::hook_NtQueryInformationFile( + hdl, &status, buffer, sizeof(buffer), FileNameInformation); + ASSERT_EQ(STATUS_SUCCESS, res); + ASSERT_EQ(STATUS_SUCCESS, status.Status); + ASSERT_EQ(sizeof(ULONG) + 7 * 2, status.Information); + + FILE_NAME_INFORMATION* fileNameInfo = + reinterpret_cast(buffer); + ASSERT_EQ(L"\\np.exe", std::wstring(fileNameInfo->FileName, fileNameInfo->FileNameLength / 2)); + } + + { + char buffer[2048]; + const auto res = usvfs::hook_NtQueryObject(hdl, ObjectNameInformation, buffer, + sizeof(buffer), nullptr); + ASSERT_EQ(STATUS_SUCCESS, res); + + OBJECT_NAME_INFORMATION* information = + reinterpret_cast(buffer); + ASSERT_EQ(c_drive_device + L"np.exe", + std::wstring(information->Name.Buffer, + information->Name.Length / sizeof(wchar_t))); + } + + { + // expected length is sizeof struct + size of path (in bytes), including the + // null-character + const ULONG expectedLength = + sizeof(OBJECT_NAME_INFORMATION) + c_drive_device.size() * 2 + 12 + 2; + ULONG requiredLength; + NTSTATUS res; + char buffer[2048]; + + res = usvfs::hook_NtQueryObject(hdl, ObjectNameInformation, buffer, sizeof(OBJECT_NAME_INFORMATION) - 1, &requiredLength); + ASSERT_EQ(STATUS_INFO_LENGTH_MISMATCH, res); + ASSERT_EQ(expectedLength, requiredLength); + + res = usvfs::hook_NtQueryObject(hdl, ObjectNameInformation, buffer, sizeof(OBJECT_NAME_INFORMATION), &requiredLength); + ASSERT_EQ(STATUS_BUFFER_OVERFLOW, res); + ASSERT_EQ(expectedLength, requiredLength); + } + + usvfs::hook_NtClose(hdl); } TEST_F(USVFSTestAuto, CannotCreateLinkToFileInNonexistantDirectory) { - ASSERT_EQ(FALSE, usvfsVirtualLinkFile(REAL_FILEW, L"c:/this_directory_shouldnt_exist/np.exe", FALSE)); + ASSERT_EQ(FALSE, usvfsVirtualLinkFile( + REAL_FILEW, L"c:/this_directory_shouldnt_exist/np.exe", FALSE)); } TEST_F(USVFSTestAuto, CanCreateMultipleLinks) { - static LPCWSTR outFile = LR"(C:\np.exe)"; - static LPCWSTR outDir = LR"(C:\logs)"; + static LPCWSTR outFile = LR"(C:\np.exe)"; + static LPCWSTR outDir = LR"(C:\logs)"; static LPCWSTR outDirCanonizeTest = LR"(C:\.\not/../logs\.\a\.\b\.\c\..\.\..\.\..\)"; ASSERT_EQ(TRUE, usvfsVirtualLinkFile(REAL_FILEW, outFile, 0)); ASSERT_EQ(TRUE, usvfsVirtualLinkDirectoryStatic(REAL_DIRW, outDir, 0)); @@ -385,22 +504,25 @@ TEST_F(USVFSTestAuto, CanCreateMultipleLinks) ASSERT_NE(INVALID_FILE_ATTRIBUTES, usvfs::hook_GetFileAttributesW(outFile)); ASSERT_NE(INVALID_FILE_ATTRIBUTES, usvfs::hook_GetFileAttributesW(outDir)); ASSERT_EQ(0UL, usvfs::hook_GetFileAttributesW(outFile) & FILE_ATTRIBUTE_DIRECTORY); - ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDir) & FILE_ATTRIBUTE_DIRECTORY); - ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDirCanonizeTest) & FILE_ATTRIBUTE_DIRECTORY); + ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDir) & FILE_ATTRIBUTE_DIRECTORY); + ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDirCanonizeTest) & + FILE_ATTRIBUTE_DIRECTORY); } -int main(int argc, char **argv) { +int main(int argc, char** argv) +{ using namespace test; auto dllPath = path_of_usvfs_lib(platform_dependant_executable("usvfs", "dll")); ScopedLoadLibrary loadDll(dllPath.c_str()); if (!loadDll) { - std::wcerr << L"failed to load usvfs dll: " << dllPath.c_str() << L", " << GetLastError() << std::endl; + std::wcerr << L"failed to load usvfs dll: " << dllPath.c_str() << L", " + << GetLastError() << std::endl; return 1; } - // note: this makes the logger available only to functions statically linked to the test binary, not those - // called in the dll + // note: this makes the logger available only to functions statically linked to the + // test binary, not those called in the dll auto logger = spdlog::stdout_logger_mt("usvfs"); logger->set_level(spdlog::level::warn); testing::InitGoogleTest(&argc, argv); diff --git a/test/usvfs_global_test_runner/CMakeLists.txt b/test/usvfs_global_test_runner/CMakeLists.txt new file mode 100644 index 0000000..0e338a5 --- /dev/null +++ b/test/usvfs_global_test_runner/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.16) + +find_package(GTest CONFIG REQUIRED) + +# 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) + +# 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/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp b/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp new file mode 100644 index 0000000..7de2f60 --- /dev/null +++ b/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp @@ -0,0 +1,162 @@ +#include + +#define WIN32_LEAN_AND_MEAN +#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")); + + std::filesystem::path dataPathFromHandle; + { + const auto doc_txt = data / "docs" / "doc.txt"; + 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))); + } +} + +// 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(); +} \ No newline at end of file diff --git a/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp b/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp new file mode 100644 index 0000000..4f6e672 --- /dev/null +++ b/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp @@ -0,0 +1,205 @@ +#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/test/usvfs_global_test_runner/usvfs_global_test_fixture.h b/test/usvfs_global_test_runner/usvfs_global_test_fixture.h new file mode 100644 index 0000000..277e78e --- /dev/null +++ b/test/usvfs_global_test_runner/usvfs_global_test_fixture.h @@ -0,0 +1,82 @@ +#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"; +}; \ No newline at end of file diff --git a/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp b/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp new file mode 100644 index 0000000..afe219e --- /dev/null +++ b/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp @@ -0,0 +1,52 @@ +#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, 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(); +} diff --git a/test/usvfs_test_runner/usvfs_test/usvfs_test_base.cpp b/test/usvfs_test_runner/usvfs_test/usvfs_test_base.cpp index c887970..a2c2fb4 100644 --- a/test/usvfs_test_runner/usvfs_test/usvfs_test_base.cpp +++ b/test/usvfs_test_runner/usvfs_test/usvfs_test_base.cpp @@ -100,14 +100,11 @@ class usvfs_connector { using path = test::path; usvfs_connector(const usvfs_test_options& options) - : m_exit_future(m_exit_signal.get_future()) + : m_usvfs_log(test::ScopedFILE::open(options.usvfs_log, L"wt")), + m_exit_future(m_exit_signal.get_future()) { winapi::ex::wide::createPath(options.usvfs_log.parent_path().c_str()); - errno_t err = _wfopen_s(m_usvfs_log, options.usvfs_log.c_str(), L"wt"); - if (err || !m_usvfs_log) - throw_testWinFuncFailed("_wfopen_s", options.usvfs_log.string().c_str(), err); - std::wcout << "Connecting VFS..." << std::endl; std::unique_ptr parameters{ @@ -273,11 +270,7 @@ class mappings_reader mappings_list read(const path& mapfile) { - test::ScopedFILE map; - errno_t err = _wfopen_s(map, mapfile.c_str(), L"rt"); - if (err || !map) - throw_testWinFuncFailed("_wfopen_s", mapfile.string().c_str(), err); - + const auto map = test::ScopedFILE::open(mapfile, L"rt"); mappings_list mappings; char line[1024]; @@ -564,10 +557,7 @@ bool usvfs_test_base::recursive_compare_dirs(path rel_path, path gold_base, path test::ScopedFILE usvfs_test_base::output() { - test::ScopedFILE log; - errno_t err = _wfopen_s(log, m_o.output.c_str(), m_clean_output ? L"wt" : L"at"); - if (err || !log) - throw_testWinFuncFailed("_wfopen_s", m_o.output.string().c_str(), err); + auto log = test::ScopedFILE::open(m_o.output, m_clean_output ? L"wt" : L"at"); m_clean_output = false; return log; } @@ -576,8 +566,8 @@ void usvfs_test_base::clean_output() { using namespace std; - test::ScopedFILE in; - errno_t err = _wfopen_s(in, m_o.output.c_str(), L"rt"); + errno_t err; + auto in = test::ScopedFILE::open(m_o.output, L"rt", err); if (err == ENOENT) { wcerr << L"warning: no " << m_o.output << L" to clean." << endl; return; @@ -585,14 +575,11 @@ void usvfs_test_base::clean_output() else if (err || !in) throw_testWinFuncFailed("_wfopen_s", m_o.output.string().c_str(), err); - test::ScopedFILE out; path clean = m_o.output.parent_path() / m_o.output.stem(); clean += OUTPUT_CLEAN_SUFFIX; clean += m_o.output.extension(); - err = _wfopen_s(out, clean.c_str(), L"wt"); - if (err || !in) - throw_testWinFuncFailed("_wfopen_s", clean.string().c_str(), err); + auto out = test::ScopedFILE::open(clean, L"wt"); wcout << L"Cleaning " << m_o.output << " to " << clean << endl; char line[1024]; diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json index 7990f77..ced1af0 100644 --- a/vcpkg-configuration.json +++ b/vcpkg-configuration.json @@ -2,7 +2,7 @@ "default-registry": { "kind": "git", "repository": "https://github.com/Microsoft/vcpkg", - "baseline": "f61a294e765b257926ae9e9d85f96468a0af74e7" + "baseline": "f06267da58f3c06b3a1aff9cbf5c2906e595ebc9" }, "registries": [ { diff --git a/vcpkg.json b/vcpkg.json index 818fe75..a0f9d7f 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -15,7 +15,6 @@ "boost-thread", "boost-predef" ], - "builtin-baseline": "f61a294e765b257926ae9e9d85f96468a0af74e7", "features": { "testing": { "description": "Build USVFS tests.", diff --git a/vsbuild/common.props b/vsbuild/common.props index 3240495..6cda5ee 100644 --- a/vsbuild/common.props +++ b/vsbuild/common.props @@ -12,6 +12,7 @@ false true _WINDOWS;NDEBUG;BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE;BOOST_INTERPROCESS_SHARED_DIR_FUNC;%(PreprocessorDefinitions) + stdcpp20 comsuppw.lib;%(AdditionalDependencies) diff --git a/vsbuild/shared_test.vcxproj b/vsbuild/shared_test.vcxproj index f02da6f..1417d22 100644 --- a/vsbuild/shared_test.vcxproj +++ b/vsbuild/shared_test.vcxproj @@ -9,14 +9,6 @@ DebugTest x64 - - Debug - Win32 - - - Debug - x64 - ReleaseTest Win32 @@ -25,14 +17,6 @@ ReleaseTest x64 - - Release - Win32 - - - Release - x64 - 15.0 @@ -47,19 +31,6 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application false @@ -73,19 +44,6 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application false @@ -94,165 +52,27 @@ Unicode - - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - + Disabled - stdcpp20 Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) MultiThreadedDebugDLL Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - - - - - MaxSpeed - true - true - stdcpp20 - NotUsing - pch.h - MultiThreadedDLL - $(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - - - true - true - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - + MaxSpeed true true - stdcpp20 - $(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) pch.h MultiThreadedDLL @@ -260,7 +80,6 @@ true true Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib diff --git a/vsbuild/test_common.props b/vsbuild/test_common.props index adbb8a6..2812bdc 100644 --- a/vsbuild/test_common.props +++ b/vsbuild/test_common.props @@ -11,11 +11,15 @@ %(AdditionalIncludeDirectories);$(GTEST_PATH)\googletest\include - UNITTEST;%(PreprocessorDefinitions) - stdcpp17 + USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - %(AdditionalLibraryDirectories);$(GTEST_PATH)\build$(PLATFORM_32)\lib + + %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib + + + %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib + gtest.lib;%(AdditionalDependencies) diff --git a/vsbuild/test_file_operations.vcxproj b/vsbuild/test_file_operations.vcxproj index 832cd49..85a616c 100644 --- a/vsbuild/test_file_operations.vcxproj +++ b/vsbuild/test_file_operations.vcxproj @@ -1,5 +1,6 @@ + DebugTest @@ -9,14 +10,6 @@ DebugTest x64 - - Debug - Win32 - - - Debug - x64 - ReleaseTest Win32 @@ -25,15 +18,8 @@ ReleaseTest x64 - - Release - Win32 - - - Release - x64 - + 15.0 {0B2FF5AF-8580-458C-8EF4-10E6B6398D3A} @@ -47,19 +33,6 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application false @@ -73,19 +46,6 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application false @@ -94,171 +54,35 @@ Unicode - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - + - Console ntdll.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - + Disabled - stdcpp20 Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) MultiThreadedDebugDLL Console - ntdll.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - ntdll.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - ntdll.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - + MaxSpeed true true - stdcpp20 - - - true - true - Console - ntdll.lib;%(AdditionalDependencies) - - - - - MaxSpeed - true - true - stdcpp20 - $(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - pch.h - MultiThreadedDLL - - - true - true - Console - ntdll.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - ntdll.lib;%(AdditionalDependencies) - - - - - MaxSpeed - true - true - stdcpp20 - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - $(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) pch.h MultiThreadedDLL @@ -266,8 +90,6 @@ true true Console - ntdll.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib diff --git a/vsbuild/testinject_bin.vcxproj b/vsbuild/testinject_bin.vcxproj index 9447615..a7810e9 100644 --- a/vsbuild/testinject_bin.vcxproj +++ b/vsbuild/testinject_bin.vcxproj @@ -9,14 +9,6 @@ DebugTest x64 - - Debug - Win32 - - - Debug - x64 - ReleaseTest Win32 @@ -25,14 +17,6 @@ ReleaseTest x64 - - Release - Win32 - - - Release - x64 - 15.0 @@ -94,168 +78,38 @@ Unicode - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - + Disabled - stdcpp20 Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) MultiThreadedDebugDLL Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - + MaxSpeed true true - stdcpp20 - - - true - true - Console - - - - - MaxSpeed - true - true - stdcpp20 - MultiThreadedDLL - - - true - true - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - - - - - MaxSpeed - true - true - stdcpp20 + pch.h MultiThreadedDLL true true Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - + diff --git a/vsbuild/testinject_dll.vcxproj b/vsbuild/testinject_dll.vcxproj index e1a00cb..1173c9f 100644 --- a/vsbuild/testinject_dll.vcxproj +++ b/vsbuild/testinject_dll.vcxproj @@ -9,14 +9,6 @@ DebugTest x64 - - Debug - Win32 - - - Debug - x64 - ReleaseTest Win32 @@ -25,14 +17,6 @@ ReleaseTest x64 - - Release - Win32 - - - Release - x64 - 15.0 @@ -47,19 +31,6 @@ v143 Unicode - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - DynamicLibrary false @@ -73,19 +44,6 @@ v143 Unicode - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - DynamicLibrary false @@ -94,158 +52,36 @@ Unicode - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - + Disabled - stdcpp20 Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) MultiThreadedDebugDLL - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib + Console - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - - - - - MaxSpeed - true - true - stdcpp20 - MultiThreadedDLL - - - true - true - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - - - + MaxSpeed true true - stdcpp20 - - - true - true - - - - - MaxSpeed - true - true - stdcpp20 + pch.h MultiThreadedDLL true true - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib + Console diff --git a/vsbuild/thooklib_test.vcxproj b/vsbuild/thooklib_test.vcxproj index 7e0fcf7..aac7ff6 100644 --- a/vsbuild/thooklib_test.vcxproj +++ b/vsbuild/thooklib_test.vcxproj @@ -9,14 +9,6 @@ DebugTest x64 - - Debug - Win32 - - - Debug - x64 - ReleaseTest Win32 @@ -25,14 +17,6 @@ ReleaseTest x64 - - Release - Win32 - - - Release - x64 - 15.0 @@ -98,163 +82,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + Disabled - stdcpp20 Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) MultiThreadedDebugDLL Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - stdcpp20 - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - - - + MaxSpeed true true - stdcpp20 - $(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - MultiThreadedDLL - - - true - true - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - - - - - MaxSpeed - true - true - stdcpp20 + pch.h MultiThreadedDLL true true Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib diff --git a/vsbuild/tinjectlib_test.vcxproj b/vsbuild/tinjectlib_test.vcxproj index ebe4c57..81d0732 100644 --- a/vsbuild/tinjectlib_test.vcxproj +++ b/vsbuild/tinjectlib_test.vcxproj @@ -9,14 +9,6 @@ DebugTest x64 - - Debug - Win32 - - - Debug - x64 - ReleaseTest Win32 @@ -25,14 +17,6 @@ ReleaseTest x64 - - Release - Win32 - - - Release - x64 - 15.0 @@ -94,166 +78,36 @@ Unicode - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Disabled - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - + Disabled Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) MultiThreadedDebugDLL - stdcpp20 Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - Disabled - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - - - - - MaxSpeed - true - true - stdcpp20 - MultiThreadedDLL - - - true - true - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - - - + MaxSpeed true true - stdcpp20 + pch.h MultiThreadedDLL true true Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib diff --git a/vsbuild/tvfs_test.vcxproj b/vsbuild/tvfs_test.vcxproj index 2eb330c..ef57cc5 100644 --- a/vsbuild/tvfs_test.vcxproj +++ b/vsbuild/tvfs_test.vcxproj @@ -1,5 +1,6 @@ - + DebugTest @@ -9,14 +10,6 @@ DebugTest x64 - - Debug - Win32 - - - Debug - x64 - ReleaseTest Win32 @@ -25,14 +18,6 @@ ReleaseTest x64 - - Release - Win32 - - - Release - x64 - 15.0 @@ -47,20 +32,8 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - + Application false v143 @@ -73,19 +46,6 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application false @@ -94,182 +54,40 @@ Unicode - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - + Disabled - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h Default - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) pch.h - Default - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) MultiThreadedDebugDLL - stdcpp20 - - - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - Default - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - Default - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - MaxSpeed - true - true ..\src\usvfs_dll;%(AdditionalIncludeDirectories) - stdcpp20 - true - true Console usvfs_$(PlatformShortName).dll - + MaxSpeed true true - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - stdcpp20 - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) pch.h MultiThreadedDLL - - - true - true - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - - - - - MaxSpeed - true - true ..\src\usvfs_dll;%(AdditionalIncludeDirectories) - stdcpp20 - - - true - true - Console - usvfs_$(PlatformShortName).dll - - - - - MaxSpeed - true - true - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - stdcpp20 - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - pch.h - MultiThreadedDLL true true Console usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib @@ -289,4 +107,4 @@ - \ No newline at end of file + diff --git a/vsbuild/usvfs.sln b/vsbuild/usvfs.sln index ab43584..0703e1a 100644 --- a/vsbuild/usvfs.sln +++ b/vsbuild/usvfs.sln @@ -85,6 +85,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "usvfs_test_runner", "usvfs_ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "injection", "injection", "{60262FE7-A84B-4B73-BE5D-0B5886A28BB8}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "usvfs_global_test", "usvfs_global_test.vcxproj", "{4A355317-3634-4C98-AFB1-8BA718D54727}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "usvfs_global_test_runner", "usvfs_global_test_runner.vcxproj", "{B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -349,6 +353,30 @@ Global {0452CB4D-A906-4717-94AC-7A450E479BE1}.ReleaseTest|x64.Build.0 = ReleaseTest|x64 {0452CB4D-A906-4717-94AC-7A450E479BE1}.ReleaseTest|x86.ActiveCfg = ReleaseTest|Win32 {0452CB4D-A906-4717-94AC-7A450E479BE1}.ReleaseTest|x86.Build.0 = ReleaseTest|Win32 + {4A355317-3634-4C98-AFB1-8BA718D54727}.Debug|x64.ActiveCfg = DebugTest|x64 + {4A355317-3634-4C98-AFB1-8BA718D54727}.Debug|x86.ActiveCfg = DebugTest|Win32 + {4A355317-3634-4C98-AFB1-8BA718D54727}.DebugTest|x64.ActiveCfg = DebugTest|x64 + {4A355317-3634-4C98-AFB1-8BA718D54727}.DebugTest|x64.Build.0 = DebugTest|x64 + {4A355317-3634-4C98-AFB1-8BA718D54727}.DebugTest|x86.ActiveCfg = DebugTest|Win32 + {4A355317-3634-4C98-AFB1-8BA718D54727}.DebugTest|x86.Build.0 = DebugTest|Win32 + {4A355317-3634-4C98-AFB1-8BA718D54727}.Release|x64.ActiveCfg = ReleaseTest|x64 + {4A355317-3634-4C98-AFB1-8BA718D54727}.Release|x86.ActiveCfg = ReleaseTest|Win32 + {4A355317-3634-4C98-AFB1-8BA718D54727}.ReleaseTest|x64.ActiveCfg = ReleaseTest|x64 + {4A355317-3634-4C98-AFB1-8BA718D54727}.ReleaseTest|x64.Build.0 = ReleaseTest|x64 + {4A355317-3634-4C98-AFB1-8BA718D54727}.ReleaseTest|x86.ActiveCfg = ReleaseTest|Win32 + {4A355317-3634-4C98-AFB1-8BA718D54727}.ReleaseTest|x86.Build.0 = ReleaseTest|Win32 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.Debug|x64.ActiveCfg = DebugTest|x64 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.Debug|x86.ActiveCfg = DebugTest|Win32 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.DebugTest|x64.ActiveCfg = DebugTest|x64 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.DebugTest|x64.Build.0 = DebugTest|x64 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.DebugTest|x86.ActiveCfg = DebugTest|Win32 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.DebugTest|x86.Build.0 = DebugTest|Win32 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.Release|x64.ActiveCfg = ReleaseTest|x64 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.Release|x86.ActiveCfg = ReleaseTest|Win32 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.ReleaseTest|x64.ActiveCfg = ReleaseTest|x64 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.ReleaseTest|x64.Build.0 = ReleaseTest|x64 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.ReleaseTest|x86.ActiveCfg = ReleaseTest|Win32 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6}.ReleaseTest|x86.Build.0 = ReleaseTest|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -369,6 +397,8 @@ Global {0B2FF5AF-8580-458C-8EF4-10E6B6398D3A} = {EDA9B67D-1E64-4CAB-8391-10712538C821} {CEAF96EC-0BAA-4C02-B91B-5C4C49B5455B} = {EDA9B67D-1E64-4CAB-8391-10712538C821} {0452CB4D-A906-4717-94AC-7A450E479BE1} = {EDA9B67D-1E64-4CAB-8391-10712538C821} + {4A355317-3634-4C98-AFB1-8BA718D54727} = {EDA9B67D-1E64-4CAB-8391-10712538C821} + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6} = {EDA9B67D-1E64-4CAB-8391-10712538C821} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C416F0AE-21DB-4936-84B9-065D8AEC1597} diff --git a/vsbuild/usvfs_global_test.vcxproj b/vsbuild/usvfs_global_test.vcxproj new file mode 100644 index 0000000..3269163 --- /dev/null +++ b/vsbuild/usvfs_global_test.vcxproj @@ -0,0 +1,97 @@ + + + + + DebugTest + Win32 + + + ReleaseTest + Win32 + + + DebugTest + x64 + + + ReleaseTest + x64 + + + + + + + + + + + 17.0 + Win32Proj + {4a355317-3634-4c98-afb1-8ba718d54727} + usvfsglobaltest + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + Disabled + Default + pch.h + MultiThreadedDebugDLL + + + Console + + + + + MaxSpeed + true + true + pch.h + MultiThreadedDLL + + + true + true + Console + gtest.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/vsbuild/usvfs_global_test.vcxproj.filters b/vsbuild/usvfs_global_test.vcxproj.filters new file mode 100644 index 0000000..9b5a872 --- /dev/null +++ b/vsbuild/usvfs_global_test.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {7d4fa8c9-4c03-4eaf-82d9-2d53fc06540a} + + + + + src + + + src + + + + + inc + + + \ No newline at end of file diff --git a/vsbuild/usvfs_global_test_runner.vcxproj b/vsbuild/usvfs_global_test_runner.vcxproj new file mode 100644 index 0000000..e08d185 --- /dev/null +++ b/vsbuild/usvfs_global_test_runner.vcxproj @@ -0,0 +1,116 @@ + + + + + DebugTest + Win32 + + + ReleaseTest + Win32 + + + DebugTest + x64 + + + ReleaseTest + x64 + + + + + + + + + 17.0 + Win32Proj + usvfsglobaltestrunner + 10.0 + {B785B3FB-2F86-4042-94BB-EEE4C30D4EA6} + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + Disabled + Default + pch.h + MultiThreadedDebugDLL + ..\src\usvfs_dll;%(AdditionalIncludeDirectories) + + + Console + usvfs_$(PlatformShortName).dll + + + + + MaxSpeed + true + true + pch.h + MultiThreadedDLL + ..\src\usvfs_dll;%(AdditionalIncludeDirectories) + + + true + true + Console + usvfs_$(PlatformShortName).dll + + + + + {2bb3300b-f08a-4063-95c4-8a0fadae6c51} + + + {cdaddec0-b72b-43b4-831f-72ba85b72805} + + + {562b0058-4701-4284-8b40-d87648a3f64c} + + + {4a355317-3634-4c98-afb1-8ba718d54727} + + + + + + + + + + \ No newline at end of file diff --git a/vsbuild/usvfs_global_test_runner.vcxproj.filters b/vsbuild/usvfs_global_test_runner.vcxproj.filters new file mode 100644 index 0000000..a5640a9 --- /dev/null +++ b/vsbuild/usvfs_global_test_runner.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {87a9ca7b-9f75-4770-9cab-e7e1d149c76b} + + + {ca9cea9d-9155-45c4-a9bf-ed98b3f28105} + + + + + src + + + src + + + src + + + + + inc + + + inc + + + \ No newline at end of file diff --git a/vsbuild/usvfs_test.vcxproj b/vsbuild/usvfs_test.vcxproj index 0de3921..5b0f24a 100644 --- a/vsbuild/usvfs_test.vcxproj +++ b/vsbuild/usvfs_test.vcxproj @@ -9,14 +9,6 @@ DebugTest x64 - - Debug - Win32 - - - Debug - x64 - ReleaseTest Win32 @@ -25,14 +17,6 @@ ReleaseTest x64 - - Release - Win32 - - - Release - x64 - 15.0 @@ -47,19 +31,6 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application false @@ -73,19 +44,6 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application false @@ -94,183 +52,40 @@ Unicode - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + Disabled - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) Default pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) MultiThreadedDebugDLL - stdcpp20 - - - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - Default - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - Default - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - Default - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - MaxSpeed - true - true ..\src\usvfs_dll;%(AdditionalIncludeDirectories) - stdcpp20 - true - true Console usvfs_$(PlatformShortName).dll - + MaxSpeed true true - ..\src\usvfs_dll;%(AdditionalIncludeDirectories) - stdcpp20 - NotUsing - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) pch.h MultiThreadedDLL - - - true - true - Console - usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - - - - - MaxSpeed - true - true ..\src\usvfs_dll;%(AdditionalIncludeDirectories) - stdcpp20 - - - true - true - Console - usvfs_$(PlatformShortName).dll - - - - - MaxSpeed - true - true - ..\src\usvfs_dll;%(AdditionalIncludeDirectories) - stdcpp20 - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - pch.h - MultiThreadedDLL true true Console usvfs_$(PlatformShortName).dll - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib diff --git a/vsbuild/usvfs_test_runner.vcxproj b/vsbuild/usvfs_test_runner.vcxproj index 8a54caa..f11dbd2 100644 --- a/vsbuild/usvfs_test_runner.vcxproj +++ b/vsbuild/usvfs_test_runner.vcxproj @@ -9,14 +9,6 @@ DebugTest x64 - - Debug - Win32 - - - Debug - x64 - ReleaseTest Win32 @@ -25,14 +17,6 @@ ReleaseTest x64 - - Release - Win32 - - - Release - x64 - 15.0 @@ -47,19 +31,6 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application false @@ -73,19 +44,6 @@ v143 Unicode - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application false @@ -94,163 +52,29 @@ Unicode - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - Disabled - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - + Disabled Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) MultiThreadedDebugDLL - stdcpp20 Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - Disabled - Default - ..\src\usvfs_dll;$(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - pch.h - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - stdcpp20 - - - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Debug\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - - - - - MaxSpeed - true - true - stdcpp20 - $(GTEST_PATH)\googletest\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) - pch.h - MultiThreadedDLL - - - true - true - Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib - - - - - MaxSpeed - true - true - stdcpp20 - - - true - true - Console - + MaxSpeed true true - stdcpp20 - USVFS_BUILD_TESTS;UNITTEST;%(PreprocessorDefinitions) pch.h MultiThreadedDLL @@ -258,7 +82,6 @@ true true Console - %(AdditionalLibraryDirectories);$(GTEST_LIBDIR);$(GTEST_PATH)\build\$(Platform)\Release\lib