diff --git a/.github/workflows/build-test-package-linux.yml b/.github/workflows/build-test-package-linux.yml index 99c2121b..97f3584c 100644 --- a/.github/workflows/build-test-package-linux.yml +++ b/.github/workflows/build-test-package-linux.yml @@ -47,11 +47,15 @@ jobs: ./package-release.sh "${{ env.VERSION }}" build --enable-tests mkdir "${HOME}/tests-prefix" WINEPREFIX="${HOME}/tests-prefix" WINEDEBUG=-all wine \ - "./build/dxvk-nvapi-${{ env.VERSION }}/x64/nvapi64-tests.exe" [@unit-tests] -o tests.log + "./build/dxvk-nvapi-${{ env.VERSION }}/x64/nvapi64-tests.exe" [@unit-tests] -o nvapi64-tests.log + WINEPREFIX="${HOME}/tests-prefix" WINEDEBUG=-all wine \ + "./build/dxvk-nvapi-${{ env.VERSION }}/x64/nvofapi64-tests.exe" [@unit-tests] -o nvofapi64-tests.log - name: Present tests results if: success() || failure() - run: if [ -e tests.log ]; then cat tests.log; fi + run: | + if [ -e nvapi64-tests.log ]; then cat nvapi64-tests.log; fi + if [ -e nvofapi64-tests.log ]; then cat nvofapi64-tests.log; fi - name: Create tarball if: github.ref_type == 'tag' diff --git a/.github/workflows/build-test-windows.yml b/.github/workflows/build-test-windows.yml index 8ec4fe32..23bfa7e3 100644 --- a/.github/workflows/build-test-windows.yml +++ b/.github/workflows/build-test-windows.yml @@ -54,6 +54,7 @@ jobs: shell: pwsh run: | & .\build-msvc-x64\tests\nvapi64-tests.exe [@unit-tests] + & .\build-msvc-x64\tests\nvofapi64-tests.exe [@unit-tests] - name: Build MSVC layer shell: pwsh diff --git a/tests/meson.build b/tests/meson.build index e2e84c82..4a82addf 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -52,3 +52,32 @@ nvapi_exe = executable(target_name, [ nvapi_src, catch2_src, nvapi_tests_src, dx dependencies : [ lib_dxgi, lib_d3d11, lib_version ], include_directories : [ nvapi_headers, vk_headers ], install : true) + +nvofapi_src = files([ + '../src/util/util_string.cpp', + '../src/util/util_env.cpp', + '../src/util/util_log.cpp', + '../src/shared/resource_factory.cpp', + '../src/shared/vk.cpp', + '../src/nvofapi/nvofapi_image.cpp', + '../src/nvofapi/nvofapi_instance.cpp', + '../src/nvofapi/nvofapi_d3d12_instance.cpp', + '../src/nvofapi/nvofapi_vk_instance.cpp', + '../src/nvofapi_globals.cpp', + '../src/nvofapi.cpp', + '../src/nvofapi_d3d12.cpp', + '../src/nvofapi_vk.cpp', +]) + +nvofapi_tests_src = files([ + 'nvofapi/main.cpp', + 'nvofapi/nvofapi_d3d12.cpp', + 'nvofapi/nvofapi_vk.cpp', +]) + +target_name = 'nvofapi'+target_suffix+'-tests' +nvofapi_exe = executable(target_name, [ nvofapi_src, catch2_src, nvofapi_tests_src, dxvk_nvapi_version ], + cpp_args : '-DDXVK_NVAPI_TARGET_NAME="'+target_name+'"', + dependencies : [ lib_version ], + include_directories : [ nvapi_headers, vk_headers ], + install : true) diff --git a/tests/nvofapi/main.cpp b/tests/nvofapi/main.cpp new file mode 100644 index 00000000..30fef622 --- /dev/null +++ b/tests/nvofapi/main.cpp @@ -0,0 +1,8 @@ +#define CATCH_CONFIG_MAIN +#include "../../inc/catch_amalgamated.hpp" +#include "section_listener.h" + +CATCH_REGISTER_TAG_ALIAS("[@unit-tests]", "[d3d12],[vk]") +CATCH_REGISTER_TAG_ALIAS("[@all]", "[d3d12],[vk]") + +CATCH_REGISTER_LISTENER(SectionListener) diff --git a/tests/nvofapi/mock_factory.h b/tests/nvofapi/mock_factory.h new file mode 100644 index 00000000..9e58a6ac --- /dev/null +++ b/tests/nvofapi/mock_factory.h @@ -0,0 +1,21 @@ +#pragma once + +#include "nvofapi_tests_private.h" +#include "../../src/shared/resource_factory.h" +#include "nvofapi_vulkan_mocks.h" + +using namespace trompeloeil; + +class MockFactory final : public dxvk::ResourceFactory { + + public: + MockFactory(std::unique_ptr vkMock) + : m_vkMock(std::move(vkMock)) {}; + + std::unique_ptr CreateVulkan(const char*) override { + return std::move(m_vkMock); + } + + private: + std::unique_ptr m_vkMock; +}; diff --git a/tests/nvofapi/nvofapi_d3d12.cpp b/tests/nvofapi/nvofapi_d3d12.cpp new file mode 100644 index 00000000..5270cb8d --- /dev/null +++ b/tests/nvofapi/nvofapi_d3d12.cpp @@ -0,0 +1,32 @@ +#include "nvofapi_tests_private.h" +#include "nvofapi_vulkan_mocks.h" +#include "mock_factory.h" + +using namespace trompeloeil; + +TEST_CASE("D3D12 methods succeed", "[.d3d12]") { + auto vk = std::make_unique(); + auto vkDevice = std::make_unique(); + + SECTION("CreateInstanceD3D12 fails to initialize with major version other than 5") { + NV_OF_D3D12_API_FUNCTION_LIST functionList{}; + REQUIRE(NvOFAPICreateInstanceD3D12(0, &functionList) == NV_OF_ERR_INVALID_VERSION); + } + + SECTION("CreateInstanceD3D12 initializes") { + NV_OF_D3D12_API_FUNCTION_LIST functionList{}; + REQUIRE(NvOFAPICreateInstanceD3D12(80, &functionList) == NV_OF_SUCCESS); + + SECTION("CreateInstanceVk fails to initialize when Vulkan is not available") { + ALLOW_CALL(*vk, IsAvailable()).RETURN(false); + FORBID_CALL(*vk, GetInstanceProcAddr(_, _)); + FORBID_CALL(*vk, GetDeviceProcAddr(_, _)); + + resourceFactory = std::make_unique(std::move(vk)); + + // D3D12Vkd3dDeviceMock device; + // NvOFHandle hOFInstance; + // REQUIRE(functionList.nvCreateOpticalFlowD3D12(static_cast(&device), &hOFInstance) == NV_OF_ERR_GENERIC); + } + } +} diff --git a/tests/nvofapi/nvofapi_tests_private.h b/tests/nvofapi/nvofapi_tests_private.h new file mode 100644 index 00000000..69795d0a --- /dev/null +++ b/tests/nvofapi/nvofapi_tests_private.h @@ -0,0 +1,11 @@ +#pragma once + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif // defined(__GNUC__) || defined(__clang__) + +#include "../../src/nvofapi_private.h" +#include "../../src/nvofapi_globals.h" + +#include "../../inc/catch_amalgamated.hpp" +#include "../../inc/catch2/trompeloeil.hpp" diff --git a/tests/nvofapi/nvofapi_vk.cpp b/tests/nvofapi/nvofapi_vk.cpp new file mode 100644 index 00000000..4e39f4e4 --- /dev/null +++ b/tests/nvofapi/nvofapi_vk.cpp @@ -0,0 +1,48 @@ +#include "nvofapi_tests_private.h" +#include "nvofapi_vulkan_mocks.h" +#include "mock_factory.h" + +using namespace trompeloeil; + +TEST_CASE("Vk methods succeed", "[.vk]") { + + + SECTION("CreateInstanceVk fails to initialize with major version other than 5") { + NV_OF_VK_API_FUNCTION_LIST functionList{}; + REQUIRE(NvOFAPICreateInstanceVk(0, &functionList) == NV_OF_ERR_INVALID_VERSION); + } + + SECTION("CreateInstanceVk initializes") { + auto vk = std::make_unique(); + auto vkDevice = std::make_unique(); + + NV_OF_VK_API_FUNCTION_LIST functionList{}; + REQUIRE(NvOFAPICreateInstanceVk(80, &functionList) == NV_OF_SUCCESS); + + SECTION("CreateInstanceVk fails to initialize when Vulkan is not available") { + ALLOW_CALL(*vk, IsAvailable()).RETURN(false); + FORBID_CALL(*vk, GetInstanceProcAddr(_, _)); + FORBID_CALL(*vk, GetDeviceProcAddr(_, _)); + + resourceFactory = std::make_unique(std::move(vk)); + + VkInstance vkInstance{}; + VkPhysicalDevice vkPhysicalDevice{}; + NvOFHandle hOFInstance; + REQUIRE(functionList.nvCreateOpticalFlowVk(vkInstance, vkPhysicalDevice, reinterpret_cast(vkDevice.get()), &hOFInstance) == NV_OF_ERR_GENERIC); + } + + SECTION("CreateInstanceVk returns success") { + ALLOW_CALL(*vk, IsAvailable()).RETURN(true); + FORBID_CALL(*vk, GetInstanceProcAddr(_, _)); + FORBID_CALL(*vk, GetDeviceProcAddr(_, _)); + + resourceFactory = std::make_unique(std::move(vk)); + + VkInstance vkInstance{}; + VkPhysicalDevice vkPhysicalDevice{}; + NvOFHandle hOFInstance; + REQUIRE(functionList.nvCreateOpticalFlowVk(vkInstance, vkPhysicalDevice, reinterpret_cast(vkDevice.get()), &hOFInstance) == NV_OF_SUCCESS); + } + } +} diff --git a/tests/nvofapi/nvofapi_vulkan_mocks.h b/tests/nvofapi/nvofapi_vulkan_mocks.h new file mode 100644 index 00000000..9f65160e --- /dev/null +++ b/tests/nvofapi/nvofapi_vulkan_mocks.h @@ -0,0 +1,38 @@ +#pragma once + +#include "nvofapi_tests_private.h" +#include "../../src/shared/vk.h" + +using namespace trompeloeil; + +class VkDeviceMock { + MAKE_MOCK4(vkCreateSemaphore, VkResult(VkDevice, const VkSemaphoreCreateInfo*, const VkAllocationCallbacks*, VkSemaphore*)); + MAKE_MOCK3(vkDestroySemaphore, void(VkDevice, VkSemaphore, const VkAllocationCallbacks*)); + + static VkResult CreateSemaphore(VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore) { + return reinterpret_cast(device)->vkCreateSemaphore(device, pCreateInfo, pAllocator, pSemaphore); + } + static void DestroySemaphore(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator) { + reinterpret_cast(device)->vkDestroySemaphore(device, semaphore, pAllocator); + } +}; + +class VkQueueMock { + +}; + +class VkMock final : public mock_interface { + IMPLEMENT_CONST_MOCK0(IsAvailable); + IMPLEMENT_CONST_MOCK2(GetInstanceProcAddr); + IMPLEMENT_CONST_MOCK2(GetDeviceProcAddr); + IMPLEMENT_CONST_MOCK2(GetDeviceExtensions); + IMPLEMENT_CONST_MOCK3(GetPhysicalDeviceProperties2); + + [[nodiscard]] static std::array, 2> ConfigureDefaultPFN(VkMock& mock) { + return { + NAMED_ALLOW_CALL(mock, GetDeviceProcAddr(_, eq(std::string_view("vkCreateSemaphore")))) + .RETURN(reinterpret_cast(VkDeviceMock::CreateSemaphore)), + NAMED_ALLOW_CALL(mock, GetDeviceProcAddr(_, eq(std::string_view("vkDestroySemaphore")))) + .RETURN(reinterpret_cast(VkDeviceMock::DestroySemaphore))}; + } +}; diff --git a/tests/nvofapi/section_listener.h b/tests/nvofapi/section_listener.h new file mode 100644 index 00000000..8fea7838 --- /dev/null +++ b/tests/nvofapi/section_listener.h @@ -0,0 +1,13 @@ +#pragma once + +#include "nvofapi_tests_private.h" + +class SectionListener : public Catch::EventListenerBase { + + public: + using Catch::EventListenerBase::EventListenerBase; + + void sectionEnded(Catch::SectionStats const&) override { + resourceFactory.reset(); + } +};